Merge "Delete some ide autogen files"
diff --git a/Android.bp b/Android.bp
index 0277af0..1fbdc15 100644
--- a/Android.bp
+++ b/Android.bp
@@ -118,7 +118,6 @@
         ":libbluetooth-binder-aidl",
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
-        ":packagemanager_aidl",
         ":libupdate_engine_aidl",
         ":resourcemanager_aidl",
         ":storaged_aidl",
@@ -209,6 +208,7 @@
     name: "framework-internal-utils",
     static_libs: [
         "apex_aidl_interface-java",
+        "packagemanager_aidl-java",
         "framework-protos",
         "updatable-driver-protos",
         "ota_metadata_proto_java",
@@ -357,9 +357,6 @@
     srcs: [
         "core/java/android/net/annotations/PolicyDirection.java",
         "core/java/com/android/internal/util/HexDump.java",
-        "core/java/com/android/internal/util/IState.java",
-        "core/java/com/android/internal/util/State.java",
-        "core/java/com/android/internal/util/StateMachine.java",
         "core/java/com/android/internal/util/WakeupMessage.java",
         "services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
         "telephony/java/android/telephony/Annotation.java",
@@ -408,11 +405,9 @@
     srcs: [
         "core/java/android/content/pm/BaseParceledListSlice.java",
         "core/java/android/content/pm/ParceledListSlice.java",
-        "core/java/android/os/HandlerExecutor.java",
         "core/java/com/android/internal/util/AsyncChannel.java",
         "core/java/com/android/internal/util/AsyncService.java",
         "core/java/com/android/internal/util/Protocol.java",
-        "core/java/com/android/internal/util/Preconditions.java",
         "telephony/java/android/telephony/Annotation.java",
         ":net-utils-framework-wifi-common-srcs",
     ],
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 65a6547..1767678 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -77,22 +77,16 @@
         // Module sources
         ":art.module.public.api{.public.stubs.source}",
         ":conscrypt.module.public.api{.public.stubs.source}",
-        ":framework-connectivity-sources",
-        ":framework-mediaprovider-sources",
-        ":framework-permission-sources",
-        ":framework-sdkextensions-sources",
-        ":framework-statsd-sources",
-        ":framework-tethering-srcs",
-        ":framework-wifi-updatable-sources",
         ":i18n.module.public.api{.public.stubs.source}",
-        ":ike-srcs",
-        ":updatable-media-srcs",
 
         // No longer part of the stubs, but are included in the docs.
         ":android-test-base-sources",
         ":android-test-mock-sources",
         ":android-test-runner-sources",
     ],
+    arg_files: [
+        "core/res/AndroidManifest.xml",
+    ],
     libs: framework_docs_only_libs,
     create_doc_stubs: true,
     annotations_enabled: true,
@@ -106,6 +100,7 @@
     merge_annotations_dirs: [
         "metalava-manual",
     ],
+    write_sdk_values: true,
 }
 
 droidstubs {
@@ -114,6 +109,24 @@
     args: metalava_framework_docs_args,
 }
 
+// Defaults module for doc-stubs targets that use module source code as input.
+// This is the default/normal.
+stubs_defaults {
+    name: "framework-doc-stubs-sources-default",
+    defaults: ["framework-doc-stubs-default"],
+    srcs: [
+        ":framework-connectivity-sources",
+        ":framework-mediaprovider-sources",
+        ":framework-permission-sources",
+        ":framework-sdkextensions-sources",
+        ":framework-statsd-sources",
+        ":framework-tethering-srcs",
+        ":framework-wifi-updatable-sources",
+        ":ike-srcs",
+        ":updatable-media-srcs",
+    ],
+}
+
 droidstubs {
     name: "android-non-updatable-doc-stubs-system",
     defaults: ["android-non-updatable-doc-stubs-defaults"],
@@ -123,26 +136,46 @@
 
 droidstubs {
     name: "framework-doc-stubs",
-    defaults: ["framework-doc-stubs-default"],
-    arg_files: [
-        "core/res/AndroidManifest.xml",
-    ],
+    defaults: ["framework-doc-stubs-sources-default"],
     args: metalava_framework_docs_args,
-    write_sdk_values: true,
 }
 
 droidstubs {
     name: "framework-doc-system-stubs",
-    defaults: ["framework-doc-stubs-default"],
-    arg_files: [
-        "core/res/AndroidManifest.xml",
-    ],
+    defaults: ["framework-doc-stubs-sources-default"],
     args: metalava_framework_docs_args +
         " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
-    write_sdk_values: true,
     api_levels_sdk_type: "system",
 }
 
+// Experimental target building doc stubs with module stub source code as input.
+// This is intended to eventually replace framework-doc-stubs, once all diffs
+// have been eliminated.
+droidstubs {
+    name: "framework-doc-stubs-module-stubs",
+    defaults: ["framework-doc-stubs-default"],
+    args: metalava_framework_docs_args,
+    srcs: [
+        ":android.net.ipsec.ike{.public.stubs.source}",
+        ":framework-connectivity{.public.stubs.source}",
+        ":framework-media{.public.stubs.source}",
+        ":framework-mediaprovider{.public.stubs.source}",
+        ":framework-permission{.public.stubs.source}",
+        ":framework-sdkextensions{.public.stubs.source}",
+        ":framework-statsd{.public.stubs.source}",
+        ":framework-tethering{.public.stubs.source}",
+        ":framework-wifi{.public.stubs.source}",
+    ],
+    aidl: {
+        local_include_dirs: [
+            "apex/media/aidl/stable",
+        ],
+        include_dirs: [
+            "packages/modules/Connectivity/framework/aidl-export",
+        ],
+    },
+}
+
 /////////////////////////////////////////////////////////////////////
 // API docs are created from the generated stub source files
 // using droiddoc
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 91e2074..c967e51 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -31,5 +31,6 @@
     ],
     platform_apis: true,
     test_suites: ["device-tests"],
+    data: ["trace_configs/*"],
     certificate: "platform",
 }
diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml
index 9117561..bec3cc9 100644
--- a/apct-tests/perftests/multiuser/AndroidTest.xml
+++ b/apct-tests/perftests/multiuser/AndroidTest.xml
@@ -16,14 +16,51 @@
 <configuration description="Runs MultiUserPerfTests metric instrumentation.">
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-metric-instrumentation" />
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="MultiUserPerfTests.apk" />
         <option name="test-file-name" value="MultiUserPerfDummyApp.apk" />
     </target_preparer>
 
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file" key="trace_config_multi_user.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+        <!--Install the content provider automatically when we push some file in sdcard folder.-->
+        <!--Needed to avoid the installation during the test suite.-->
+        <option name="push-file" key="trace_config_multi_user.textproto" value="/sdcard/sample.textproto" />
+    </target_preparer>
+
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path" />
+    </metrics_collector>
+
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false" />
+
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.perftests.multiuser" />
         <option name="hidden-api-checks" value="false"/>
+
+        <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+        <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+        <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+        <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+        <!-- ProcLoadListener related arguments -->
+        <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+        <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+        <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+        <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+        <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+        <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+
+        <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
     </test>
 </configuration>
diff --git a/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
new file mode 100644
index 0000000..93b06e8
--- /dev/null
+++ b/apct-tests/perftests/multiuser/trace_configs/trace_config_multi_user.textproto
@@ -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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 1000
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 10000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers {
+  size_kb: 32768
+  fill_policy: RING_BUFFER
+}
+
+# procfs polling
+buffers {
+  size_kb: 8192
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "linux.ftrace"
+    target_buffer: 0
+    ftrace_config {
+      # These parameters affect only the kernel trace buffer size and how
+      # frequently it gets moved into the userspace buffer defined above.
+      buffer_size_kb: 16384
+      drain_period_ms: 250
+
+      # Store certain high-volume "sched" ftrace events in a denser format
+      # (falling back to the default format if not supported by the tracer).
+      compact_sched {
+        enabled: true
+      }
+
+      # Enables symbol name resolution against /proc/kallsyms
+      symbolize_ksyms: true
+
+      # We need to do process tracking to ensure kernel ftrace events targeted at short-lived
+      # threads are associated correctly
+      ftrace_events: "task/task_newtask"
+      ftrace_events: "task/task_rename"
+      ftrace_events: "sched/sched_process_exit"
+      ftrace_events: "sched/sched_process_free"
+
+      # Memory events
+      ftrace_events: "rss_stat"
+      ftrace_events: "ion_heap_shrink"
+      ftrace_events: "ion_heap_grow"
+      ftrace_events: "ion/ion_stat"
+      ftrace_events: "dmabuf_heap/dma_heap_stat"
+      ftrace_events: "oom_score_adj_update"
+      ftrace_events: "gpu_mem/gpu_mem_total"
+
+      # Old (kernel) LMK
+      ftrace_events: "lowmemorykiller/lowmemory_kill"
+
+      atrace_apps: "*"
+
+      atrace_categories: "am"
+      atrace_categories: "binder_driver"
+      atrace_categories: "bionic"
+      atrace_categories: "dalvik"
+      atrace_categories: "input"
+      atrace_categories: "pm"
+      atrace_categories: "res"
+      atrace_categories: "rro"
+      atrace_categories: "ss"
+      atrace_categories: "view"
+      atrace_categories: "wm"
+
+      atrace_categories: "freq"
+      atrace_categories: "sched"
+      atrace_categories: "sync"
+      atrace_categories: "workq"
+
+    }
+  }
+}
+
+data_sources: {
+  config {
+    name: "android.gpu.memory"
+    target_buffer: 0
+  }
+}
+
+data_sources {
+  config {
+    name: "linux.process_stats"
+    target_buffer: 1
+    process_stats_config {
+      proc_stats_poll_ms: 10000
+    }
+  }
+}
+
+data_sources {
+  config {
+    name: "linux.sys_stats"
+    target_buffer: 1
+    sys_stats_config {
+      meminfo_period_ms: 1000
+      meminfo_counters: MEMINFO_MEM_TOTAL
+      meminfo_counters: MEMINFO_MEM_FREE
+      meminfo_counters: MEMINFO_MEM_AVAILABLE
+      meminfo_counters: MEMINFO_BUFFERS
+      meminfo_counters: MEMINFO_CACHED
+      meminfo_counters: MEMINFO_SWAP_CACHED
+      meminfo_counters: MEMINFO_ACTIVE
+      meminfo_counters: MEMINFO_INACTIVE
+      meminfo_counters: MEMINFO_ACTIVE_ANON
+      meminfo_counters: MEMINFO_INACTIVE_ANON
+      meminfo_counters: MEMINFO_ACTIVE_FILE
+      meminfo_counters: MEMINFO_INACTIVE_FILE
+      meminfo_counters: MEMINFO_UNEVICTABLE
+      meminfo_counters: MEMINFO_SWAP_TOTAL
+      meminfo_counters: MEMINFO_SWAP_FREE
+      meminfo_counters: MEMINFO_DIRTY
+      meminfo_counters: MEMINFO_WRITEBACK
+      meminfo_counters: MEMINFO_ANON_PAGES
+      meminfo_counters: MEMINFO_MAPPED
+      meminfo_counters: MEMINFO_SHMEM
+    }
+  }
+}
+
+data_sources: {
+  config: {
+    name: "android.surfaceflinger.frametimeline"
+  }
+}
diff --git a/api/Android.bp b/api/Android.bp
index fbfbc7c..0acd759 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,9 +24,8 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-python_binary_host {
-    name: "api_versions_trimmer",
-    srcs: ["api_versions_trimmer.py"],
+python_defaults {
+    name: "python3_version_defaults",
     version: {
         py2: {
             enabled: false,
@@ -38,6 +37,12 @@
     },
 }
 
+python_binary_host {
+    name: "api_versions_trimmer",
+    srcs: ["api_versions_trimmer.py"],
+    defaults: ["python3_version_defaults"],
+}
+
 python_test_host {
     name: "api_versions_trimmer_unittests",
     main: "api_versions_trimmer_unittests.py",
@@ -45,24 +50,35 @@
         "api_versions_trimmer_unittests.py",
         "api_versions_trimmer.py",
     ],
+    defaults: ["python3_version_defaults"],
     test_options: {
         unit_test: true,
     },
-    version: {
-        py2: {
-            enabled: false,
-        },
-        py3: {
-            enabled: true,
-            embedded_launcher: false,
-        },
+}
+
+python_binary_host {
+    name: "merge_annotation_zips",
+    srcs: ["merge_annotation_zips.py"],
+    defaults: ["python3_version_defaults"],
+}
+
+python_test_host {
+    name: "merge_annotation_zips_test",
+    main: "merge_annotation_zips_test.py",
+    srcs: [
+        "merge_annotation_zips.py",
+        "merge_annotation_zips_test.py",
+    ],
+    defaults: ["python3_version_defaults"],
+    test_options: {
+        unit_test: true,
     },
 }
 
 metalava_cmd = "$(location metalava)"
 // Silence reflection warnings. See b/168689341
 metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
-metalava_cmd += " --no-banner --format=v2 "
+metalava_cmd += " --quiet --no-banner --format=v2 "
 
 genrule {
     name: "current-api-xml",
@@ -118,13 +134,13 @@
         ":android-incompatibilities.api.public.latest",
         ":frameworks-base-api-current.txt",
     ],
-    out: ["stdout.txt"],
+    out: ["updated-baseline.txt"],
     tools: ["metalava"],
     cmd: metalava_cmd +
         "--check-compatibility:api:released $(location :android.api.public.latest) " +
         "--baseline:compatibility:released $(location :android-incompatibilities.api.public.latest) " +
-        "$(location :frameworks-base-api-current.txt) " +
-        "> $(genDir)/stdout.txt",
+        "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
+        "$(location :frameworks-base-api-current.txt)",
 }
 
 genrule {
@@ -231,14 +247,14 @@
         ":frameworks-base-api-current.txt",
         ":frameworks-base-api-system-current.txt",
     ],
-    out: ["stdout.txt"],
+    out: ["updated-baseline.txt"],
     tools: ["metalava"],
     cmd: metalava_cmd +
         "--check-compatibility:api:released $(location :android.api.system.latest) " +
         "--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
         "--baseline:compatibility:released $(location :android-incompatibilities.api.system.latest) " +
-        "$(location :frameworks-base-api-system-current.txt) " +
-        "> $(genDir)/stdout.txt",
+        "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
+        "$(location :frameworks-base-api-system-current.txt)",
 }
 
 genrule {
@@ -320,7 +336,7 @@
         ":frameworks-base-api-current.txt",
         ":frameworks-base-api-module-lib-current.txt",
     ],
-    out: ["stdout.txt"],
+    out: ["updated-baseline.txt"],
     tools: ["metalava"],
     cmd: metalava_cmd +
         "--check-compatibility:api:released $(location :android.api.module-lib.latest) " +
@@ -329,8 +345,8 @@
         // MODULE_LIBS -> public.
         "--check-compatibility:base $(location :frameworks-base-api-current.txt) " +
         "--baseline:compatibility:released $(location :android-incompatibilities.api.module-lib.latest) " +
-        "$(location :frameworks-base-api-module-lib-current.txt) " +
-        "> $(genDir)/stdout.txt",
+        "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " +
+        "$(location :frameworks-base-api-module-lib-current.txt)",
 }
 
 genrule {
diff --git a/api/api_versions_trimmer_unittests.py b/api/api_versions_trimmer_unittests.py
index 4eb929e..d2e5b7d 100644
--- a/api/api_versions_trimmer_unittests.py
+++ b/api/api_versions_trimmer_unittests.py
@@ -304,4 +304,4 @@
 
 
 if __name__ == "__main__":
-  unittest.main()
+  unittest.main(verbosity=2)
diff --git a/api/merge_annotation_zips.py b/api/merge_annotation_zips.py
new file mode 100755
index 0000000..9c67d7b
--- /dev/null
+++ b/api/merge_annotation_zips.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+"""Script to merge annotation XML files (created by e.g. metalava)."""
+
+from pathlib import Path
+import sys
+import xml.etree.ElementTree as ET
+import zipfile
+
+
+def validate_xml_assumptions(root):
+  """Verify the format of the annotations XML matches expectations"""
+  prevName = ""
+  assert root.tag == 'root'
+  for child in root:
+    assert child.tag == 'item', 'unexpected tag: %s' % child.tag
+    assert list(child.attrib.keys()) == ['name'], 'unexpected attribs: %s' % child.attrib.keys()
+    assert prevName < child.get('name'), 'items unexpectedly not strictly sorted (possibly duplicate entries)'
+    prevName = child.get('name')
+
+
+def merge_xml(a, b):
+  """Merge two annotation xml files"""
+  for xml in [a, b]:
+    validate_xml_assumptions(xml)
+  a.extend(b[:])
+  a[:] = sorted(a[:], key=lambda x: x.get('name'))
+  validate_xml_assumptions(a)
+
+
+def merge_zip_file(out_dir, zip_file):
+  """Merge the content of the zip_file into out_dir"""
+  for filename in zip_file.namelist():
+    path = Path(out_dir, filename)
+    if path.exists():
+      existing_xml = ET.parse(path)
+      with zip_file.open(filename) as other_file:
+        other_xml = ET.parse(other_file)
+      merge_xml(existing_xml.getroot(), other_xml.getroot())
+      existing_xml.write(path, encoding='UTF-8', xml_declaration=True)
+    else:
+      zip_file.extract(filename, out_dir)
+
+
+def main():
+  out_dir = Path(sys.argv[1])
+  zip_filenames = sys.argv[2:]
+
+  assert not out_dir.exists()
+  out_dir.mkdir()
+  for zip_filename in zip_filenames:
+    with zipfile.ZipFile(zip_filename) as zip_file:
+      merge_zip_file(out_dir, zip_file)
+
+
+if __name__ == "__main__":
+  main()
diff --git a/api/merge_annotation_zips_test.py b/api/merge_annotation_zips_test.py
new file mode 100644
index 0000000..26795c47
--- /dev/null
+++ b/api/merge_annotation_zips_test.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+import io
+from pathlib import Path
+import tempfile
+import unittest
+import zipfile
+
+import merge_annotation_zips
+
+
+zip_a = {
+  'android/provider/annotations.xml':
+  """<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <item name="android.provider.BlockedNumberContract boolean isBlocked(android.content.Context, java.lang.String)">
+    <annotation name="androidx.annotation.WorkerThread"/>
+  </item>
+  <item name="android.provider.SimPhonebookContract.SimRecords android.net.Uri getItemUri(int, int, int) 2">
+    <annotation name="androidx.annotation.IntRange">
+      <val name="from" val="1" />
+    </annotation>
+  </item>
+</root>""",
+  'android/os/annotations.xml':
+  """<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <item name="android.app.ActionBar void setCustomView(int) 0">
+    <annotation name="androidx.annotation.LayoutRes"/>
+  </item>
+</root>
+"""
+}
+
+zip_b = {
+  'android/provider/annotations.xml':
+  """<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <item name="android.provider.MediaStore QUERY_ARG_MATCH_FAVORITE">
+    <annotation name="androidx.annotation.IntDef">
+      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
+      <val name="flag" val="true" />
+    </annotation>
+  </item>
+  <item name="android.provider.MediaStore QUERY_ARG_MATCH_PENDING">
+    <annotation name="androidx.annotation.IntDef">
+      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
+      <val name="flag" val="true" />
+    </annotation>
+  </item>
+</root>"""
+}
+
+zip_c = {
+  'android/app/annotations.xml':
+  """<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <item name="android.app.ActionBar void setCustomView(int) 0">
+    <annotation name="androidx.annotation.LayoutRes"/>
+  </item>
+</root>"""
+}
+
+merged_provider = """<?xml version='1.0' encoding='UTF-8'?>
+<root>
+  <item name="android.provider.BlockedNumberContract boolean isBlocked(android.content.Context, java.lang.String)">
+    <annotation name="androidx.annotation.WorkerThread" />
+  </item>
+  <item name="android.provider.MediaStore QUERY_ARG_MATCH_FAVORITE">
+    <annotation name="androidx.annotation.IntDef">
+      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
+      <val name="flag" val="true" />
+    </annotation>
+  </item>
+  <item name="android.provider.MediaStore QUERY_ARG_MATCH_PENDING">
+    <annotation name="androidx.annotation.IntDef">
+      <val name="value" val="{android.provider.MediaStore.MATCH_DEFAULT, android.provider.MediaStore.MATCH_INCLUDE, android.provider.MediaStore.MATCH_EXCLUDE, android.provider.MediaStore.MATCH_ONLY}" />
+      <val name="flag" val="true" />
+    </annotation>
+  </item>
+<item name="android.provider.SimPhonebookContract.SimRecords android.net.Uri getItemUri(int, int, int) 2">
+    <annotation name="androidx.annotation.IntRange">
+      <val name="from" val="1" />
+    </annotation>
+  </item>
+</root>"""
+
+
+
+class MergeAnnotationZipsTest(unittest.TestCase):
+
+  def test_merge_zips(self):
+    with tempfile.TemporaryDirectory() as out_dir:
+      for zip_content in [zip_a, zip_b, zip_c]:
+        f = io.BytesIO()
+        with zipfile.ZipFile(f, "w") as zip_file:
+          for filename, content in zip_content.items():
+            zip_file.writestr(filename, content)
+          merge_annotation_zips.merge_zip_file(out_dir, zip_file)
+
+      # Unchanged
+      self.assertEqual(zip_a['android/os/annotations.xml'], Path(out_dir, 'android/os/annotations.xml').read_text())
+      self.assertEqual(zip_c['android/app/annotations.xml'], Path(out_dir, 'android/app/annotations.xml').read_text())
+
+      # Merged
+      self.assertEqual(merged_provider, Path(out_dir, 'android/provider/annotations.xml').read_text())
+
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)
diff --git a/cmds/idmap2/include/idmap2/FileUtils.h b/cmds/idmap2/include/idmap2/FileUtils.h
index c4e0e1f..8fcaeb1 100644
--- a/cmds/idmap2/include/idmap2/FileUtils.h
+++ b/cmds/idmap2/include/idmap2/FileUtils.h
@@ -17,6 +17,8 @@
 #ifndef IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
 #define IDMAP2_INCLUDE_IDMAP2_FILEUTILS_H_
 
+#include <sys/types.h>
+
 #include <string>
 
 namespace android::idmap2::utils {
diff --git a/core/api/current.txt b/core/api/current.txt
index bc61d15..5c07254 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -954,7 +954,7 @@
     field public static final int measureWithLargestChild = 16843476; // 0x10102d4
     field public static final int mediaRouteButtonStyle = 16843693; // 0x10103ad
     field public static final int mediaRouteTypes = 16843694; // 0x10103ae
-    field public static final int memtagMode = 16844313; // 0x1010619
+    field public static final int memtagMode = 16844324; // 0x1010624
     field public static final int menuCategory = 16843230; // 0x10101de
     field public static final int mimeGroup = 16844309; // 0x1010615
     field public static final int mimeType = 16842790; // 0x1010026
@@ -978,7 +978,7 @@
     field public static final int multiArch = 16843918; // 0x101048e
     field public static final int multiprocess = 16842771; // 0x1010013
     field public static final int name = 16842755; // 0x1010003
-    field public static final int nativeHeapZeroInitialized = 16844314; // 0x101061a
+    field public static final int nativeHeapZeroInitialized = 16844325; // 0x1010625
     field public static final int navigationBarColor = 16843858; // 0x1010452
     field public static final int navigationBarDividerColor = 16844141; // 0x101056d
     field public static final int navigationContentDescription = 16843969; // 0x10104c1
@@ -8657,6 +8657,15 @@
     field public static final int TELEPHONY = 4194304; // 0x400000
   }
 
+  public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+    method public void close();
+    method protected void finalize();
+    method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH) public static final String ACTION_CSIS_CONNECTION_STATE_CHANGED = "android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED";
+  }
+
   public final class BluetoothDevice implements android.os.Parcelable {
     method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
     method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
@@ -8703,6 +8712,7 @@
     field public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
     field public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
     field public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
+    field public static final String EXTRA_IS_COORDINATED_SET_MEMBER = "android.bluetooth.extra.IS_COORDINATED_SET_MEMBER";
     field public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
     field public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
     field public static final String EXTRA_PAIRING_VARIANT = "android.bluetooth.device.extra.PAIRING_VARIANT";
@@ -29936,6 +29946,11 @@
     ctor public OperationCanceledException(String);
   }
 
+  public interface OutcomeReceiver<R, E extends java.lang.Throwable> {
+    method public default void onError(@NonNull E);
+    method public void onResult(@NonNull R);
+  }
+
   public final class Parcel {
     method public void appendFrom(android.os.Parcel, int, int);
     method @Nullable public android.os.IBinder[] createBinderArray();
@@ -29961,6 +29976,7 @@
     method public boolean hasFileDescriptors();
     method public byte[] marshall();
     method @NonNull public static android.os.Parcel obtain();
+    method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
     method @Nullable public Object[] readArray(@Nullable ClassLoader);
     method @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader);
     method public void readBinderArray(@NonNull android.os.IBinder[]);
@@ -29983,12 +29999,15 @@
     method public int readInt();
     method public void readIntArray(@NonNull int[]);
     method public void readList(@NonNull java.util.List, @Nullable ClassLoader);
+    method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>);
     method public long readLong();
     method public void readLongArray(@NonNull long[]);
     method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
     method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
+    method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
     method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
     method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
+    method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
     method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
     method @Nullable public android.os.PersistableBundle readPersistableBundle();
     method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
@@ -30127,7 +30146,7 @@
 
   public interface Parcelable {
     method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final int CONTENTS_FILE_DESCRIPTOR = 1; // 0x1
     field public static final int PARCELABLE_WRITE_RETURN_VALUE = 1; // 0x1
   }
@@ -41031,6 +41050,7 @@
     method public String getNetworkOperator();
     method public String getNetworkOperatorName();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
+    method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getNetworkSlicingConfiguration(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.data.SlicingConfig,android.telephony.TelephonyManager.SlicingException>);
     method public String getNetworkSpecifier();
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
     method @Deprecated public int getPhoneCount();
@@ -41264,6 +41284,13 @@
     field public static final int ERROR_TIMEOUT = 1; // 0x1
   }
 
+  public static class TelephonyManager.SlicingException extends java.lang.Exception {
+    ctor public TelephonyManager.SlicingException(int);
+    method public int getErrorCode();
+    field public static final int ERROR_MODEM_ERROR = 2; // 0x2
+    field public static final int ERROR_TIMEOUT = 1; // 0x1
+  }
+
   public abstract static class TelephonyManager.UssdResponseCallback {
     ctor public TelephonyManager.UssdResponseCallback();
     method public void onReceiveUssdResponse(android.telephony.TelephonyManager, String, CharSequence);
@@ -41439,6 +41466,88 @@
     method @NonNull public android.telephony.data.ApnSetting.Builder setUser(@Nullable String);
   }
 
+  public final class NetworkSliceInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @IntRange(from=0xffffffff, to=0xfffffe) public int getMappedHplmnSliceDifferentiator();
+    method public int getMappedHplmnSliceServiceType();
+    method @IntRange(from=0xffffffff, to=0xfffffe) public int getSliceDifferentiator();
+    method public int getSliceServiceType();
+    method public int getStatus();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NetworkSliceInfo> CREATOR;
+    field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff
+    field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1
+    field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3
+    field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0
+    field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2
+    field public static final int SLICE_STATUS_ALLOWED = 2; // 0x2
+    field public static final int SLICE_STATUS_CONFIGURED = 1; // 0x1
+    field public static final int SLICE_STATUS_DEFAULT_CONFIGURED = 5; // 0x5
+    field public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN = 3; // 0x3
+    field public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA = 4; // 0x4
+    field public static final int SLICE_STATUS_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class NetworkSliceInfo.Builder {
+    ctor public NetworkSliceInfo.Builder();
+    method @NonNull public android.telephony.data.NetworkSliceInfo build();
+    method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=0xffffffff, to=0xfffffe) int);
+    method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceServiceType(int);
+    method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceDifferentiator(@IntRange(from=0xffffffff, to=0xfffffe) int);
+    method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceServiceType(int);
+    method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setStatus(int);
+  }
+
+  public final class RouteSelectionDescriptor implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.List<java.lang.String> getDataNetworkName();
+    method @IntRange(from=0x0, to=0xff) public int getPrecedence();
+    method public int getSessionType();
+    method @NonNull public java.util.List<android.telephony.data.NetworkSliceInfo> getSliceInfo();
+    method public int getSscMode();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.RouteSelectionDescriptor> CREATOR;
+    field public static final int ROUTE_SSC_MODE_1 = 1; // 0x1
+    field public static final int ROUTE_SSC_MODE_2 = 2; // 0x2
+    field public static final int ROUTE_SSC_MODE_3 = 3; // 0x3
+    field public static final int SESSION_TYPE_IPV4 = 0; // 0x0
+    field public static final int SESSION_TYPE_IPV4V6 = 2; // 0x2
+    field public static final int SESSION_TYPE_IPV6 = 1; // 0x1
+  }
+
+  public final class SlicingConfig implements android.os.Parcelable {
+    ctor public SlicingConfig();
+    method public int describeContents();
+    method @NonNull public java.util.List<android.telephony.data.NetworkSliceInfo> getSliceInfo();
+    method @NonNull public java.util.List<android.telephony.data.UrspRule> getUrspRules();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.SlicingConfig> CREATOR;
+  }
+
+  public final class TrafficDescriptor implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getDataNetworkName();
+    method @Nullable public String getOsAppId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR;
+  }
+
+  public static final class TrafficDescriptor.Builder {
+    ctor public TrafficDescriptor.Builder();
+    method @NonNull public android.telephony.data.TrafficDescriptor build();
+    method @NonNull public android.telephony.data.TrafficDescriptor.Builder setDataNetworkName(@NonNull String);
+    method @NonNull public android.telephony.data.TrafficDescriptor.Builder setOsAppId(@NonNull String);
+  }
+
+  public final class UrspRule implements android.os.Parcelable {
+    method public int describeContents();
+    method @IntRange(from=0x0, to=0xff) public int getPrecedence();
+    method @NonNull public java.util.List<android.telephony.data.RouteSelectionDescriptor> getRouteSelectionDescriptor();
+    method @NonNull public java.util.List<android.telephony.data.TrafficDescriptor> getTrafficDescriptors();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.UrspRule> CREATOR;
+  }
+
 }
 
 package android.telephony.emergency {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cd41e58..e601583 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1507,8 +1507,24 @@
     method public void onOobData(int, @NonNull android.bluetooth.OobData);
   }
 
+  public final class BluetoothCsipSetCoordinator implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+    method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<java.lang.Integer> getAllGroupIds(@Nullable android.os.ParcelUuid);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+    method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.Map getGroupUuidMapByDevice(@Nullable android.bluetooth.BluetoothDevice);
+    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.UUID groupLock(int, @Nullable java.util.concurrent.Executor, @Nullable android.bluetooth.BluetoothCsipSetCoordinator.ClientLockCallback);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean groupUnlock(@NonNull java.util.UUID);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CSIS_DEVICE_AVAILABLE = "android.bluetooth.action.CSIS_DEVICE_AVAILABLE";
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CSIS_SET_MEMBER_AVAILABLE = "android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE";
+  }
+
+  public static interface BluetoothCsipSetCoordinator.ClientLockCallback {
+    method public void onGroupLockSet(int, int, boolean);
+  }
+
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean createBond(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean fetchUuidsWithSdp(int);
     method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
@@ -1560,7 +1576,10 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInbandRingingEnabled();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean startScoUsingVirtualVoiceCall();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean stopScoUsingVirtualVoiceCall();
   }
 
   public final class BluetoothHearingAid implements android.bluetooth.BluetoothProfile {
@@ -7241,6 +7260,7 @@
   public class SystemConfigManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+    method @NonNull public java.util.List<java.lang.String> getEnabledComponentOverrides(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public int[] getSystemPermissionUids(@NonNull String);
   }
 
@@ -9671,6 +9691,25 @@
     field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
   }
 
+  public final class ModemActivityInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+    method public long getIdleTimeMillis();
+    method public static int getNumTxPowerLevels();
+    method public long getReceiveTimeMillis();
+    method public long getSleepTimeMillis();
+    method public long getTimestampMillis();
+    method public long getTransmitDurationMillisAtPowerLevel(int);
+    method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+    field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+    field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+    field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+    field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+    field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+  }
+
   public final class NetworkRegistrationInfo implements android.os.Parcelable {
     method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo();
     method public int getRegistrationState();
@@ -10337,6 +10376,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void reportDefaultNetworkStatus(boolean);
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.ModemActivityInfo,android.telephony.TelephonyManager.ModemActivityInfoException>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption();
@@ -10509,6 +10549,14 @@
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
 
+  public static class TelephonyManager.ModemActivityInfoException extends java.lang.Exception {
+    method public int getErrorCode();
+    field public static final int ERROR_INVALID_INFO_RECEIVED = 2; // 0x2
+    field public static final int ERROR_MODEM_RESPONSE_ERROR = 3; // 0x3
+    field public static final int ERROR_PHONE_NOT_AVAILABLE = 1; // 0x1
+    field public static final int ERROR_UNKNOWN = 0; // 0x0
+  }
+
   public final class ThermalMitigationRequest implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.telephony.DataThrottlingRequest getDataThrottlingRequest();
@@ -10772,32 +10820,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR;
   }
 
-  public final class NetworkSliceInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getMappedHplmnSliceDifferentiator();
-    method public int getMappedHplmnSliceServiceType();
-    method @IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) public int getSliceDifferentiator();
-    method public int getSliceServiceType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.NetworkSliceInfo> CREATOR;
-    field public static final int MAX_SLICE_DIFFERENTIATOR = 16777214; // 0xfffffe
-    field public static final int MIN_SLICE_DIFFERENTIATOR = -1; // 0xffffffff
-    field public static final int SLICE_DIFFERENTIATOR_NO_SLICE = -1; // 0xffffffff
-    field public static final int SLICE_SERVICE_TYPE_EMBB = 1; // 0x1
-    field public static final int SLICE_SERVICE_TYPE_MIOT = 3; // 0x3
-    field public static final int SLICE_SERVICE_TYPE_NONE = 0; // 0x0
-    field public static final int SLICE_SERVICE_TYPE_URLLC = 2; // 0x2
-  }
-
-  public static final class NetworkSliceInfo.Builder {
-    ctor public NetworkSliceInfo.Builder();
-    method @NonNull public android.telephony.data.NetworkSliceInfo build();
-    method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceDifferentiator(@IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
-    method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setMappedHplmnSliceServiceType(int);
-    method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceDifferentiator(@IntRange(from=android.telephony.data.NetworkSliceInfo.MIN_SLICE_DIFFERENTIATOR, to=android.telephony.data.NetworkSliceInfo.MAX_SLICE_DIFFERENTIATOR) int);
-    method @NonNull public android.telephony.data.NetworkSliceInfo.Builder setSliceServiceType(int);
-  }
-
   public final class NrQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
     method public int describeContents();
     method @NonNull public java.time.Duration getBitRateWindowDuration();
@@ -10854,15 +10876,6 @@
     method @NonNull public android.telephony.data.ThrottleStatus.Builder setTransportType(int);
   }
 
-  public final class TrafficDescriptor implements android.os.Parcelable {
-    ctor public TrafficDescriptor(@Nullable String, @Nullable String);
-    method public int describeContents();
-    method @Nullable public String getDnn();
-    method @Nullable public String getOsAppId();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.TrafficDescriptor> CREATOR;
-  }
-
 }
 
 package android.telephony.euicc {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6613947..d905bbe 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1649,6 +1649,28 @@
     field public static final String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
   }
 
+  public final class ModemActivityInfo implements android.os.Parcelable {
+    ctor public ModemActivityInfo(long, int, int, @NonNull int[], int);
+    method public int describeContents();
+    method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo);
+    method public long getIdleTimeMillis();
+    method public static int getNumTxPowerLevels();
+    method public long getReceiveTimeMillis();
+    method public long getSleepTimeMillis();
+    method public long getTimestampMillis();
+    method public long getTransmitDurationMillisAtPowerLevel(int);
+    method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int);
+    method public boolean isEmpty();
+    method public boolean isValid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR;
+    field public static final int TX_POWER_LEVEL_0 = 0; // 0x0
+    field public static final int TX_POWER_LEVEL_1 = 1; // 0x1
+    field public static final int TX_POWER_LEVEL_2 = 2; // 0x2
+    field public static final int TX_POWER_LEVEL_3 = 3; // 0x3
+    field public static final int TX_POWER_LEVEL_4 = 4; // 0x4
+  }
+
   public class PhoneNumberUtils {
     method public static int getMinMatchForTest();
     method public static void setMinMatchForTest(int);
diff --git a/core/java/Android.bp b/core/java/Android.bp
index eee696b..3b2d883 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -332,13 +332,29 @@
 filegroup {
     name: "framework-cellbroadcast-shared-srcs",
     srcs: [
-        "android/os/HandlerExecutor.java",
         "android/util/LocalLog.java",
+    ],
+}
+
+java_library {
+    name: "modules-utils-statemachine",
+    srcs: [
         "com/android/internal/util/IState.java",
-        "com/android/internal/util/Preconditions.java",
         "com/android/internal/util/State.java",
         "com/android/internal/util/StateMachine.java",
     ],
+    libs: [
+        "framework-annotations-lib",
+        "unsupportedappusage",
+    ],
+    sdk_version: "module_current",
+    min_sdk_version: "29",
+
+    visibility: ["//visibility:public"],
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
 }
 
 filegroup {
@@ -365,10 +381,7 @@
         "android/util/Rational.java",
         "com/android/internal/util/FastXmlSerializer.java",
         "com/android/internal/util/HexDump.java",
-        "com/android/internal/util/IState.java",
         "com/android/internal/util/MessageUtils.java",
-        "com/android/internal/util/State.java",
-        "com/android/internal/util/StateMachine.java",
         "com/android/internal/util/WakeupMessage.java",
     ],
     visibility: [
diff --git a/core/java/android/annotation/SuppressLint.java b/core/java/android/annotation/SuppressLint.java
new file mode 100644
index 0000000..2d3456b
--- /dev/null
+++ b/core/java/android/annotation/SuppressLint.java
@@ -0,0 +1,38 @@
+/*
+ * 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.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Indicates that Lint should ignore the specified warnings for the annotated element. */
+@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
+@Retention(RetentionPolicy.CLASS)
+public @interface SuppressLint {
+    /**
+     * The set of warnings (identified by the lint issue id) that should be
+     * ignored by lint. It is not an error to specify an unrecognized name.
+     */
+    String[] value();
+}
diff --git a/core/java/android/annotation/TargetApi.java b/core/java/android/annotation/TargetApi.java
new file mode 100644
index 0000000..975318e
--- /dev/null
+++ b/core/java/android/annotation/TargetApi.java
@@ -0,0 +1,36 @@
+/*
+ * 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.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Indicates that Lint should treat this type as targeting a given API level, no matter what the
+    project target is. */
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
+@Retention(RetentionPolicy.CLASS)
+public @interface TargetApi {
+    /**
+     * This sets the target api level for the type..
+     */
+    int value();
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 41170a4..749e8f5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6299,7 +6299,13 @@
             final File cacheDir = context.getCacheDir();
             if (cacheDir != null) {
                 // Provide a usable directory for temporary files
-                System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+                String tmpdir = cacheDir.getAbsolutePath();
+                System.setProperty("java.io.tmpdir", tmpdir);
+                try {
+                    android.system.Os.setenv("TMPDIR", tmpdir, true);
+                } catch (ErrnoException ex) {
+                    Log.w(TAG, "Unable to initialize $TMPDIR", ex);
+                }
             } else {
                 Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property "
                         + "due to missing cache directory");
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 71b28fb..6c435b9 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -54,7 +54,7 @@
  * @see android.support.v4.app.JobIntentService
  *
  * @deprecated IntentService is subject to all the
- *   <a href="/preview/features/background.html">background execution limits</a>
+ *   <a href="{@docRoot}about/versions/oreo/background.html">background execution limits</a>
  *   imposed with Android 8.0 (API level 26). Consider using {@link androidx.work.WorkManager}
  *   or {@link androidx.core.app.JobIntentService}, which uses jobs
  *   instead of services when running on Android 8.0 or higher.
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d487025..313dd3e 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2819,6 +2819,10 @@
         } 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);
+            return true;
         } else {
             return false;
         }
@@ -2908,6 +2912,11 @@
                 BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
                 vcs.close();
                 break;
+            case BluetoothProfile.CSIP_SET_COORDINATOR:
+                BluetoothCsipSetCoordinator csipSetCoordinator =
+                        (BluetoothCsipSetCoordinator) proxy;
+                csipSetCoordinator.close();
+                break;
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
new file mode 100644
index 0000000..cb542e5
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothCsipSetCoordinator.java
@@ -0,0 +1,550 @@
+/*
+ * 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.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.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.CloseGuard;
+import android.util.Log;
+
+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;
+
+/**
+ * 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)
+    @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 BluetoothAdapter mAdapter;
+    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(
+                            Binder.allowBlocking(service));
+                }
+            };
+
+    /**
+     * Create a BluetoothCsipSetCoordinator proxy object for interacting with the local
+     * Bluetooth CSIS service.
+     */
+    /*package*/ BluetoothCsipSetCoordinator(Context context, ServiceListener listener) {
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        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();
+        try {
+            if (service != null && isEnabled()) {
+                IBluetoothCsipSetCoordinatorLockCallback delegate = null;
+                if ((executor != null) && (cb != null)) {
+                    delegate = new BluetoothCsipSetCoordinatorLockCallbackDelegate(executor, cb);
+                }
+                return service.groupLock(groupId, delegate).getUuid();
+            }
+            if (service == null) {
+                Log.w(TAG, "Proxy not attached to service");
+            }
+            return null;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return null;
+        }
+    }
+
+    /**
+     * 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();
+        try {
+            if (service != null && isEnabled()) {
+                service.groupUnlock(new ParcelUuid(lockUuid));
+                return true;
+            }
+            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;
+        }
+    }
+
+    /**
+     * 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();
+        try {
+            if (service != null && isEnabled()) {
+                return service.getGroupUuidMapByDevice(device);
+            }
+            if (service == null) {
+                Log.w(TAG, "Proxy not attached to service");
+            }
+            return new HashMap<>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new HashMap<>();
+        }
+    }
+
+    /**
+     * 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();
+        try {
+            if (service != null && isEnabled()) {
+                return service.getAllGroupIds(uuid);
+            }
+            if (service == null) {
+                Log.w(TAG, "Proxy not attached to service");
+            }
+            return new ArrayList<Integer>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new ArrayList<Integer>();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public @NonNull List<BluetoothDevice> getConnectedDevices() {
+        if (VDBG) {
+            log("getConnectedDevices()");
+        }
+        final IBluetoothCsipSetCoordinator service = getService();
+        if (service != null && isEnabled()) {
+            try {
+                return service.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public
+    @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+            @NonNull int[] states) {
+        if (VDBG) {
+            log("getDevicesMatchingStates(states=" + Arrays.toString(states) + ")");
+        }
+        final IBluetoothCsipSetCoordinator service = getService();
+        if (service != null && isEnabled()) {
+            try {
+                return service.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public
+    @BluetoothProfile.BtProfileState int getConnectionState(
+            @Nullable BluetoothDevice device) {
+        if (VDBG) {
+            log("getState(" + device + ")");
+        }
+        final IBluetoothCsipSetCoordinator service = getService();
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return service.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    /**
+     * 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();
+        try {
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+                        && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                    return false;
+                }
+                return service.setConnectionPolicy(device, connectionPolicy);
+            }
+            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;
+        }
+    }
+
+    /**
+     * 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();
+        try {
+            if (service != null && isEnabled() && isValidDevice(device)) {
+                return service.getConnectionPolicy(device);
+            }
+            if (service == null) {
+                Log.w(TAG, "Proxy not attached to service");
+            }
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+        }
+    }
+
+    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
index 38fb90d..1054b4c 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -106,7 +106,7 @@
      * <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} if they are available.
+     * {@link #EXTRA_RSSI} and/or {@link #EXTRA_IS_COORDINATED_SET_MEMBER} if they are available.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} and
      * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to receive.
      */
@@ -257,6 +257,15 @@
     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.
      */
@@ -1326,7 +1335,7 @@
      * @throws IllegalArgumentException if an invalid transport was specified
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean createBond(int transport) {
         return createBondInternal(transport, null, null);
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 632572d..18aa23a 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -1018,8 +1018,8 @@
      *  - binder is dead or Bluetooth is disabled or other error
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    @UnsupportedAppUsage
     public boolean startScoUsingVirtualVoiceCall() {
         if (DBG) log("startScoUsingVirtualVoiceCall()");
         final IBluetoothHeadset service = mService;
@@ -1048,8 +1048,8 @@
      *  - binder is dead or Bluetooth is disabled or other error
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    @UnsupportedAppUsage
     public boolean stopScoUsingVirtualVoiceCall() {
         if (DBG) log("stopScoUsingVirtualVoiceCall()");
         final IBluetoothHeadset service = mService;
@@ -1227,7 +1227,8 @@
      * @return true if in-band ringing is enabled, false if in-band ringing is disabled
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.BLUETOOTH)
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean isInbandRingingEnabled() {
         if (DBG) {
             log("isInbandRingingEnabled()");
diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS
index 2239100..fbee577 100644
--- a/core/java/android/bluetooth/OWNERS
+++ b/core/java/android/bluetooth/OWNERS
@@ -1,5 +1,6 @@
 # Bug component: 27441
 
-zachoverflow@google.com
-siyuanh@google.com
 rahulsabnis@google.com
+sattiraju@google.com
+siyuanh@google.com
+zachoverflow@google.com
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
index 0ba92cc..504a7bd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
@@ -19,7 +19,6 @@
 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;
@@ -168,19 +167,6 @@
                 + '}';
     }
 
-    public static final Parcelable.Creator<ParsedIntentInfo> CREATOR =
-            new Parcelable.Creator<ParsedIntentInfo>() {
-                @Override
-                public ParsedIntentInfo createFromParcel(Parcel source) {
-                    return new ParsedIntentInfo(source);
-                }
-
-                @Override
-                public ParsedIntentInfo[] newArray(int size) {
-                    return new ParsedIntentInfo[size];
-                }
-            };
-
     public boolean isHasDefault() {
         return hasDefault;
     }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 0a76a9c..9e78a6b 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1440,14 +1440,14 @@
      *                Assuming that the bottom edge of the device faces the
      *                user and that the screen is face-up, tilting the top edge
      *                of the device toward the ground creates a positive pitch
-     *                angle. The range of values is -&pi; to &pi;.</li>
+     *                angle. The range of values is -&pi;/2 to &pi;/2.</li>
      * <li>values[2]: <i>Roll</i>, angle of rotation about the y axis. This
      *                value represents the angle between a plane perpendicular
      *                to the device's screen and a plane perpendicular to the
      *                ground. Assuming that the bottom edge of the device faces
      *                the user and that the screen is face-up, tilting the left
      *                edge of the device toward the ground creates a positive
-     *                roll angle. The range of values is -&pi;/2 to &pi;/2.</li>
+     *                roll angle. The range of values is -&pi; to &pi;.</li>
      * </ul>
      * <p>
      * Applying these three rotations in the azimuth, pitch, roll order
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 68917a8..08f75df 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -47,7 +47,6 @@
 import android.util.BackupUtils;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.net.module.util.NetworkIdentityUtils;
 
@@ -151,24 +150,6 @@
         }
     }
 
-    private static boolean sForceAllNetworkTypes = false;
-
-    /**
-     * Results in matching against all mobile network types.
-     *
-     * <p>See {@link #matchesMobile} and {@link matchesMobileWildcard}.
-     */
-    @VisibleForTesting
-    public static void forceAllNetworkTypes() {
-        sForceAllNetworkTypes = true;
-    }
-
-    /** Resets the affect of {@link #forceAllNetworkTypes}. */
-    @VisibleForTesting
-    public static void resetForceAllNetworkTypes() {
-        sForceAllNetworkTypes = false;
-    }
-
     /**
      * Template to match {@link ConnectivityManager#TYPE_MOBILE} networks with
      * the given IMSI.
@@ -611,7 +592,7 @@
             // Only metered mobile network would be matched regardless of metered filter.
             // This is used to exclude non-metered APNs, e.g. IMS. See ag/908650.
             // TODO: Respect metered filter and remove mMetered condition.
-            return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered))
+            return (ident.mType == TYPE_MOBILE && ident.mMetered)
                     && !ArrayUtils.isEmpty(mMatchSubscriberIds)
                     && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
                     && matchesCollapsedRatType(ident);
@@ -726,7 +707,7 @@
         if (ident.mType == TYPE_WIMAX) {
             return true;
         } else {
-            return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered))
+            return (ident.mType == TYPE_MOBILE && ident.mMetered)
                     && matchesCollapsedRatType(ident);
         }
     }
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index 5a25cfc..ae8d010 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -23,6 +23,9 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.content.Context;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -126,6 +129,24 @@
     private static final boolean DBG = false;
 
     /**
+     * When enabled, apps targeting < Android 12 are considered legacy for
+     * the NSD native daemon.
+     * The platform will only keep the daemon running as long as there are
+     * any legacy apps connected.
+     *
+     * After Android 12, directly communicate with native daemon might not
+     * work since the native damon won't always stay alive.
+     * Use the NSD APIs from NsdManager as the replacement is recommended.
+     * An another alternative could be bundling your own mdns solutions instead of
+     * depending on the system mdns native daemon.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
+    public static final long RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS = 191844585L;
+
+    /**
      * Broadcast intent action to indicate whether network service discovery is
      * enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state
      * information as int.
@@ -203,6 +224,9 @@
     public static final int DAEMON_CLEANUP                          = BASE + 21;
 
     /** @hide */
+    public static final int DAEMON_STARTUP                          = BASE + 22;
+
+    /** @hide */
     public static final int ENABLE                                  = BASE + 24;
     /** @hide */
     public static final int DISABLE                                 = BASE + 25;
@@ -232,6 +256,8 @@
         EVENT_NAMES.put(RESOLVE_SERVICE, "RESOLVE_SERVICE");
         EVENT_NAMES.put(RESOLVE_SERVICE_FAILED, "RESOLVE_SERVICE_FAILED");
         EVENT_NAMES.put(RESOLVE_SERVICE_SUCCEEDED, "RESOLVE_SERVICE_SUCCEEDED");
+        EVENT_NAMES.put(DAEMON_CLEANUP, "DAEMON_CLEANUP");
+        EVENT_NAMES.put(DAEMON_STARTUP, "DAEMON_STARTUP");
         EVENT_NAMES.put(ENABLE, "ENABLE");
         EVENT_NAMES.put(DISABLE, "DISABLE");
         EVENT_NAMES.put(NATIVE_DAEMON_EVENT, "NATIVE_DAEMON_EVENT");
@@ -494,6 +520,12 @@
         } catch (InterruptedException e) {
             fatal("Interrupted wait at init");
         }
+        if (CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) {
+            return;
+        }
+        // Only proactively start the daemon if the target SDK < S, otherwise the internal service
+        // would automatically start/stop the native daemon as needed.
+        mAsyncChannel.sendMessage(DAEMON_STARTUP);
     }
 
     private static void fatal(String msg) {
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 6da02f5..7ce8d72 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -260,6 +260,9 @@
     /**
      * Returns the value for key {@code key}.
      *
+     * This call should always be made after {@link #unparcel()} or inside a lock after making sure
+     * {@code mMap} is not null.
+     *
      * @hide
      */
     final Object getValue(String key) {
@@ -270,15 +273,15 @@
     /**
      * Returns the value for a certain position in the array map.
      *
+     * This call should always be made after {@link #unparcel()} or inside a lock after making sure
+     * {@code mMap} is not null.
+     *
      * @hide
      */
     final Object getValueAt(int i) {
         Object object = mMap.valueAt(i);
         if (object instanceof Supplier<?>) {
-            Supplier<?> supplier = (Supplier<?>) object;
-            synchronized (this) {
-                object = supplier.get();
-            }
+            object = ((Supplier<?>) object).get();
             mMap.setValueAt(i, object);
         }
         return object;
@@ -428,7 +431,7 @@
      *
      * @hide
      */
-    public static boolean kindofEquals(BaseBundle a, BaseBundle b) {
+    public static boolean kindofEquals(@Nullable BaseBundle a, @Nullable BaseBundle b) {
         return (a == b) || (a != null && a.kindofEquals(b));
     }
 
@@ -1045,7 +1048,7 @@
      */
     char getChar(String key, char defaultValue) {
         unparcel();
-        Object o = getValue(key);
+        Object o = mMap.get(key);
         if (o == null) {
             return defaultValue;
         }
@@ -1448,7 +1451,7 @@
     @Nullable
     short[] getShortArray(@Nullable String key) {
         unparcel();
-        Object o = getValue(key);
+        Object o = mMap.get(key);
         if (o == null) {
             return null;
         }
@@ -1471,7 +1474,7 @@
     @Nullable
     char[] getCharArray(@Nullable String key) {
         unparcel();
-        Object o = getValue(key);
+        Object o = mMap.get(key);
         if (o == null) {
             return null;
         }
@@ -1540,7 +1543,7 @@
     @Nullable
     float[] getFloatArray(@Nullable String key) {
         unparcel();
-        Object o = getValue(key);
+        Object o = mMap.get(key);
         if (o == null) {
             return null;
         }
diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl
index 4d160da..d83d94a 100644
--- a/core/java/android/os/ISystemConfig.aidl
+++ b/core/java/android/os/ISystemConfig.aidl
@@ -40,4 +40,9 @@
      * @see SystemConfigManager#getSystemPermissionUids
      */
     int[] getSystemPermissionUids(String permissionName);
+
+    /**
+     * @see SystemConfigManager#getEnabledComponentOverrides
+     */
+    List<String> getEnabledComponentOverrides(String packageName);
 }
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index a870c04..c575c80e 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -69,3 +69,6 @@
 
 # UpdateEngine
 per-file *UpdateEngine* = file:/platform/system/update_engine:/OWNERS
+
+# VINTF
+per-file Vintf* = file:/platform/system/libvintf:/OWNERS
diff --git a/core/java/android/os/OutcomeReceiver.java b/core/java/android/os/OutcomeReceiver.java
new file mode 100644
index 0000000..01b2764
--- /dev/null
+++ b/core/java/android/os/OutcomeReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * 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.annotation.NonNull;
+
+/**
+ * Callback interface intended for use when an asynchronous operation may result in a failure.
+ *
+ * This interface may be used in cases where an asynchronous API may complete either with a value
+ * or with a {@link Throwable} that indicates an error.
+ * @param <R> The type of the result that's being sent.
+ * @param <E> The type of the {@link Throwable} that contains more information about the error.
+ */
+public interface OutcomeReceiver<R, E extends Throwable> {
+    /**
+     * Called when the asynchronous operation succeeds and delivers a result value.
+     * @param result The value delivered by the asynchronous operation.
+     */
+    void onResult(@NonNull R result);
+
+    /**
+     * Called when the asynchronous operation fails. The mode of failure is indicated by the
+     * {@link Throwable} passed as an argument to this method.
+     * @param error A subclass of {@link Throwable} with more details about the error that occurred.
+     */
+    default void onError(@NonNull E error) {}
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 00db972..358aa29 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -261,6 +263,10 @@
     private static final int VAL_SIZE = 26;
     private static final int VAL_SIZEF = 27;
     private static final int VAL_DOUBLEARRAY = 28;
+    private static final int VAL_CHAR = 29;
+    private static final int VAL_SHORTARRAY = 30;
+    private static final int VAL_CHARARRAY = 31;
+    private static final int VAL_FLOATARRAY = 32;
 
     // The initial int32 in a Binder call's reply Parcel header:
     // Keep these in sync with libbinder's binder/Status.h.
@@ -282,6 +288,8 @@
 
     @CriticalNative
     private static native void nativeMarkSensitive(long nativePtr);
+    @FastNative
+    private static native void nativeMarkForBinder(long nativePtr, IBinder binder);
     @CriticalNative
     private static native int nativeDataSize(long nativePtr);
     @CriticalNative
@@ -444,6 +452,21 @@
     }
 
     /**
+     * Retrieve a new Parcel object from the pool for use with a specific binder.
+     *
+     * Associate this parcel with a binder object. This marks the parcel as being prepared for a
+     * transaction on this specific binder object. Based on this, the format of the wire binder
+     * protocol may change. For future compatibility, it is recommended to use this for all
+     * Parcels.
+     */
+    @NonNull
+    public static Parcel obtain(@NonNull IBinder binder) {
+        Parcel parcel = Parcel.obtain();
+        parcel.markForBinder(binder);
+        return parcel;
+    }
+
+    /**
      * Put a Parcel object back into the pool.  You must not touch
      * the object after this call.
      */
@@ -498,6 +521,16 @@
 
     /**
      * Parcel data should be zero'd before realloc'd or deleted.
+     *
+     * Note: currently this feature requires multiple things to work in concert:
+     * - markSensitive must be called on every relative Parcel
+     * - FLAG_CLEAR_BUF must be passed into the kernel
+     * This requires having code which does the right thing in every method and in every backend
+     * of AIDL. Rather than exposing this API, it should be replaced with a single API on
+     * IBinder objects which can be called once, and the information should be fed into the
+     * Parcel using markForBinder APIs. In terms of code size and number of API calls, this is
+     * much more extensible.
+     *
      * @hide
      */
     public final void markSensitive() {
@@ -505,9 +538,16 @@
     }
 
     /**
+     * @hide
+     */
+    private void markForBinder(@NonNull IBinder binder) {
+        nativeMarkForBinder(mNativePtr, binder);
+    }
+
+    /**
      * Returns the total amount of data contained in the parcel.
      */
-    public final int dataSize() {
+    public int dataSize() {
         return nativeDataSize(mNativePtr);
     }
 
@@ -1281,6 +1321,46 @@
         }
     }
 
+    /** @hide */
+    public void writeShortArray(@Nullable short[] val) {
+        if (val != null) {
+            int n = val.length;
+            writeInt(n);
+            for (int i = 0; i < n; i++) {
+                writeInt(val[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /** @hide */
+    @Nullable
+    public short[] createShortArray() {
+        int n = readInt();
+        if (n >= 0 && n <= (dataAvail() >> 2)) {
+            short[] val = new short[n];
+            for (int i = 0; i < n; i++) {
+                val[i] = (short) readInt();
+            }
+            return val;
+        } else {
+            return null;
+        }
+    }
+
+    /** @hide */
+    public void readShortArray(@NonNull short[] val) {
+        int n = readInt();
+        if (n == val.length) {
+            for (int i = 0; i < n; i++) {
+                val[i] = (short) readInt();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
     public final void writeCharArray(@Nullable char[] val) {
         if (val != null) {
             int N = val.length;
@@ -1946,6 +2026,14 @@
             return VAL_SIZE;
         } else if (v instanceof double[]) {
             return VAL_DOUBLEARRAY;
+        } else if (v instanceof Character) {
+            return VAL_CHAR;
+        } else if (v instanceof short[]) {
+            return VAL_SHORTARRAY;
+        } else if (v instanceof char[]) {
+            return VAL_CHARARRAY;
+        } else  if (v instanceof float[]) {
+            return VAL_FLOATARRAY;
         } else {
             Class<?> clazz = v.getClass();
             if (clazz.isArray() && clazz.getComponentType() == Object.class) {
@@ -2048,6 +2136,18 @@
             case VAL_DOUBLEARRAY:
                 writeDoubleArray((double[]) v);
                 break;
+            case VAL_CHAR:
+                writeInt((Character) v);
+                break;
+            case VAL_SHORTARRAY:
+                writeShortArray((short[]) v);
+                break;
+            case VAL_CHARARRAY:
+                writeCharArray((char[]) v);
+                break;
+            case VAL_FLOATARRAY:
+                writeFloatArray((float[]) v);
+                break;
             case VAL_OBJECTARRAY:
                 writeArray((Object[]) v);
                 break;
@@ -2728,7 +2828,20 @@
      */
     public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
         int N = readInt();
-        readListInternal(outVal, N, loader);
+        readListInternal(outVal, N, loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readList(List, ClassLoader)} but accepts {@code clazz} parameter as
+     * the type required for each item. If the item to be deserialized is not an instance
+     * of that class or any of its children class
+     * a {@link BadParcelableException} will be thrown.
+     */
+    public <T> void readList(@NonNull List<? super T> outVal,
+            @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        Objects.requireNonNull(clazz);
+        int n = readInt();
+        readListInternal(outVal, n, loader, clazz);
     }
 
     /**
@@ -2927,7 +3040,7 @@
             return null;
         }
         ArrayList l = new ArrayList(N);
-        readListInternal(l, N, loader);
+        readListInternal(l, N, loader, /* clazz */ null);
         return l;
     }
 
@@ -3327,20 +3440,29 @@
      */
     @Nullable
     public final Object readValue(@Nullable ClassLoader loader) {
+        return readValue(loader, /* clazz */ null);
+    }
+
+
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    @Nullable
+    private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
         int type = readInt();
-        final Object object;
+        final T object;
         if (isLengthPrefixed(type)) {
             int length = readInt();
             int start = dataPosition();
-            object = readValue(type, loader);
+            object = readValue(type, loader, clazz);
             int actual = dataPosition() - start;
             if (actual != length) {
-                Log.w(TAG,
+                Slog.wtfStack(TAG,
                         "Unparcelling of " + object + " of type " + Parcel.valueTypeToString(type)
                                 + "  consumed " + actual + " bytes, but " + length + " expected.");
             }
         } else {
-            object = readValue(type, loader);
+            object = readValue(type, loader, clazz);
         }
         return object;
     }
@@ -3377,7 +3499,7 @@
             setDataPosition(MathUtils.addOrThrow(dataPosition(), length));
             return new LazyValue(this, start, length, type, loader);
         } else {
-            return readValue(type, loader);
+            return readValue(type, loader, /* clazz */ null);
         }
     }
 
@@ -3386,12 +3508,19 @@
         private final int mLength;
         private final int mType;
         @Nullable private final ClassLoader mLoader;
-        @Nullable private Parcel mSource;
         @Nullable private Object mObject;
-        @Nullable private Parcel mValueParcel;
+        @Nullable private volatile Parcel mValueParcel;
+
+        /**
+         * This goes from non-null to null once. Always check the nullability of this object before
+         * performing any operations, either involving itself or mObject since the happens-before
+         * established by this volatile will guarantee visibility of either. We can assume this
+         * parcel won't change anymore.
+         */
+        @Nullable private volatile Parcel mSource;
 
         LazyValue(Parcel source, int position, int length, int type, @Nullable ClassLoader loader) {
-            mSource = source;
+            mSource = requireNonNull(source);
             mPosition = position;
             mLength = length;
             mType = type;
@@ -3400,46 +3529,41 @@
 
         @Override
         public Object get() {
-            if (mObject == null) {
-                int restore = mSource.dataPosition();
-                try {
-                    mSource.setDataPosition(mPosition);
-                    mObject = mSource.readValue(mLoader);
-                } finally {
-                    mSource.setDataPosition(restore);
-                }
-                mSource = null;
-                if (mValueParcel != null) {
-                    mValueParcel.recycle();
-                    mValueParcel = null;
+            Parcel source = mSource;
+            if (source != null) {
+                synchronized (source) {
+                    int restore = source.dataPosition();
+                    try {
+                        source.setDataPosition(mPosition);
+                        mObject = source.readValue(mLoader);
+                    } finally {
+                        source.setDataPosition(restore);
+                    }
+                    mSource = null;
                 }
             }
             return mObject;
         }
 
         public void writeToParcel(Parcel out) {
-            if (mObject == null) {
-                int restore = mSource.dataPosition();
-                try {
-                    mSource.setDataPosition(mPosition);
-                    out.writeInt(mSource.readInt()); // Type
-                    out.writeInt(mSource.readInt()); // Length
-                    out.appendFrom(mSource, mSource.dataPosition(), mLength);
-                } finally {
-                    mSource.setDataPosition(restore);
-                }
+            Parcel source = mSource;
+            if (source != null) {
+                out.appendFrom(source, mPosition, mLength + 8);
             } else {
                 out.writeValue(mObject);
             }
         }
 
         public boolean hasFileDescriptors() {
-            return getValueParcel().hasFileDescriptors();
+            Parcel source = mSource;
+            return (source != null)
+                    ? getValueParcel(source).hasFileDescriptors()
+                    : Parcel.hasFileDescriptors(mObject);
         }
 
         @Override
         public String toString() {
-            return mObject == null
+            return (mSource != null)
                     ? "Supplier{" + valueTypeToString(mType) + "@" + mPosition + "+" + mLength + '}'
                     : "Supplier{" + mObject + "}";
         }
@@ -3458,155 +3582,224 @@
                 return false;
             }
             LazyValue value = (LazyValue) other;
-            // Check if they are either both serialized or both deserialized
-            if ((mObject == null) != (value.mObject == null)) {
+            // Check if they are either both serialized or both deserialized.
+            Parcel source = mSource;
+            Parcel otherSource = value.mSource;
+            if ((source == null) != (otherSource == null)) {
                 return false;
             }
-            // If both are deserialized, compare the live objects
-            if (mObject != null) {
-                return mObject.equals(value.mObject);
+            // If both are deserialized, compare the live objects.
+            if (source == null) {
+                // Note that here it's guaranteed that both mObject references contain valid values
+                // (possibly null) since mSource will have provided the memory barrier for those and
+                // once deserialized we never go back to serialized state.
+                return Objects.equals(mObject, value.mObject);
             }
-            // Better safely fail here since this could mean we get different objects
+            // Better safely fail here since this could mean we get different objects.
             if (!Objects.equals(mLoader, value.mLoader)) {
                 return false;
             }
-            // Otherwise compare metadata prior to comparing payload
+            // Otherwise compare metadata prior to comparing payload.
             if (mType != value.mType || mLength != value.mLength) {
                 return false;
             }
-            // Finally we compare the payload
-            return getValueParcel().compareData(value.getValueParcel()) == 0;
+            // Finally we compare the payload.
+            return getValueParcel(source).compareData(value.getValueParcel(otherSource)) == 0;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mObject, mLoader, mType, mLength);
+            // Accessing mSource first to provide memory barrier for mObject
+            return Objects.hash(mSource == null, mObject, mLoader, mType, mLength);
         }
 
         /** This extracts the parcel section responsible for the object and returns it. */
-        private Parcel getValueParcel() {
-            if (mValueParcel == null) {
-                mValueParcel = Parcel.obtain();
+        private Parcel getValueParcel(Parcel source) {
+            Parcel parcel = mValueParcel;
+            if (parcel == null) {
+                parcel = Parcel.obtain();
                 // mLength is the length of object representation, excluding the type and length.
                 // mPosition is the position of the entire value container, right before the type.
                 // So, we add 4 bytes for the type + 4 bytes for the length written.
-                mValueParcel.appendFrom(mSource, mPosition, mLength + 8);
+                parcel.appendFrom(source, mPosition, mLength + 8);
+                mValueParcel = parcel;
             }
-            return mValueParcel;
+            return parcel;
         }
     }
 
     /**
      * Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
      * type first.
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
      */
+    @SuppressWarnings("unchecked")
     @Nullable
-    private Object readValue(int type, @Nullable ClassLoader loader) {
+    private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        final Object object;
         switch (type) {
-        case VAL_NULL:
-            return null;
+            case VAL_NULL:
+                object = null;
+                break;
 
-        case VAL_STRING:
-            return readString();
+            case VAL_STRING:
+                object = readString();
+                break;
 
-        case VAL_INTEGER:
-            return readInt();
+            case VAL_INTEGER:
+                object = readInt();
+                break;
 
-        case VAL_MAP:
-            return readHashMap(loader);
+            case VAL_MAP:
+                object = readHashMap(loader);
+                break;
 
-        case VAL_PARCELABLE:
-            return readParcelable(loader);
+            case VAL_PARCELABLE:
+                object = readParcelableInternal(loader, clazz);
+                break;
 
-        case VAL_SHORT:
-            return (short) readInt();
+            case VAL_SHORT:
+                object = (short) readInt();
+                break;
 
-        case VAL_LONG:
-            return readLong();
+            case VAL_LONG:
+                object = readLong();
+                break;
 
-        case VAL_FLOAT:
-            return readFloat();
+            case VAL_FLOAT:
+                object = readFloat();
+                break;
 
-        case VAL_DOUBLE:
-            return readDouble();
+            case VAL_DOUBLE:
+                object = readDouble();
+                break;
 
-        case VAL_BOOLEAN:
-            return readInt() == 1;
+            case VAL_BOOLEAN:
+                object = readInt() == 1;
+                break;
 
-        case VAL_CHARSEQUENCE:
-            return readCharSequence();
+            case VAL_CHARSEQUENCE:
+                object = readCharSequence();
+                break;
 
-        case VAL_LIST:
-            return readArrayList(loader);
+            case VAL_LIST:
+                object = readArrayList(loader);
+                break;
 
-        case VAL_BOOLEANARRAY:
-            return createBooleanArray();
+            case VAL_BOOLEANARRAY:
+                object = createBooleanArray();
+                break;
 
-        case VAL_BYTEARRAY:
-            return createByteArray();
+            case VAL_BYTEARRAY:
+                object = createByteArray();
+                break;
 
-        case VAL_STRINGARRAY:
-            return readStringArray();
+            case VAL_STRINGARRAY:
+                object = readStringArray();
+                break;
 
-        case VAL_CHARSEQUENCEARRAY:
-            return readCharSequenceArray();
+            case VAL_CHARSEQUENCEARRAY:
+                object = readCharSequenceArray();
+                break;
 
-        case VAL_IBINDER:
-            return readStrongBinder();
+            case VAL_IBINDER:
+                object = readStrongBinder();
+                break;
 
-        case VAL_OBJECTARRAY:
-            return readArray(loader);
+            case VAL_OBJECTARRAY:
+                object = readArray(loader);
+                break;
 
-        case VAL_INTARRAY:
-            return createIntArray();
+            case VAL_INTARRAY:
+                object = createIntArray();
+                break;
 
-        case VAL_LONGARRAY:
-            return createLongArray();
+            case VAL_LONGARRAY:
+                object = createLongArray();
+                break;
 
-        case VAL_BYTE:
-            return readByte();
+            case VAL_BYTE:
+                object = readByte();
+                break;
 
-        case VAL_SERIALIZABLE:
-            return readSerializable(loader);
+            case VAL_SERIALIZABLE:
+                object = readSerializable(loader);
+                break;
 
-        case VAL_PARCELABLEARRAY:
-            return readParcelableArray(loader);
+            case VAL_PARCELABLEARRAY:
+                object = readParcelableArray(loader);
+                break;
 
-        case VAL_SPARSEARRAY:
-            return readSparseArray(loader);
+            case VAL_SPARSEARRAY:
+                object = readSparseArray(loader);
+                break;
 
-        case VAL_SPARSEBOOLEANARRAY:
-            return readSparseBooleanArray();
+            case VAL_SPARSEBOOLEANARRAY:
+                object = readSparseBooleanArray();
+                break;
 
-        case VAL_BUNDLE:
-            return readBundle(loader); // loading will be deferred
+            case VAL_BUNDLE:
+                object = readBundle(loader); // loading will be deferred
+                break;
 
-        case VAL_PERSISTABLEBUNDLE:
-            return readPersistableBundle(loader);
+            case VAL_PERSISTABLEBUNDLE:
+                object = readPersistableBundle(loader);
+                break;
 
-        case VAL_SIZE:
-            return readSize();
+            case VAL_SIZE:
+                object = readSize();
+                break;
 
-        case VAL_SIZEF:
-            return readSizeF();
+            case VAL_SIZEF:
+                object = readSizeF();
+                break;
 
-        case VAL_DOUBLEARRAY:
-            return createDoubleArray();
+            case VAL_DOUBLEARRAY:
+                object = createDoubleArray();
+                break;
 
-        default:
-            int off = dataPosition() - 4;
-            throw new RuntimeException(
-                "Parcel " + this + ": Unmarshalling unknown type code " + type + " at offset " + off);
+            case VAL_CHAR:
+                object = (char) readInt();
+                break;
+
+            case VAL_SHORTARRAY:
+                object = createShortArray();
+                break;
+
+            case VAL_CHARARRAY:
+                object = createCharArray();
+                break;
+
+            case VAL_FLOATARRAY:
+                object = createFloatArray();
+                break;
+
+            default:
+                int off = dataPosition() - 4;
+                throw new RuntimeException(
+                    "Parcel " + this + ": Unmarshalling unknown type code " + type
+                            + " at offset " + off);
         }
+        if (clazz != null && !clazz.isInstance(object)) {
+            throw new BadParcelableException("Unparcelled object " + object
+                    + " is not an instance of required class " + clazz.getName()
+                    + " provided in the parameter");
+        }
+        return (T) object;
     }
 
     private boolean isLengthPrefixed(int type) {
+        // In general, we want custom types and containers of custom types to be length-prefixed,
+        // this allows clients (eg. Bundle) to skip their content during deserialization. The
+        // exception to this is Bundle, since Bundle is already length-prefixed and already copies
+        // the correspondent section of the parcel internally.
         switch (type) {
+            case VAL_MAP:
             case VAL_PARCELABLE:
-            case VAL_PARCELABLEARRAY:
             case VAL_LIST:
             case VAL_SPARSEARRAY:
-            case VAL_BUNDLE:
+            case VAL_PARCELABLEARRAY:
+            case VAL_OBJECTARRAY:
             case VAL_SERIALIZABLE:
                 return true;
             default:
@@ -3625,17 +3818,42 @@
      * @throws BadParcelableException Throws BadParcelableException if there
      * was an error trying to instantiate the Parcelable.
      */
-    @SuppressWarnings("unchecked")
     @Nullable
     public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
-        Parcelable.Creator<?> creator = readParcelableCreator(loader);
+        return readParcelableInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readParcelable(ClassLoader)} but accepts {@code clazz} parameter as the type
+     * required for each item. If the item to be deserialized is not an instance of that class or
+     * any of its children classes a {@link BadParcelableException} will be thrown.
+     */
+    @Nullable
+    public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
+            @NonNull Class<T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readParcelableInternal(loader, clazz);
+    }
+
+    /**
+     *
+     * @param clazz The type of the parcelable expected or {@code null} for performing no checks.
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        if (clazz != null && !Parcelable.class.isAssignableFrom(clazz)) {
+            throw new BadParcelableException("About to unparcel a parcelable object "
+                    + " but class required " + clazz.getName() + " is not Parcelable");
+        }
+        Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
         if (creator == null) {
             return null;
         }
         if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
-          Parcelable.ClassLoaderCreator<?> classLoaderCreator =
-              (Parcelable.ClassLoaderCreator<?>) creator;
-          return (T) classLoaderCreator.createFromParcel(this, loader);
+            Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+                    (Parcelable.ClassLoaderCreator<?>) creator;
+            return (T) classLoaderCreator.createFromParcel(this, loader);
         }
         return (T) creator.createFromParcel(this);
     }
@@ -3669,6 +3887,28 @@
      */
     @Nullable
     public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
+        return readParcelableCreatorInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readParcelableCreator(ClassLoader)} but accepts {@code clazz} parameter
+     * as the required type. If the item to be deserialized is not an instance of that class
+     * or any of its children classes a {@link BadParcelableException} will be thrown.
+     */
+    @Nullable
+    public <T> Parcelable.Creator<T> readParcelableCreator(
+            @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readParcelableCreatorInternal(loader, clazz);
+    }
+
+    /**
+     * @param clazz The type of the parcelable expected or {@code null} for performing no checks.
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> Parcelable.Creator<T> readParcelableCreatorInternal(
+            @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
         String name = readString();
         if (name == null) {
             return null;
@@ -3684,7 +3924,15 @@
             creator = map.get(name);
         }
         if (creator != null) {
-            return creator;
+            if (clazz != null) {
+                Class<?> parcelableClass = creator.getClass().getEnclosingClass();
+                if (!clazz.isAssignableFrom(parcelableClass)) {
+                    throw new BadParcelableException("Parcelable creator " + name + " is not "
+                            + "a subclass of required class " + clazz.getName()
+                            + " provided in the parameter");
+                }
+            }
+            return (Parcelable.Creator<T>) creator;
         }
 
         try {
@@ -3700,6 +3948,14 @@
                 throw new BadParcelableException("Parcelable protocol requires subclassing "
                         + "from Parcelable on class " + name);
             }
+            if (clazz != null) {
+                if (!clazz.isAssignableFrom(parcelableClass)) {
+                    throw new BadParcelableException("Parcelable creator " + name + " is not "
+                            + "a subclass of required class " + clazz.getName()
+                            + " provided in the parameter");
+                }
+            }
+
             Field f = parcelableClass.getField("CREATOR");
             if ((f.getModifiers() & Modifier.STATIC) == 0) {
                 throw new BadParcelableException("Parcelable protocol requires "
@@ -3737,7 +3993,7 @@
             map.put(name, creator);
         }
 
-        return creator;
+        return (Parcelable.Creator<T>) creator;
     }
 
     /**
@@ -3983,13 +4239,21 @@
         return result;
     }
 
-    private void readListInternal(@NonNull List outVal, int N,
+    private void readListInternal(@NonNull List outVal, int n,
             @Nullable ClassLoader loader) {
-        while (N > 0) {
-            Object value = readValue(loader);
+        readListInternal(outVal, n, loader, null);
+    }
+
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    private <T> void readListInternal(@NonNull List<? super T> outVal, int n,
+            @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        while (n > 0) {
+            T value = readValue(loader, clazz);
             //Log.d(TAG, "Unmarshalling value=" + value);
             outVal.add(value);
-            N--;
+            n--;
         }
     }
 
@@ -4068,6 +4332,10 @@
             case VAL_SIZE: return "VAL_SIZE";
             case VAL_SIZEF: return "VAL_SIZEF";
             case VAL_DOUBLEARRAY: return "VAL_DOUBLEARRAY";
+            case VAL_CHAR: return "VAL_CHAR";
+            case VAL_SHORTARRAY: return "VAL_SHORTARRAY";
+            case VAL_CHARARRAY: return "VAL_CHARARRAY";
+            case VAL_FLOATARRAY: return "VAL_FLOATARRAY";
             case VAL_OBJECTARRAY: return "VAL_OBJECTARRAY";
             case VAL_SERIALIZABLE: return "VAL_SERIALIZABLE";
             default: return "UNKNOWN(" + type + ")";
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index a537c98..a396211 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.NonNull;
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 
@@ -202,7 +203,7 @@
      * @param flags Additional flags about how the object should be written.
      * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
      */
-    public void writeToParcel(Parcel dest, @WriteFlags int flags);
+    public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags);
 
     /**
      * Interface that must be implemented and provided as a public CREATOR
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 06a2c87..4e8418b 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -29,7 +29,14 @@
 
 import java.util.Map;
 
-/** @hide */
+/**
+ * Manage binder services as registered with the binder context manager. These services must be
+ * declared statically on an Android device (SELinux access_vector service_manager, w/ service
+ * names in service_contexts files), and they do not follow the activity lifecycle. When
+ * building applications, android.app.Service should be preferred.
+ *
+ * @hide
+ **/
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class ServiceManager {
     private static final String TAG = "ServiceManager";
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 755c35f..3739040 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -98,6 +98,10 @@
         return mServiceManager.updatableViaApex(name);
     }
 
+    public ConnectionInfo getConnectionInfo(String name) throws RemoteException {
+        return mServiceManager.getConnectionInfo(name);
+    }
+
     public void registerClientCallback(String name, IBinder service, IClientCallback cb)
             throws RemoteException {
         throw new RemoteException();
diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java
index 9bfa8ad..a6316df 100644
--- a/core/java/android/os/SystemConfigManager.java
+++ b/core/java/android/os/SystemConfigManager.java
@@ -17,6 +17,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -129,4 +130,21 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get enabled component for a specific package
+     *
+     * @param packageName The target package.
+     * @return The enabled component
+     * {@hide}
+     */
+    @SystemApi
+    @NonNull
+    public List<String> getEnabledComponentOverrides(@NonNull String packageName) {
+        try {
+            return mInterface.getEnabledComponentOverrides(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index bf0b655..1f11197 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -97,8 +97,11 @@
      * ["android.hidl.manager@1.0", "android.hardware.camera.device@1.0",
      *  "android.hardware.camera.device@3.2"]. There are no duplicates.
      *
-     * For AIDL HALs, the version is stripped away
-     * (e.g. "android.hardware.light").
+     * For AIDL HALs, the version is a single number
+     * (e.g. "android.hardware.light@1"). Historically, this API strips the
+     * version number for AIDL HALs (e.g. "android.hardware.light"). Users
+     * of this API must be able to handle both for backwards compatibility.
+     *
      * @hide
      */
     @TestApi
diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java
index 121fd33..fc17002 100644
--- a/core/java/android/os/connectivity/CellularBatteryStats.java
+++ b/core/java/android/os/connectivity/CellularBatteryStats.java
@@ -109,7 +109,7 @@
                         CellSignalStrength.getNumSignalStrengthLevels()));
         mTxTimeMs = Arrays.copyOfRange(
                 txTimeMs, 0,
-                Math.min(txTimeMs.length, ModemActivityInfo.TX_POWER_LEVELS));
+                Math.min(txTimeMs.length, ModemActivityInfo.getNumTxPowerLevels()));
         mMonitoredRailChargeConsumedMaMs = monitoredRailChargeConsumedMaMs;
     }
 
diff --git a/core/java/android/permission/OWNERS b/core/java/android/permission/OWNERS
index 19a3a8b..b5466b6 100644
--- a/core/java/android/permission/OWNERS
+++ b/core/java/android/permission/OWNERS
@@ -1,11 +1,12 @@
 # Bug component: 137825
 
-eugenesusla@google.com
 evanseverson@google.com
 evanxinchen@google.com
 ewol@google.com
 guojing@google.com
 jaysullivan@google.com
+olekarg@google.com
+pyuli@google.com
 ntmyren@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index f0f0867..505f400 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -2351,7 +2351,10 @@
         final int ellipsisStringLen = ellipsisString.length();
         // Use the ellipsis string only if there are that at least as many characters to replace.
         final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen;
-        for (int i = 0; i < ellipsisCount; i++) {
+        final int min = Math.max(0, start - ellipsisStart - lineStart);
+        final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart);
+
+        for (int i = min; i < max; i++) {
             final char c;
             if (useEllipsisString && i < ellipsisStringLen) {
                 c = ellipsisString.charAt(i);
@@ -2360,9 +2363,7 @@
             }
 
             final int a = i + ellipsisStart + lineStart;
-            if (start <= a && a < end) {
-                dest[destoff + a - start] = c;
-            }
+            dest[destoff + a - start] = c;
         }
     }
 
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 4cf0a36..418d92c 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -645,7 +645,7 @@
             e.fillInStackTrace();
             Log.w(TAG, "New hash " + hash
                     + " is before end of array hash " + mHashes[index-1]
-                    + " at index " + index + " key " + key, e);
+                    + " at index " + index + (DEBUG ? " key " + key : ""), e);
             put(key, value);
             return;
         }
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 844b156..c7609a6 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -310,7 +310,7 @@
         String extension = null;
 
         // If we couldn't do anything with the hint, move toward the content disposition
-        if (filename == null && contentDisposition != null) {
+        if (contentDisposition != null) {
             filename = parseContentDisposition(contentDisposition);
             if (filename != null) {
                 int index = filename.lastIndexOf('/') + 1;
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index ea39f6d..fcaeeff 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -396,8 +396,9 @@
                 // Prompt user to add this person to contacts
                 final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri);
                 if (extras != null) {
-                    extras.remove(EXTRA_URI_CONTENT);
-                    intent.putExtras(extras);
+                    Bundle bundle = new Bundle(extras);
+                    bundle.remove(EXTRA_URI_CONTENT);
+                    intent.putExtras(bundle);
                 }
                 getContext().startActivity(intent);
             }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index dd2940f..e8db609 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -66,7 +66,6 @@
 import android.telephony.CellSignalStrength;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ModemActivityInfo;
-import android.telephony.ModemActivityInfo.TransmitPower;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
@@ -7205,7 +7204,7 @@
         public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() {
             if (mModemControllerActivity == null) {
                 mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        ModemActivityInfo.TX_POWER_LEVELS);
+                        ModemActivityInfo.getNumTxPowerLevels());
             }
             return mModemControllerActivity;
         }
@@ -8687,7 +8686,7 @@
 
             if (in.readInt() != 0) {
                 mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase,
-                        ModemActivityInfo.TX_POWER_LEVELS, in);
+                        ModemActivityInfo.getNumTxPowerLevels(), in);
             } else {
                 mModemControllerActivity = null;
             }
@@ -9953,7 +9952,7 @@
         mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS);
         mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
-                ModemActivityInfo.TX_POWER_LEVELS);
+                ModemActivityInfo.getNumTxPowerLevels());
         mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
                 mOnBatteryTimeBase);
@@ -11131,26 +11130,7 @@
         }
     }
 
-    private ModemActivityInfo mLastModemActivityInfo =
-            new ModemActivityInfo(0, 0, 0, new int[0], 0);
-
-    private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) {
-        if (activityInfo == null) {
-            return null;
-        }
-        int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
-        for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
-            txTimeMs[i] = activityInfo.getTransmitPowerInfo().get(i).getTimeInMillis()
-                    - mLastModemActivityInfo.getTransmitPowerInfo().get(i).getTimeInMillis();
-        }
-        ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(),
-                activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(),
-                activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(),
-                txTimeMs,
-                activityInfo.getReceiveTimeMillis() - mLastModemActivityInfo.getReceiveTimeMillis());
-        mLastModemActivityInfo = activityInfo;
-        return deltaInfo;
-    }
+    private ModemActivityInfo mLastModemActivityInfo = null;
 
     /**
      * Distribute Cell radio energy info and network traffic to apps.
@@ -11159,7 +11139,9 @@
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
         }
-        ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo);
+        ModemActivityInfo deltaInfo = mLastModemActivityInfo == null ? activityInfo
+                : mLastModemActivityInfo.getDelta(activityInfo);
+        mLastModemActivityInfo = activityInfo;
 
         // Add modem tx power to history.
         addModemTxPowerToHistory(deltaInfo);
@@ -11191,10 +11173,9 @@
                 mModemActivity.getSleepTimeCounter().addCountLocked(
                         deltaInfo.getSleepTimeMillis());
                 mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getReceiveTimeMillis());
-                for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) {
                     mModemActivity.getTxTimeCounters()[lvl]
-                        .addCountLocked(deltaInfo.getTransmitPowerInfo()
-                            .get(lvl).getTimeInMillis());
+                        .addCountLocked(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl));
                 }
 
                 // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -11208,11 +11189,11 @@
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
                             + deltaInfo.getReceiveTimeMillis() *
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
-                    List<TransmitPower> txPowerInfo = deltaInfo.getTransmitPowerInfo();
-                    for (int i = 0; i < Math.min(txPowerInfo.size(),
+                    for (int i = 0; i < Math.min(ModemActivityInfo.getNumTxPowerLevels(),
                             CellSignalStrength.getNumSignalStrengthLevels()); i++) {
-                        energyUsed += txPowerInfo.get(i).getTimeInMillis() * mPowerProfile
-                            .getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+                        energyUsed += deltaInfo.getTransmitDurationMillisAtPowerLevel(i)
+                                * mPowerProfile.getAveragePower(
+                                        PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
                     }
 
                     // We store the power drain as mAms.
@@ -11307,10 +11288,10 @@
                             }
 
                             if (totalTxPackets > 0 && entry.txPackets > 0) {
-                                for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
-                                    long txMs =
-                                            entry.txPackets * deltaInfo.getTransmitPowerInfo()
-                                                .get(lvl).getTimeInMillis();
+                                for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels();
+                                        lvl++) {
+                                    long txMs = entry.txPackets
+                                            * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl);
                                     txMs /= totalTxPackets;
                                     activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
                                 }
@@ -11341,20 +11322,16 @@
         if (activityInfo == null) {
             return;
         }
-        List<TransmitPower> txPowerInfo = activityInfo.getTransmitPowerInfo();
-        if (txPowerInfo == null || txPowerInfo.size() != ModemActivityInfo.TX_POWER_LEVELS) {
-            return;
-        }
         final long elapsedRealtime = mClocks.elapsedRealtime();
         final long uptime = mClocks.uptimeMillis();
         int levelMaxTimeSpent = 0;
-        for (int i = 1; i < txPowerInfo.size(); i++) {
-            if (txPowerInfo.get(i).getTimeInMillis() > txPowerInfo.get(levelMaxTimeSpent)
-                .getTimeInMillis()) {
+        for (int i = 1; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
+            if (activityInfo.getTransmitDurationMillisAtPowerLevel(i)
+                    > activityInfo.getTransmitDurationMillisAtPowerLevel(levelMaxTimeSpent)) {
                 levelMaxTimeSpent = i;
             }
         }
-        if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
+        if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) {
             mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
             addHistoryRecordLocked(elapsedRealtime, uptime);
         }
@@ -12771,7 +12748,7 @@
            timeInRxSignalStrengthLevelMs[i]
                = getPhoneSignalStrengthTime(i, rawRealTime, which) / 1000;
         }
-        long[] txTimeMs = new long[Math.min(ModemActivityInfo.TX_POWER_LEVELS,
+        long[] txTimeMs = new long[Math.min(ModemActivityInfo.getNumTxPowerLevels(),
             counter.getTxTimeCounters().length)];
         long totalTxTimeMs = 0;
         for (int i = 0; i < txTimeMs.length; i++) {
@@ -14817,7 +14794,7 @@
         mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS, in);
         mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
-                ModemActivityInfo.TX_POWER_LEVELS, in);
+                ModemActivityInfo.getNumTxPowerLevels(), in);
         mHasWifiReporting = in.readInt() != 0;
         mHasBluetoothReporting = in.readInt() != 0;
         mHasModemReporting = in.readInt() != 0;
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index ea3b3a7..7766b77 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -10,4 +10,6 @@
 per-file *ChargeCalculator* = file:/BATTERY_STATS_OWNERS
 per-file *PowerCalculator* = file:/BATTERY_STATS_OWNERS
 per-file *PowerEstimator* = file:/BATTERY_STATS_OWNERS
+per-file *Kernel* = file:/BATTERY_STATS_OWNERS
+per-file *MultiState* = file:/BATTERY_STATS_OWNERS
 
diff --git a/core/java/com/android/internal/util/IState.java b/core/java/com/android/internal/util/IState.java
index 07837bf..41b3d5e 100644
--- a/core/java/com/android/internal/util/IState.java
+++ b/core/java/com/android/internal/util/IState.java
@@ -27,12 +27,12 @@
 public interface IState {
 
     /**
-     * Returned by processMessage to indicate the the message was processed.
+     * Returned by processMessage to indicate the message was processed.
      */
     static final boolean HANDLED = true;
 
     /**
-     * Returned by processMessage to indicate the the message was NOT processed.
+     * Returned by processMessage to indicate the message was NOT processed.
      */
     static final boolean NOT_HANDLED = false;
 
diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java
index 4613dad..d5c0f60 100644
--- a/core/java/com/android/internal/util/State.java
+++ b/core/java/com/android/internal/util/State.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.util;
 
+import android.annotation.SuppressLint;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Message;
@@ -25,6 +26,7 @@
  *
  * The class for implementing states in a StateMachine
  */
+@SuppressLint("AndroidFrameworkRequiresPermission")
 public class State implements IState {
 
     /**
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 4cff785..cb8d9d1 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -48,7 +48,7 @@
  * in Object Oriented programming and are used to perform initialization and
  * cleanup of the state respectively. The <code>getName</code> method returns the
  * name of the state; the default implementation returns the class name. It may be
- * desirable to have <code>getName</code> return the the state instance name instead,
+ * desirable to have <code>getName</code> return the state instance name instead,
  * in particular if a particular state class has multiple instances.</p>
  *
  * <p>When a state machine is created, <code>addState</code> is used to build the
@@ -433,14 +433,14 @@
 
     /**
      * Convenience constant that maybe returned by processMessage
-     * to indicate the the message was processed and is not to be
+     * to indicate the message was processed and is not to be
      * processed by parent states
      */
     public static final boolean HANDLED = true;
 
     /**
      * Convenience constant that maybe returned by processMessage
-     * to indicate the the message was NOT processed and is to be
+     * to indicate the message was NOT processed and is to be
      * processed by parent states
      */
     public static final boolean NOT_HANDLED = false;
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 701960e..6fb2904 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -75,3 +75,7 @@
 # VINTF
 per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS
 per-file android_os_VintfRuntimeInfo* = file:platform/system/libvintf:/OWNERS
+
+# Battery
+per-file com_android_internal_os_Kernel* = file:/BATTERY_STATS_OWNERS
+per-file com_android_internal_os_*MultiStateCounter* = file:/BATTERY_STATS_OWNERS
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 241570a..ac32038 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -98,6 +98,15 @@
     }
 }
 
+static void android_os_Parcel_markForBinder(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                            jobject binder)
+{
+    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+    if (parcel) {
+        parcel->markForBinder(ibinderForJavaObject(env, binder));
+    }
+}
+
 static jint android_os_Parcel_dataSize(jlong nativePtr)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
@@ -766,7 +775,9 @@
 
 static const JNINativeMethod gParcelMethods[] = {
     // @CriticalNative
-    {"nativeMarkSensitive",             "(J)V", (void*)android_os_Parcel_markSensitive},
+    {"nativeMarkSensitive",       "(J)V", (void*)android_os_Parcel_markSensitive},
+    // @FastNative
+    {"nativeMarkForBinder",       "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_markForBinder},
     // @CriticalNative
     {"nativeDataSize",            "(J)I", (void*)android_os_Parcel_dataSize},
     // @CriticalNative
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 515c08d..288327e 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1129,14 +1129,14 @@
 }
 
 // Relabel directory
-static void relabelDir(const char* path, security_context_t context, fail_fn_t fail_fn) {
+static void relabelDir(const char* path, const char* context, fail_fn_t fail_fn) {
   if (setfilecon(path, context) != 0) {
     fail_fn(CREATE_ERROR("Failed to setfilecon %s %s", path, strerror(errno)));
   }
 }
 
 // Relabel all directories under a path non-recursively.
-static void relabelAllDirs(const char* path, security_context_t context, fail_fn_t fail_fn) {
+static void relabelAllDirs(const char* path, const char* context, fail_fn_t fail_fn) {
   DIR* dir = opendir(path);
   if (dir == nullptr) {
     fail_fn(CREATE_ERROR("Failed to opendir %s", path));
@@ -1211,7 +1211,7 @@
   snprintf(internalDePath, PATH_MAX, "/data/user_de");
   snprintf(externalPrivateMountPath, PATH_MAX, "/mnt/expand");
 
-  security_context_t dataDataContext = nullptr;
+  char* dataDataContext = nullptr;
   if (getfilecon(internalDePath, &dataDataContext) < 0) {
     fail_fn(CREATE_ERROR("Unable to getfilecon on %s %s", internalDePath,
         strerror(errno)));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0135e45c..35a3cde 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -196,6 +196,9 @@
         android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.action.CSIS_CONNECTION_STATE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.action.CSIS_DEVICE_AVAILABLE" />
+    <protected-broadcast android:name="android.bluetooth.action.CSIS_SET_MEMBER_AVAILABLE" />
     <protected-broadcast
         android:name="android.bluetooth.volume-control.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 14d3147..ac19121 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3026,6 +3026,11 @@
          and one pSIM) -->
     <integer name="config_num_physical_slots">1</integer>
 
+    <!-- When a radio power off request is received, we will delay completing the request until
+         either IMS moves to the deregistered state or the timeout defined by this configuration
+         elapses. If 0, this feature is disabled and we do not delay radio power off requests.-->
+    <integer name="config_delay_for_ims_dereg_millis">0</integer>
+
     <!--Thresholds for LTE dbm in status bar-->
     <integer-array translatable="false" name="config_lteDbmThresholds">
         <item>-140</item>    <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6a4702b..c1c1858 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3046,6 +3046,9 @@
     <public name="canPauseRecording" />
     <!-- attribute definitions go here -->
     <public name="requireDeviceScreenOn" />
+  </public-group>
+
+  <public-group type="attr" first-id="0x01010624">
     <public name="memtagMode" />
     <public name="nativeHeapZeroInitialized" />
   </public-group>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2be5152..aebad6a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -476,6 +476,7 @@
   <java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
   <java-symbol type="string" name="config_deviceSpecificAudioService" />
   <java-symbol type="integer" name="config_num_physical_slots" />
+  <java-symbol type="integer" name="config_delay_for_ims_dereg_millis" />
   <java-symbol type="array" name="config_integrityRuleProviderPackages" />
   <java-symbol type="bool" name="config_useAssistantVolume" />
   <java-symbol type="string" name="config_bandwidthEstimateSource" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 37d059a..f1c66c5 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -265,7 +265,7 @@
 
     <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
          visual voicemail code for T-Mobile: 122 -->
-    <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245" />
+    <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611" />
 
     <!-- Vietnam: 1-5 digits (standard system default, not country specific) -->
     <shortcode country="vn" pattern="\\d{1,5}" free="5001|9055" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 000e870..2d63351 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -88,6 +88,11 @@
                     false /* launchActivity */);
 
     @Test
+    public void testTemporaryDirectory() throws Exception {
+        assertEquals(System.getProperty("java.io.tmpdir"), System.getenv("TMPDIR"));
+    }
+
+    @Test
     public void testDoubleRelaunch() throws Exception {
         final Activity activity = mActivityTestRule.launchActivity(new Intent());
         final IApplicationThread appThread = activity.getActivityThread().getApplicationThread();
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index 4cc70ba..9d2cab3 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -16,16 +16,24 @@
 
 package android.os;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
+import android.util.Log;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Objects;
+
 /**
  * Unit tests for bundle that requires accessing hidden APS.  Tests that can be written only with
  * public APIs should go in the CTS counterpart.
@@ -35,6 +43,14 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BundleTest {
+    private Log.TerribleFailureHandler mWtfHandler;
+
+    @After
+    public void tearDown() throws Exception {
+        if (mWtfHandler != null) {
+            Log.setWtfHandler(mWtfHandler);
+        }
+    }
 
     /**
      * Take a bundle, write it to a parcel and return the parcel.
@@ -217,4 +233,193 @@
         // return true
         assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
     }
+
+    @Test
+    public void kindofEquals_lazyValues() {
+        Parcelable p1 = new CustomParcelable(13, "Tiramisu");
+        Parcelable p2 = new CustomParcelable(13, "Tiramisu");
+
+        // 2 maps with live objects
+        Bundle a = new Bundle();
+        a.putParcelable("key1", p1);
+        Bundle b = new Bundle();
+        b.putParcelable("key1", p2);
+        assertTrue(Bundle.kindofEquals(a, b));
+
+        // 2 identical parcels
+        a.readFromParcel(getParcelledBundle(a));
+        a.setClassLoader(getClass().getClassLoader());
+        b.readFromParcel(getParcelledBundle(b));
+        b.setClassLoader(getClass().getClassLoader());
+        assertTrue(Bundle.kindofEquals(a, b));
+
+        // 2 lazy values with identical parcels inside
+        a.isEmpty();
+        b.isEmpty();
+        assertTrue(Bundle.kindofEquals(a, b));
+
+        // 1 lazy value vs 1 live object
+        a.getParcelable("key1");
+        assertFalse(Bundle.kindofEquals(a, b));
+
+        // 2 live objects
+        b.getParcelable("key1");
+        assertTrue(Bundle.kindofEquals(a, b));
+    }
+
+    @Test
+    public void kindofEquals_lazyValuesWithIdenticalParcels_returnsTrue() {
+        Parcelable p1 = new CustomParcelable(13, "Tiramisu");
+        Parcelable p2 = new CustomParcelable(13, "Tiramisu");
+        Bundle a = new Bundle();
+        a.putParcelable("key", p1);
+        a.readFromParcel(getParcelledBundle(a));
+        a.setClassLoader(getClass().getClassLoader());
+        Bundle b = new Bundle();
+        b.putParcelable("key", p2);
+        b.readFromParcel(getParcelledBundle(b));
+        b.setClassLoader(getClass().getClassLoader());
+        // 2 lazy values with identical parcels inside
+        a.isEmpty();
+        b.isEmpty();
+
+        assertTrue(Bundle.kindofEquals(a, b));
+    }
+
+    @Test
+    public void kindofEquals_lazyValuesAndDifferentClassLoaders_returnsFalse() {
+        Parcelable p1 = new CustomParcelable(13, "Tiramisu");
+        Parcelable p2 = new CustomParcelable(13, "Tiramisu");
+        Bundle a = new Bundle();
+        a.putParcelable("key", p1);
+        a.readFromParcel(getParcelledBundle(a));
+        a.setClassLoader(getClass().getClassLoader());
+        Bundle b = new Bundle();
+        b.putParcelable("key", p2);
+        b.readFromParcel(getParcelledBundle(b));
+        b.setClassLoader(Bundle.class.getClassLoader()); // BCP
+        // 2 lazy values with identical parcels inside
+        a.isEmpty();
+        b.isEmpty();
+
+        assertFalse(Bundle.kindofEquals(a, b));
+    }
+
+    @Test
+    public void kindofEquals_lazyValuesOfDifferentTypes_returnsFalse() {
+        Parcelable p = new CustomParcelable(13, "Tiramisu");
+        Parcelable[] ps = {p};
+        Bundle a = new Bundle();
+        a.putParcelable("key", p);
+        a.readFromParcel(getParcelledBundle(a));
+        a.setClassLoader(getClass().getClassLoader());
+        Bundle b = new Bundle();
+        b.putParcelableArray("key", ps);
+        b.readFromParcel(getParcelledBundle(b));
+        b.setClassLoader(getClass().getClassLoader());
+        a.isEmpty();
+        b.isEmpty();
+
+        assertFalse(Bundle.kindofEquals(a, b));
+    }
+
+    @Test
+    public void kindofEquals_lazyValuesWithDifferentLengths_returnsFalse() {
+        Parcelable p1 = new CustomParcelable(13, "Tiramisu");
+        Parcelable p2 = new CustomParcelable(13, "Tiramisuuuuuuuu");
+        Bundle a = new Bundle();
+        a.putParcelable("key", p1);
+        a.readFromParcel(getParcelledBundle(a));
+        a.setClassLoader(getClass().getClassLoader());
+        Bundle b = new Bundle();
+        b.putParcelable("key", p2);
+        b.readFromParcel(getParcelledBundle(b));
+        b.setClassLoader(getClass().getClassLoader());
+        a.isEmpty();
+        b.isEmpty();
+
+        assertFalse(Bundle.kindofEquals(a, b));
+    }
+
+    @Test
+    public void readWriteLengthMismatch_logsWtf() throws Exception {
+        mWtfHandler = Log.setWtfHandler((tag, e, system) -> {
+            throw new RuntimeException(e);
+        });
+        Parcelable parcelable = new CustomParcelable(13, "Tiramisu").setHasLengthMismatch(true);
+        Bundle bundle = new Bundle();
+        bundle.putParcelable("p", parcelable);
+        bundle.readFromParcel(getParcelledBundle(bundle));
+        bundle.setClassLoader(getClass().getClassLoader());
+        RuntimeException e = assertThrows(RuntimeException.class, () -> bundle.getParcelable("p"));
+        assertThat(e.getCause()).isInstanceOf(Log.TerribleFailure.class);
+    }
+
+    private static class CustomParcelable implements Parcelable {
+        public final int integer;
+        public final String string;
+        public boolean hasLengthMismatch;
+
+        CustomParcelable(int integer, String string) {
+            this.integer = integer;
+            this.string = string;
+        }
+
+        protected CustomParcelable(Parcel in) {
+            integer = in.readInt();
+            string = in.readString();
+            hasLengthMismatch = in.readBoolean();
+        }
+
+        public CustomParcelable setHasLengthMismatch(boolean hasLengthMismatch) {
+            this.hasLengthMismatch = hasLengthMismatch;
+            return this;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(integer);
+            out.writeString(string);
+            out.writeBoolean(hasLengthMismatch);
+            if (hasLengthMismatch) {
+                out.writeString("extra-write");
+            }
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof CustomParcelable)) {
+                return false;
+            }
+            CustomParcelable
+                    that = (CustomParcelable) other;
+            return integer == that.integer
+                    && hasLengthMismatch == that.hasLengthMismatch
+                    && string.equals(that.string);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(integer, string, hasLengthMismatch);
+        }
+
+        public static final Creator<CustomParcelable> CREATOR = new Creator<CustomParcelable>() {
+            @Override
+            public CustomParcelable createFromParcel(Parcel in) {
+                return new CustomParcelable(in);
+            }
+            @Override
+            public CustomParcelable[] newArray(int size) {
+                return new CustomParcelable[size];
+            }
+        };
+    }
 }
diff --git a/core/tests/hosttests/Android.mk b/core/tests/hosttests/Android.mk
deleted file mode 100644
index f26d401..0000000
--- a/core/tests/hosttests/Android.mk
+++ /dev/null
@@ -1,20 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/hosttests/test-apps/Android.mk b/core/tests/hosttests/test-apps/Android.mk
deleted file mode 100644
index e25764f..0000000
--- a/core/tests/hosttests/test-apps/Android.mk
+++ /dev/null
@@ -1,21 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.bp b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.bp
new file mode 100644
index 0000000..d439124
--- /dev/null
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.bp
@@ -0,0 +1,38 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "DownloadManagerTestApp",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "android-common",
+        "mockwebserver",
+        "junit",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+
+    platform_apis: true,
+
+    // Need to run as system app to get access to Settings. This test won't work for user builds.
+    certificate: "platform",
+}
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
deleted file mode 100644
index d9e6151..0000000
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ /dev/null
@@ -1,37 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver junit
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_PACKAGE_NAME := DownloadManagerTestApp
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-ifneq ($(TARGET_BUILD_VARIANT),user)
-# Need to run as system app to get access to Settings. This test won't work for user builds.
-LOCAL_CERTIFICATE := platform
-endif
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
new file mode 100644
index 0000000..d0645b0
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.bp
@@ -0,0 +1,47 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// The application with a minimal main dex
+android_test_helper_app {
+    name: "MultiDexLegacyAndException",
+
+    static_libs: [
+        "android-support-multidex",
+        "android-support-multidex-instrumentation",
+        "androidx.test.rules",
+    ],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    javacflags: ["-nowarn"],
+
+    main_dex_rules: [":mainDexClassesRules"],
+    dxflags: [
+        // --debug triggers the old --minimal-main-dex behavior
+        "--debug",
+    ],
+    optimize: {
+        // disable optimization to force D8 instead of R8, as R8 doesn't support
+        // --main-dex-rules.
+        enabled: false,
+    },
+
+    min_sdk_version: "16",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
deleted file mode 100644
index 2d8556f..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ /dev/null
@@ -1,51 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-
-## The application with a minimal main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex android-support-multidex-instrumentation androidx.test.rules
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyAndException
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVACFLAGS := -nowarn
-
-mainDexList:= \
-    $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-LOCAL_MIN_SDK_VERSION := 8
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
-	$(hide) mkdir -p $(dir $@)
-	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
-	echo "com/android/multidexlegacyandexception/Test.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
new file mode 100644
index 0000000..c0c8aba
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.bp
@@ -0,0 +1,70 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// The application with a minimal main dex
+android_test_helper_app {
+    name: "MultiDexLegacyTestApp",
+
+    static_libs: ["android-support-multidex"],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    main_dex_rules: [
+        ":mainDexClassesRules",
+        "mainDexClasses.rules",
+    ],
+    dxflags: [
+        // --debug triggers the old --minimal-main-dex behavior
+        "--debug",
+    ],
+    optimize: {
+        // disable optimization to force D8 instead of R8, as R8 doesn't support
+        // --main-dex-rules.
+        enabled: false,
+    },
+
+    min_sdk_version: "16",
+}
+
+android_test_helper_app {
+    name: "MultiDexLegacyTestApp2",
+
+    static_libs: ["android-support-multidex"],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    main_dex_rules: [
+        ":mainDexClassesRules",
+        "mainDexClasses.rules",
+    ],
+    dxflags: [
+        // --release disables the old --minimal-main-dex behavior
+        "--release",
+    ],
+    optimize: {
+        // disable optimization to force D8 instead of R8, as R8 doesn't support
+        // --main-dex-rules.
+        enabled: false,
+    },
+
+    min_sdk_version: "16",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
deleted file mode 100644
index d7af2d9..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ /dev/null
@@ -1,87 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-
-## The application with a minimal main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-mainDexList:= \
-	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-LOCAL_MIN_SDK_VERSION := 8
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
-	$(hide) mkdir -p $(dir $@)
-	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
-	echo "com/android/multidexlegacytestapp/Test.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
-
-## The application with a full main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-mainDexList2:= \
-	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList2)
-
-LOCAL_MIN_SDK_VERSION := 8
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList2): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
-	$(hide) mkdir -p $(dir $@)
-	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
-	echo "com/android/multidexlegacytestapp/Test.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList2)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/mainDexClasses.rules b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/mainDexClasses.rules
new file mode 100644
index 0000000..91e6ddb
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/mainDexClasses.rules
@@ -0,0 +1 @@
+-keep class com.android.multidexlegacytestapp.Test
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
new file mode 100644
index 0000000..fe29416
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.bp
@@ -0,0 +1,34 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// The application with a minimal main dex
+android_test {
+    name: "MultiDexLegacyTestAppTests",
+
+    static_libs: ["android-support-multidex-instrumentation"],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    javacflags: ["-nowarn"],
+
+    min_sdk_version: "16",
+
+    instrumentation_for: "MultiDexLegacyTestApp",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk
deleted file mode 100644
index 236c740..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests/Android.mk
+++ /dev/null
@@ -1,43 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-
-## The application with a minimal main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestAppTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVACFLAGS := -nowarn
-
-LOCAL_MIN_SDK_VERSION := 8
-
-LOCAL_INSTRUMENTATION_FOR := MultiDexLegacyTestApp
-
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.dex.output.multidex.legacy=true
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
new file mode 100644
index 0000000..c558153
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.bp
@@ -0,0 +1,57 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+// The tests with only one dex
+android_test {
+    name: "MultiDexLegacyTestAppTests2",
+
+    static_libs: [
+        "android-support-multidex-instrumentation",
+        "androidx.test.rules",
+    ],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    javacflags: ["-nowarn"],
+
+    min_sdk_version: "16",
+
+    instrumentation_for: "MultiDexLegacyTestApp",
+}
+
+// The tests with a minimal main dex
+android_test {
+    name: "MultiDexLegacyTestAppTests2-multidex",
+
+    static_libs: [
+        "android-support-multidex-instrumentation",
+        "androidx.test.rules",
+    ],
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    javacflags: ["-nowarn"],
+
+    min_sdk_version: "16",
+
+    instrumentation_for: "MultiDexLegacyTestApp",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk
deleted file mode 100644
index 6f6ccfe..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppTests2/Android.mk
+++ /dev/null
@@ -1,69 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-
-## The tests with only one dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation androidx.test.rules
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestAppTests2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVACFLAGS := -nowarn
-
-LOCAL_MIN_SDK_VERSION := 8
-
-LOCAL_INSTRUMENTATION_FOR := MultiDexLegacyTestApp
-
-include $(BUILD_PACKAGE)
-
-
-## The tests with a minimal main dex
-include $(CLEAR_VARS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex-instrumentation androidx.test.rules
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 8
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestAppTests2-multidex
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_JAVACFLAGS := -nowarn
-
-LOCAL_MIN_SDK_VERSION := 8
-
-LOCAL_INSTRUMENTATION_FOR := MultiDexLegacyTestApp
-
-LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.dex.output.multidex.legacy=true
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.bp
new file mode 100644
index 0000000..15bc4ef
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.bp
@@ -0,0 +1,48 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "MultiDexLegacyTestApp_without_corrupted",
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    static_libs: ["android-support-multidex"],
+}
+
+java_genrule {
+    name: "MultiDexLegacyTestApp_genrule",
+    srcs: [
+        ":MultiDexLegacyTestApp_without_corrupted",
+    ],
+    tools: [
+        "soong_zip",
+        "merge_zips",
+    ],
+    out: ["MultiDexLegacyTestApp_with_corrupted.apk"],
+    cmd: "touch $(genDir)/classes2.dex &&" +
+        " $(location soong_zip) -o $(genDir)/corrupted.zip -j -f $(genDir)/classes2.dex &&" +
+        " $(location merge_zips) $(out) $(location :MultiDexLegacyTestApp_without_corrupted) $(genDir)/corrupted.zip",
+}
+
+android_test_import {
+    name: "MultiDexLegacyTestApp_corrupted",
+    apk: ":MultiDexLegacyTestApp_genrule",
+    default_dev_cert: true,
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk
deleted file mode 100644
index 33a46ea..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestAppWithCorruptedDex/Android.mk
+++ /dev/null
@@ -1,41 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 18
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp_corrupted
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_PACKAGE)
-
-corrupted_classes2_dex := $(dir $(built_dex))/classes2.dex
-
-$(corrupted_classes2_dex): $(built_dex)
-	$(hide) touch $@
-
-$(LOCAL_BUILT_MODULE): $(corrupted_classes2_dex)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.bp
new file mode 100644
index 0000000..de0657f
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.bp
@@ -0,0 +1,38 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "MultiDexLegacyTestServices",
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    static_libs: ["android-support-multidex"],
+
+    main_dex_rules: [":mainDexClassesRules"],
+    dxflags: [
+        // --debug triggers the old --minimal-main-dex behavior
+        "--debug",
+    ],
+    optimize: {
+        // disable optimization to force D8 instead of R8, as R8 doesn't support
+        // --main-dex-rules.
+        enabled: false,
+    },
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
deleted file mode 100644
index efc0688..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ /dev/null
@@ -1,47 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestServices
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-mainDexList:= \
-	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
-	$(hide) mkdir -p $(dir $@)
-	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp
index 56f10fe..b62b25c 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests/Android.bp
@@ -24,7 +24,7 @@
 android_test {
     name: "MultiDexLegacyTestServicesTests",
     srcs: ["src/**/*.java"],
-    sdk_version: "9",
+    sdk_version: "16",
     dex_preopt: {
         enabled: false,
     },
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
new file mode 100644
index 0000000..75c753c
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.bp
@@ -0,0 +1,28 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "MultiDexLegacyTestServicesTests2",
+
+    srcs: ["src/**/*.java"],
+
+    libs: ["android-support-multidex"],
+    static_libs: ["androidx.test.rules"],
+
+    sdk_version: "16",
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
deleted file mode 100644
index 3920fd6..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServicesTests2/Android.mk
+++ /dev/null
@@ -1,35 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyTestServicesTests2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_JAVA_LIBRARIES := android-support-multidex
-LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_PACKAGE)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.bp
new file mode 100644
index 0000000..23c62dc
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.bp
@@ -0,0 +1,41 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "MultiDexLegacyVersionedTestApp_v1",
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    static_libs: ["android-support-multidex"],
+
+    main_dex_rules: [
+        ":mainDexClassesRules",
+        "mainDexClasses.rules",
+    ],
+    dxflags: [
+        // --debug triggers the old --minimal-main-dex behavior
+        "--debug",
+    ],
+    optimize: {
+        // disable optimization to force D8 instead of R8, as R8 doesn't support
+        // --main-dex-rules.
+        enabled: false,
+    },
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
deleted file mode 100644
index 2323ad9..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ /dev/null
@@ -1,48 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v1
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-mainDexList:= \
-	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
-	$(hide) mkdir -p $(dir $@)
-	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
-	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/mainDexClasses.rules b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/mainDexClasses.rules
new file mode 100644
index 0000000..1cdf3af
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/mainDexClasses.rules
@@ -0,0 +1 @@
+-keep class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.bp
new file mode 100644
index 0000000..6cd3df7
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.bp
@@ -0,0 +1,41 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "MultiDexLegacyVersionedTestApp_v2",
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    static_libs: ["android-support-multidex"],
+
+    main_dex_rules: [
+        ":mainDexClassesRules",
+        "mainDexClasses.rules",
+    ],
+    dxflags: [
+        // --debug triggers the old --minimal-main-dex behavior
+        "--debug",
+    ],
+    optimize: {
+        // disable optimization to force D8 instead of R8, as R8 doesn't support
+        // --main-dex-rules.
+        enabled: false,
+    },
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
deleted file mode 100644
index 79a5906..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ /dev/null
@@ -1,48 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v2
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-mainDexList:= \
-	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
-	$(hide) mkdir -p $(dir $@)
-	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
-	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/mainDexClasses.rules b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/mainDexClasses.rules
new file mode 100644
index 0000000..1cdf3af
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/mainDexClasses.rules
@@ -0,0 +1 @@
+-keep class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.bp b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.bp
new file mode 100644
index 0000000..34dba40
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.bp
@@ -0,0 +1,41 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "MultiDexLegacyVersionedTestApp_v3",
+
+    srcs: ["src/**/*.java"],
+
+    sdk_version: "16",
+
+    static_libs: ["android-support-multidex"],
+
+    main_dex_rules: [
+        ":mainDexClassesRules",
+        "mainDexClasses.rules",
+    ],
+    dxflags: [
+        // --debug triggers the old --minimal-main-dex behavior
+        "--debug",
+    ],
+    optimize: {
+        // disable optimization to force D8 instead of R8, as R8 doesn't support
+        // --main-dex-rules.
+        enabled: false,
+    },
+}
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
deleted file mode 100644
index 521bad0..0000000
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ /dev/null
@@ -1,48 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 9
-
-LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v3
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
-
-mainDexList:= \
-	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_EMMA_INSTRUMENT := false
-
-LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
-
-include $(BUILD_PACKAGE)
-
-$(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
-	$(hide) mkdir -p $(dir $@)
-	PROGUARD_HOME=$(PROGUARD_HOME) $(MAINDEXCLASSES) $< 1>$@
-	echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
-
-$(built_dex_intermediate): $(mainDexList)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/mainDexClasses.rules b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/mainDexClasses.rules
new file mode 100644
index 0000000..1cdf3af
--- /dev/null
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/mainDexClasses.rules
@@ -0,0 +1 @@
+-keep class com.android.framework.multidexlegacyversionedtestapp.MultiDexUpdateTest
diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
index edf473e..b85cb9c 100644
--- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
@@ -542,83 +542,83 @@
     public void testStateMachineEnterExitTransitionToTest() throws Exception {
         //if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
 
-        StateMachineEnterExitTransitionToTest smEnterExitTranstionToTest =
-            new StateMachineEnterExitTransitionToTest("smEnterExitTranstionToTest");
-        smEnterExitTranstionToTest.start();
-        if (smEnterExitTranstionToTest.isDbg()) {
+        StateMachineEnterExitTransitionToTest smEnterExitTransitionToTest =
+                new StateMachineEnterExitTransitionToTest("smEnterExitTransitionToTest");
+        smEnterExitTransitionToTest.start();
+        if (smEnterExitTransitionToTest.isDbg()) {
             tlog("testStateMachineEnterExitTransitionToTest E");
         }
 
-        synchronized (smEnterExitTranstionToTest) {
-            smEnterExitTranstionToTest.sendMessage(TEST_CMD_1);
+        synchronized (smEnterExitTransitionToTest) {
+            smEnterExitTransitionToTest.sendMessage(TEST_CMD_1);
 
             try {
                 // wait for the messages to be handled
-                smEnterExitTranstionToTest.wait();
+                smEnterExitTransitionToTest.wait();
             } catch (InterruptedException e) {
                 tloge("testStateMachineEnterExitTransitionToTest: exception while waiting "
                     + e.getMessage());
             }
         }
 
-        dumpLogRecs(smEnterExitTranstionToTest);
+        dumpLogRecs(smEnterExitTransitionToTest);
 
-        assertEquals(9, smEnterExitTranstionToTest.getLogRecCount());
+        assertEquals(9, smEnterExitTransitionToTest.getLogRecCount());
         LogRec lr;
 
-        lr = smEnterExitTranstionToTest.getLogRec(0);
+        lr = smEnterExitTransitionToTest.getLogRec(0);
         assertEquals(ENTER, lr.getInfo());
-        assertEquals(smEnterExitTranstionToTest.mS1, lr.getState());
+        assertEquals(smEnterExitTransitionToTest.mS1, lr.getState());
 
-        lr = smEnterExitTranstionToTest.getLogRec(1);
+        lr = smEnterExitTransitionToTest.getLogRec(1);
         assertEquals(EXIT, lr.getInfo());
-        assertEquals(smEnterExitTranstionToTest.mS1, lr.getState());
+        assertEquals(smEnterExitTransitionToTest.mS1, lr.getState());
 
-        lr = smEnterExitTranstionToTest.getLogRec(2);
+        lr = smEnterExitTransitionToTest.getLogRec(2);
         assertEquals(ENTER, lr.getInfo());
-        assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
+        assertEquals(smEnterExitTransitionToTest.mS2, lr.getState());
 
-        lr = smEnterExitTranstionToTest.getLogRec(3);
+        lr = smEnterExitTransitionToTest.getLogRec(3);
         assertEquals(TEST_CMD_1, lr.getWhat());
-        assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
-        assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState());
-        assertEquals(smEnterExitTranstionToTest.mS3, lr.getDestState());
+        assertEquals(smEnterExitTransitionToTest.mS2, lr.getState());
+        assertEquals(smEnterExitTransitionToTest.mS2, lr.getOriginalState());
+        assertEquals(smEnterExitTransitionToTest.mS3, lr.getDestState());
 
-        lr = smEnterExitTranstionToTest.getLogRec(4);
+        lr = smEnterExitTransitionToTest.getLogRec(4);
         assertEquals(TEST_CMD_1, lr.getWhat());
-        assertEquals(smEnterExitTranstionToTest.mS2, lr.getState());
-        assertEquals(smEnterExitTranstionToTest.mS2, lr.getOriginalState());
-        assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
+        assertEquals(smEnterExitTransitionToTest.mS2, lr.getState());
+        assertEquals(smEnterExitTransitionToTest.mS2, lr.getOriginalState());
+        assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState());
         assertEquals(EXIT, lr.getInfo());
 
-        lr = smEnterExitTranstionToTest.getLogRec(5);
+        lr = smEnterExitTransitionToTest.getLogRec(5);
         assertEquals(TEST_CMD_1, lr.getWhat());
         assertEquals(ENTER, lr.getInfo());
-        assertEquals(smEnterExitTranstionToTest.mS3, lr.getState());
-        assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState());
-        assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
+        assertEquals(smEnterExitTransitionToTest.mS3, lr.getState());
+        assertEquals(smEnterExitTransitionToTest.mS3, lr.getOriginalState());
+        assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState());
 
-        lr = smEnterExitTranstionToTest.getLogRec(6);
+        lr = smEnterExitTransitionToTest.getLogRec(6);
         assertEquals(TEST_CMD_1, lr.getWhat());
         assertEquals(EXIT, lr.getInfo());
-        assertEquals(smEnterExitTranstionToTest.mS3, lr.getState());
-        assertEquals(smEnterExitTranstionToTest.mS3, lr.getOriginalState());
-        assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
+        assertEquals(smEnterExitTransitionToTest.mS3, lr.getState());
+        assertEquals(smEnterExitTransitionToTest.mS3, lr.getOriginalState());
+        assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState());
 
-        lr = smEnterExitTranstionToTest.getLogRec(7);
+        lr = smEnterExitTransitionToTest.getLogRec(7);
         assertEquals(TEST_CMD_1, lr.getWhat());
         assertEquals(ENTER, lr.getInfo());
-        assertEquals(smEnterExitTranstionToTest.mS4, lr.getState());
-        assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState());
-        assertEquals(smEnterExitTranstionToTest.mS4, lr.getDestState());
+        assertEquals(smEnterExitTransitionToTest.mS4, lr.getState());
+        assertEquals(smEnterExitTransitionToTest.mS4, lr.getOriginalState());
+        assertEquals(smEnterExitTransitionToTest.mS4, lr.getDestState());
 
-        lr = smEnterExitTranstionToTest.getLogRec(8);
+        lr = smEnterExitTransitionToTest.getLogRec(8);
         assertEquals(TEST_CMD_1, lr.getWhat());
         assertEquals(EXIT, lr.getInfo());
-        assertEquals(smEnterExitTranstionToTest.mS4, lr.getState());
-        assertEquals(smEnterExitTranstionToTest.mS4, lr.getOriginalState());
+        assertEquals(smEnterExitTransitionToTest.mS4, lr.getState());
+        assertEquals(smEnterExitTransitionToTest.mS4, lr.getOriginalState());
 
-        if (smEnterExitTranstionToTest.isDbg()) {
+        if (smEnterExitTransitionToTest.isDbg()) {
             tlog("testStateMachineEnterExitTransitionToTest X");
         }
     }
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 8f175bb..1e68585 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -160,7 +160,7 @@
      * not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
      *
      * <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, byt
+     * from the verifier. The content can be defined in the way appropriate for the credential, but
      * there are three requirements that must be met to work with this API:
      * <ul>
      * <li>The content must be a CBOR-encoded structure.</li>
@@ -205,9 +205,9 @@
      * must appear somewhere in {@code sessionTranscript} and ditto for the 32 bytes for the Y
      * coordinate.
      *
-     * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1}
-     * structure as defined in RFC 8152. For the payload nil shall be used and the
-     * detached payload is the ReaderAuthenticationBytes CBOR described below.
+     * <p>If {@code readerSignature} is not {@code null} it must be the bytes of a
+     * {@code COSE_Sign1} structure as defined in RFC 8152. For the payload nil shall be used and
+     * the detached payload is the ReaderAuthenticationBytes CBOR described below.
      * <pre>
      *     ReaderAuthentication = [
      *       "ReaderAuthentication",
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index e08b99d..d897e94f 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -32,6 +32,8 @@
 
 using namespace android::uirenderer::renderthread;
 
+static constexpr bool sEnableExtraCropInset = true;
+
 namespace android {
 namespace uirenderer {
 
@@ -71,6 +73,20 @@
         ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
         return CopyResult::SourceEmpty;
     }
+
+    if (sEnableExtraCropInset &&
+        (cropRect.right - cropRect.left != bitmap->width() ||
+        cropRect.bottom - cropRect.top != bitmap->height())) {
+       /*
+        * When we need use filtering, we should also make border shrink here like gui.
+        * But we could not check format for YUV or RGB here... Just use 1 pix.
+        */
+        cropRect.left += 0.5f;
+        cropRect.top  += 0.5f;
+        cropRect.right -= 0.5f;
+        cropRect.bottom -= 0.5f;
+    }
+
     UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
     AHardwareBuffer_Desc description;
     AHardwareBuffer_describe(sourceBuffer.get(), &description);
diff --git a/libs/hwui/jni/PaintFilter.cpp b/libs/hwui/jni/PaintFilter.cpp
index ec115b4..86d4742 100644
--- a/libs/hwui/jni/PaintFilter.cpp
+++ b/libs/hwui/jni/PaintFilter.cpp
@@ -74,7 +74,7 @@
     result |= RegisterMethodsOrDie(env, "android/graphics/PaintFlagsDrawFilter", paintflags_methods,
                                    NELEM(paintflags_methods));
 
-    return 0;
+    return result;
 }
 
 }
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 9657b25e..c7c503d 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2178,12 +2178,6 @@
                 if (size == null || size.getWidth() * size.getHeight() <= 0) {
                     continue;
                 }
-                if (size.getWidth() > SIZE_RANGE.getUpper()
-                        || size.getHeight() > SIZE_RANGE.getUpper()) {
-                    size = new Size(
-                            Math.min(size.getWidth(), SIZE_RANGE.getUpper()),
-                            Math.min(size.getHeight(), SIZE_RANGE.getUpper()));
-                }
                 Range<Long> range = Utils.parseLongRange(map.get(key), null);
                 if (range == null || range.getLower() < 0 || range.getUpper() < 0) {
                     continue;
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 559a61d..0f9e89a 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -900,7 +900,7 @@
     private @NonNull List<Bitmap> getFramesAtIndexInternal(
             int frameIndex, int numFrames, @Nullable BitmapParams params) {
         if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO))) {
-            throw new IllegalStateException("Does not contail video or image sequences");
+            throw new IllegalStateException("Does not contain video or image sequences");
         }
         int frameCount = Integer.parseInt(
                 extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_FRAME_COUNT));
@@ -1018,7 +1018,7 @@
 
     private Bitmap getImageAtIndexInternal(int imageIndex, @Nullable BitmapParams params) {
         if (!"yes".equals(extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE))) {
-            throw new IllegalStateException("Does not contail still images");
+            throw new IllegalStateException("Does not contain still images");
         }
 
         String imageCount = extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_COUNT);
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 0d53ab1..2691983 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-
 #include <stdio.h>
+#include <unordered_set>
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AudioEffects-JNI"
@@ -58,21 +58,14 @@
 struct effect_callback_cookie {
     jclass      audioEffect_class;  // AudioEffect class
     jobject     audioEffect_ref;    // AudioEffect object instance
- };
-
-// ----------------------------------------------------------------------------
-class AudioEffectJniStorage {
-    public:
-        effect_callback_cookie mCallbackData;
-
-    AudioEffectJniStorage() {
-    }
-
-    ~AudioEffectJniStorage() {
-    }
-
+    bool        busy;
+    Condition   cond;
 };
 
+// ----------------------------------------------------------------------------
+struct AudioEffectJniStorage {
+    effect_callback_cookie mCallbackData{};
+};
 
 jint AudioEffectJni::translateNativeErrorToJava(int code) {
     switch(code) {
@@ -101,6 +94,7 @@
 }
 
 static Mutex sLock;
+static std::unordered_set<effect_callback_cookie*> sAudioEffectCallBackCookies;
 
 // ----------------------------------------------------------------------------
 static void effectCallback(int event, void* user, void *info) {
@@ -121,7 +115,13 @@
         ALOGW("effectCallback error user %p, env %p", user, env);
         return;
     }
-
+    {
+        Mutex::Autolock l(sLock);
+        if (sAudioEffectCallBackCookies.count(callbackInfo) == 0) {
+            return;
+        }
+        callbackInfo->busy = true;
+    }
     ALOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
             callbackInfo,
             callbackInfo->audioEffect_ref,
@@ -188,6 +188,11 @@
         env->ExceptionDescribe();
         env->ExceptionClear();
     }
+    {
+        Mutex::Autolock l(sLock);
+        callbackInfo->busy = false;
+        callbackInfo->cond.broadcast();
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -396,6 +401,10 @@
         setAudioEffect(env, thiz, lpAudioEffect);
     }
 
+    {
+        Mutex::Autolock l(sLock);
+        sAudioEffectCallBackCookies.insert(&lpJniStorage->mCallbackData);
+    }
     env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
 
     return (jint) AUDIOEFFECT_SUCCESS;
@@ -427,6 +436,7 @@
 
 
 // ----------------------------------------------------------------------------
+#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
 static void android_media_AudioEffect_native_release(JNIEnv *env,  jobject thiz) {
     sp<AudioEffect> lpAudioEffect = setAudioEffect(env, thiz, 0);
     if (lpAudioEffect == 0) {
@@ -442,7 +452,17 @@
     env->SetLongField(thiz, fields.fidJniData, 0);
 
     if (lpJniStorage) {
-        ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
+        Mutex::Autolock l(sLock);
+        effect_callback_cookie *lpCookie = &lpJniStorage->mCallbackData;
+        ALOGV("deleting lpJniStorage: %p\n", lpJniStorage);
+        sAudioEffectCallBackCookies.erase(lpCookie);
+        while (lpCookie->busy) {
+            if (lpCookie->cond.waitRelative(sLock,
+                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
+                                                    NO_ERROR) {
+                break;
+            }
+        }
         env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
         env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
         delete lpJniStorage;
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 4c5970a..609fafe 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <stdio.h>
+#include <unordered_set>
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "visualizers-JNI"
@@ -63,6 +64,12 @@
     jclass      visualizer_class;  // Visualizer class
     jobject     visualizer_ref;    // Visualizer object instance
 
+    // 'busy_count' and 'cond' together with 'sLock' are used to serialize
+    // concurrent access to the callback cookie from 'setup'/'release'
+    // and the callback.
+    int         busy_count;
+    Condition   cond;
+
     // Lazily allocated arrays used to hold callback data provided to java
     // applications.  These arrays are allocated during the first callback and
     // reallocated when the size of the callback data changes.  Allocating on
@@ -70,14 +77,12 @@
     // reference to the provided data (they need to make a copy if they want to
     // hold onto outside of the callback scope), but it avoids GC thrash caused
     // by constantly allocating and releasing arrays to hold callback data.
+    // 'callback_data_lock' must never be held at the same time with 'sLock'.
     Mutex       callback_data_lock;
     jbyteArray  waveform_data;
     jbyteArray  fft_data;
 
-    visualizer_callback_cookie() {
-        waveform_data = NULL;
-        fft_data = NULL;
-    }
+    // Assumes use of default initialization by the client.
 
     ~visualizer_callback_cookie() {
         cleanupBuffers();
@@ -102,15 +107,8 @@
  };
 
 // ----------------------------------------------------------------------------
-class VisualizerJniStorage {
-    public:
-        visualizer_callback_cookie mCallbackData;
-
-    VisualizerJniStorage() {
-    }
-
-    ~VisualizerJniStorage() {
-    }
+struct VisualizerJniStorage {
+    visualizer_callback_cookie mCallbackData{};
 };
 
 
@@ -136,6 +134,7 @@
 }
 
 static Mutex sLock;
+static std::unordered_set<visualizer_callback_cookie*> sVisualizerCallBackCookies;
 
 // ----------------------------------------------------------------------------
 static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
@@ -173,11 +172,19 @@
         return;
     }
 
+    {
+        Mutex::Autolock l(sLock);
+        if (sVisualizerCallBackCookies.count(callbackInfo) == 0) {
+            return;
+        }
+        callbackInfo->busy_count++;
+    }
     ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
             callbackInfo,
             callbackInfo->visualizer_ref,
             callbackInfo->visualizer_class);
 
+    {
     AutoMutex lock(&callbackInfo->callback_data_lock);
 
     if (waveformSize != 0 && waveform != NULL) {
@@ -219,11 +226,17 @@
                 jArray);
         }
     }
+    }  // callback_data_lock scope
 
     if (env->ExceptionCheck()) {
         env->ExceptionDescribe();
         env->ExceptionClear();
     }
+    {
+        Mutex::Autolock l(sLock);
+        callbackInfo->busy_count--;
+        callbackInfo->cond.broadcast();
+    }
 }
 
 // ----------------------------------------------------------------------------
@@ -332,16 +345,41 @@
                                                      void *info) {
     if ((event == AudioEffect::EVENT_ERROR) &&
         (*((status_t*)info) == DEAD_OBJECT)) {
-        VisualizerJniStorage* lpJniStorage = (VisualizerJniStorage*)user;
-        visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
+        visualizer_callback_cookie* callbackInfo =
+                (visualizer_callback_cookie *)user;
         JNIEnv *env = AndroidRuntime::getJNIEnv();
 
+        if (!user || !env) {
+            ALOGW("effectCallback error user %p, env %p", user, env);
+            return;
+        }
+        {
+            Mutex::Autolock l(sLock);
+            if (sVisualizerCallBackCookies.count(callbackInfo) == 0) {
+                return;
+            }
+            callbackInfo->busy_count++;
+        }
+        ALOGV("effectCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
+            callbackInfo,
+            callbackInfo->visualizer_ref,
+            callbackInfo->visualizer_class);
+
         env->CallStaticVoidMethod(
             callbackInfo->visualizer_class,
             fields.midPostNativeEvent,
             callbackInfo->visualizer_ref,
             NATIVE_EVENT_SERVER_DIED,
             0, NULL);
+        if (env->ExceptionCheck()) {
+            env->ExceptionDescribe();
+            env->ExceptionClear();
+        }
+        {
+            Mutex::Autolock l(sLock);
+            callbackInfo->busy_count--;
+            callbackInfo->cond.broadcast();
+        }
     }
 }
 
@@ -389,7 +427,7 @@
     }
     lpVisualizer->set(0,
                       android_media_visualizer_effect_callback,
-                      lpJniStorage,
+                      &lpJniStorage->mCallbackData,
                       (audio_session_t) sessionId);
 
     lStatus = translateError(lpVisualizer->initCheck());
@@ -410,6 +448,10 @@
 
     setVisualizer(env, thiz, lpVisualizer);
 
+    {
+        Mutex::Autolock l(sLock);
+        sVisualizerCallBackCookies.insert(&lpJniStorage->mCallbackData);
+    }
     env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
 
     return VISUALIZER_SUCCESS;
@@ -432,13 +474,15 @@
 }
 
 // ----------------------------------------------------------------------------
+#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
 static void android_media_visualizer_native_release(JNIEnv *env,  jobject thiz) {
-    { //limit scope so that lpVisualizer is deleted before JNI storage data.
+    {
         sp<Visualizer> lpVisualizer = setVisualizer(env, thiz, 0);
         if (lpVisualizer == 0) {
             return;
         }
         lpVisualizer->release();
+        // Visualizer can still can be held by AudioEffect::EffectClient
     }
     // delete the JNI data
     VisualizerJniStorage* lpJniStorage =
@@ -449,9 +493,22 @@
     env->SetLongField(thiz, fields.fidJniData, 0);
 
     if (lpJniStorage) {
+        {
+        Mutex::Autolock l(sLock);
+        visualizer_callback_cookie *lpCookie = &lpJniStorage->mCallbackData;
         ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
+        sVisualizerCallBackCookies.erase(lpCookie);
+        while (lpCookie->busy_count > 0) {
+            if (lpCookie->cond.waitRelative(sLock,
+                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
+                                                    NO_ERROR) {
+                break;
+            }
+        }
+        ALOG_ASSERT(lpCookie->busy_count == 0, "Unbalanced busy_count inc/dec");
         env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_class);
         env->DeleteGlobalRef(lpJniStorage->mCallbackData.visualizer_ref);
+        }  // sLock scope
         delete lpJniStorage;
     }
 }
@@ -707,4 +764,3 @@
 {
     return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
-
diff --git a/media/tests/EffectsTest/AndroidManifest.xml b/media/tests/EffectsTest/AndroidManifest.xml
index 9b59891..ad0c10e 100644
--- a/media/tests/EffectsTest/AndroidManifest.xml
+++ b/media/tests/EffectsTest/AndroidManifest.xml
@@ -13,6 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<!--
+Make sure to enable access to the mic in settings and run:
+adb shell am compat enable ALLOW_TEST_API_ACCESS com.android.effectstest
+-->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.effectstest">
diff --git a/media/tests/EffectsTest/res/layout/bassboosttest.xml b/media/tests/EffectsTest/res/layout/bassboosttest.xml
index ac912c8..5f9132c 100644
--- a/media/tests/EffectsTest/res/layout/bassboosttest.xml
+++ b/media/tests/EffectsTest/res/layout/bassboosttest.xml
@@ -187,6 +187,11 @@
                  android:layout_height="wrap_content"
                  android:scaleType="fitXY"/>
 
+            <Button android:id="@+id/hammer_on_release_bug"
+                    android:layout_width="fill_parent" android:layout_height="wrap_content"
+                    android:text="@string/hammer_on_release_bug_name">
+            </Button>
+
         </LinearLayout>
 
     </ScrollView>
diff --git a/media/tests/EffectsTest/res/layout/visualizertest.xml b/media/tests/EffectsTest/res/layout/visualizertest.xml
index 18d7a36..85dabbc 100644
--- a/media/tests/EffectsTest/res/layout/visualizertest.xml
+++ b/media/tests/EffectsTest/res/layout/visualizertest.xml
@@ -175,6 +175,11 @@
 
     </LinearLayout>
 
+    <Button android:id="@+id/hammer_on_release_bug"
+            android:layout_width="fill_parent" android:layout_height="wrap_content"
+            android:text="@string/hammer_on_release_bug_name">
+    </Button>
+
     <ImageView
          android:src="@android:drawable/divider_horizontal_dark"
          android:layout_width="fill_parent"
diff --git a/media/tests/EffectsTest/res/values/strings.xml b/media/tests/EffectsTest/res/values/strings.xml
index 7c12da1..a44c7e9 100644
--- a/media/tests/EffectsTest/res/values/strings.xml
+++ b/media/tests/EffectsTest/res/values/strings.xml
@@ -37,4 +37,6 @@
     <string name="send_level_name">Send Level</string>
     <!-- Toggles use of a multi-threaded client for an effect [CHAR LIMIT=24] -->
     <string name="effect_multithreaded">Multithreaded Use</string>
+    <!-- Runs a stress test for a bug related to simultaneous release of multiple effect instances [CHAR LIMIT=24] -->
+    <string name="hammer_on_release_bug_name">Hammer on release()</string>
 </resources>
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
index cce2acc..a207bf1 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
@@ -17,29 +17,24 @@
 package com.android.effectstest;
 
 import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.BassBoost;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.View.OnClickListener;
 import android.view.View;
-import android.view.ViewGroup;
+import android.view.View.OnClickListener;
 import android.widget.Button;
-import android.widget.TextView;
-import android.widget.EditText;
-import android.widget.SeekBar;
-import android.widget.ToggleButton;
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
-import java.nio.ByteOrder;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.Map;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.ToggleButton;
 
-import android.media.audiofx.BassBoost;
-import android.media.audiofx.AudioEffect;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
 
 public class BassBoostTest extends Activity implements OnCheckedChangeListener {
 
@@ -78,6 +73,9 @@
         mReleaseButton = (ToggleButton)findViewById(R.id.bbReleaseButton);
         mOnOffButton = (ToggleButton)findViewById(R.id.bassboostOnOff);
 
+        final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug);
+        hammerReleaseTest.setEnabled(false);
+
         getEffect(sSession);
 
         if (mBassBoost != null) {
@@ -93,6 +91,14 @@
             mStrength = new BassBoostParam(mBassBoost, 0, 1000, seekBar, textView);
             seekBar.setOnSeekBarChangeListener(mStrength);
             mStrength.setEnabled(mBassBoost.getStrengthSupported());
+
+            hammerReleaseTest.setEnabled(true);
+            hammerReleaseTest.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    runHammerReleaseTest(hammerReleaseTest);
+                }
+            });
         }
     }
 
@@ -273,4 +279,52 @@
         }
     }
 
+    // Stress-tests releasing of AudioEffect by doing repeated creation
+    // and subsequent releasing. Also forces emission of callbacks from
+    // the AudioFlinger by setting a control status listener. Since all
+    // effect instances are bound to the same session, the AF will
+    // notify them about the change in their status. This can reveal racy
+    // behavior w.r.t. releasing.
+    class HammerReleaseTest extends Thread {
+        private static final int NUM_EFFECTS = 10;
+        private static final int NUM_ITERATIONS = 100;
+        private final int mSession;
+        private final Runnable mOnComplete;
+
+        HammerReleaseTest(int session, Runnable onComplete) {
+            mSession = session;
+            mOnComplete = onComplete;
+        }
+
+        @Override
+        public void run() {
+            Log.w(TAG, "HammerReleaseTest started");
+            BassBoost[] effects = new BassBoost[NUM_EFFECTS];
+            for (int i = 0; i < NUM_ITERATIONS; i++) {
+                for (int j = 0; j < NUM_EFFECTS; j++) {
+                    effects[j] = new BassBoost(0, mSession);
+                    effects[j].setControlStatusListener(mEffectListener);
+                    yield();
+                }
+                for (int j = NUM_EFFECTS - 1; j >= 0; j--) {
+                    Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]);
+                    effects[j].release();
+                    effects[j] = null;
+                    yield();
+                }
+            }
+            Log.w(TAG, "HammerReleaseTest ended");
+            runOnUiThread(mOnComplete);
+        }
+    }
+
+    private void runHammerReleaseTest(Button controlButton) {
+        controlButton.setEnabled(false);
+        HammerReleaseTest thread = new HammerReleaseTest(sSession,
+                () -> {
+                    controlButton.setEnabled(true);
+                });
+        thread.start();
+    }
+
 }
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
index 2e141c5..dcfe11a 100644
--- a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
@@ -17,6 +17,7 @@
 package com.android.effectstest;
 
 import android.app.Activity;
+import android.media.audiofx.Visualizer;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -24,6 +25,8 @@
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
 import android.widget.CompoundButton;
 import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.EditText;
@@ -74,11 +77,22 @@
         mCallbackOn = false;
         mCallbackButton.setChecked(mCallbackOn);
 
+        final Button hammerReleaseTest = (Button) findViewById(R.id.hammer_on_release_bug);
+        hammerReleaseTest.setEnabled(false);
+
         mMultithreadedButton.setOnCheckedChangeListener(this);
         if (getEffect(sSession) != null) {
             mReleaseButton.setOnCheckedChangeListener(this);
             mOnOffButton.setOnCheckedChangeListener(this);
             mCallbackButton.setOnCheckedChangeListener(this);
+
+            hammerReleaseTest.setEnabled(true);
+            hammerReleaseTest.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    runHammerReleaseTest(hammerReleaseTest);
+                }
+            });
         }
     }
 
@@ -214,4 +228,50 @@
         }
     }
 
+    // Stress-tests releasing of AudioEffect by doing repeated creation
+    // and subsequent releasing. Unlike a similar class in BassBoostTest,
+    // this one doesn't sets a control status listener because Visualizer
+    // doesn't inherit from AudioEffect and doesn't implement this method
+    // by itself.
+    class HammerReleaseTest extends Thread {
+        private static final int NUM_EFFECTS = 10;
+        private static final int NUM_ITERATIONS = 100;
+        private final int mSession;
+        private final Runnable mOnComplete;
+
+        HammerReleaseTest(int session, Runnable onComplete) {
+            mSession = session;
+            mOnComplete = onComplete;
+        }
+
+        @Override
+        public void run() {
+            Log.w(TAG, "HammerReleaseTest started");
+            Visualizer[] effects = new Visualizer[NUM_EFFECTS];
+            for (int i = 0; i < NUM_ITERATIONS; i++) {
+                for (int j = 0; j < NUM_EFFECTS; j++) {
+                    effects[j] = new Visualizer(mSession);
+                    yield();
+                }
+                for (int j = NUM_EFFECTS - 1; j >= 0; j--) {
+                    Log.w(TAG, "HammerReleaseTest releasing effect " + (Object) effects[j]);
+                    effects[j].release();
+                    effects[j] = null;
+                    yield();
+                }
+            }
+            Log.w(TAG, "HammerReleaseTest ended");
+            runOnUiThread(mOnComplete);
+        }
+    }
+
+    private void runHammerReleaseTest(Button controlButton) {
+        controlButton.setEnabled(false);
+        HammerReleaseTest thread = new HammerReleaseTest(sSession,
+                () -> {
+                    controlButton.setEnabled(true);
+                });
+        thread.start();
+    }
+
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 59d8acb..70baf1d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -18,6 +18,7 @@
 
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
@@ -125,6 +126,9 @@
         addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler());
         addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler());
 
+        addHandler(BluetoothCsipSetCoordinator.ACTION_CSIS_SET_MEMBER_AVAILABLE,
+                new SetMemberAvailableHandler());
+
         registerAdapterIntentReceiver();
     }
 
@@ -293,6 +297,8 @@
                 BluetoothDevice device) {
             short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
             String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
+            final boolean isCoordinatedSetMember =
+                    intent.getBooleanExtra(BluetoothDevice.EXTRA_IS_COORDINATED_SET_MEMBER, false);
             // TODO Pick up UUID. They should be available for 2.1 devices.
             // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
@@ -307,6 +313,7 @@
             }
             cachedDevice.setRssi(rssi);
             cachedDevice.setJustDiscovered(true);
+            cachedDevice.setIsCoordinatedSetMember(isCoordinatedSetMember);
         }
     }
 
@@ -335,6 +342,12 @@
             }
             int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
                     BluetoothDevice.ERROR);
+
+            if (mDeviceManager.onBondStateChangedIfProcess(device, bondState)) {
+                Log.d(TAG, "Should not update UI for the set member");
+                return;
+            }
+
             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
             if (cachedDevice == null) {
                 Log.w(TAG, "Got bonding state changed for " + device +
@@ -348,8 +361,10 @@
             cachedDevice.onBondingStateChanged(bondState);
 
             if (bondState == BluetoothDevice.BOND_NONE) {
-                /* Check if we need to remove other Hearing Aid devices */
-                if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
+                // Check if we need to remove other Coordinated set member devices / Hearing Aid
+                // devices
+                if (cachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID
+                        || cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
                     mDeviceManager.onDeviceUnpaired(cachedDevice);
                 }
                 int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
@@ -496,4 +511,29 @@
             dispatchAudioModeChanged();
         }
     }
+
+    private class SetMemberAvailableHandler implements Handler {
+        @Override
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            final String action = intent.getAction();
+            if (device == null) {
+                Log.e(TAG, "SetMemberAvailableHandler: device is null");
+                return;
+            }
+
+            if (action == null) {
+                Log.e(TAG, "SetMemberAvailableHandler: action is null");
+                return;
+            }
+
+            final int groupId = intent.getIntExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID,
+                    BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+            if (groupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+                Log.e(TAG, "SetMemberAvailableHandler: Invalid group id");
+                return;
+            }
+
+            mDeviceManager.onSetMemberAppear(device, groupId);
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4c80b91..78fc139 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -18,6 +18,7 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
@@ -41,7 +42,9 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -66,6 +69,7 @@
     private final Object mProfileLock = new Object();
     BluetoothDevice mDevice;
     private long mHiSyncId;
+    private int mGroupId;
     // Need this since there is no method for getting RSSI
     short mRssi;
     // mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
@@ -80,6 +84,8 @@
 
     boolean mJustDiscovered;
 
+    boolean mIsCoordinatedSetMember = false;
+
     private final Collection<Callback> mCallbacks = new CopyOnWriteArrayList<>();
 
     /**
@@ -98,6 +104,8 @@
     private boolean mIsA2dpProfileConnectedFail = false;
     private boolean mIsHeadsetProfileConnectedFail = false;
     private boolean mIsHearingAidProfileConnectedFail = false;
+    // Group member devices for the coordinated set
+    private Set<CachedBluetoothDevice> mMemberDevices = new HashSet<CachedBluetoothDevice>();
     // Group second device for Hearing Aid
     private CachedBluetoothDevice mSubDevice;
 
@@ -131,6 +139,7 @@
         mDevice = device;
         fillData();
         mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID;
+        mGroupId = BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
     }
 
     /**
@@ -297,6 +306,42 @@
         return mHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID;
     }
 
+    /**
+     * Mark the discovered device as member of coordinated set.
+     *
+     * @param isCoordinatedSetMember {@code true}, if the device is a member of a coordinated set.
+     */
+    public void setIsCoordinatedSetMember(boolean isCoordinatedSetMember) {
+        mIsCoordinatedSetMember = isCoordinatedSetMember;
+    }
+
+    /**
+     * Check if the device is a CSIP member device.
+     *
+     * @return {@code true}, if this device supports CSIP, otherwise returns {@code false}.
+     */
+    public boolean isCoordinatedSetMemberDevice() {
+        return mIsCoordinatedSetMember;
+    }
+
+    /**
+    * Get the coordinated set group id.
+    *
+    * @return the group id.
+    */
+    public int getGroupId() {
+        return mGroupId;
+    }
+
+    /**
+    * Set the coordinated set group id.
+    *
+    * @param id the group id from the CSIP.
+    */
+    public void setGroupId(int id) {
+        mGroupId = id;
+    }
+
     void onBondingDockConnect() {
         // Attempt to connect if UUIDs are available. Otherwise,
         // we will connect when the ACTION_UUID intent arrives.
@@ -1171,4 +1216,52 @@
         mSubDevice.mJustDiscovered = tmpJustDiscovered;
         fetchActiveDevices();
     }
+
+    /**
+     * @return a set of member devices that are in the same coordinated set with this device.
+     */
+    public Set<CachedBluetoothDevice> getMemberDevice() {
+        return mMemberDevices;
+    }
+
+    /**
+     * Store the member devices that are in the same coordinated set.
+     */
+    public void setMemberDevice(CachedBluetoothDevice memberDevice) {
+        mMemberDevices.add(memberDevice);
+    }
+
+    /**
+     * Remove a device from the member device sets.
+     */
+    public void removeMemberDevice(CachedBluetoothDevice memberDevice) {
+        mMemberDevices.remove(memberDevice);
+    }
+
+    /**
+     * In order to show the preference for the whole group, we always set the main device as the
+     * first connected device in the coordinated set, and then switch the content of the main
+     * device and member devices.
+     *
+     * @param prevMainDevice the previous Main device, it will be added into the member device set.
+     * @param newMainDevie the new Main device, it will be removed from the member device set.
+     */
+    public void switchMemberDeviceContent(CachedBluetoothDevice prevMainDevice,
+            CachedBluetoothDevice newMainDevie) {
+        // Backup from main device
+        final BluetoothDevice tmpDevice = mDevice;
+        final short tmpRssi = mRssi;
+        final boolean tmpJustDiscovered = mJustDiscovered;
+        // Set main device from sub device
+        mDevice = newMainDevie.mDevice;
+        mRssi = newMainDevie.mRssi;
+        mJustDiscovered = newMainDevie.mJustDiscovered;
+        setMemberDevice(prevMainDevice);
+        mMemberDevices.remove(newMainDevie);
+        // Set sub device from backup
+        newMainDevie.mDevice = tmpDevice;
+        newMainDevie.mRssi = tmpRssi;
+        newMainDevie.mJustDiscovered = tmpJustDiscovered;
+        fetchActiveDevices();
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index cca9cfa..0256615 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -18,6 +18,7 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.util.Log;
 
@@ -26,6 +27,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 /**
  * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
@@ -41,11 +43,15 @@
     final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
     @VisibleForTesting
     HearingAidDeviceManager mHearingAidDeviceManager;
+    @VisibleForTesting
+    CsipDeviceManager mCsipDeviceManager;
+    BluetoothDevice mOngoingSetMemberPair;
 
     CachedBluetoothDeviceManager(Context context, LocalBluetoothManager localBtManager) {
         mContext = context;
         mBtManager = localBtManager;
         mHearingAidDeviceManager = new HearingAidDeviceManager(localBtManager, mCachedDevices);
+        mCsipDeviceManager = new CsipDeviceManager(localBtManager, mCachedDevices);
     }
 
     public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
@@ -79,7 +85,16 @@
             if (cachedDevice.getDevice().equals(device)) {
                 return cachedDevice;
             }
-            // Check sub devices if it exists
+            // Check the member devices for the coordinated set if it exists
+            final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+            if (memberDevices != null) {
+                for (CachedBluetoothDevice memberDevice : memberDevices) {
+                    if (memberDevice.getDevice().equals(device)) {
+                        return memberDevice;
+                    }
+                }
+            }
+            // Check sub devices for hearing aid if it exists
             CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
             if (subDevice != null && subDevice.getDevice().equals(device)) {
                 return subDevice;
@@ -102,8 +117,10 @@
             newDevice = findDevice(device);
             if (newDevice == null) {
                 newDevice = new CachedBluetoothDevice(mContext, profileManager, device);
+                mCsipDeviceManager.initCsipDeviceIfNeeded(newDevice);
                 mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
-                if (!mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
+                if (!mCsipDeviceManager.setMemberDeviceIfNeeded(newDevice)
+                        && !mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
                     mCachedDevices.add(newDevice);
                     mBtManager.getEventManager().dispatchDeviceAdded(newDevice);
                 }
@@ -114,13 +131,23 @@
     }
 
     /**
-     * Returns device summary of the pair of the hearing aid passed as the parameter.
+     * Returns device summary of the pair of the hearing aid / CSIP passed as the parameter.
      *
      * @param CachedBluetoothDevice device
-     * @return Device summary, or if the pair does not exist or if it is not a hearing aid,
-     * then {@code null}.
+     * @return Device summary, or if the pair does not exist or if it is not a hearing aid or
+     * a CSIP set member, then {@code null}.
      */
     public synchronized String getSubDeviceSummary(CachedBluetoothDevice device) {
+        final Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice();
+        if (memberDevices != null) {
+            for (CachedBluetoothDevice memberDevice : memberDevices) {
+                if (!memberDevice.isConnected()) {
+                    return null;
+                }
+            }
+
+            return device.getConnectionSummary();
+        }
         CachedBluetoothDevice subDevice = device.getSubDevice();
         if (subDevice != null && subDevice.isConnected()) {
             return subDevice.getConnectionSummary();
@@ -132,12 +159,22 @@
      * Search for existing sub device {@link CachedBluetoothDevice}.
      *
      * @param device the address of the Bluetooth device
-     * @return true for found sub device or false.
+     * @return true for found sub / member device or false.
      */
     public synchronized boolean isSubDevice(BluetoothDevice device) {
         for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
             if (!cachedDevice.getDevice().equals(device)) {
-                // Check sub devices if it exists
+                // Check the member devices of the coordinated set if it exists
+                Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+                if (memberDevices != null) {
+                    for (CachedBluetoothDevice memberDevice : memberDevices) {
+                        if (memberDevice.getDevice().equals(device)) {
+                            return true;
+                        }
+                    }
+                    continue;
+                }
+                // Check sub devices of hearing aid if it exists
                 CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
                 if (subDevice != null && subDevice.getDevice().equals(device)) {
                     return true;
@@ -157,6 +194,14 @@
     }
 
     /**
+     * Updates the Csip devices; specifically the GroupId's. This routine is called when the
+     * CSIS is connected and the GroupId's are now available.
+     */
+    public synchronized void updateCsipDevices() {
+        mCsipDeviceManager.updateCsipDevices();
+    }
+
+    /**
      * Attempts to get the name of a remote device, otherwise returns the address.
      *
      * @param device The remote device.
@@ -185,6 +230,16 @@
     private void clearNonBondedSubDevices() {
         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+            final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+            if (memberDevices != null) {
+                for (CachedBluetoothDevice memberDevice : memberDevices) {
+                    // Member device exists and it is not bonded
+                    if (memberDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
+                        cachedDevice.removeMemberDevice(memberDevice);
+                    }
+                }
+                return;
+            }
             CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
             if (subDevice != null
                     && subDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
@@ -201,6 +256,13 @@
         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
             cachedDevice.setJustDiscovered(false);
+            final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+            if (memberDevices != null) {
+                for (CachedBluetoothDevice memberDevice : memberDevices) {
+                    memberDevice.setJustDiscovered(false);
+                }
+                return;
+            }
             final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
             if (subDevice != null) {
                 subDevice.setJustDiscovered(false);
@@ -214,10 +276,19 @@
         if (bluetoothState == BluetoothAdapter.STATE_TURNING_OFF) {
             for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
                 CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
-                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
-                if (subDevice != null) {
-                    if (subDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
-                        cachedDevice.setSubDevice(null);
+                final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+                if (memberDevices != null) {
+                    for (CachedBluetoothDevice memberDevice : memberDevices) {
+                        if (memberDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
+                            cachedDevice.removeMemberDevice(memberDevice);
+                        }
+                    }
+                } else {
+                    CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
+                    if (subDevice != null) {
+                        if (subDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
+                            cachedDevice.setSubDevice(null);
+                        }
                     }
                 }
                 if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
@@ -229,13 +300,32 @@
     }
 
     public synchronized boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice
-            cachedDevice, int state) {
-        return mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
+            cachedDevice, int state, int profileId) {
+        if (profileId == BluetoothProfile.HEARING_AID) {
+            return mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
                 state);
+        }
+        if (profileId == BluetoothProfile.CSIP_SET_COORDINATOR) {
+            return mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
+                state);
+        }
+        return false;
     }
 
     public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
-        CachedBluetoothDevice mainDevice = mHearingAidDeviceManager.findMainDevice(device);
+        CachedBluetoothDevice mainDevice = mCsipDeviceManager.findMainDevice(device);
+        final Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice();
+        if (memberDevices != null) {
+            // Main device is unpaired, to unpair the member device
+            for (CachedBluetoothDevice memberDevice : memberDevices) {
+                memberDevice.unpair();
+                device.removeMemberDevice(memberDevice);
+            }
+        } else if (mainDevice != null) {
+            // the member device unpaired, to unpair main device
+            mainDevice.unpair();
+        }
+        mainDevice = mHearingAidDeviceManager.findMainDevice(device);
         CachedBluetoothDevice subDevice = device.getSubDevice();
         if (subDevice != null) {
             // Main device is unpaired, to unpair sub device
@@ -248,6 +338,74 @@
         }
     }
 
+    /**
+     * Called when we found a set member of a group. The function will check the {@code groupId} if
+     * it exists and if there is a ongoing pair, the device would be ignored.
+     *
+     * @param device The found device
+     * @param groupId The group id of the found device
+     */
+    public synchronized void onSetMemberAppear(BluetoothDevice device, int groupId) {
+        Log.d(TAG, "onSetMemberAppear, groupId: " + groupId + " device: " + device.toString());
+
+        if (mOngoingSetMemberPair != null) {
+            Log.d(TAG, "Ongoing set memberPairing in process, drop it!");
+            return;
+        }
+
+        if (mCsipDeviceManager.onSetMemberAppear(device, groupId)) {
+            mOngoingSetMemberPair = device;
+        }
+    }
+
+    /**
+     * Called when the bond state change. If the bond state change is related with the
+     * ongoing set member pair, the cachedBluetoothDevice will be created but the UI
+     * would not be updated. For the other case, return {@code false} to go through the normal
+     * flow.
+     *
+     * @param device The device
+     * @param bondState The new bond state
+     *
+     * @return {@code true}, if the bond state change for the device is handled inside this
+     * function, and would not like to update the UI. If not, return {@code false}.
+     */
+    public synchronized boolean onBondStateChangedIfProcess(BluetoothDevice device, int bondState) {
+        if (mOngoingSetMemberPair == null || !mOngoingSetMemberPair.equals(device)) {
+            return false;
+        }
+
+        if (bondState == BluetoothDevice.BOND_BONDING) {
+            return true;
+        }
+
+        mOngoingSetMemberPair = null;
+        if (bondState != BluetoothDevice.BOND_NONE) {
+            if (findDevice(device) == null) {
+                final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
+                CachedBluetoothDevice newDevice =
+                        new CachedBluetoothDevice(mContext, profileManager, device);
+                mCachedDevices.add(newDevice);
+                findDevice(device).connect();
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if the device is the one which is initial paired locally by CSIP. The setting
+     * would depned on it to accept the pairing request automatically
+     *
+     * @param device The device
+     *
+     * @return {@code true}, if the device is ongoing pair by CSIP. Otherwise, return
+     * {@code false}.
+     */
+    public boolean isOngoingPairByCsip(BluetoothDevice device) {
+        return !(mOngoingSetMemberPair == null) && mOngoingSetMemberPair.equals(device);
+    }
+
     private void log(String msg) {
         if (DEBUG) {
             Log.d(TAG, msg);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
new file mode 100644
index 0000000..347e14b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -0,0 +1,270 @@
+/*
+ * 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.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * CsipDeviceManager manages the set of remote CSIP Bluetooth devices.
+ */
+public class CsipDeviceManager {
+    private static final String TAG = "CsipDeviceManager";
+    private static final boolean DEBUG = BluetoothUtils.D;
+
+    private final LocalBluetoothManager mBtManager;
+    private final List<CachedBluetoothDevice> mCachedDevices;
+
+    CsipDeviceManager(LocalBluetoothManager localBtManager,
+            List<CachedBluetoothDevice> cachedDevices) {
+        mBtManager = localBtManager;
+        mCachedDevices = cachedDevices;
+    };
+
+    void initCsipDeviceIfNeeded(CachedBluetoothDevice newDevice) {
+        // Current it only supports the base uuid for CSIP and group this set in UI.
+        final int groupId = getBaseGroupId(newDevice.getDevice());
+        if (isValidGroupId(groupId)) {
+            log("initCsipDeviceIfNeeded: " + newDevice + " (group: " + groupId + ")");
+            // Once groupId is valid, assign groupId
+            newDevice.setGroupId(groupId);
+        }
+    }
+
+    private int getBaseGroupId(BluetoothDevice device) {
+        final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
+        final CsipSetCoordinatorProfile profileProxy = profileManager
+                .getCsipSetCoordinatorProfile();
+        if (profileProxy != null) {
+            final Map<Integer, ParcelUuid> groupIdMap = profileProxy
+                    .getGroupUuidMapByDevice(device);
+            if (groupIdMap == null) {
+                return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+            }
+
+            for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
+                if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+                    return entry.getKey();
+                }
+            }
+        }
+        return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+    }
+
+    boolean setMemberDeviceIfNeeded(CachedBluetoothDevice newDevice) {
+        final int groupId = newDevice.getGroupId();
+        if (isValidGroupId(groupId)) {
+            final CachedBluetoothDevice CsipDevice = getCachedDevice(groupId);
+            log("setMemberDeviceIfNeeded, main: " + CsipDevice + ", member: " + newDevice);
+            // Just add one of the coordinated set from a pair in the list that is shown in the UI.
+            // Once there is other devices with the same groupId, to add new device as member
+            // devices.
+            if (CsipDevice != null) {
+                CsipDevice.setMemberDevice(newDevice);
+                newDevice.setName(CsipDevice.getName());
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isValidGroupId(int groupId) {
+        return groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+    }
+
+    private CachedBluetoothDevice getCachedDevice(int groupId) {
+        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+            CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+            if (cachedDevice.getGroupId() == groupId) {
+                return cachedDevice;
+            }
+        }
+        return null;
+    }
+
+    // To collect all set member devices and call #onGroupIdChanged to group device by GroupId
+    void updateCsipDevices() {
+        final Set<Integer> newGroupIdSet = new HashSet<Integer>();
+        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+            // Do nothing if GroupId has been assigned
+            if (!isValidGroupId(cachedDevice.getGroupId())) {
+                final int newGroupId = getBaseGroupId(cachedDevice.getDevice());
+                // Do nothing if there is no GroupId on Bluetooth device
+                if (isValidGroupId(newGroupId)) {
+                    cachedDevice.setGroupId(newGroupId);
+                    newGroupIdSet.add(newGroupId);
+                }
+            }
+        }
+        for (int groupId : newGroupIdSet) {
+            onGroupIdChanged(groupId);
+        }
+    }
+
+    // Group devices by groupId
+    @VisibleForTesting
+    void onGroupIdChanged(int groupId) {
+        int firstMatchedIndex = -1;
+        CachedBluetoothDevice mainDevice = null;
+
+        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
+            final CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
+            if (cachedDevice.getGroupId() != groupId) {
+                continue;
+            }
+
+            if (firstMatchedIndex == -1) {
+                // Found the first one
+                firstMatchedIndex = i;
+                mainDevice = cachedDevice;
+                continue;
+            }
+
+            log("onGroupIdChanged: removed from UI device =" + cachedDevice
+                    + ", with groupId=" + groupId + " firstMatchedIndex=" + firstMatchedIndex);
+
+            mainDevice.setMemberDevice(cachedDevice);
+            mCachedDevices.remove(i);
+            mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
+            break;
+        }
+    }
+
+    // @return {@code true}, the event is processed inside the method. It is for updating
+    // le audio device on group relationship when receiving connected or disconnected.
+    // @return {@code false}, it is not le audio device or to process it same as other profiles
+    boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice cachedDevice,
+            int state) {
+        log("onProfileConnectionStateChangedIfProcessed: " + cachedDevice + ", state: " + state);
+        switch (state) {
+            case BluetoothProfile.STATE_CONNECTED:
+                onGroupIdChanged(cachedDevice.getGroupId());
+                CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice);
+                if (mainDevice != null) {
+                    if (mainDevice.isConnected()) {
+                        // When main device exists and in connected state, receiving member device
+                        // connection. To refresh main device UI
+                        mainDevice.refresh();
+                        return true;
+                    } else {
+                        // When both LE Audio devices are disconnected, receiving member device
+                        // connection. To switch content and dispatch to notify UI change
+                        mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
+                        mainDevice.switchMemberDeviceContent(mainDevice, cachedDevice);
+                        mainDevice.refresh();
+                        // It is necessary to do remove and add for updating the mapping on
+                        // preference and device
+                        mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
+                        return true;
+                    }
+                }
+                break;
+            case BluetoothProfile.STATE_DISCONNECTED:
+                mainDevice = findMainDevice(cachedDevice);
+                if (mainDevice != null) {
+                    // When main device exists, receiving sub device disconnection
+                    // To update main device UI
+                    mainDevice.refresh();
+                    return true;
+                }
+                final Set<CachedBluetoothDevice> memberSet = cachedDevice.getMemberDevice();
+                if (memberSet == null) {
+                    break;
+                }
+
+                for (CachedBluetoothDevice device: memberSet) {
+                    if (device.isConnected()) {
+                        // Main device is disconnected and sub device is connected
+                        // To copy data from sub device to main device
+                        mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
+                        cachedDevice.switchMemberDeviceContent(device, cachedDevice);
+                        cachedDevice.refresh();
+                        // It is necessary to do remove and add for updating the mapping on
+                        // preference and device
+                        mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice);
+                        return true;
+                    }
+                }
+                break;
+            default:
+                // Do not handle this state.
+        }
+        return false;
+    }
+
+    CachedBluetoothDevice findMainDevice(CachedBluetoothDevice device) {
+        if (device == null || mCachedDevices == null) {
+            return null;
+        }
+
+        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+            if (isValidGroupId(cachedDevice.getGroupId())) {
+                Set<CachedBluetoothDevice> memberSet = cachedDevice.getMemberDevice();
+                if (memberSet == null) {
+                    continue;
+                }
+
+                for (CachedBluetoothDevice memberDevice: memberSet) {
+                    if (memberDevice != null && memberDevice.equals(device)) {
+                        return cachedDevice;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Called when we found a set member of a group. The function will check bond state, and
+     * the {@code groupId} if it exists, and then create the bond.
+     *
+     * @param device The found device
+     * @param groupId The group id of the found device
+     *
+     * @return {@code true}, if the we create bond with the device. Otherwise, return
+     * {@code false}.
+     */
+    public boolean onSetMemberAppear(BluetoothDevice device, int groupId) {
+        if (device.getBondState() != BluetoothDevice.BOND_NONE) {
+            return false;
+        }
+
+        if (getCachedDevice(groupId) != null) {
+            device.createBond(BluetoothDevice.TRANSPORT_LE);
+            return true;
+        }
+
+        return false;
+    }
+
+    private void log(String msg) {
+        if (DEBUG) {
+            Log.d(TAG, msg);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
new file mode 100644
index 0000000..6da249c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipSetCoordinatorProfile.java
@@ -0,0 +1,251 @@
+/*
+ * 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 com.android.settingslib.bluetooth;
+
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CSIP Set Coordinator handles Bluetooth CSIP Set Coordinator role profile.
+ */
+public class CsipSetCoordinatorProfile implements LocalBluetoothProfile {
+    private static final String TAG = "CsipSetCoordinatorProfile";
+    private static final boolean VDBG = true;
+
+    private Context mContext;
+
+    private BluetoothCsipSetCoordinator mService;
+    private boolean mIsProfileReady;
+
+    private final CachedBluetoothDeviceManager mDeviceManager;
+
+    static final String NAME = "CSIP Set Coordinator";
+    private final LocalBluetoothProfileManager mProfileManager;
+
+    // Order of this profile in device profiles list
+    private static final int ORDINAL = 1;
+
+    // These callbacks run on the main thread.
+    private final class CoordinatedSetServiceListener implements BluetoothProfile.ServiceListener {
+        @RequiresApi(32)
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (VDBG) {
+                Log.d(TAG, "Bluetooth service connected");
+            }
+            mService = (BluetoothCsipSetCoordinator) proxy;
+            // We just bound to the service, so refresh the UI for any connected CSIP devices.
+            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+            while (!deviceList.isEmpty()) {
+                BluetoothDevice nextDevice = deviceList.remove(0);
+                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+                // we may add a new device here, but generally this should not happen
+                if (device == null) {
+                    if (VDBG) {
+                        Log.d(TAG, "CsipSetCoordinatorProfile found new device: " + nextDevice);
+                    }
+                    device = mDeviceManager.addDevice(nextDevice);
+                }
+                device.onProfileStateChanged(
+                        CsipSetCoordinatorProfile.this, BluetoothProfile.STATE_CONNECTED);
+                device.refresh();
+            }
+
+            mDeviceManager.updateCsipDevices();
+            mProfileManager.callServiceConnectedListeners();
+            mIsProfileReady = true;
+        }
+
+        public void onServiceDisconnected(int profile) {
+            if (VDBG) {
+                Log.d(TAG, "Bluetooth service disconnected");
+            }
+            mProfileManager.callServiceDisconnectedListeners();
+            mIsProfileReady = false;
+        }
+    }
+
+    CsipSetCoordinatorProfile(Context context, CachedBluetoothDeviceManager deviceManager,
+            LocalBluetoothProfileManager profileManager) {
+        mContext = context;
+        mDeviceManager = deviceManager;
+        mProfileManager = profileManager;
+
+        BluetoothAdapter.getDefaultAdapter().getProfileProxy(context,
+                new CoordinatedSetServiceListener(), BluetoothProfile.CSIP_SET_COORDINATOR);
+    }
+
+    /**
+     * Get CSIP devices matching connection states{
+     *
+     * @code BluetoothProfile.STATE_CONNECTED,
+     * @code BluetoothProfile.STATE_CONNECTING,
+     * @code BluetoothProfile.STATE_DISCONNECTING}
+     *
+     * @return Matching device list
+     */
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (mService == null) {
+            return new ArrayList<BluetoothDevice>(0);
+        }
+        return mService.getDevicesMatchingConnectionStates(
+                new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING,
+                        BluetoothProfile.STATE_DISCONNECTING});
+    }
+
+    /**
+     * Gets the connection status of the device.
+     *
+     * @code BluetoothProfile.STATE_CONNECTED,
+     * @code BluetoothProfile.STATE_CONNECTING,
+     * @code BluetoothProfile.STATE_DISCONNECTING}
+     *
+     * @return Connection status, {@code BluetoothProfile.STATE_DISCONNECTED} if unknown.
+     */
+    public int getConnectionStatus(BluetoothDevice device) {
+        if (mService == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+        return mService.getConnectionState(device);
+    }
+
+    @Override
+    public boolean isProfileReady() {
+        return mIsProfileReady;
+    }
+
+    @Override
+    public int getProfileId() {
+        return BluetoothProfile.CSIP_SET_COORDINATOR;
+    }
+
+    @Override
+    public boolean accessProfileEnabled() {
+        return false;
+    }
+
+    @Override
+    public boolean isAutoConnectable() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled(BluetoothDevice device) {
+        if (mService == null || device == null) {
+            return false;
+        }
+        return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
+    }
+
+    @Override
+    public int getConnectionPolicy(BluetoothDevice device) {
+        if (mService == null || device == null) {
+            return CONNECTION_POLICY_FORBIDDEN;
+        }
+        return mService.getConnectionPolicy(device);
+    }
+
+    @Override
+    public boolean setEnabled(BluetoothDevice device, boolean enabled) {
+        boolean isEnabled = false;
+        if (mService == null || device == null) {
+            return false;
+        }
+        if (enabled) {
+            if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) {
+                isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED);
+            }
+        } else {
+            isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN);
+        }
+
+        return isEnabled;
+    }
+
+    @Override
+    public int getOrdinal() {
+        return ORDINAL;
+    }
+
+    @Override
+    public int getNameResource(BluetoothDevice device) {
+        return R.string.summary_empty;
+    }
+
+    @Override
+    public int getSummaryResourceForDevice(BluetoothDevice device) {
+        int state = getConnectionStatus(device);
+        return BluetoothUtils.getConnectionStateSummary(state);
+    }
+
+    @Override
+    public int getDrawableResource(BluetoothClass btClass) {
+        return 0;
+    }
+
+    /**
+     * Get the device's groups and correspondsing uuids map.
+     * @param device the bluetooth device
+     * @return Map of groups ids and related UUIDs
+     */
+    public Map<Integer, ParcelUuid> getGroupUuidMapByDevice(BluetoothDevice device) {
+        if (mService == null || device == null) {
+            return null;
+        }
+        return mService.getGroupUuidMapByDevice(device);
+    }
+
+    /**
+     * Return the profile name as a string.
+     */
+    public String toString() {
+        return NAME;
+    }
+
+    @RequiresApi(32)
+    protected void finalize() {
+        if (VDBG) {
+            Log.d(TAG, "finalize()");
+        }
+        if (mService != null) {
+            try {
+                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(
+                        BluetoothProfile.CSIP_SET_COORDINATOR, mService);
+                mService = null;
+            } catch (Throwable t) {
+                Log.w(TAG, "Error cleaning up CSIP Set Coordinator proxy", t);
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index c4cb6a1..24113c5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -19,6 +19,7 @@
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothA2dpSink;
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHeadsetClient;
@@ -100,6 +101,7 @@
     private PbapClientProfile mPbapClientProfile;
     private PbapServerProfile mPbapProfile;
     private HearingAidProfile mHearingAidProfile;
+    private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
     private SapProfile mSapProfile;
     private VolumeControlProfile mVolumeControlProfile;
 
@@ -230,7 +232,16 @@
             // Note: no event handler for VCP, only for being connectable.
             mProfileNameMap.put(VolumeControlProfile.NAME, mVolumeControlProfile);
         }
-
+        if (mCsipSetCoordinatorProfile == null
+                && supportedList.contains(BluetoothProfile.CSIP_SET_COORDINATOR)) {
+            if (DEBUG) {
+                Log.d(TAG, "Adding local CSIP set coordinator profile");
+            }
+            mCsipSetCoordinatorProfile =
+                    new CsipSetCoordinatorProfile(mContext, mDeviceManager, this);
+            addProfile(mCsipSetCoordinatorProfile, mCsipSetCoordinatorProfile.NAME,
+                    BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED);
+        }
         mEventManager.registerProfileIntentReceiver();
     }
 
@@ -307,11 +318,35 @@
                     }
                 }
             }
+
+            if (getCsipSetCoordinatorProfile() != null
+                    && mProfile instanceof CsipSetCoordinatorProfile
+                    && newState == BluetoothProfile.STATE_CONNECTED) {
+                // Check if the GroupID has being initialized
+                if (cachedDevice.getGroupId() == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+                    final Map<Integer, ParcelUuid> groupIdMap = getCsipSetCoordinatorProfile()
+                            .getGroupUuidMapByDevice(cachedDevice.getDevice());
+                    if (groupIdMap != null) {
+                        for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
+                            if (entry.getValue().equals(BluetoothUuid.BASE_UUID)) {
+                                cachedDevice.setGroupId(entry.getKey());
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+
             cachedDevice.onProfileStateChanged(mProfile, newState);
             // Dispatch profile changed after device update
-            if (!(cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
-                    && mDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
-                    newState))) {
+            boolean needDispatchProfileConnectionState = true;
+            if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
+                    || cachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+                needDispatchProfileConnectionState = !mDeviceManager
+                        .onProfileConnectionStateChangedIfProcessed(cachedDevice, newState,
+                        mProfile.getProfileId());
+            }
+            if (needDispatchProfileConnectionState) {
                 cachedDevice.refresh();
                 mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
                         mProfile.getProfileId());
@@ -462,6 +497,10 @@
         return mHidDeviceProfile;
     }
 
+    public CsipSetCoordinatorProfile getCsipSetCoordinatorProfile() {
+        return mCsipSetCoordinatorProfile;
+    }
+
     /**
      * Fill in a list of LocalBluetoothProfile objects that are supported by
      * the local device and the remote device.
@@ -582,6 +621,12 @@
             removedProfiles.remove(mVolumeControlProfile);
         }
 
+        if (mCsipSetCoordinatorProfile != null
+                && ArrayUtils.contains(uuids, BluetoothUuid.COORDINATED_SET)) {
+            profiles.add(mCsipSetCoordinatorProfile);
+            removedProfiles.remove(mCsipSetCoordinatorProfile);
+        }
+
         if (DEBUG) {
             Log.d(TAG,"New Profiles" + profiles.toString());
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index fd5b053..4f8fa2f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -206,7 +206,7 @@
         mContext.sendBroadcast(mIntent);
 
         verify(mDeviceManager).onProfileConnectionStateChangedIfProcessed(mCachedBluetoothDevice,
-                BluetoothProfile.STATE_CONNECTED);
+                BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
     }
 
     /**
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 10600e3..0e9a51d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -513,7 +513,7 @@
         <provider android:name=".HeapDumpProvider"
                   android:authorities="com.android.shell.heapdump"
                   android:grantUriPermissions="true"
-                  android:exported="true" />
+                  android:exported="false" />
 
         <activity
             android:name=".BugreportWarningActivity"
diff --git a/services/OWNERS b/services/OWNERS
index b7128a3..a083319 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -4,3 +4,4 @@
 per-file art-profile* = calin@google.com, ngeoffray@google.com, vmarko@google.com
 
 per-file java/com/android/server/* = toddke@google.com,patb@google.com
+per-file tests/servicestests/src/com/android/server/systemconfig/* = patb@google.com
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 00cc753..bdeb4d5 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -175,8 +175,6 @@
     private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
     private boolean mBinding;
     private boolean mUnbinding;
-    private int mWaitForEnableRetry;
-    private int mWaitForDisableRetry;
 
     private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
 
@@ -933,14 +931,15 @@
         if (mState == BluetoothAdapter.STATE_ON
                 || mState == BluetoothAdapter.STATE_BLE_ON
                 || mState == BluetoothAdapter.STATE_TURNING_ON
-                || mState == BluetoothAdapter.STATE_TURNING_OFF) {
-            Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+                || 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);
+            sendEnableMsg(false, BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
+                    packageName, true);
         }
         return true;
     }
@@ -1734,6 +1733,8 @@
 
     private class BluetoothHandler extends Handler {
         boolean mGetNameAddressOnly = false;
+        private int mWaitForEnableRetry;
+        private int mWaitForDisableRetry;
 
         BluetoothHandler(Looper looper) {
             super(looper);
@@ -1781,11 +1782,12 @@
 
                 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, 0), ENABLE_DISABLE_DELAY_MS);
+                                quietEnable, isBle), ENABLE_DISABLE_DELAY_MS);
                         break;
                     }
 
@@ -1800,13 +1802,28 @@
                     try {
                         mBluetoothLock.readLock().lock();
                         if (mBluetooth != null) {
+                            boolean isHandled = true;
                             int state = mBluetooth.getState();
-                            if (state == BluetoothAdapter.STATE_BLE_ON) {
-                                Slog.w(TAG, "BT Enable in BLE_ON State, going to ON");
-                                mBluetooth.onLeServiceUp();
-                                persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-                                break;
+                            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();
+                                        persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+                                    }
+                                    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);
@@ -2559,7 +2576,12 @@
     }
 
     private void sendEnableMsg(boolean quietMode, int reason, String packageName) {
-        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0));
+        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();
     }
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index a9f3a1b..462ed5c 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -82,6 +82,8 @@
 
     private static final int INVALID_ID = 0;
     private int mUniqueId = 1;
+    // The count of the connected legacy clients.
+    private int mLegacyClientCount = 0;
 
     private class NsdStateMachine extends StateMachine {
 
@@ -107,7 +109,9 @@
             sendMessageDelayed(NsdManager.DAEMON_CLEANUP, mCleanupDelayMs);
         }
         private void maybeScheduleStop() {
-            if (!isAnyRequestActive()) {
+            // The native daemon should stay alive and can't be cleanup
+            // if any legacy client connected.
+            if (!isAnyRequestActive() && mLegacyClientCount == 0) {
                 scheduleStop();
             }
         }
@@ -175,11 +179,11 @@
                         if (cInfo != null) {
                             cInfo.expungeAllRequests();
                             mClients.remove(msg.replyTo);
+                            if (cInfo.isLegacy()) {
+                                mLegacyClientCount -= 1;
+                            }
                         }
-                        //Last client
-                        if (mClients.size() == 0) {
-                            scheduleStop();
-                        }
+                        maybeScheduleStop();
                         break;
                     case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
                         AsyncChannel ac = new AsyncChannel();
@@ -208,6 +212,17 @@
                     case NsdManager.DAEMON_CLEANUP:
                         mDaemon.maybeStop();
                         break;
+                    // This event should be only sent by the legacy (target SDK < S) clients.
+                    // Mark the sending client as legacy.
+                    case NsdManager.DAEMON_STARTUP:
+                        cInfo = mClients.get(msg.replyTo);
+                        if (cInfo != null) {
+                            cancelStop();
+                            cInfo.setLegacy();
+                            mLegacyClientCount += 1;
+                            maybeStartDaemon();
+                        }
+                        break;
                     case NsdManager.NATIVE_DAEMON_EVENT:
                     default:
                         Slog.e(TAG, "Unhandled " + msg);
@@ -863,6 +878,9 @@
         /* A map from client id to the type of the request we had received */
         private final SparseIntArray mClientRequests = new SparseIntArray();
 
+        // The target SDK of this client < Build.VERSION_CODES.S
+        private boolean mIsLegacy = false;
+
         private ClientInfo(AsyncChannel c, Messenger m) {
             mChannel = c;
             mMessenger = m;
@@ -875,6 +893,7 @@
             sb.append("mChannel ").append(mChannel).append("\n");
             sb.append("mMessenger ").append(mMessenger).append("\n");
             sb.append("mResolvedService ").append(mResolvedService).append("\n");
+            sb.append("mIsLegacy ").append(mIsLegacy).append("\n");
             for(int i = 0; i< mClientIds.size(); i++) {
                 int clientID = mClientIds.keyAt(i);
                 sb.append("clientId ").append(clientID).
@@ -884,6 +903,14 @@
             return sb.toString();
         }
 
+        private boolean isLegacy() {
+            return mIsLegacy;
+        }
+
+        private void setLegacy() {
+            mIsLegacy = true;
+        }
+
         // Remove any pending requests from the global map when we get rid of a client,
         // and send cancellations to the daemon.
         private void expungeAllRequests() {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b1ffaeb..6d85273 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -734,11 +734,8 @@
         }
         ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
 
-        if (!r.mAllowWhileInUsePermissionInFgs) {
-            r.mAllowWhileInUsePermissionInFgs =
-                    shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,
-                            callingUid, service, r, allowBackgroundActivityStarts);
-        }
+        setFgsRestrictionLocked(callingPackage, callingPid, callingUid, r,
+                allowBackgroundActivityStarts);
 
         return cmp;
     }
@@ -1411,14 +1408,6 @@
                         +  String.format("0x%08X", manifestType)
                         + " in service element of manifest file");
                 }
-                // If the foreground service is not started from TOP process, do not allow it to
-                // have while-in-use location/camera/microphone access.
-                if (!r.mAllowWhileInUsePermissionInFgs) {
-                    Slog.w(TAG,
-                            "Foreground service started from background can not have "
-                                    + "location/camera/microphone access: service "
-                                    + r.shortInstanceName);
-                }
             }
             boolean alreadyStartedOp = false;
             boolean stopProcStatsOp = false;
@@ -1466,6 +1455,57 @@
                     ignoreForeground = true;
                 }
 
+                if (!ignoreForeground) {
+                    if (r.mStartForegroundCount == 0) {
+                        /*
+                        If the service was started with startService(), not
+                        startForegroundService(), and if startForeground() isn't called within
+                        mFgsStartForegroundTimeoutMs, then we check the state of the app
+                        (who owns the service, which is the app that called startForeground())
+                        again. If the app is in the foreground, or in any other cases where
+                        FGS-starts are allowed, then we still allow the FGS to be started.
+                        Otherwise, startForeground() would fail.
+
+                        If the service was started with startForegroundService(), then the service
+                        must call startForeground() within a timeout anyway, so we don't need this
+                        check.
+                        */
+                        if (!r.fgRequired) {
+                            final long delayMs = SystemClock.elapsedRealtime() - r.createRealTime;
+                            if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) {
+                                resetFgsRestrictionLocked(r);
+                                setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid,
+                                        r.appInfo.uid, r, false);
+                                EventLog.writeEvent(0x534e4554, "183147114",
+                                        r.appInfo.uid,
+                                        "call setFgsRestrictionLocked again due to "
+                                                + "startForegroundTimeout");
+                            }
+                        }
+                    } else if (r.mStartForegroundCount >= 1) {
+                        // The second or later time startForeground() is called after service is
+                        // started. Check for app state again.
+                        final long delayMs = SystemClock.elapsedRealtime() -
+                                r.mLastSetFgsRestrictionTime;
+                        if (delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs) {
+                            resetFgsRestrictionLocked(r);
+                            setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.pid,
+                                    r.appInfo.uid, r, false);
+                            EventLog.writeEvent(0x534e4554, "183147114", r.appInfo.uid,
+                                    "call setFgsRestrictionLocked for "
+                                            + (r.mStartForegroundCount + 1) + "th startForeground");
+                        }
+                    }
+                    // If the foreground service is not started from TOP process, do not allow it to
+                    // have while-in-use location/camera/microphone access.
+                    if (!r.mAllowWhileInUsePermissionInFgs) {
+                        Slog.w(TAG,
+                                "Foreground service started from background can not have "
+                                        + "location/camera/microphone access: service "
+                                        + r.shortInstanceName);
+                    }
+                }
+
                 // Apps under strict background restrictions simply don't get to have foreground
                 // services, so now that we've enforced the startForegroundService() contract
                 // we only do the machinery of making the service foreground when the app
@@ -1501,6 +1541,7 @@
                             active.mNumActive++;
                         }
                         r.isForeground = true;
+                        r.mStartForegroundCount++;
                         if (!stopProcStatsOp) {
                             ServiceState stracker = r.getTracker();
                             if (stracker != null) {
@@ -1559,6 +1600,7 @@
                     decActiveForegroundAppLocked(smap, r);
                 }
                 r.isForeground = false;
+                resetFgsRestrictionLocked(r);
                 ServiceState stracker = r.getTracker();
                 if (stracker != null) {
                     stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
@@ -2118,12 +2160,7 @@
                 }
             }
 
-            if (!s.mAllowWhileInUsePermissionInFgs) {
-                s.mAllowWhileInUsePermissionInFgs =
-                        shouldAllowWhileInUsePermissionInFgsLocked(callingPackage,
-                                callingPid, callingUid,
-                                service, s, false);
-            }
+            setFgsRestrictionLocked(callingPackage, callingPid, callingUid, s, false);
 
             if (s.app != null) {
                 if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
@@ -3419,7 +3456,7 @@
         r.isForeground = false;
         r.foregroundId = 0;
         r.foregroundNoti = null;
-        r.mAllowWhileInUsePermissionInFgs = false;
+        resetFgsRestrictionLocked(r);
 
         // Clear start entries.
         r.clearDeliveredStartsLocked();
@@ -4900,7 +4937,7 @@
      * @return true if allow, false otherwise.
      */
     private boolean shouldAllowWhileInUsePermissionInFgsLocked(String callingPackage,
-            int callingPid, int callingUid, Intent intent, ServiceRecord r,
+            int callingPid, int callingUid, ServiceRecord r,
             boolean allowBackgroundActivityStarts) {
         // Is the background FGS start restriction turned on?
         if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) {
@@ -4986,6 +5023,28 @@
     boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
             String callingPackage) {
         return shouldAllowWhileInUsePermissionInFgsLocked(
-                callingPackage, callingPid, callingUid, null, null, false);
+                callingPackage, callingPid, callingUid, null, false);
+    }
+
+    /**
+     * In R, mAllowWhileInUsePermissionInFgs is to allow while-in-use permissions in foreground
+     *  service or not. while-in-use permissions in FGS started from background might be restricted.
+     * @param callingPackage caller app's package name.
+     * @param callingUid caller app's uid.
+     * @param r the service to start.
+     * @return true if allow, false otherwise.
+     */
+    private void setFgsRestrictionLocked(String callingPackage,
+            int callingPid, int callingUid, ServiceRecord r,
+            boolean allowBackgroundActivityStarts) {
+        r.mLastSetFgsRestrictionTime = SystemClock.elapsedRealtime();
+        if (!r.mAllowWhileInUsePermissionInFgs) {
+            r.mAllowWhileInUsePermissionInFgs = shouldAllowWhileInUsePermissionInFgsLocked(
+                    callingPackage, callingPid, callingUid, r, allowBackgroundActivityStarts);
+        }
+    }
+
+    private void resetFgsRestrictionLocked(ServiceRecord r) {
+        r.mAllowWhileInUsePermissionInFgs = false;
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 7be843f..00d8208e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -88,6 +88,7 @@
     static final String KEY_PROCESS_START_ASYNC = "process_start_async";
     static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
     static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
+    static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout";
     static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold";
 
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
@@ -121,6 +122,7 @@
     private static final boolean DEFAULT_PROCESS_START_ASYNC = true;
     private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
     private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
+    private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000;
     private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000;
 
     // Flag stored in the DeviceConfig API.
@@ -273,6 +275,12 @@
     // this long.
     public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION;
 
+    /**
+     * When service started from background, before the timeout it can be promoted to FGS by calling
+     * Service.startForeground().
+     */
+    volatile long mFgsStartForegroundTimeoutMs = DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS;
+
     // Indicates whether the activity starts logging is enabled.
     // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
     volatile boolean mFlagActivityStartsLoggingEnabled;
@@ -421,6 +429,9 @@
                             case KEY_MIN_ASSOC_LOG_DURATION:
                                 updateMinAssocLogDuration();
                                 break;
+                            case KEY_FGS_START_FOREGROUND_TIMEOUT:
+                                updateFgsStartForegroundTimeout();
+                                break;
                             default:
                                 break;
                         }
@@ -697,6 +708,13 @@
                 /* defaultValue */ DEFAULT_MIN_ASSOC_LOG_DURATION);
     }
 
+    private void updateFgsStartForegroundTimeout() {
+        mFgsStartForegroundTimeoutMs = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_FGS_START_FOREGROUND_TIMEOUT,
+                DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS);
+    }
+
     void dump(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
                 + Settings.Global.ACTIVITY_MANAGER_CONSTANTS + ":");
@@ -769,6 +787,8 @@
         pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.toArray()));
         pw.print("  "); pw.print(KEY_MIN_ASSOC_LOG_DURATION); pw.print("=");
         pw.println(MIN_ASSOC_LOG_DURATION);
+        pw.print("  "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("=");
+        pw.println(mFgsStartForegroundTimeoutMs);
 
         pw.println();
         if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index af89907..50f3520 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -94,7 +94,6 @@
 import com.android.server.compat.PlatformCompat;
 
 import java.io.BufferedReader;
-import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -787,8 +786,7 @@
             return -1;
         }
 
-        File file = new File(filename);
-        file.delete();
+        // Writes an error message to stderr on failure
         ParcelFileDescriptor fd = openFileForSystem(filename, "w");
         if (fd == null) {
             return -1;
@@ -942,16 +940,16 @@
             String logNameTimeString = LOG_NAME_TIME_FORMATTER.format(localDateTime);
             heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof";
         }
-        pw.println("File: " + heapFile);
-        pw.flush();
 
-        File file = new File(heapFile);
-        file.delete();
+        // Writes an error message to stderr on failure
         ParcelFileDescriptor fd = openFileForSystem(heapFile, "w");
         if (fd == null) {
             return -1;
         }
 
+        pw.println("File: " + heapFile);
+        pw.flush();
+
         final CountDownLatch latch = new CountDownLatch(1);
 
         final RemoteCallback finishCallback = new RemoteCallback(new OnResultListener() {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 39f79ca..ef47b1e 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -22,6 +22,7 @@
 import android.net.wifi.WifiManager;
 import android.os.BatteryStats;
 import android.os.Bundle;
+import android.os.OutcomeReceiver;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.ServiceManager;
@@ -40,6 +41,7 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 
+import java.util.concurrent.ExecutionException;
 import libcore.util.EmptyArray;
 
 import java.util.concurrent.CompletableFuture;
@@ -405,7 +407,7 @@
         // We will request data from external processes asynchronously, and wait on a timeout.
         SynchronousResultReceiver wifiReceiver = null;
         SynchronousResultReceiver bluetoothReceiver = null;
-        SynchronousResultReceiver modemReceiver = null;
+        CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null);
         boolean railUpdated = false;
 
         if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
@@ -460,8 +462,22 @@
             }
 
             if (mTelephony != null) {
-                modemReceiver = new SynchronousResultReceiver("telephony");
-                mTelephony.requestModemActivityInfo(modemReceiver);
+                CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
+                mTelephony.requestModemActivityInfo(Runnable::run,
+                        new OutcomeReceiver<ModemActivityInfo,
+                                TelephonyManager.ModemActivityInfoException>() {
+                            @Override
+                            public void onResult(ModemActivityInfo result) {
+                                temp.complete(result);
+                            }
+
+                            @Override
+                            public void onError(TelephonyManager.ModemActivityInfoException e) {
+                                Slog.w(TAG, "error reading modem stats:" + e);
+                                temp.complete(null);
+                            }
+                        });
+                modemFuture = temp;
             }
             if (!railUpdated) {
                 synchronized (mStats) {
@@ -472,7 +488,17 @@
 
         final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
         final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver);
-        final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+        ModemActivityInfo modemInfo = null;
+        try {
+            modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+                    TimeUnit.MILLISECONDS);
+        } catch (TimeoutException | InterruptedException e) {
+            Slog.w(TAG, "timeout or interrupt reading modem stats: " + e);
+        } catch (ExecutionException e) {
+            Slog.w(TAG, "exception reading modem stats: " + e.getCause());
+        }
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
 
         synchronized (mStats) {
             mStats.addHistoryEventLocked(
@@ -519,11 +545,7 @@
         }
 
         if (modemInfo != null) {
-            if (modemInfo.isValid()) {
-                mStats.updateMobileRadioState(modemInfo);
-            } else {
-                Slog.w(TAG, "modem info is invalid: " + modemInfo);
-            }
+            mStats.updateMobileRadioState(modemInfo);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 580ceca..34ba3e0 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1208,7 +1208,7 @@
     public void noteModemControllerActivity(ModemActivityInfo info) {
         enforceCallingPermission();
 
-        if (info == null || !info.isValid()) {
+        if (info == null) {
             Slog.e(TAG, "invalid modem data given: " + info);
             return;
         }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 1b65dba..0e62828 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -142,6 +142,10 @@
     // allow while-in-use permissions in foreground service or not.
     // while-in-use permissions in FGS started from background might be restricted.
     boolean mAllowWhileInUsePermissionInFgs;
+    // The number of times Service.startForeground() is called;
+    int mStartForegroundCount;
+    // Last time mAllowWhileInUsePermissionInFgs is set.
+    long mLastSetFgsRestrictionTime;
 
     // the most recent package that start/bind this service.
     String mRecentCallingPackage;
@@ -406,6 +410,8 @@
         }
         pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
                 pw.println(mAllowWhileInUsePermissionInFgs);
+        pw.print(prefix); pw.print("startForegroundCount=");
+        pw.println(mStartForegroundCount);
         pw.print(prefix); pw.print("recentCallingPackage=");
                 pw.println(mRecentCallingPackage);
         if (delayed) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index c24973d..8926e20 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -242,7 +242,7 @@
         mValidationInfo.append(opcode, new ValidationInfo(validator, addrType));
     }
 
-    int isValid(HdmiCecMessage message) {
+    int isValid(HdmiCecMessage message, boolean isMessageReceived) {
         int opcode = message.getOpcode();
         ValidationInfo info = mValidationInfo.get(opcode);
         if (info == null) {
@@ -256,6 +256,22 @@
             HdmiLogger.warning("Unexpected source: " + message);
             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) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index cca8be8..b049d01 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -556,6 +556,12 @@
         // on boot, if device is interactive, set HDMI CEC state as powered on as well
         if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
             mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
+            // Start all actions that were queued because the device was in standby
+            if (mAddressAllocated) {
+                for (HdmiCecLocalDevice localDevice : getAllLocalDevices()) {
+                    localDevice.startQueuedActions();
+                }
+            }
         }
     }
 
@@ -1122,7 +1128,7 @@
     @ServiceThreadOnly
     void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
         assertRunOnServiceThread();
-        if (mMessageValidator.isValid(command) == HdmiCecMessageValidator.OK) {
+        if (mMessageValidator.isValid(command, false) == HdmiCecMessageValidator.OK) {
             mCecController.sendCommand(command, callback);
         } else {
             HdmiLogger.error("Invalid message type:" + command);
@@ -1153,7 +1159,7 @@
     @ServiceThreadOnly
     boolean handleCecCommand(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        int errorCode = mMessageValidator.isValid(message);
+        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.
@@ -3353,8 +3359,8 @@
         invokeInputChangeListener(info);
     }
 
-   void setMhlInputChangeEnabled(boolean enabled) {
-       mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
+    void setMhlInputChangeEnabled(boolean enabled) {
+        mMhlController.setOption(OPTION_MHL_INPUT_SWITCHING, toInt(enabled));
 
         synchronized (mLock) {
             mMhlInputChangeEnabled = enabled;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5babfeb..f83d059 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -123,6 +123,7 @@
 import android.app.ITransientNotification;
 import android.app.ITransientNotificationCallback;
 import android.app.IUriGrantsManager;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -479,6 +480,8 @@
     final ArrayMap<NotificationRecord, ArrayList<CancelNotificationRunnable>> mDelayedCancelations =
             new ArrayMap<>();
 
+    private KeyguardManager mKeyguardManager;
+
     // The last key in this list owns the hardware.
     ArrayList<String> mLights = new ArrayList<>();
 
@@ -1726,6 +1729,11 @@
     }
 
     @VisibleForTesting
+    void setKeyguardManager(KeyguardManager keyguardManager) {
+        mKeyguardManager = keyguardManager;
+    }
+
+    @VisibleForTesting
     ShortcutHelper getShortcutHelper() {
         return mShortcutHelper;
     }
@@ -2330,6 +2338,7 @@
             mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
             mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
             mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+            mKeyguardManager = getContext().getSystemService(KeyguardManager.class);
             mZenModeHelper.onSystemReady();
             mRoleObserver = new RoleObserver(getContext().getSystemService(RoleManager.class),
                     mPackageManager, getContext().getMainExecutor());
@@ -6902,7 +6911,6 @@
         boolean beep = false;
         boolean blink = false;
 
-        final Notification notification = record.getSbn().getNotification();
         final String key = record.getKey();
 
         // Should this notification make noise, vibe, or use the LED?
@@ -6924,7 +6932,7 @@
         if (!record.isUpdate
                 && record.getImportance() > IMPORTANCE_MIN
                 && !suppressedByDnd) {
-            sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+            sendAccessibilityEvent(record);
             sentAccessibilityEvent = true;
         }
 
@@ -6946,7 +6954,7 @@
                 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
                 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
                     if (!sentAccessibilityEvent) {
-                        sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+                        sendAccessibilityEvent(record);
                         sentAccessibilityEvent = true;
                     }
                     if (DBG) Slog.v(TAG, "Interrupting!");
@@ -7663,17 +7671,30 @@
         return (x < low) ? low : ((x > high) ? high : x);
     }
 
-    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
+    void sendAccessibilityEvent(NotificationRecord record) {
         if (!mAccessibilityManager.isEnabled()) {
             return;
         }
 
-        AccessibilityEvent event =
+        final Notification notification = record.getNotification();
+        final CharSequence packageName = record.getSbn().getPackageName();
+        final AccessibilityEvent event =
             AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
         event.setPackageName(packageName);
         event.setClassName(Notification.class.getName());
-        event.setParcelableData(notification);
-        CharSequence tickerText = notification.tickerText;
+        final int visibilityOverride = record.getPackageVisibilityOverride();
+        final int notifVisibility = visibilityOverride == NotificationManager.VISIBILITY_NO_OVERRIDE
+                ? notification.visibility : visibilityOverride;
+        final int userId = record.getUser().getIdentifier();
+        final boolean needPublic = userId >= 0 && mKeyguardManager.isDeviceLocked(userId);
+        if (needPublic && notifVisibility != Notification.VISIBILITY_PUBLIC) {
+            // Emit the public version if we're on the lockscreen and this notification isn't
+            // publicly visible.
+            event.setParcelableData(notification.publicVersion);
+        } else {
+            event.setParcelableData(notification);
+        }
+        final CharSequence tickerText = notification.tickerText;
         if (!TextUtils.isEmpty(tickerText)) {
             event.getText().add(tickerText);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1173df6..4767823 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -14289,9 +14289,15 @@
             return new ParceledListSlice<IntentFilter>(result) {
                 @Override
                 protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) {
-                    // IntentFilter has final Parcelable methods, so redirect to the subclass
-                    ((ParsedIntentInfo) parcelable).writeIntentInfoToParcel(dest,
-                            callFlags);
+                    parcelable.writeToParcel(dest, callFlags);
+                }
+
+                @Override
+                protected void writeParcelableCreator(IntentFilter parcelable, Parcel dest) {
+                    // All Parcel#writeParcelableCreator does is serialize the class name to
+                    // access via reflection to grab its CREATOR. This does that manually, pointing
+                    // to the parent IntentFilter so that all of the subclass fields are ignored.
+                    dest.writeString(IntentFilter.class.getName());
                 }
             };
         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 836e615..ae940d0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -608,6 +608,8 @@
 
     private int mPowerButtonSuppressionDelayMillis = POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS;
 
+    private boolean mLockNowPending = false;
+
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
     private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
     private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5;
@@ -4941,6 +4943,7 @@
                     mKeyguardDelegate.doKeyguardTimeout(options);
                 }
                 mLockScreenTimerActive = false;
+                mLockNowPending = false;
                 options = null;
             }
         }
@@ -4950,7 +4953,7 @@
         }
     }
 
-    ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout();
+    final ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout();
 
     @Override
     public void lockNow(Bundle options) {
@@ -4962,6 +4965,9 @@
             mScreenLockTimeout.setLockOptions(options);
         }
         mHandler.post(mScreenLockTimeout);
+        synchronized (mScreenLockTimeout) {
+            mLockNowPending = true;
+        }
     }
 
     // TODO (b/113840485): Move this logic to DisplayPolicy when lockscreen supports multi-display.
@@ -4977,6 +4983,10 @@
 
     private void updateLockScreenTimeout() {
         synchronized (mScreenLockTimeout) {
+            if (mLockNowPending) {
+                Log.w(TAG, "lockNow pending, ignore updating lockscreen timeout");
+                return;
+            }
             final boolean enable = !mAllowLockscreenWhenOnDisplays.isEmpty()
                     && mDefaultDisplayPolicy.isAwake()
                     && mKeyguardDelegate != null && mKeyguardDelegate.isSecure(mCurrentUserId);
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 392792d..b75bce8 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -662,6 +662,12 @@
 
         @Override
         public String getDefaultSmsPackage(int userId) {
+            userId = handleIncomingUser(userId, false, "getDefaultSmsPackage");
+            if (!mUserManagerInternal.exists(userId)) {
+                Slog.e(LOG_TAG, "user " + userId + " does not exist");
+                return null;
+            }
+
             long identity = Binder.clearCallingIdentity();
             try {
                 return CollectionUtils.firstOrNull(
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 0390015..71b3e61 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -102,6 +102,7 @@
 import android.os.IStoraged;
 import android.os.IThermalEventListener;
 import android.os.IThermalService;
+import android.os.OutcomeReceiver;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -172,6 +173,7 @@
 import com.android.server.storage.DiskStatsFileLogger;
 import com.android.server.storage.DiskStatsLoggingService;
 
+import java.util.concurrent.ExecutionException;
 import libcore.io.IoUtils;
 
 import org.json.JSONArray;
@@ -1731,22 +1733,47 @@
     int pullModemActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
         long token = Binder.clearCallingIdentity();
         try {
-            SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
-            mTelephony.requestModemActivityInfo(modemReceiver);
-            final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+            CompletableFuture<ModemActivityInfo> modemFuture = new CompletableFuture<>();
+            mTelephony.requestModemActivityInfo(Runnable::run,
+                    new OutcomeReceiver<ModemActivityInfo,
+                            TelephonyManager.ModemActivityInfoException>() {
+                        @Override
+                        public void onResult(ModemActivityInfo result) {
+                            modemFuture.complete(result);
+                        }
+
+                        @Override
+                        public void onError(TelephonyManager.ModemActivityInfoException e) {
+                            Slog.w(TAG, "error reading modem stats:" + e);
+                            modemFuture.complete(null);
+                        }
+                    });
+
+            ModemActivityInfo modemInfo;
+            try {
+                modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
+                        TimeUnit.MILLISECONDS);
+            } catch (TimeoutException | InterruptedException e) {
+                Slog.w(TAG, "timeout or interrupt reading modem stats: " + e);
+                return StatsManager.PULL_SKIP;
+            } catch (ExecutionException e) {
+                Slog.w(TAG, "exception reading modem stats: " + e.getCause());
+                return StatsManager.PULL_SKIP;
+            }
+
             if (modemInfo == null) {
                 return StatsManager.PULL_SKIP;
             }
             StatsEvent e = StatsEvent.newBuilder()
                     .setAtomId(atomTag)
-                    .writeLong(modemInfo.getTimestamp())
+                    .writeLong(modemInfo.getTimestampMillis())
                     .writeLong(modemInfo.getSleepTimeMillis())
                     .writeLong(modemInfo.getIdleTimeMillis())
-                    .writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis())
-                    .writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis())
-                    .writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis())
-                    .writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis())
-                    .writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis())
+                    .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(0))
+                    .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(1))
+                    .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(2))
+                    .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(3))
+                    .writeLong(modemInfo.getTransmitDurationMillisAtPowerLevel(4))
                     .writeLong(modemInfo.getReceiveTimeMillis())
                     .build();
             pulledData.add(e);
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 277163a6..630317a 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -1265,12 +1265,14 @@
                     if (inputId != null) {
                         if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
                             if (previousCableConnectionStatus != connection.getInputStateLocked()) {
-                                mListener.onStateChanged(inputId, connection.getInputStateLocked());
+                                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+                                    connection.getInputStateLocked(), 0, inputId).sendToTarget();
                             }
                         } else {
                             if ((previousConfigsLength == 0)
                                     != (connection.getConfigsLengthLocked() == 0)) {
-                                mListener.onStateChanged(inputId, connection.getInputStateLocked());
+                                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
+                                    connection.getInputStateLocked(), 0, inputId).sendToTarget();
                             }
                         }
                     }
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 382398a..e0cc8e1 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -352,7 +352,7 @@
     }
 
     private void handleSafeModeStatusChanged() {
-        logDbg("VcnGatewayConnection safe mode status changed");
+        logVdbg("VcnGatewayConnection safe mode status changed");
         boolean hasSafeModeGatewayConnection = false;
 
         // If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode
@@ -368,7 +368,7 @@
                 hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE;
         if (oldStatus != mCurrentStatus) {
             mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection);
-            logDbg(
+            logInfo(
                     "Safe mode "
                             + (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE ? "entered" : "exited"));
         }
@@ -539,6 +539,16 @@
         Slog.d(TAG, getLogPrefix() + msg, tr);
     }
 
+    private void logInfo(String msg) {
+        Slog.i(TAG, getLogPrefix() + msg);
+        LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg);
+    }
+
+    private void logInfo(String msg, Throwable tr) {
+        Slog.i(TAG, getLogPrefix() + msg, tr);
+        LOCAL_LOG.log(getLogPrefix() + "INFO: " + msg + tr);
+    }
+
     private void logErr(String msg) {
         Slog.e(TAG, getLogPrefix() + msg);
         LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg);
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 450257f..7dec4e7 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1677,10 +1677,8 @@
             mFailedAttempts = 0;
             cancelSafeModeAlarm();
 
-            if (mIsInSafeMode) {
-                mIsInSafeMode = false;
-                mGatewayStatusCallback.onSafeModeStatusChanged();
-            }
+            mIsInSafeMode = false;
+            mGatewayStatusCallback.onSafeModeStatusChanged();
         }
 
         protected void applyTransform(
diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java
index a2768c6..3a9b2dc 100644
--- a/services/java/com/android/server/SystemConfigService.java
+++ b/services/java/com/android/server/SystemConfigService.java
@@ -21,6 +21,7 @@
 import android.Manifest;
 import android.content.Context;
 import android.os.ISystemConfig;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.SparseArray;
 
@@ -84,6 +85,21 @@
             }
             return ArrayUtils.convertToIntArray(uids);
         }
+
+        @Override
+        public List<String> getEnabledComponentOverrides(String packageName) {
+            ArrayMap<String, Boolean> systemComponents = SystemConfig.getInstance()
+                    .getComponentsEnabledStates(packageName);
+            List<String> enabledComponent = new ArrayList<>();
+            if (systemComponents != null) {
+                for (Map.Entry<String, Boolean> entry : systemComponents.entrySet()) {
+                    if (Boolean.TRUE.equals(entry.getValue())) {
+                        enabledComponent.add(entry.getKey());
+                    }
+                }
+            }
+            return enabledComponent;
+        }
     };
 
     public SystemConfigService(Context context) {
diff --git a/services/net/java/android/net/ConnectivityModuleConnector.java b/services/net/java/android/net/ConnectivityModuleConnector.java
index 62f2c35..c6b15c1 100644
--- a/services/net/java/android/net/ConnectivityModuleConnector.java
+++ b/services/net/java/android/net/ConnectivityModuleConnector.java
@@ -278,7 +278,10 @@
             // This code path is only run by the system server: only the system server binds
             // to the NetworkStack as a service. Other processes get the NetworkStack from
             // the ServiceManager.
-            maybeCrashWithTerribleFailure("Lost network stack", mPackageName);
+            maybeCrashWithTerribleFailure(
+                "Lost network stack. This is not the root cause of any issue, it is a side "
+                + "effect of a crash that happened earlier. Earlier logs should point to the "
+                + "actual issue.", mPackageName);
         }
     }
 
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 3e5cbea..c45d084 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -125,7 +125,7 @@
         mMessageValidator =
                 new HdmiCecMessageValidator(mHdmiControlService) {
                     @Override
-                    int isValid(HdmiCecMessage message) {
+                    int isValid(HdmiCecMessage message, boolean isMessageReceived) {
                         return HdmiCecMessageValidator.OK;
                     }
                 };
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 ae7f422..ae99dab 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -629,7 +629,7 @@
     }
 
     private IntegerSubject assertMessageValidity(String message) {
-        return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message)));
+        return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message), false));
     }
 
     /**
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index ad15a99..11ea4a4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -46,6 +46,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.Notification.Builder;
 import android.app.NotificationChannel;
@@ -103,6 +104,8 @@
     NotificationUsageStats mUsageStats;
     @Mock
     IAccessibilityManager mAccessibilityService;
+    @Mock
+    KeyguardManager mKeyguardManager;
     NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
     private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
             1 << 30);
@@ -147,6 +150,7 @@
         when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
 
         long serviceReturnValue = IntPair.of(
                 AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
@@ -168,6 +172,7 @@
         mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN);
         mService.setUsageStats(mUsageStats);
         mService.setAccessibilityManager(accessibilityManager);
+        mService.setKeyguardManager(mKeyguardManager);
         mService.mScreenOn = false;
         mService.mInCallStateOffHook = false;
         mService.mNotificationPulseEnabled = true;
@@ -484,6 +489,94 @@
     }
 
     @Test
+    public void testLockedPrivateA11yRedaction() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+        r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+        AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+        when(accessibilityManager.isEnabled()).thenReturn(true);
+        mService.setAccessibilityManager(accessibilityManager);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        ArgumentCaptor<AccessibilityEvent> eventCaptor =
+                ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+        verify(accessibilityManager, times(1))
+                .sendAccessibilityEvent(eventCaptor.capture());
+
+        AccessibilityEvent event = eventCaptor.getValue();
+        assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+    }
+
+    @Test
+    public void testLockedOverridePrivateA11yRedaction() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE);
+        r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+        AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+        when(accessibilityManager.isEnabled()).thenReturn(true);
+        mService.setAccessibilityManager(accessibilityManager);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        ArgumentCaptor<AccessibilityEvent> eventCaptor =
+                ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+        verify(accessibilityManager, times(1))
+                .sendAccessibilityEvent(eventCaptor.capture());
+
+        AccessibilityEvent event = eventCaptor.getValue();
+        assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+    }
+
+    @Test
+    public void testLockedPublicA11yNoRedaction() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+        r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+        AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+        when(accessibilityManager.isEnabled()).thenReturn(true);
+        mService.setAccessibilityManager(accessibilityManager);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        ArgumentCaptor<AccessibilityEvent> eventCaptor =
+                ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+        verify(accessibilityManager, times(1))
+                .sendAccessibilityEvent(eventCaptor.capture());
+
+        AccessibilityEvent event = eventCaptor.getValue();
+        assertEquals(r.getNotification(), event.getParcelableData());
+    }
+
+    @Test
+    public void testUnlockedPrivateA11yNoRedaction() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+        r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
+        AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+        when(accessibilityManager.isEnabled()).thenReturn(true);
+        mService.setAccessibilityManager(accessibilityManager);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        ArgumentCaptor<AccessibilityEvent> eventCaptor =
+                ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+        verify(accessibilityManager, times(1))
+                .sendAccessibilityEvent(eventCaptor.capture());
+
+        AccessibilityEvent event = eventCaptor.getValue();
+        assertEquals(r.getNotification(), event.getParcelableData());
+    }
+
+    @Test
     public void testBeepInsistently() throws Exception {
         NotificationRecord r = getInsistentBeepyNotification();
 
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 5e3d26a..705b491 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -81,7 +81,8 @@
  * <pre>
  * {@code
  * <service android:name="your.package.YourInCallServiceImplementation"
- *          android:permission="android.permission.BIND_INCALL_SERVICE">
+ *          android:permission="android.permission.BIND_INCALL_SERVICE"
+ *          android:exported="true">
  *      <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
  *      <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
  *          android:value="true" />
@@ -91,6 +92,10 @@
  * </service>
  * }
  * </pre>
+ *
+ * <em>Note: You should NOT mark your {@link InCallService} with the attribute
+ * {@code android:exported="false"}; doing so can result in a failure to bind to your implementation
+ * during calls.</em>
  * <p>
  * In addition to implementing the {@link InCallService} API, you must also declare an activity in
  * your manifest which handles the {@link Intent#ACTION_DIAL} intent.  The example below illustrates
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index debb119..0bf8ce6 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -16,8 +16,12 @@
 
 package android.telephony;
 
+import android.annotation.DurationMillisLong;
+import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -25,46 +29,50 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
- * Reports modem activity information.
+ * Contains information about the modem's activity. May be useful for power stats reporting.
  * @hide
  */
+@SystemApi
+@TestApi
 public final class ModemActivityInfo implements Parcelable {
+    private static final int TX_POWER_LEVELS = 5;
+
     /**
-     * Tx(transmit) power level. see power index below
-     * <ul>
-     *   <li> index 0 = tx_power < 0dBm. </li>
-     *   <li> index 1 = 0dBm < tx_power < 5dBm. </li>
-     *   <li> index 2 = 5dBm < tx_power < 15dBm. </li>
-     *   <li> index 3 = 15dBm < tx_power < 20dBm. </li>
-     *   <li> index 4 = tx_power > 20dBm. </li>
-     * </ul>
-     */
-    public static final int TX_POWER_LEVELS = 5;
-    /**
-     * Tx(transmit) power level 0: tx_power < 0dBm
+     * Corresponds to transmit power of less than 0dBm.
      */
     public static final int TX_POWER_LEVEL_0 = 0;
+
     /**
-     * Tx(transmit) power level 1: 0dBm < tx_power < 5dBm
+     * Corresponds to transmit power between 0dBm and 5dBm.
      */
     public static final int TX_POWER_LEVEL_1 = 1;
+
     /**
-     * Tx(transmit) power level 2: 5dBm < tx_power < 15dBm
+     * Corresponds to transmit power between 5dBm and 15dBm.
      */
     public static final int TX_POWER_LEVEL_2 = 2;
+
     /**
-     * Tx(transmit) power level 3: 15dBm < tx_power < 20dBm.
+     * Corresponds to transmit power between 15dBm and 20dBm.
      */
     public static final int TX_POWER_LEVEL_3 = 3;
+
     /**
-     * Tx(transmit) power level 4: tx_power > 20dBm
+     * Corresponds to transmit power above 20dBm.
      */
     public static final int TX_POWER_LEVEL_4 = 4;
 
+    /**
+     * The number of transmit power levels. Fixed by HAL definition.
+     */
+    public static int getNumTxPowerLevels() {
+        return TX_POWER_LEVELS;
+    }
+
     /** @hide */
     @IntDef(prefix = {"TX_POWER_LEVEL_"}, value = {
             TX_POWER_LEVEL_0,
@@ -82,34 +90,39 @@
         new Range<>(5, 15),
         new Range<>(15, 20),
         new Range<>(20, Integer.MAX_VALUE)
-
     };
 
     private long mTimestamp;
     private int mSleepTimeMs;
     private int mIdleTimeMs;
-    private List<TransmitPower> mTransmitPowerInfo = new ArrayList<>(TX_POWER_LEVELS);
+    private int[] mTxTimeMs;
     private int mRxTimeMs;
 
+    /**
+     * @hide
+     */
+    @TestApi
     public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
                         @NonNull int[] txTimeMs, int rxTimeMs) {
+        Objects.requireNonNull(txTimeMs);
+        if (txTimeMs.length != TX_POWER_LEVELS) {
+            throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS");
+        }
         mTimestamp = timestamp;
         mSleepTimeMs = sleepTimeMs;
         mIdleTimeMs = idleTimeMs;
-        populateTransmitPowerRange(txTimeMs);
+        mTxTimeMs = txTimeMs;
         mRxTimeMs = rxTimeMs;
     }
 
-    /** helper API to populate tx power range for each bucket **/
-    private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) {
-        int i = 0;
-        for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) {
-            mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i]));
-        }
-        // Make sure that mTransmitPowerInfo is fully initialized.
-        for ( ; i < TX_POWER_LEVELS; i++) {
-            mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], 0));
-        }
+    /**
+     * Provided for convenience in manipulation since the API exposes long values but internal
+     * representations are ints.
+     * @hide
+     */
+    public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs,
+            @NonNull int[] txTimeMs, long rxTimeMs) {
+        this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs);
     }
 
     @Override
@@ -118,7 +131,7 @@
             + " mTimestamp=" + mTimestamp
             + " mSleepTimeMs=" + mSleepTimeMs
             + " mIdleTimeMs=" + mIdleTimeMs
-            + " mTransmitPowerInfo[]=" + mTransmitPowerInfo.toString()
+            + " mTxTimeMs[]=" + Arrays.toString(mTxTimeMs)
             + " mRxTimeMs=" + mRxTimeMs
             + "}";
     }
@@ -129,14 +142,12 @@
 
     public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR =
             new Parcelable.Creator<ModemActivityInfo>() {
-        public ModemActivityInfo createFromParcel(Parcel in) {
+        public ModemActivityInfo createFromParcel(@NonNull Parcel in) {
             long timestamp = in.readLong();
             int sleepTimeMs = in.readInt();
             int idleTimeMs = in.readInt();
             int[] txTimeMs = new int[TX_POWER_LEVELS];
-            for (int i = 0; i < TX_POWER_LEVELS; i++) {
-                txTimeMs[i] = in.readInt();
-            }
+            in.readIntArray(txTimeMs);
             int rxTimeMs = in.readInt();
             return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs,
                                 txTimeMs, rxTimeMs);
@@ -147,21 +158,25 @@
         }
     };
 
-    public void writeToParcel(Parcel dest, int flags) {
+    /**
+     * @param dest The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     */
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeLong(mTimestamp);
         dest.writeInt(mSleepTimeMs);
         dest.writeInt(mIdleTimeMs);
-        for (int i = 0; i < TX_POWER_LEVELS; i++) {
-            dest.writeInt(mTransmitPowerInfo.get(i).getTimeInMillis());
-        }
+        dest.writeIntArray(mTxTimeMs);
         dest.writeInt(mRxTimeMs);
     }
 
     /**
-     * @return milliseconds since boot, including mTimeInMillis spent in sleep.
-     * @see SystemClock#elapsedRealtime()
+     * Gets the timestamp at which this modem activity info was recorded.
+     *
+     * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this
+     * {@link ModemActivityInfo} was recorded.
      */
-    public long getTimestamp() {
+    public @ElapsedRealtimeLong long getTimestampMillis() {
         return mTimestamp;
     }
 
@@ -171,35 +186,48 @@
     }
 
     /**
-     * @return an arrayList of {@link TransmitPower} with each element representing the total time where
-     * transmitter is awake time (in ms) for a given power range (in dbm).
+     * Gets the amount of time the modem spent transmitting at a certain power level.
      *
-     * @see #TX_POWER_LEVELS
+     * @param powerLevel The power level to query.
+     * @return The amount of time, in milliseconds, that the modem spent transmitting at the
+     * given power level.
      */
-    @NonNull
-    public List<TransmitPower> getTransmitPowerInfo() {
-        return mTransmitPowerInfo;
+    public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel(
+            @TxPowerLevel int powerLevel) {
+        return mTxTimeMs[powerLevel];
+    }
+
+    /**
+     * Gets the range of transmit powers corresponding to a certain power level.
+     *
+     * @param powerLevel The power level to query
+     * @return A {@link Range} object representing the range of intensities (in dBm) to which this
+     * power level corresponds.
+     */
+    public @NonNull Range<Integer> getTransmitPowerRange(@TxPowerLevel int powerLevel) {
+        return TX_POWER_RANGES[powerLevel];
     }
 
     /** @hide */
     public void setTransmitTimeMillis(int[] txTimeMs) {
-        populateTransmitPowerRange(txTimeMs);
-    }
-
-    /** @hide */
-    @NonNull
-    public int[] getTransmitTimeMillis() {
-        int[] transmitTimeMillis = new int[TX_POWER_LEVELS];
-        for (int i = 0; i < transmitTimeMillis.length; i++) {
-            transmitTimeMillis[i] = mTransmitPowerInfo.get(i).getTimeInMillis();
-        }
-        return transmitTimeMillis;
+        mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS);
     }
 
     /**
-     * @return total mTimeInMillis (in ms) when modem is in a low power or sleep state.
+     * @return The raw array of transmit power durations
+     * @hide
      */
-    public int getSleepTimeMillis() {
+    @NonNull
+    public int[] getTransmitTimeMillis() {
+        return mTxTimeMs;
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is in a low power or sleep state.
+     *
+     * @return Time in milliseconds.
+     */
+    public @DurationMillisLong long getSleepTimeMillis() {
         return mSleepTimeMs;
     }
 
@@ -209,10 +237,44 @@
     }
 
     /**
-     * @return total mTimeInMillis (in ms) when modem is awake but neither the transmitter nor receiver are
-     * active.
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     * @hide
      */
-    public int getIdleTimeMillis() {
+    public void setSleepTimeMillis(long sleepTimeMillis) {
+        mSleepTimeMs = (int) sleepTimeMillis;
+    }
+
+    /**
+     * Computes the difference between this instance of {@link ModemActivityInfo} and another
+     * instance.
+     *
+     * This method should be used to compute the amount of activity that has happened between two
+     * samples of modem activity taken at separate times. The sample passed in as an argument to
+     * this method should be the one that's taken later in time (and therefore has more activity).
+     * @param other The other instance of {@link ModemActivityInfo} to diff against.
+     * @return An instance of {@link ModemActivityInfo} representing the difference in modem
+     * activity.
+     */
+    public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) {
+        int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+        for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+            txTimeMs[i] = other.mTxTimeMs[i] - mTxTimeMs[i];
+        }
+        return new ModemActivityInfo(other.getTimestampMillis(),
+                other.getSleepTimeMillis() - getSleepTimeMillis(),
+                other.getIdleTimeMillis() - getIdleTimeMillis(),
+                txTimeMs,
+                other.getReceiveTimeMillis() - getReceiveTimeMillis());
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is awake but neither transmitting
+     * nor receiving.
+     *
+     * @return Time in milliseconds.
+     */
+    public @DurationMillisLong long getIdleTimeMillis() {
         return mIdleTimeMs;
     }
 
@@ -222,9 +284,20 @@
     }
 
     /**
-     * @return rx(receive) mTimeInMillis in ms.
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     * @hide
      */
-    public int getReceiveTimeMillis() {
+    public void setIdleTimeMillis(long idleTimeMillis) {
+        mIdleTimeMs = (int) idleTimeMillis;
+    }
+
+    /**
+     * Gets the amount of time (in milliseconds) when the modem is awake and receiving data.
+     *
+     * @return Time in milliseconds.
+     */
+    public @DurationMillisLong long getReceiveTimeMillis() {
         return mRxTimeMs;
     }
 
@@ -234,71 +307,56 @@
     }
 
     /**
+     * Provided for convenience, since the API surface needs to return longs but internal
+     * representations are ints.
+     * @hide
+     */
+    public void setReceiveTimeMillis(long receiveTimeMillis) {
+        mRxTimeMs = (int) receiveTimeMillis;
+    }
+
+    /**
      * Indicates if the modem has reported valid {@link ModemActivityInfo}.
      *
      * @return {@code true} if this {@link ModemActivityInfo} record is valid,
      * {@code false} otherwise.
+     * @hide
      */
+    @TestApi
     public boolean isValid() {
-        for (TransmitPower powerInfo : getTransmitPowerInfo()) {
-            if(powerInfo.getTimeInMillis() < 0) {
-                return false;
-            }
-        }
+        boolean isTxPowerValid = Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0);
 
-        return ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
+        return isTxPowerValid && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0)
                 && (getReceiveTimeMillis() >= 0) && !isEmpty());
     }
 
-    private boolean isEmpty() {
-        for (TransmitPower txVal : getTransmitPowerInfo()) {
-            if(txVal.getTimeInMillis() != 0) {
-                return false;
-            }
-        }
+    /** @hide */
+    @TestApi
+    public boolean isEmpty() {
+        boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0
+                || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0);
 
-        return ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
+        return isTxPowerEmpty && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0)
                 && (getReceiveTimeMillis() == 0));
     }
 
-    /**
-     * Transmit power Information, including the power range in dbm and the total time (in ms) where
-     * the transmitter is active/awake for this power range.
-     * e.g, range: 0dbm(lower) ~ 5dbm(upper)
-     *      time: 5ms
-     */
-    public class TransmitPower {
-        private int mTimeInMillis;
-        private Range<Integer> mPowerRangeInDbm;
-        /** @hide */
-        public TransmitPower(@NonNull Range<Integer> range, int time) {
-            this.mTimeInMillis = time;
-            this.mPowerRangeInDbm = range;
-        }
 
-        /**
-         * @return the total time in ms where the transmitter is active/wake for this power range
-         * {@link #getPowerRangeInDbm()}.
-         */
-        public int getTimeInMillis() {
-            return mTimeInMillis;
-        }
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ModemActivityInfo that = (ModemActivityInfo) o;
+        return mTimestamp == that.mTimestamp
+                && mSleepTimeMs == that.mSleepTimeMs
+                && mIdleTimeMs == that.mIdleTimeMs
+                && mRxTimeMs == that.mRxTimeMs
+                && Arrays.equals(mTxTimeMs, that.mTxTimeMs);
+    }
 
-        /**
-         * @return the power range in dbm. e.g, range: 0dbm(lower) ~ 5dbm(upper)
-         */
-        @NonNull
-        public Range<Integer> getPowerRangeInDbm() {
-            return mPowerRangeInDbm;
-        }
-
-        @Override
-        public String toString() {
-            return "TransmitPower{"
-                + " mTimeInMillis=" + mTimeInMillis
-                + " mPowerRangeInDbm={" + mPowerRangeInDbm.getLower()
-                + "," + mPowerRangeInDbm.getUpper()
-                + "}}";
-        }
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mRxTimeMs);
+        result = 31 * result + Arrays.hashCode(mTxTimeMs);
+        return result;
     }
 }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 6da61b7..1677c61 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -444,7 +444,9 @@
         mArfcnRsrpBoost = s.mArfcnRsrpBoost;
         synchronized (mNetworkRegistrationInfos) {
             mNetworkRegistrationInfos.clear();
-            mNetworkRegistrationInfos.addAll(s.getNetworkRegistrationInfoList());
+            for (NetworkRegistrationInfo nri : s.getNetworkRegistrationInfoList()) {
+                mNetworkRegistrationInfos.add(new NetworkRegistrationInfo(nri));
+            }
         }
         mNrFrequencyRange = s.mNrFrequencyRange;
         mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw;
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index fe7e5976..41e24dd 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -26,8 +26,10 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -101,9 +103,11 @@
             }
 
             mSignalThresholdInfos = new ArrayList<>(signalThresholdInfos);
-            // Sort the collection with RAN ascending order, make the ordering not matter for equals
+            // Sort the collection with RAN and then SignalMeasurementType ascending order, make the
+            // ordering not matter for equals
             mSignalThresholdInfos.sort(
-                    Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType));
+                    Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType)
+                            .thenComparing(SignalThresholdInfo::getSignalMeasurementType));
             return this;
         }
 
@@ -144,7 +148,7 @@
          * @return the SignalStrengthUpdateRequest object
          *
          * @throws IllegalArgumentException if the SignalThresholdInfo collection is empty size, the
-         * radio access network type in the collection is not unique
+         * signal measurement type for the same RAN in the collection is not unique
          */
         public @NonNull SignalStrengthUpdateRequest build() {
             return new SignalStrengthUpdateRequest(mSignalThresholdInfos,
@@ -258,14 +262,23 @@
     }
 
     /**
-     * Throw IAE when the RAN in the collection is not unique.
+     * Throw IAE if SignalThresholdInfo collection is null or empty,
+     * or the SignalMeasurementType for the same RAN in the collection is not unique.
      */
     private static void validate(Collection<SignalThresholdInfo> infos) {
-        Set<Integer> uniqueRan = new HashSet<>(infos.size());
+        if (infos == null || infos.isEmpty()) {
+            throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty");
+        }
+
+        // Map from RAN to set of SignalMeasurementTypes
+        Map<Integer, Set<Integer>> ranToTypes = new HashMap<>(infos.size());
         for (SignalThresholdInfo info : infos) {
             final int ran = info.getRadioAccessNetworkType();
-            if (!uniqueRan.add(ran)) {
-                throw new IllegalArgumentException("RAN: " + ran + " is not unique");
+            final int type = info.getSignalMeasurementType();
+            ranToTypes.putIfAbsent(ran, new HashSet<>());
+            if (!ranToTypes.get(ran).add(type)) {
+                throw new IllegalArgumentException(
+                        "SignalMeasurementType " + type + " for RAN " + ran + " is not unique");
             }
         }
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4189784..f55dc8b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -57,7 +57,9 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.OutcomeReceiver;
 import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -84,6 +86,7 @@
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
 import android.telephony.data.ApnSetting;
 import android.telephony.data.ApnSetting.MvnoType;
+import android.telephony.data.SlicingConfig;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
 import android.telephony.gba.UaSecurityProtocolIdentifier;
@@ -176,6 +179,9 @@
      */
     public static final String MODEM_ACTIVITY_RESULT_KEY = "controller_activity";
 
+    /** @hide */
+    public static final String EXCEPTION_RESULT_KEY = "exception";
+
     /**
      * The process name of the Phone app as well as many other apps that use this process name, such
      * as settings and vendor components.
@@ -10855,26 +10861,149 @@
         return null;
     }
 
-
     /**
-     * Requests the modem activity info. The recipient will place the result
-     * in `result`.
-     * @param result The object on which the recipient will send the resulting
-     * {@link android.telephony.ModemActivityInfo} object with key of
-     * {@link #MODEM_ACTIVITY_RESULT_KEY}.
+     * Exception that may be supplied to the callback provided in {@link #requestModemActivityInfo}.
      * @hide
      */
-    public void requestModemActivityInfo(@NonNull ResultReceiver result) {
+    @SystemApi
+    public static class ModemActivityInfoException extends Exception {
+        /** Indicates that an unknown error occurred */
+        public static final int ERROR_UNKNOWN = 0;
+
+        /**
+         * Indicates that the modem or phone processes are not available (such as when the device
+         * is in airplane mode).
+         */
+        public static final int ERROR_PHONE_NOT_AVAILABLE = 1;
+
+        /**
+         * Indicates that the modem supplied an invalid instance of {@link ModemActivityInfo}
+         */
+        public static final int ERROR_INVALID_INFO_RECEIVED = 2;
+
+        /**
+         * Indicates that the modem encountered an internal failure when processing the request
+         * for activity info.
+         */
+        public static final int ERROR_MODEM_RESPONSE_ERROR = 3;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"ERROR_"},
+                value = {
+                        ERROR_UNKNOWN,
+                        ERROR_PHONE_NOT_AVAILABLE,
+                        ERROR_INVALID_INFO_RECEIVED,
+                        ERROR_MODEM_RESPONSE_ERROR,
+                })
+        public @interface ModemActivityInfoError {}
+
+        private final int mErrorCode;
+
+        /** @hide */
+        public ModemActivityInfoException(@ModemActivityInfoError int errorCode) {
+            mErrorCode = errorCode;
+        }
+
+        public @ModemActivityInfoError int getErrorCode() {
+            return mErrorCode;
+        }
+
+        @Override
+        public String toString() {
+            switch (mErrorCode) {
+                case ERROR_UNKNOWN: return "ERROR_UNKNOWN";
+                case ERROR_PHONE_NOT_AVAILABLE: return "ERROR_PHONE_NOT_AVAILABLE";
+                case ERROR_INVALID_INFO_RECEIVED: return "ERROR_INVALID_INFO_RECEIVED";
+                case ERROR_MODEM_RESPONSE_ERROR: return "ERROR_MODEM_RESPONSE_ERROR";
+                default: return "UNDEFINED";
+            }
+        }
+    }
+
+    /**
+     * Requests the current modem activity info.
+     *
+     * The provided instance of {@link ModemActivityInfo} represents the cumulative activity since
+     * the last restart of the phone process.
+     *
+     * @param callback A callback object to which the result will be delivered. If there was an
+     *                 error processing the request, {@link OutcomeReceiver#onError} will be called
+     *                 with more details about the error.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void requestModemActivityInfo(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<ModemActivityInfo, ModemActivityInfoException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        // Pass no handler into the receiver, since we're going to be trampolining the call to the
+        // listener onto the provided executor.
+        ResultReceiver wrapperResultReceiver = new ResultReceiver(null) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle data) {
+                if (data == null) {
+                    Log.w(TAG, "requestModemActivityInfo: received null bundle");
+                    sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+                    return;
+                }
+                data.setDefusable(true);
+                if (data.containsKey(EXCEPTION_RESULT_KEY)) {
+                    int receivedErrorCode = data.getInt(EXCEPTION_RESULT_KEY);
+                    sendErrorToListener(receivedErrorCode);
+                    return;
+                }
+
+                if (!data.containsKey(MODEM_ACTIVITY_RESULT_KEY)) {
+                    Log.w(TAG, "requestModemActivityInfo: Bundle did not contain expected key");
+                    sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+                    return;
+                }
+                Parcelable receivedResult = data.getParcelable(MODEM_ACTIVITY_RESULT_KEY);
+                if (!(receivedResult instanceof ModemActivityInfo)) {
+                    Log.w(TAG, "requestModemActivityInfo: Bundle contained something that wasn't "
+                            + "a ModemActivityInfo.");
+                    sendErrorToListener(ModemActivityInfoException.ERROR_UNKNOWN);
+                    return;
+                }
+                ModemActivityInfo modemActivityInfo = (ModemActivityInfo) receivedResult;
+                if (!modemActivityInfo.isValid()) {
+                    Log.w(TAG, "requestModemActivityInfo: Received an invalid ModemActivityInfo");
+                    sendErrorToListener(ModemActivityInfoException.ERROR_INVALID_INFO_RECEIVED);
+                    return;
+                }
+                Log.d(TAG, "requestModemActivityInfo: Sending result to app: " + modemActivityInfo);
+                sendResultToListener(modemActivityInfo);
+            }
+
+            private void sendResultToListener(ModemActivityInfo info) {
+                Binder.withCleanCallingIdentity(() ->
+                        executor.execute(() ->
+                                callback.onResult(info)));
+            }
+
+            private void sendErrorToListener(int code) {
+                ModemActivityInfoException e = new ModemActivityInfoException(code);
+                Binder.withCleanCallingIdentity(() ->
+                        executor.execute(() ->
+                                callback.onError(e)));
+            }
+        };
+
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                service.requestModemActivityInfo(result);
+                service.requestModemActivityInfo(wrapperResultReceiver);
                 return;
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#getModemActivityInfo", e);
         }
-        result.send(0, null);
+        executor.execute(() -> callback.onError(
+                new ModemActivityInfoException(
+                        ModemActivityInfoException.ERROR_PHONE_NOT_AVAILABLE)));
     }
 
     /**
@@ -15093,4 +15222,96 @@
             return PhoneCapability.DEFAULT_SSSS_CAPABILITY;
         }
     }
+
+    /**
+     * Exception that may be supplied to the callback in {@link #getNetworkSlicingConfiguration} if
+     * something goes awry.
+     */
+    public static class SlicingException extends Exception {
+        /**
+         * Getting the current slicing configuration successfully. Used internally only.
+         * @hide
+         */
+        public static final int SUCCESS = 0;
+
+        /**
+         * The system timed out waiting for a response from the Radio.
+         */
+        public static final int ERROR_TIMEOUT = 1;
+
+        /**
+         * The modem returned a failure.
+         */
+        public static final int ERROR_MODEM_ERROR = 2;
+
+        /** @hide */
+        @IntDef(prefix = {"ERROR_"}, value = {
+                ERROR_TIMEOUT,
+                ERROR_MODEM_ERROR,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface SlicingError {}
+
+        private final int mErrorCode;
+
+        public SlicingException(@SlicingError int errorCode) {
+            mErrorCode = errorCode;
+        }
+
+        /**
+         * Fetches the error code associated with this exception.
+         * @return An error code.
+         */
+        public @SlicingError int getErrorCode() {
+            return mErrorCode;
+        }
+    }
+
+    /** @hide */
+    public static final String KEY_SLICING_CONFIG_HANDLE = "slicing_config_handle";
+
+    /**
+     * Request to get the current slicing configuration including URSP rules and
+     * NSSAIs (configured, allowed and rejected).
+     *
+     * This method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+     *     is a privileged permission that can only be granted to apps preloaded on the device.
+     *     <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     * </ul>
+     *
+     * @param executor the executor on which callback will be invoked.
+     * @param callback a callback to receive the current slicing configuration.
+     */
+    @SuppressAutoDoc // No support for carrier privileges (b/72967236).
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public void getNetworkSlicingConfiguration(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<SlicingConfig, SlicingException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                throw new IllegalStateException("telephony service is null.");
+            }
+            telephony.getSlicingConfig(new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle result) {
+                        if (resultCode != SlicingException.SUCCESS) {
+                            executor.execute(() -> callback.onError(
+                                    new SlicingException(resultCode)));
+                            return;
+                        }
+                        SlicingConfig slicingConfig =
+                                result.getParcelable(KEY_SLICING_CONFIG_HANDLE);
+                        executor.execute(() -> callback.onResult(slicingConfig));
+                    }
+            });
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/data/NetworkSliceInfo.java b/telephony/java/android/telephony/data/NetworkSliceInfo.java
index 1d90095..232a930 100644
--- a/telephony/java/android/telephony/data/NetworkSliceInfo.java
+++ b/telephony/java/android/telephony/data/NetworkSliceInfo.java
@@ -19,8 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -35,10 +33,7 @@
  * SliceServiceType defines the type of service provided by the slice, and SliceDifferentiator is
  * used to differentiate between multiple slices of the same type. If the devices is not on HPLMN,
  * the mappedHplmn versions of these 2 fields indicate the corresponding values in HPLMN.
- *
- * @hide
  */
-@SystemApi
 public final class NetworkSliceInfo implements Parcelable {
     /**
      * When set on a Slice Differentiator, this value indicates that there is no corresponding
@@ -68,14 +63,14 @@
 
     /**
      * The min acceptable value for a Slice Differentiator
+     * @hide
      */
-    @SuppressLint("MinMaxConstant")
     public static final int MIN_SLICE_DIFFERENTIATOR = -1;
 
     /**
      * The max acceptable value for a Slice Differentiator
+     * @hide
      */
-    @SuppressLint("MinMaxConstant")
     public static final int MAX_SLICE_DIFFERENTIATOR = 0xFFFFFE;
 
     /** @hide */
@@ -88,6 +83,62 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface SliceServiceType {}
 
+    /**
+     * The slice status is unknown. This can happen during IWLAN->cellular handover when the
+     * NetworkSliceInfo is received over IWLAN.
+     */
+    public static final int SLICE_STATUS_UNKNOWN = 0;
+
+    /**
+     * The slice is configured but not allowed or rejected yet.
+     */
+    public static final int SLICE_STATUS_CONFIGURED = 1;
+
+    /**
+     * The slice is allowed to be used.
+     */
+    public static final int SLICE_STATUS_ALLOWED = 2;
+
+    /**
+     * The slice is rejected because not available in PLMN.
+     */
+    public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN = 3;
+
+    /**
+     * The slice is rejected because not available in registered area.
+     */
+    public static final int SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA = 4;
+
+    /**
+     * The slice is configured by home operator(HPLMN) in default and is used if configured/allowed
+     * slices are not available for the serving PLMN.
+     */
+    public static final int SLICE_STATUS_DEFAULT_CONFIGURED = 5;
+
+    /**
+     * The min acceptable value for a slice status.
+     * @hide
+     */
+    public static final int MIN_SLICE_STATUS = SLICE_STATUS_UNKNOWN;
+
+    /**
+     * The max acceptable value for a slice status.
+     * @hide
+     */
+    public static final int MAX_SLICE_STATUS = SLICE_STATUS_DEFAULT_CONFIGURED;
+
+    /** @hide */
+    @IntDef(prefix = { "SLICE_STATUS_" }, value = {
+            SLICE_STATUS_UNKNOWN,
+            SLICE_STATUS_CONFIGURED,
+            SLICE_STATUS_ALLOWED,
+            SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN,
+            SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA,
+            SLICE_STATUS_DEFAULT_CONFIGURED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SliceStatus {}
+
 
     @SliceServiceType
     private final int mSliceServiceType;
@@ -97,14 +148,18 @@
     private final int mMappedHplmnSliceServiceType;
     @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
     private final int mMappedHplmnSliceDifferentiator;
+    @SliceStatus
+    @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS)
+    private final int mStatus;
 
     private NetworkSliceInfo(@SliceServiceType int sliceServiceType,
             int sliceDifferentiator, int mappedHplmnSliceServiceType,
-            int mappedHplmnSliceDifferentiator) {
+            int mappedHplmnSliceDifferentiator, int status) {
         mSliceServiceType = sliceServiceType;
         mSliceDifferentiator = sliceDifferentiator;
         mMappedHplmnSliceDifferentiator = mappedHplmnSliceDifferentiator;
         mMappedHplmnSliceServiceType = mappedHplmnSliceServiceType;
+        mStatus = status;
     }
 
     /**
@@ -157,11 +212,21 @@
         return mMappedHplmnSliceDifferentiator;
     }
 
+    /**
+     * Field to indicate the current status of the slice.
+     * @return the current status for this slice info.
+     */
+    @SliceStatus
+    public int getStatus() {
+        return mStatus;
+    }
+
     private NetworkSliceInfo(@NonNull Parcel in) {
         mSliceServiceType = in.readInt();
         mSliceDifferentiator = in.readInt();
         mMappedHplmnSliceServiceType = in.readInt();
         mMappedHplmnSliceDifferentiator = in.readInt();
+        mStatus = in.readInt();
     }
 
     @Override
@@ -175,6 +240,7 @@
         dest.writeInt(mSliceDifferentiator);
         dest.writeInt(mMappedHplmnSliceServiceType);
         dest.writeInt(mMappedHplmnSliceDifferentiator);
+        dest.writeInt(mStatus);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<NetworkSliceInfo> CREATOR =
@@ -200,6 +266,7 @@
                 + ", mMappedHplmnSliceServiceType="
                 + sliceServiceTypeToString(mMappedHplmnSliceServiceType)
                 + ", mMappedHplmnSliceDifferentiator=" + mMappedHplmnSliceDifferentiator
+                + ", mStatus=" + sliceStatusToString(mStatus)
                 + '}';
     }
 
@@ -218,6 +285,25 @@
         }
     }
 
+    private static String sliceStatusToString(@SliceStatus int sliceStatus) {
+        switch(sliceStatus) {
+            case SLICE_STATUS_UNKNOWN:
+                return "UNKNOWN";
+            case SLICE_STATUS_CONFIGURED:
+                return "CONFIGURED";
+            case SLICE_STATUS_ALLOWED:
+                return "ALLOWED";
+            case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_PLMN:
+                return "REJECTED_NOT_AVAILABLE_IN_PLMN";
+            case SLICE_STATUS_REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA:
+                return "REJECTED_NOT_AVAILABLE_IN_REGISTERED_AREA";
+            case SLICE_STATUS_DEFAULT_CONFIGURED:
+                return "DEFAULT_CONFIGURED";
+            default:
+                return Integer.toString(sliceStatus);
+        }
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -226,13 +312,14 @@
         return mSliceServiceType == sliceInfo.mSliceServiceType
                 && mSliceDifferentiator == sliceInfo.mSliceDifferentiator
                 && mMappedHplmnSliceServiceType == sliceInfo.mMappedHplmnSliceServiceType
-                && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator;
+                && mMappedHplmnSliceDifferentiator == sliceInfo.mMappedHplmnSliceDifferentiator
+                && mStatus == sliceInfo.mStatus;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mSliceServiceType, mSliceDifferentiator, mMappedHplmnSliceServiceType,
-                mMappedHplmnSliceDifferentiator);
+                mMappedHplmnSliceDifferentiator, mStatus);
     }
 
     /**
@@ -257,6 +344,9 @@
         private int mMappedHplmnSliceServiceType = SLICE_SERVICE_TYPE_NONE;
         @IntRange(from = MIN_SLICE_DIFFERENTIATOR, to = MAX_SLICE_DIFFERENTIATOR)
         private int mMappedHplmnSliceDifferentiator = SLICE_DIFFERENTIATOR_NO_SLICE;
+        @SliceStatus
+        @IntRange(from = MIN_SLICE_STATUS, to = MAX_SLICE_STATUS)
+        private int mStatus = SLICE_STATUS_UNKNOWN;
 
         /**
          * Default constructor for Builder.
@@ -281,8 +371,7 @@
          * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
          * corresponding Slice.
          *
-         * @throws IllegalArgumentException if the parameter is not between
-         * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+         * @throws IllegalArgumentException if the parameter is not in the expected range.
          *
          * @return The same instance of the builder.
          */
@@ -316,8 +405,7 @@
          * A value of {@link #SLICE_DIFFERENTIATOR_NO_SLICE} indicates that there is no
          * corresponding Slice of the HPLMN.
          *
-         * @throws IllegalArgumentException if the parameter is not between
-         * {@link #MIN_SLICE_DIFFERENTIATOR} and {@link #MAX_SLICE_DIFFERENTIATOR}.
+         * @throws IllegalArgumentException if the parameter is not in the expected range.
          *
          * @return The same instance of the builder.
          */
@@ -334,6 +422,22 @@
         }
 
         /**
+         * Set the slice status.
+         *
+         * @throws IllegalArgumentException if the status is invalid.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setStatus(@SliceStatus int status) {
+            if (status < MIN_SLICE_STATUS || status > MAX_SLICE_STATUS) {
+                throw new IllegalArgumentException("The slice status is not valid");
+            }
+            this.mStatus = status;
+            return this;
+        }
+
+        /**
          * Build the {@link NetworkSliceInfo}.
          *
          * @return the {@link NetworkSliceInfo} object.
@@ -341,7 +445,8 @@
         @NonNull
         public NetworkSliceInfo build() {
             return new NetworkSliceInfo(this.mSliceServiceType, this.mSliceDifferentiator,
-                    this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator);
+                    this.mMappedHplmnSliceServiceType, this.mMappedHplmnSliceDifferentiator,
+                    this.mStatus);
         }
     }
 }
diff --git a/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl b/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl
new file mode 100644
index 0000000..563a00e
--- /dev/null
+++ b/telephony/java/android/telephony/data/RouteSelectionDescriptor.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.data;
+
+parcelable RouteSelectionDescriptor;
diff --git a/telephony/java/android/telephony/data/RouteSelectionDescriptor.java b/telephony/java/android/telephony/data/RouteSelectionDescriptor.java
new file mode 100644
index 0000000..c2457f2
--- /dev/null
+++ b/telephony/java/android/telephony/data/RouteSelectionDescriptor.java
@@ -0,0 +1,263 @@
+/**
+ * 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.telephony.data;
+
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a single route selection descriptor as defined in
+ * 3GPP TS 24.526.
+ */
+public final class RouteSelectionDescriptor implements Parcelable {
+    /**
+     * The min acceptable value for the precedence of a route selection descriptor.
+     * @hide
+     */
+    public static final int MIN_ROUTE_PRECEDENCE = 0;
+
+    /**
+     * The max acceptable value for the precedence of a route selection descriptor.
+     * @hide
+     */
+    public static final int MAX_ROUTE_PRECEDENCE = 255;
+
+    /**
+     * The route selection descriptor is for the session with IPV4 type.
+     */
+    public static final int SESSION_TYPE_IPV4 = 0;
+
+    /**
+     * The route selection descriptor is for the session with IPV6 type.
+     */
+    public static final int SESSION_TYPE_IPV6 = 1;
+
+    /**
+     * The route selection descriptor is for the session with both IP and IPV6 types.
+     */
+    public static final int SESSION_TYPE_IPV4V6 = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "SESSION_TYPE_" }, value = {
+            SESSION_TYPE_IPV4,
+            SESSION_TYPE_IPV6,
+            SESSION_TYPE_IPV4V6,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RouteSessionType {}
+
+    /**
+     * The route selection descriptor is using SSC mode 1. The session will provide continual
+     * support when UE's location is updated.
+     */
+    public static final int ROUTE_SSC_MODE_1 = 1;
+
+    /**
+     * The route selection descriptor is using SSC mode 2. The new session for the same network
+     * will be established after releasing the old session when UE's location is updated.
+     */
+    public static final int ROUTE_SSC_MODE_2 = 2;
+
+    /**
+     * The route selection descriptor is using SSC mode 3. The new session for the same network is
+     * allowed to be established before releasing the old session when UE's location is updated.
+     */
+    public static final int ROUTE_SSC_MODE_3 = 3;
+
+    /**
+     * The min acceptable value for the SSC mode of a route selection descriptor.
+     * @hide
+     */
+    public static final int MIN_ROUTE_SSC_MODE = ROUTE_SSC_MODE_1;
+
+    /**
+     * The max acceptable value for the SSC mode of a route selection descriptor.
+     * @hide
+     */
+    public static final int MAX_ROUTE_SSC_MODE = ROUTE_SSC_MODE_3;
+
+    /** @hide */
+    @IntDef(prefix = { "ROUTE_SSC_MODE_" }, value = {
+            ROUTE_SSC_MODE_1,
+            ROUTE_SSC_MODE_2,
+            ROUTE_SSC_MODE_3,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RouteSscMode {}
+
+    @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE)
+    private final int mPrecedence;
+    @RouteSessionType
+    private final int mSessionType;
+    @RouteSscMode
+    @IntRange(from = MIN_ROUTE_SSC_MODE, to = MAX_ROUTE_SSC_MODE)
+    private final int mSscMode;
+    private final List<NetworkSliceInfo> mSliceInfo;
+    private final List<String> mDnn;
+
+    /** @hide */
+    RouteSelectionDescriptor(android.hardware.radio.V1_6.RouteSelectionDescriptor rsd) {
+        this(rsd.precedence, rsd.sessionType.value(), rsd.sscMode.value(), rsd.sliceInfo,
+                rsd.dnn);
+    }
+
+    /** @hide */
+    public RouteSelectionDescriptor(int precedence, int sessionType, int sscMode,
+            List<android.hardware.radio.V1_6.SliceInfo> sliceInfo, List<String> dnn) {
+        mPrecedence = precedence;
+        mSessionType = sessionType;
+        mSscMode = sscMode;
+        mSliceInfo = new ArrayList<NetworkSliceInfo>();
+        for (android.hardware.radio.V1_6.SliceInfo si : sliceInfo) {
+            mSliceInfo.add(sliceInfoBuilder(si));
+        }
+        mDnn = new ArrayList<String>();
+        mDnn.addAll(dnn);
+    }
+
+    private NetworkSliceInfo sliceInfoBuilder(android.hardware.radio.V1_6.SliceInfo si) {
+        NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder()
+                .setSliceServiceType(si.sst)
+                .setMappedHplmnSliceServiceType(si.mappedHplmnSst);
+        if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) {
+            builder
+                .setSliceDifferentiator(si.sliceDifferentiator)
+                .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD);
+        }
+        return builder.build();
+    }
+
+    private RouteSelectionDescriptor(Parcel p) {
+        mPrecedence = p.readInt();
+        mSessionType = p.readInt();
+        mSscMode = p.readInt();
+        mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR);
+        mDnn = new ArrayList<String>();
+        p.readStringList(mDnn);
+    }
+
+    /**
+     * Precedence value in the range of 0 to 255. Higher value has lower precedence.
+     * @return the precedence value for this route selection descriptor.
+     */
+    @IntRange(from = MIN_ROUTE_PRECEDENCE, to = MAX_ROUTE_PRECEDENCE)
+    public int getPrecedence() {
+        return mPrecedence;
+    }
+
+    /**
+     * This is used for checking which session type defined in 3GPP TS 23.501 is allowed for the
+     * route in a route selection descriptor.
+     * @return the session type for this route selection descriptor.
+     */
+    @RouteSessionType
+    public int getSessionType() {
+        return mSessionType;
+    }
+
+    /**
+     * SSC mode stands for Session and Service Continuity mode (which specifies the IP continuity
+     * mode) as defined in 3GPP TS 23.501.
+     * @return the SSC mode for this route selection descriptor.
+     */
+    @RouteSscMode
+    public int getSscMode() {
+        return mSscMode;
+    }
+
+    /**
+     * This is the list of all the slices available in the route selection descriptor as indicated
+     * by the network. These are the slices that can be used by the device if this route selection
+     * descriptor is used based the traffic (see 3GPP TS 23.501 for details).
+     * @return the list of all the slices available in the route selection descriptor.
+     */
+    public @NonNull List<NetworkSliceInfo> getSliceInfo() {
+        return mSliceInfo;
+    }
+
+    /**
+     * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003. There
+     * can be 0 or more DNNs specified in a route selection descriptor.
+     * @return the list of DNN for this route selection descriptor.
+     */
+    public @NonNull List<String> getDataNetworkName() {
+        return mDnn;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPrecedence);
+        dest.writeInt(mSessionType);
+        dest.writeInt(mSscMode);
+        dest.writeTypedList(mSliceInfo, flags);
+        dest.writeStringList(mDnn);
+    }
+
+    public static final @NonNull Parcelable.Creator<RouteSelectionDescriptor> CREATOR =
+            new Parcelable.Creator<RouteSelectionDescriptor>() {
+                @Override
+                public RouteSelectionDescriptor createFromParcel(Parcel source) {
+                    return new RouteSelectionDescriptor(source);
+                }
+
+                @Override
+                public RouteSelectionDescriptor[] newArray(int size) {
+                    return new RouteSelectionDescriptor[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        RouteSelectionDescriptor that = (RouteSelectionDescriptor) o;
+        return mPrecedence == that.mPrecedence
+                && mSessionType == that.mSessionType
+                && mSscMode == that.mSscMode
+                && mSliceInfo.size() == that.mSliceInfo.size()
+                && mSliceInfo.containsAll(that.mSliceInfo)
+                && mDnn.size() == that.mDnn.size()
+                && mDnn.containsAll(that.mDnn);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPrecedence, mSessionType, mSscMode, mSliceInfo, mDnn);
+    }
+
+    @Override
+    public String toString() {
+        return "{.precedence = " + mPrecedence + ", .sessionType = " + mSessionType
+                + ", .sscMode = " + mSscMode + ", .sliceInfo = " + mSliceInfo
+                + ", .dnn = " + mDnn + "}";
+    }
+}
diff --git a/telephony/java/android/telephony/data/SlicingConfig.aidl b/telephony/java/android/telephony/data/SlicingConfig.aidl
new file mode 100644
index 0000000..ad93d8c
--- /dev/null
+++ b/telephony/java/android/telephony/data/SlicingConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.data;
+
+parcelable SlicingConfig;
diff --git a/telephony/java/android/telephony/data/SlicingConfig.java b/telephony/java/android/telephony/data/SlicingConfig.java
new file mode 100644
index 0000000..990e4d2
--- /dev/null
+++ b/telephony/java/android/telephony/data/SlicingConfig.java
@@ -0,0 +1,137 @@
+/**
+ * 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.telephony.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a slicing configuration
+ */
+public final class SlicingConfig implements Parcelable {
+    private final List<UrspRule> mUrspRules;
+    private final List<NetworkSliceInfo> mSliceInfo;
+
+    public SlicingConfig() {
+        mUrspRules = new ArrayList<UrspRule>();
+        mSliceInfo = new ArrayList<NetworkSliceInfo>();
+    }
+
+    /** @hide */
+    public SlicingConfig(android.hardware.radio.V1_6.SlicingConfig sc) {
+        this(sc.urspRules, sc.sliceInfo);
+    }
+
+    /** @hide */
+    public SlicingConfig(List<android.hardware.radio.V1_6.UrspRule> urspRules,
+            List<android.hardware.radio.V1_6.SliceInfo> sliceInfo) {
+        mUrspRules = new ArrayList<UrspRule>();
+        for (android.hardware.radio.V1_6.UrspRule ur : urspRules) {
+            mUrspRules.add(new UrspRule(ur.precedence, ur.trafficDescriptors,
+                    ur.routeSelectionDescriptor));
+        }
+        mSliceInfo = new ArrayList<NetworkSliceInfo>();
+        for (android.hardware.radio.V1_6.SliceInfo si : sliceInfo) {
+            mSliceInfo.add(sliceInfoBuilder(si));
+        }
+    }
+
+    private NetworkSliceInfo sliceInfoBuilder(android.hardware.radio.V1_6.SliceInfo si) {
+        NetworkSliceInfo.Builder builder = new NetworkSliceInfo.Builder()
+                .setSliceServiceType(si.sst)
+                .setMappedHplmnSliceServiceType(si.mappedHplmnSst);
+        if (si.sliceDifferentiator != NetworkSliceInfo.SLICE_DIFFERENTIATOR_NO_SLICE) {
+            builder
+                .setSliceDifferentiator(si.sliceDifferentiator)
+                .setMappedHplmnSliceDifferentiator(si.mappedHplmnSD);
+        }
+        return builder.build();
+    }
+
+    /** @hide */
+    public SlicingConfig(Parcel p) {
+        mUrspRules = p.createTypedArrayList(UrspRule.CREATOR);
+        mSliceInfo = p.createTypedArrayList(NetworkSliceInfo.CREATOR);
+    }
+
+    /**
+     * This list contains the current URSP rules. Empty list represents that no rules are
+     * configured.
+     * @return the current URSP rules for this slicing configuration.
+     */
+    public @NonNull List<UrspRule> getUrspRules() {
+        return mUrspRules;
+    }
+
+    /**
+     * @return the list of all slices for this slicing configuration.
+     */
+    public @NonNull List<NetworkSliceInfo> getSliceInfo() {
+        return mSliceInfo;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedList(mUrspRules, flags);
+        dest.writeTypedList(mSliceInfo, flags);
+    }
+
+    public static final @NonNull Parcelable.Creator<SlicingConfig> CREATOR =
+            new Parcelable.Creator<SlicingConfig>() {
+                @Override
+                public SlicingConfig createFromParcel(Parcel source) {
+                    return new SlicingConfig(source);
+                }
+
+                @Override
+                public SlicingConfig[] newArray(int size) {
+                    return new SlicingConfig[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SlicingConfig that = (SlicingConfig) o;
+        return mUrspRules.size() == that.mUrspRules.size()
+                && mUrspRules.containsAll(that.mUrspRules)
+                && mSliceInfo.size() == that.mSliceInfo.size()
+                && mSliceInfo.containsAll(that.mSliceInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUrspRules, mSliceInfo);
+    }
+
+    @Override
+    public String toString() {
+        return "{.urspRules = " + mUrspRules + ", .sliceInfo = " + mSliceInfo + "}";
+    }
+}
diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java
index 480379d..d813bc5 100644
--- a/telephony/java/android/telephony/data/TrafficDescriptor.java
+++ b/telephony/java/android/telephony/data/TrafficDescriptor.java
@@ -18,20 +18,17 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.util.Objects;
 
 /**
- * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for URSP traffic
- * matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an optional DNN, which,
- * if present, must be used for traffic matching; it does not specify the end point to be used for
- * the data call.
- * @hide
+ * A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for UE Route Selection
+ * Policy(URSP) traffic matching as described in 3GPP TS 24.526 Section 4.2.2. It includes an
+ * optional Data Network Name(DNN), which, if present, must be used for traffic matching; it does
+ * not specify the end point to be used for the data call.
  */
-@SystemApi
 public final class TrafficDescriptor implements Parcelable {
     private final String mDnn;
     private final String mOsAppId;
@@ -45,8 +42,10 @@
      * Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2
      * @param dnn optional DNN, which must be used for traffic matching, if present
      * @param osAppId OsId + osAppId of the traffic descriptor
+     *
+     * @hide
      */
-    public TrafficDescriptor(@Nullable String dnn, @Nullable String osAppId) {
+    public TrafficDescriptor(String dnn, String osAppId) {
         mDnn = dnn;
         mOsAppId = osAppId;
     }
@@ -55,12 +54,13 @@
      * DNN stands for Data Network Name and represents an APN as defined in 3GPP TS 23.003.
      * @return the DNN of this traffic descriptor.
      */
-    public @Nullable String getDnn() {
+    public @Nullable String getDataNetworkName() {
         return mDnn;
     }
 
     /**
-     * OsAppId represents the OsId + OsAppId as defined in 3GPP TS 24.526 Section 5.2.
+     * OsAppId is the app id as defined in 3GPP TS 24.526 Section 5.2, and it identifies a traffic
+     * category.
      * @return the OS App ID of this traffic descriptor.
      */
     public @Nullable String getOsAppId() {
@@ -108,4 +108,65 @@
     public int hashCode() {
         return Objects.hash(mDnn, mOsAppId);
     }
+
+    /**
+     * Provides a convenient way to set the fields of a {@link TrafficDescriptor} when creating a
+     * new instance.
+     *
+     * <p>The example below shows how you might create a new {@code TrafficDescriptor}:
+     *
+     * <pre><code>
+     *
+     * TrafficDescriptor response = new TrafficDescriptor.Builder()
+     *     .setDnn("")
+     *     .build();
+     * </code></pre>
+     */
+    public static final class Builder {
+        private String mDnn = null;
+        private String mOsAppId = null;
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Set the Data Network Name(DNN).
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setDataNetworkName(@NonNull String dnn) {
+            this.mDnn = dnn;
+            return this;
+        }
+
+        /**
+         * Set the OS App ID.
+         *
+         * @return The same instance of the builder.
+         */
+        @NonNull
+        public Builder setOsAppId(@NonNull String osAppId) {
+            this.mOsAppId = osAppId;
+            return this;
+        }
+
+        /**
+         * Build the {@link TrafficDescriptor}.
+         *
+         * @throws IllegalArgumentException if DNN and OS App ID are null.
+         *
+         * @return the {@link TrafficDescriptor} object.
+         */
+        @NonNull
+        public TrafficDescriptor build() {
+            if (this.mDnn == null && this.mOsAppId == null) {
+                throw new IllegalArgumentException("DNN and OS App ID are null");
+            }
+            return new TrafficDescriptor(this.mDnn, this.mOsAppId);
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/data/UrspRule.aidl b/telephony/java/android/telephony/data/UrspRule.aidl
new file mode 100644
index 0000000..2bed583
--- /dev/null
+++ b/telephony/java/android/telephony/data/UrspRule.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.data;
+
+parcelable UrspRule;
diff --git a/telephony/java/android/telephony/data/UrspRule.java b/telephony/java/android/telephony/data/UrspRule.java
new file mode 100644
index 0000000..e2c47fd
--- /dev/null
+++ b/telephony/java/android/telephony/data/UrspRule.java
@@ -0,0 +1,178 @@
+/**
+ * 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.telephony.data;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.radio.V1_6.OptionalDnn;
+import android.hardware.radio.V1_6.OptionalOsAppId;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a single URSP rule as defined in 3GPP TS 24.526. URSP stands for UE Route Selection
+ * Policy. In 5G, network can provide URSP information to devices which provides information on
+ * what connection parameters should be used for what traffic.
+ */
+public final class UrspRule implements Parcelable {
+    /**
+     * The min acceptable value for the precedence of a URSP rule.
+     * @hide
+     */
+    public static final int MIN_URSP_PRECEDENCE = 0;
+
+    /**
+     * The max acceptable value for the precedence of a URSP rule.
+     * @hide
+     */
+    public static final int MAX_URSP_PRECEDENCE = 255;
+
+    @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE)
+    private final int mPrecedence;
+    private final List<TrafficDescriptor> mTrafficDescriptors;
+    private final List<RouteSelectionDescriptor> mRouteSelectionDescriptor;
+
+    UrspRule(android.hardware.radio.V1_6.UrspRule ur) {
+        this(ur.precedence, ur.trafficDescriptors, ur.routeSelectionDescriptor);
+    }
+
+    /** @hide */
+    public UrspRule(int precedence,
+            List<android.hardware.radio.V1_6.TrafficDescriptor> trafficDescriptors,
+            List<android.hardware.radio.V1_6.RouteSelectionDescriptor> routeSelectionDescriptor) {
+        mPrecedence = precedence;
+        mTrafficDescriptors = new ArrayList<TrafficDescriptor>();
+        for (android.hardware.radio.V1_6.TrafficDescriptor td : trafficDescriptors) {
+            mTrafficDescriptors.add(convertToTrafficDescriptor(td));
+        }
+        mRouteSelectionDescriptor = new ArrayList<RouteSelectionDescriptor>();
+        for (android.hardware.radio.V1_6.RouteSelectionDescriptor rsd : routeSelectionDescriptor) {
+            mRouteSelectionDescriptor.add(new RouteSelectionDescriptor(rsd));
+        }
+    }
+
+    /** Convert an ArrayList of Bytes to an exactly-sized primitive array */
+    private byte[] arrayListToPrimitiveArray(ArrayList<Byte> bytes) {
+        byte[] ret = new byte[bytes.size()];
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = bytes.get(i);
+        }
+        return ret;
+    }
+
+    private TrafficDescriptor convertToTrafficDescriptor(
+            android.hardware.radio.V1_6.TrafficDescriptor td) {
+        String dnn = td.dnn.getDiscriminator() == OptionalDnn.hidl_discriminator.noinit
+                ? null : td.dnn.value();
+        String osAppId = td.osAppId.getDiscriminator() == OptionalOsAppId.hidl_discriminator.noinit
+                ? null : new String(arrayListToPrimitiveArray(td.osAppId.value().osAppId));
+        TrafficDescriptor.Builder builder = new TrafficDescriptor.Builder();
+        if (dnn != null) {
+            builder.setDataNetworkName(dnn);
+        }
+        if (osAppId != null) {
+            builder.setOsAppId(osAppId);
+        }
+        return builder.build();
+    }
+
+    private UrspRule(Parcel p) {
+        mPrecedence = p.readInt();
+        mTrafficDescriptors = p.createTypedArrayList(TrafficDescriptor.CREATOR);
+        mRouteSelectionDescriptor = p.createTypedArrayList(RouteSelectionDescriptor.CREATOR);
+    }
+
+    /**
+     * Precedence value in the range of 0 to 255. Higher value has lower precedence.
+     * @return the precedence value for this URSP rule.
+     */
+    @IntRange(from = MIN_URSP_PRECEDENCE, to = MAX_URSP_PRECEDENCE)
+    public int getPrecedence() {
+        return mPrecedence;
+    }
+
+    /**
+     * These traffic descriptors are used as a matcher for network requests.
+     * @return the traffic descriptors which are associated to this URSP rule.
+     */
+    public @NonNull List<TrafficDescriptor> getTrafficDescriptors() {
+        return mTrafficDescriptors;
+    }
+
+    /**
+     * List of routes (connection parameters) that must be used by the device for requests matching
+     * a traffic descriptor.
+     * @return the route selection descriptors which are associated to this URSP rule.
+     */
+    public @NonNull List<RouteSelectionDescriptor> getRouteSelectionDescriptor() {
+        return mRouteSelectionDescriptor;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPrecedence);
+        dest.writeTypedList(mTrafficDescriptors, flags);
+        dest.writeTypedList(mRouteSelectionDescriptor, flags);
+    }
+
+    public static final @NonNull Parcelable.Creator<UrspRule> CREATOR =
+            new Parcelable.Creator<UrspRule>() {
+                @Override
+                public UrspRule createFromParcel(Parcel source) {
+                    return new UrspRule(source);
+                }
+
+                @Override
+                public UrspRule[] newArray(int size) {
+                    return new UrspRule[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        UrspRule that = (UrspRule) o;
+        return mPrecedence == that.mPrecedence
+                && mTrafficDescriptors.size() == that.mTrafficDescriptors.size()
+                && mTrafficDescriptors.containsAll(that.mTrafficDescriptors)
+                && mRouteSelectionDescriptor.size() == that.mRouteSelectionDescriptor.size()
+                && mRouteSelectionDescriptor.containsAll(that.mRouteSelectionDescriptor);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPrecedence, mTrafficDescriptors, mRouteSelectionDescriptor);
+    }
+
+    @Override
+    public String toString() {
+        return "{.precedence = " + mPrecedence + ", .trafficDescriptors = " + mTrafficDescriptors
+                + ", .routeSelectionDescriptor = " + mRouteSelectionDescriptor + "}";
+    }
+}
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index 0aff997..dfe5e6c9 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -766,7 +766,10 @@
      * The method is only valid to call when the session state is in
      * {@link ImsCallSession.State#IDLE}.
      *
-     * @param callee dialed string to make the call to
+     * @param callee dial string to make the call to.  The platform passes the dialed number
+     *               entered by the user as-is.  The {@link ImsService} should ensure that the
+     *               number is formatted in SIP messages appropriately (e.g. using
+     *               {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}).
      * @param profile call profile to make the call with the specified service type,
      *      call type and media information
      * @see Listener#callSessionStarted, Listener#callSessionStartFailed
@@ -788,7 +791,10 @@
      * The method is only valid to call when the session state is in
      * {@link ImsCallSession.State#IDLE}.
      *
-     * @param participants participant list to initiate an IMS conference call
+     * @param participants participant list to initiate an IMS conference call.  The platform passes
+     *               the dialed numbers entered by the user as-is.  The {@link ImsService} should
+     *               ensure that the number is formatted in SIP messages appropriately (e.g. using
+     *               {@link android.telephony.PhoneNumberUtils#formatNumberToE164(String, String)}).
      * @param profile call profile to make the call with the specified service type,
      *      call type and media information
      * @see Listener#callSessionStarted, Listener#callSessionStartFailed
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ae5f997..d812b46 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2447,4 +2447,10 @@
      * Gets the current phone capability.
      */
     PhoneCapability getPhoneCapability();
+
+    /**
+     * Request to get the current slicing configuration including URSP rules and
+     * NSSAIs (configured, allowed and rejected).
+     */
+    void getSlicingConfig(in ResultReceiver callback);
 }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 822fc44..fe8e671 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -527,6 +527,7 @@
     int RIL_REQUEST_SET_DATA_THROTTLING = 221;
     int RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP = 222;
     int RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP = 223;
+    int RIL_REQUEST_GET_SLICING_CONFIG = 224;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 460a26d..0bb6198 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -32,7 +32,9 @@
         ":android-test-mock-sources",
         // Note: Below are NOT APIs of this library. We only take APIs under
         // the android.test.mock package. They however provide private APIs that
-        // android.test.mock APIs references to.
+        // android.test.mock APIs references to. We need to have the classes in
+        // source code form to have access to the @hide comment which disappears
+        // when the classes are compiled into a Jar library.
         ":framework-core-sources-for-test-mock",
         ":framework_native_aidl",
     ],
@@ -46,6 +48,14 @@
     api_packages: [
         "android.test.mock",
     ],
+    // Only include android.test.mock.* classes. Jarjar rules below removes
+    // classes in other packages like android.content. In order to keep the
+    // list up-to-date, permitted_packages ensures that the library contains
+    // clases under android.test.mock after the jarjar rules are applied.
+    jarjar_rules: "jarjar-rules.txt",
+    permitted_packages: [
+        "android.test.mock",
+    ],
     compile_dex: true,
     default_to_stubs: true,
     dist_group: "android",
diff --git a/test-mock/jarjar-rules.txt b/test-mock/jarjar-rules.txt
new file mode 100644
index 0000000..4420a44
--- /dev/null
+++ b/test-mock/jarjar-rules.txt
@@ -0,0 +1,7 @@
+zap android.accounts.**
+zap android.app.**
+zap android.content.**
+zap android.database.**
+zap android.os.**
+zap android.util.**
+zap android.view.**
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index 840a588..086ef95 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -50,7 +50,7 @@
         "cts-install-lib-host",
     ],
     data: [
-        ":com.android.apex.cts.shim.v2_prebuilt",
+        ":StagedInstallTestApexV2",
         ":TestAppAv1",
     ],
     test_suites: ["general-tests"],
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 0f84f6e..c9a8947a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -322,6 +322,7 @@
         triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
         verify(mSafeModeTimeoutAlarm).cancel();
         assertFalse(mGatewayConnection.isInSafeMode());
+        verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
     }
 
     @Test
@@ -391,6 +392,7 @@
 
         triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
 
+        verifySafeModeStateAndCallbackFired(2 /* invocationCount */, false /* isInSafeMode */);
         assertFalse(mGatewayConnection.isInSafeMode());
     }
 
@@ -400,7 +402,7 @@
         mTestLooper.dispatchAll();
 
         triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
-        assertFalse(mGatewayConnection.isInSafeMode());
+        verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
 
         // Trigger a failed validation, and the subsequent safemode timeout.
         triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
@@ -416,7 +418,7 @@
         runnableCaptor.getValue().run();
         mTestLooper.dispatchAll();
 
-        assertTrue(mGatewayConnection.isInSafeMode());
+        verifySafeModeStateAndCallbackFired(2 /* invocationCount */, true /* isInSafeMode */);
     }
 
     private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index a696b3a..64d0bca 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -23,7 +23,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -301,6 +300,11 @@
                 expectCanceled);
     }
 
+    protected void verifySafeModeStateAndCallbackFired(int invocationCount, boolean isInSafeMode) {
+        verify(mGatewayStatusCallback, times(invocationCount)).onSafeModeStatusChanged();
+        assertEquals(isInSafeMode, mGatewayConnection.isInSafeMode());
+    }
+
     protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
             @NonNull State expectedState) {
         // Set a VcnNetworkAgent, and expect it to be unregistered and cleared
@@ -314,9 +318,8 @@
         delayedEvent.run();
         mTestLooper.dispatchAll();
 
-        verify(mGatewayStatusCallback).onSafeModeStatusChanged();
         assertEquals(expectedState, mGatewayConnection.getCurrentState());
-        assertTrue(mGatewayConnection.isInSafeMode());
+        verifySafeModeStateAndCallbackFired(1, true);
 
         verify(mockNetworkAgent).unregister();
         assertNull(mGatewayConnection.getNetworkAgent());
diff --git a/tools/bit/print.cpp b/tools/bit/print.cpp
index 35feda1..8bc6f16 100644
--- a/tools/bit/print.cpp
+++ b/tools/bit/print.cpp
@@ -17,6 +17,7 @@
 #include "print.h"
 
 #include <sys/ioctl.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <unistd.h>
 
diff --git a/tools/bit/util.h b/tools/bit/util.h
index 7ccdab1..8c66911 100644
--- a/tools/bit/util.h
+++ b/tools/bit/util.h
@@ -17,6 +17,8 @@
 #ifndef UTIL_H
 #define UTIL_H
 
+#include <sys/types.h>
+
 #include <map>
 #include <string>
 #include <vector>
diff --git a/tools/streaming_proto/Errors.cpp b/tools/streaming_proto/Errors.cpp
index 0cd9037..6890d99 100644
--- a/tools/streaming_proto/Errors.cpp
+++ b/tools/streaming_proto/Errors.cpp
@@ -1,5 +1,6 @@
 #include "Errors.h"
 
+#include <stdarg.h>
 #include <stdlib.h>
 
 namespace android {