Merge "mediav2 CTS: Update mediav2 resource directory from 1.14 to 2.0"
diff --git a/Android.bp b/Android.bp
index 0d50878..0598d5d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,33 +1,6 @@
 package {
-    default_applicable_licenses: ["cts_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-// See: http://go/android-license-faq
-license {
-    name: "cts_license",
-    visibility: [":__subpackages__"],
-    license_kinds: [
-        "SPDX-license-identifier-Apache-2.0",
-        "SPDX-license-identifier-BSD",
-        "SPDX-license-identifier-CC-BY",
-        "SPDX-license-identifier-MIT",
-        "SPDX-license-identifier-NCSA",
-        "legacy_unencumbered",
-    ],
-    // large-scale-change unable to identify any license_text files
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 java_defaults {
diff --git a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
index c19b6f7a..e1ed893 100644
--- a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
+++ b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
@@ -11,7 +11,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-"""Verify camera startup is < 500ms for both front and back primary cameras.
+"""Verify camera startup is < 600ms for both front and back primary cameras.
 """
 
 import logging
@@ -22,15 +22,14 @@
 import its_base_test
 import its_session_utils
 
-CAMERA_LAUNCH_R_PERFORMANCE_CLASS_THRESHOLD = 600  # ms
-CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 500  # ms
+CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 600  # ms
 
 
 class CameraLaunchSPerfClassTest(its_base_test.ItsBaseTest):
   """Test camera launch latency for S performance class as specified in CDD.
 
   [7.5/H-1-7] MUST have camera2 startup latency (open camera to first preview
-  frame) < 500ms as measured by the CTS camera PerformanceTest under ITS
+  frame) < 600ms as measured by the CTS camera PerformanceTest under ITS
   lighting conditions (3000K) for both primary cameras.
   """
 
@@ -41,8 +40,8 @@
         device_id=self.dut.serial,
         camera_id=self.camera_id) as cam:
 
-      perf_class_level = cam.get_performance_class_level()
-      camera_properties_utils.skip_unless(perf_class_level >= 11)
+      camera_properties_utils.skip_unless(
+          cam.is_performance_class_primary_camera())
 
       # Load chart for scene.
       props = cam.get_camera_properties()
@@ -56,14 +55,9 @@
         camera_id=self.camera_id)
 
     launch_ms = cam.measure_camera_launch_ms()
-    if perf_class_level >= 12:
-      perf_class_threshold = CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD
-    else:
-      perf_class_threshold = CAMERA_LAUNCH_R_PERFORMANCE_CLASS_THRESHOLD
-
-    if launch_ms >= perf_class_threshold:
+    if launch_ms >= CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD:
       raise AssertionError(f'camera launch time: {launch_ms} ms, THRESH: '
-                           f'{perf_class_threshold} ms')
+                           f'{CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD} ms')
     else:
       logging.debug('camera launch time: %.1f ms', launch_ms)
 
diff --git a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
index 74aefd5..ba4867b 100644
--- a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
+++ b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
@@ -22,7 +22,7 @@
 import its_base_test
 import its_session_utils
 
-JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD = 1000  # ms
+JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD = 1000  # ms
 
 
 class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest):
@@ -41,7 +41,7 @@
         camera_id=self.camera_id) as cam:
 
       camera_properties_utils.skip_unless(
-          cam.get_performance_class_level() >= 11)
+          cam.is_performance_class_primary_camera())
 
       # Load chart for scene.
       props = cam.get_camera_properties()
@@ -55,10 +55,10 @@
         camera_id=self.camera_id)
 
     jpeg_capture_ms = cam.measure_camera_1080p_jpeg_capture_ms()
-    if jpeg_capture_ms >= JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD:
+    if jpeg_capture_ms >= JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD:
       raise AssertionError(f'1080p jpeg capture time: {jpeg_capture_ms} ms, '
                            f'THRESH: '
-                           f'{JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD} ms')
+                           f'{JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD} ms')
     else:
       logging.debug('1080p jpeg capture time: %.1f ms', jpeg_capture_ms)
 
diff --git a/apps/CameraITS/utils/its_session_utils.py b/apps/CameraITS/utils/its_session_utils.py
index 3289b91..4c47388 100644
--- a/apps/CameraITS/utils/its_session_utils.py
+++ b/apps/CameraITS/utils/its_session_utils.py
@@ -1093,24 +1093,25 @@
                                       ' support')
     return data['strValue'] == 'true'
 
-  def get_performance_class_level(self):
+  def is_performance_class_primary_camera(self):
     """Query whether the camera device is an R or S performance class primary camera.
 
     A primary rear/front facing camera is a camera device with the lowest
     camera Id for that facing.
 
     Returns:
-      Performance class level in integer. R: 11. S: 12.
+      Boolean
     """
     cmd = {}
-    cmd['cmdName'] = 'getPerformanceClassLevel'
+    cmd['cmdName'] = 'isPerformanceClassPrimaryCamera'
     cmd['cameraId'] = self._camera_id
     self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
 
     data, _ = self.__read_response_from_socket()
-    if data['tag'] != 'performanceClassLevel':
-      raise error_util.CameraItsError('Failed to query performance class level')
-    return int(data['strValue'])
+    if data['tag'] != 'performanceClassPrimaryCamera':
+      raise error_util.CameraItsError('Failed to query performance class '
+                                      'primary camera')
+    return data['strValue'] == 'true'
 
   def measure_camera_launch_ms(self):
     """Measure camera launch latency in millisecond, from open to first frame.
diff --git a/apps/CtsVerifier/Android.bp b/apps/CtsVerifier/Android.bp
index a73691d..41eb311 100644
--- a/apps/CtsVerifier/Android.bp
+++ b/apps/CtsVerifier/Android.bp
@@ -16,13 +16,33 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-BSD
-    //   SPDX-license-identifier-CC-BY
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "cts_apps_CtsVerifier_opencv_license",
+        "Android-Apache-2.0",
+        "cts_apps_CtsVerifier_fatcow_license",
+    ]
+}
+
+license {
+    name: "cts_apps_CtsVerifier_opencv_license",
+    package_name: "opencv",
+    license_kinds: [
+        "SPDX-license-identifier-BSD",
+    ],
+    license_text: [
+        "libs/opencv-android_LICENSE",
+        "res/raw/opencv_library_license",
+    ],
+}
+
+// See: src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+license {
+    name: "cts_apps_CtsVerifier_fatcow_license",
+    package_name: "fatcow icons",
+    license_kinds: [
+        "SPDX-license-identifier-CC-BY-3.0",
+    ],
+    license_text: ["LICENSE_CC_BY"],
 }
 
 filegroup {
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index ce0ec65..e0d9a35 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -556,6 +556,58 @@
                        android:value="multi_display_mode" />
         </activity>
 
+        <!--
+             CTS Verifier Bluetooth Background Rfcomm Test Activity
+                 test category : bt_background_rfcomm
+                 test parent : BluetoothTestActivity
+        -->
+        <activity
+            android:name=".bluetooth.BackgroundRfcommTestActivity"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/bt_background_rfcomm_test_name"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data
+                android:name="test_category"
+                android:value="@string/bt_background_rfcomm" />
+            <meta-data
+                android:name="test_parent"
+                android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.watch" />
+            <meta-data android:name="display_mode"
+                       android:value="multi_display_mode" />
+        </activity>
+
+
+        <activity
+            android:name=".bluetooth.BackgroundRfcommTestClientActivity"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:label="@string/bt_background_rfcomm_test_client_name"
+            android:exported="true" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data
+                android:name="test_category"
+                android:value="@string/bt_background_rfcomm" />
+            <meta-data
+                android:name="test_parent"
+                android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.watch" />
+            <meta-data android:name="display_mode"
+                       android:value="multi_display_mode" />
+        </activity>
+
 <!--
      *****************************************************************************************
      **                          Begin BLE Test Sub Layer Info                            ****
@@ -1463,6 +1515,27 @@
                        android:value="multi_display_mode" />
         </activity>
 
+        <activity
+            android:name=".bluetooth.BleAdvertisingSetTestActivity"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:exported="true"
+            android:label="@string/ble_advertising_set_test_name" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data
+                android:name="test_category"
+                android:value="@string/bt_le" />
+            <meta-data
+                android:name="test_parent"
+                android:value="com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity" />
+            <meta-data android:name="display_mode"
+                       android:value="multi_display_mode" />
+        </activity>
+
         <activity android:name=".biometrics.BiometricTestList"
             android:label="@string/biometric_test"
             android:exported="true"
@@ -1749,6 +1822,23 @@
                        android:value="multi_display_mode" />
         </activity>
 
+        <activity android:name=".security.IdentityCredentialAuthenticationMultiDocument"
+                android:label="@string/sec_identity_credential_authentication_multi_document_test"
+                android:exported="true"
+                android:configChanges="keyboardHidden|orientation|screenSize" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_security" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+            <meta-data android:name="test_required_features"
+                       android:value="android.software.secure_lock_screen" />
+            <meta-data android:name="display_mode"
+                       android:value="multi_display_mode" />
+        </activity>
+
         <activity android:name=".security.FingerprintBoundKeysTest"
                 android:label="@string/sec_fingerprint_bound_key_test"
                 android:exported="true"
diff --git a/apps/CtsVerifier/LICENSE_CC_BY b/apps/CtsVerifier/LICENSE_CC_BY
new file mode 100644
index 0000000..e3feb12
--- /dev/null
+++ b/apps/CtsVerifier/LICENSE_CC_BY
@@ -0,0 +1,348 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content="HTML Tidy for Linux/x86 (vers 1 September 2005), see www.w3.org" />
+<title>Creative Commons Legal Code</title>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<link rel="stylesheet" type="text/css" href="https://creativecommons.org/includes/deed3.css" media="screen" />
+<link rel="stylesheet" type="text/css" href="https://creativecommons.org/includes/deed3-print.css" media="print" />
+<!--[if lt IE 7]><link rel="stylesheet" type="text/css" href="https://creativecommons.org/includes/deed3-ie.css" media="screen" /><![endif]-->
+<script type="text/javascript" src="https://creativecommons.org/includes/errata.js">
+</script>
+</head>
+<body>
+<p align="center" id="header"><a href="https://creativecommons.org/">Creative Commons</a></p>
+<div id="deed" class="green">
+<div id="deed-head">
+<div id="cc-logo">
+<img src="https://creativecommons.org/images/deed/cc-logo.jpg" alt="" />
+</div>
+<h1><span>Creative Commons Legal Code</span></h1>
+<div id="deed-license">
+<h2>Attribution 3.0 United States</h2>
+</div>
+</div>
+<div id="deed-main">
+<div id="deed-main-content">
+<img src="https://creativecommons.org/images/international/us.png" alt="" />
+<blockquote>
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES
+NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE
+DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE
+COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS.
+CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE
+INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES
+RESULTING FROM ITS USE.
+</blockquote>
+<h3><em>License</em></h3>
+<p>THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS
+OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR
+"LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER
+APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS
+PROHIBITED.</p>
+<p>BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU
+ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE.
+TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A
+CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE
+IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.</p>
+<p><strong>1. Definitions</strong></p>
+<ol type="a">
+<li><strong>"Collective Work"</strong> means a work, such
+as a periodical issue, anthology or encyclopedia, in
+which the Work in its entirety in unmodified form, along
+with one or more other contributions, constituting
+separate and independent works in themselves, are
+assembled into a collective whole. A work that
+constitutes a Collective Work will not be considered a
+Derivative Work (as defined below) for the purposes of
+this License.</li>
+<li><strong>"Derivative Work"</strong> means a work based
+upon the Work or upon the Work and other pre-existing
+works, such as a translation, musical arrangement,
+dramatization, fictionalization, motion picture version,
+sound recording, art reproduction, abridgment,
+condensation, or any other form in which the Work may be
+recast, transformed, or adapted, except that a work that
+constitutes a Collective Work will not be considered a
+Derivative Work for the purpose of this License. For the
+avoidance of doubt, where the Work is a musical
+composition or sound recording, the synchronization of
+the Work in timed-relation with a moving image
+("synching") will be considered a Derivative Work for the
+purpose of this License.</li>
+<li><strong>"Licensor"</strong> means the individual,
+individuals, entity or entities that offers the Work
+under the terms of this License.</li>
+<li><strong>"Original Author"</strong> means the
+individual, individuals, entity or entities who created
+the Work.</li>
+<li><strong>"Work"</strong> means the copyrightable work
+of authorship offered under the terms of this
+License.</li>
+<li><strong>"You"</strong> means an individual or entity
+exercising rights under this License who has not
+previously violated the terms of this License with
+respect to the Work, or who has received express
+permission from the Licensor to exercise rights under
+this License despite a previous violation.</li>
+</ol>
+<p><strong>2. Fair Use Rights.</strong> Nothing in this
+license is intended to reduce, limit, or restrict any
+rights arising from fair use, first sale or other
+limitations on the exclusive rights of the copyright owner
+under copyright law or other applicable laws.</p>
+<p><strong>3. License Grant.</strong> Subject to the terms
+and conditions of this License, Licensor hereby grants You
+a worldwide, royalty-free, non-exclusive, perpetual (for
+the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:</p>
+<ol type="a">
+<li>to reproduce the Work, to incorporate the Work into
+one or more Collective Works, and to reproduce the Work
+as incorporated in the Collective Works;</li>
+<li>to create and reproduce Derivative Works provided
+that any such Derivative Work, including any translation
+in any medium, takes reasonable steps to clearly label,
+demarcate or otherwise identify that changes were made to
+the original Work. For example, a translation could be
+marked "The original work was translated from English to
+Spanish," or a modification could indicate "The original
+work has been modified.";;</li>
+<li>to distribute copies or phonorecords of, display
+publicly, perform publicly, and perform publicly by means
+of a digital audio transmission the Work including as
+incorporated in Collective Works;</li>
+<li>to distribute copies or phonorecords of, display
+publicly, perform publicly, and perform publicly by means
+of a digital audio transmission Derivative Works.</li>
+<li>
+<p>For the avoidance of doubt, where the Work is a
+musical composition:</p>
+<ol type="i">
+<li><strong>Performance Royalties Under Blanket
+Licenses</strong>. Licensor waives the exclusive
+right to collect, whether individually or, in the
+event that Licensor is a member of a performance
+rights society (e.g. ASCAP, BMI, SESAC), via that
+society, royalties for the public performance or
+public digital performance (e.g. webcast) of the
+Work.</li>
+<li><strong>Mechanical Rights and Statutory
+Royalties</strong>. Licensor waives the exclusive
+right to collect, whether individually or via a music
+rights agency or designated agent (e.g. Harry Fox
+Agency), royalties for any phonorecord You create
+from the Work ("cover version") and distribute,
+subject to the compulsory license created by 17 USC
+Section 115 of the US Copyright Act (or the
+equivalent in other jurisdictions).</li>
+</ol>
+</li>
+<li><strong>Webcasting Rights and Statutory
+Royalties</strong>. For the avoidance of doubt, where the
+Work is a sound recording, Licensor waives the exclusive
+right to collect, whether individually or via a
+performance-rights society (e.g. SoundExchange),
+royalties for the public digital performance (e.g.
+webcast) of the Work, subject to the compulsory license
+created by 17 USC Section 114 of the US Copyright Act (or
+the equivalent in other jurisdictions).</li>
+</ol>
+<p>The above rights may be exercised in all media and
+formats whether now known or hereafter devised. The above
+rights include the right to make such modifications as are
+technically necessary to exercise the rights in other media
+and formats. All rights not expressly granted by Licensor
+are hereby reserved.</p>
+<p><strong>4. Restrictions.</strong> The license granted in
+Section 3 above is expressly made subject to and limited by
+the following restrictions:</p>
+<ol type="a">
+<li>You may distribute, publicly display, publicly
+perform, or publicly digitally perform the Work only
+under the terms of this License, and You must include a
+copy of, or the Uniform Resource Identifier for, this
+License with every copy or phonorecord of the Work You
+distribute, publicly display, publicly perform, or
+publicly digitally perform. You may not offer or impose
+any terms on the Work that restrict the terms of this
+License or the ability of a recipient of the Work to
+exercise the rights granted to that recipient under the
+terms of the License. You may not sublicense the Work.
+You must keep intact all notices that refer to this
+License and to the disclaimer of warranties. When You
+distribute, publicly display, publicly perform, or
+publicly digitally perform the Work, You may not impose
+any technological measures on the Work that restrict the
+ability of a recipient of the Work from You to exercise
+the rights granted to that recipient under the terms of
+the License. This Section 4(a) applies to the Work as
+incorporated in a Collective Work, but this does not
+require the Collective Work apart from the Work itself to
+be made subject to the terms of this License. If You
+create a Collective Work, upon notice from any Licensor
+You must, to the extent practicable, remove from the
+Collective Work any credit as required by Section 4(b),
+as requested. If You create a Derivative Work, upon
+notice from any Licensor You must, to the extent
+practicable, remove from the Derivative Work any credit
+as required by Section 4(b), as requested.</li>
+<li>If You distribute, publicly display, publicly
+perform, or publicly digitally perform the Work (as
+defined in Section 1 above) or any Derivative Works (as
+defined in Section 1 above) or Collective Works (as
+defined in Section 1 above), You must, unless a request
+has been made pursuant to Section 4(a), keep intact all
+copyright notices for the Work and provide, reasonable to
+the medium or means You are utilizing: (i) the name of
+the Original Author (or pseudonym, if applicable) if
+supplied, and/or (ii) if the Original Author and/or
+Licensor designate another party or parties (e.g. a
+sponsor institute, publishing entity, journal) for
+attribution ("Attribution Parties") in Licensor's
+copyright notice, terms of service or by other reasonable
+means, the name of such party or parties; the title of
+the Work if supplied; to the extent reasonably
+practicable, the Uniform Resource Identifier, if any,
+that Licensor specifies to be associated with the Work,
+unless such URI does not refer to the copyright notice or
+licensing information for the Work; and, consistent with
+Section 3(b) in the case of a Derivative Work, a credit
+identifying the use of the Work in the Derivative Work
+(e.g., "French translation of the Work by Original
+Author," or "Screenplay based on original Work by
+Original Author"). The credit required by this Section
+4(b) may be implemented in any reasonable manner;
+provided, however, that in the case of a Derivative Work
+or Collective Work, at a minimum such credit will appear,
+if a credit for all contributing authors of the
+Derivative Work or Collective Work appears, then as part
+of these credits and in a manner at least as prominent as
+the credits for the other contributing authors. For the
+avoidance of doubt, You may only use the credit required
+by this Section for the purpose of attribution in the
+manner set out above and, by exercising Your rights under
+this License, You may not implicitly or explicitly assert
+or imply any connection with, sponsorship or endorsement
+by the Original Author, Licensor and/or Attribution
+Parties, as appropriate, of You or Your use of the Work,
+without the separate, express prior written permission of
+the Original Author, Licensor and/or Attribution
+Parties.</li>
+</ol>
+<p><strong>5. Representations, Warranties and
+Disclaimer</strong></p>
+<p>UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN
+WRITING, LICENSOR OFFERS THE WORK AS-IS AND ONLY TO THE
+EXTENT OF ANY RIGHTS HELD IN THE LICENSED WORK BY THE
+LICENSOR. THE LICENSOR MAKES NO REPRESENTATIONS OR
+WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS,
+IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
+LIMITATION, WARRANTIES OF TITLE, MARKETABILITY,
+MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE,
+NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
+ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT
+APPLY TO YOU.</p>
+<p><strong>6. Limitation on Liability.</strong> EXCEPT TO
+THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL
+LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY
+SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY
+DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK,
+EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.</p>
+<p><strong>7. Termination</strong></p>
+<ol type="a">
+<li>This License and the rights granted hereunder will
+terminate automatically upon any breach by You of the
+terms of this License. Individuals or entities who have
+received Derivative Works (as defined in Section 1 above)
+or Collective Works (as defined in Section 1 above) from
+You under this License, however, will not have their
+licenses terminated provided such individuals or entities
+remain in full compliance with those licenses. Sections
+1, 2, 5, 6, 7, and 8 will survive any termination of this
+License.</li>
+<li>Subject to the above terms and conditions, the
+license granted here is perpetual (for the duration of
+the applicable copyright in the Work). Notwithstanding
+the above, Licensor reserves the right to release the
+Work under different license terms or to stop
+distributing the Work at any time; provided, however that
+any such election will not serve to withdraw this License
+(or any other license that has been, or is required to
+be, granted under the terms of this License), and this
+License will continue in full force and effect unless
+terminated as stated above.</li>
+</ol>
+ <p><strong>8. Miscellaneous</strong></p>
+<ol type="a">
+<li>Each time You distribute or publicly digitally
+perform the Work (as defined in Section 1 above) or a
+Collective Work (as defined in Section 1 above), the
+Licensor offers to the recipient a license to the Work on
+the same terms and conditions as the license granted to
+You under this License.</li>
+<li>Each time You distribute or publicly digitally
+perform a Derivative Work, Licensor offers to the
+recipient a license to the original Work on the same
+terms and conditions as the license granted to You under
+this License.</li>
+<li>If any provision of this License is invalid or
+unenforceable under applicable law, it shall not affect
+the validity or enforceability of the remainder of the
+terms of this License, and without further action by the
+parties to this agreement, such provision shall be
+reformed to the minimum extent necessary to make such
+provision valid and enforceable.</li>
+<li>No term or provision of this License shall be deemed
+waived and no breach consented to unless such waiver or
+consent shall be in writing and signed by the party to be
+charged with such waiver or consent.</li>
+<li>This License constitutes the entire agreement between
+the parties with respect to the Work licensed here. There
+are no understandings, agreements or representations with
+respect to the Work not specified here. Licensor shall
+not be bound by any additional provisions that may appear
+in any communication from You. This License may not be
+modified without the mutual written agreement of the
+Licensor and You.</li>
+</ol>
+
+<blockquote>
+<h3>Creative Commons Notice</h3>
+<p>Creative Commons is not a party to this License, and
+makes no warranty whatsoever in connection with the Work.
+Creative Commons will not be liable to You or any party
+on any legal theory for any damages whatsoever, including
+without limitation any general, special, incidental or
+consequential damages arising in connection to this
+license. Notwithstanding the foregoing two (2) sentences,
+if Creative Commons has expressly identified itself as
+the Licensor hereunder, it shall have all rights and
+obligations of Licensor.</p>
+<p>Except for the limited purpose of indicating to the
+public that the Work is licensed under the CCPL, Creative
+Commons does not authorize the use by either party of the
+trademark "Creative Commons" or any related trademark or
+logo of Creative Commons without the prior written
+consent of Creative Commons. Any permitted use will be in
+compliance with Creative Commons' then-current trademark
+usage guidelines, as may be published on its website or
+otherwise made available upon request from time to time.
+For the avoidance of doubt, this trademark restriction
+does not form part of the License.</p>
+<p>Creative Commons may be contacted at <a href="https://creativecommons.org/">https://creativecommons.org/</a>.</p>
+</blockquote>
+</div>
+</div>
+<div id="deed-foot">
+<p id="footer"><a href="./">« Back to Commons Deed</a></p>
+</div>
+</div>
+</body>
+</html>
diff --git a/apps/CtsVerifier/res/layout/ble_advertising_set.xml b/apps/CtsVerifier/res/layout/ble_advertising_set.xml
new file mode 100644
index 0000000..06225d9
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_advertising_set.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+    <TextView android:text="@string/ble_advertising_set_test_instruction"
+              android:id="@+id/ble_advertising_set_test_instruction"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:scrollbars="vertical"/>
+    <Button android:id="@+id/ble_advertising_set_start_test"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/ble_advertising_set_start_test"/>
+    <ListView android:id="@+id/ble_advertising_set_tests"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:padding="10dip"/>
+    <include android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             layout="@layout/pass_fail_buttons"/>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/bt_background_rfcomm.xml b/apps/CtsVerifier/res/layout/bt_background_rfcomm.xml
new file mode 100644
index 0000000..d63cbaa
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bt_background_rfcomm.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/bt_background_rfcomm_text"
+        style="@style/InstructionsSmallFont"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:gravity="center"
+        android:text="@string/bt_background_rfcomm_test_start_client" />
+
+    <include
+        layout="@layout/pass_fail_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/bt_background_rfcomm_client.xml b/apps/CtsVerifier/res/layout/bt_background_rfcomm_client.xml
new file mode 100644
index 0000000..0c9cf2f
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/bt_background_rfcomm_client.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/bt_background_rfcomm_client_text"
+        style="@style/InstructionsSmallFont"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:gravity="center"
+        android:text="@string/bt_background_rfcomm_test_connecting_to_server" />
+
+    <include
+        layout="@layout/pass_fail_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index edb0851..3df2bc4 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -355,6 +355,12 @@
         authentication with timeout 0 can only be accessed once per reader session.
     </string>
 
+    <!-- Strings for IdentityAuthPerPresenationMultiDocument -->
+    <string name="sec_identity_credential_authentication_multi_document_test">Identity Credential Authentication Multi-Document</string>
+    <string name="sec_identity_credential_authentication_multi_document_test_info">
+        This test is for Identity Credential presentations with multiple documents.
+    </string>
+
     <string name="sec_unlocked_device_required_test">Unlocked Device Required</string>
     <string name="sec_unlocked_device_required_test_info">
         This test ensures that keys created with setUnlockedDeviceRequired are usable when the
@@ -389,6 +395,7 @@
     <string name="bt_le">Bluetooth LE</string>
     <string name="bt_hid">Bluetooth HID</string>
     <string name="bt_le_coc">Bluetooth LE CoC</string>
+    <string name="bt_background_rfcomm">Bluetooth Background RFCOMM</string>
 
     <string name="bt_toggle_bluetooth">Toggle Bluetooth</string>
     <string name="bt_toggle_instructions">Disable and enable Bluetooth to successfully complete this test.</string>
@@ -434,6 +441,19 @@
         \n\nTap \"01 Bluetooth LE CoC Server Test\" on this device, then tap \"01 Bluetooth LE CoC Client Test\" on the other device.
         \nWhen the test is complete, move to the next item. You must complete all tests.
     </string>
+    <string name="bt_background_rfcomm_test_name">Bluetooth Background RFCOMM Test</string>
+    <string name="bt_background_rfcomm_test_info">
+        This test verifies that system applications can register RFCOMM listeners via the
+        BluetoothAdapter without needing a foreground service.
+    </string>
+    <string name="bt_background_rfcomm_test_client_name">Bluetooth Background RFCOMM Test Client</string>
+    <string name="bt_background_rfcomm_test_client_info">
+        Run by a second phone which acts as the RFCOMM client to connect to the rfcomm listener.
+        This phone should be paired with the primary phone which runs the Bluetooth Background
+        RFCOMM Test.
+    </string>
+    <string name="bt_background_rfcomm_test_uuid">1bd18454-462a-4117-bd58-77cb4d81ddbe</string>
+    <string name="bt_background_rfcomm_test_message">Test Message</string>
 
     <!-- BLE CoC client side strings -->
     <string name="ble_coc_client_test_name">01 Bluetooth LE CoC Client Test</string>
@@ -552,6 +572,13 @@
     <string name="bt_unpair">Device must be unpaired via Bluetooth settings before completing the test.\n\nUnpair the device in settings, make the server discoverable, and rescan to pick this device.</string>
     <string name="bt_settings">Bluetooth Settings</string>
 
+    <string name="bt_background_rfcomm_test_start_client">On the client device: enable (or toggle) Bluetooth and connect to this device, then start the client test.</string>
+    <string name="bt_background_rfcomm_test_socket_received">Received client connection.</string>
+    <string name="bt_background_rfcomm_test_sending_message">Sending test message.</string>
+    <string name="bt_background_rfcomm_test_waiting_for_message">Waiting for message.</string>
+    <string name="bt_background_rfcomm_test_connecting_to_server">Connecting to server.</string>
+    <string name="bt_background_rfcomm_test_doing_sdp">Doing service discovery of server.</string>
+
     <!-- BLE client side strings -->
     <string name="ble_client_service_name">Bluetooth LE GATT Client Handler Service</string>
     <string name="ble_client_test_name">01 Bluetooth LE Client Test</string>
@@ -697,6 +724,23 @@
     <string name="ble_scan_start">Start scan</string>
     <string name="ble_scan_stop">Stop scan</string>
 
+    <!-- BLE Advertising Set test strings -->
+    <string name="ble_advertising_set_test_name">Bluetooth LE Advertising Set Test</string>
+    <string name="ble_advertising_set_test_info">Bluetooth LE Advertising Set tests AdvertisingSet and AdvertisingSetCallback APIs.</string>
+    <string name="ble_advertising_set_test_instruction">Press the \"Set Up\" button first, then start the test by pressing the \"Start Test\" button. UI thread may freeze for a few seconds while enabling/disabling bluetooth adapter.</string>
+    <string name="ble_advertising_set_start_test">Start Test</string>
+    <string name="ble_advertising_set_running_test">Running Test...</string>
+    <string name="ble_advertising_set_finished_test">Finished Test</string>
+    <string name="ble_advertising_set_start">Starting advertising set.</string>
+    <string name="ble_advertising_set_enable_disable">Enabling/Disabling advertising set.</string>
+    <string name="ble_advertising_set_advertising_data">Setting advertising data.</string>
+    <string name="ble_advertising_set_advertising_params">Setting advertising parameters.</string>
+    <string name="ble_advertising_set_periodic_advertising_data">Setting periodic advertising data.</string>
+    <string name="ble_advertising_set_periodic_advertising_enabled_disabled">Enabling/Disabling periodic advertising.</string>
+    <string name="ble_advertising_set_periodic_advertising_params">Setting periodic advertising parameters.</string>
+    <string name="ble_advertising_set_scan_response_data">Setting scan response data.</string>
+    <string name="ble_advertising_set_stop">Stopping advertising set.</string>
+
     <!-- BLE connection priority test strings -->
     <string name="ble_client_connection_priority">Testing connection priority switching </string>
     <string name="ble_server_connection_priority_result_passed">All test passed</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index 4bd642d..b321c60 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -70,7 +70,10 @@
     private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId";
 
     // ReportLog file for CTS-Verifier. The "stream" name gets mapped to the test class name.
-    private static final String REPORT_LOG_NAME = "CTS-Verifier-Log";
+    public static final String GENERAL_TESTS_REPORT_LOG_NAME = "CtsVerifierGeneralTestCases";
+    public static final String AUDIO_TESTS_REPORT_LOG_NAME = "CtsVerifierAudioTestCases";
+
+    private static final String SECTION_UNDEFINED = "undefined_section_name";
 
     // Interface mostly for making documentation and refactoring easier...
     public interface PassFailActivity {
@@ -112,10 +115,16 @@
         void setTestResultAndFinish(boolean passed);
 
         /**
-         * @return A unique name (derived from the test class name) to serve as a section
-         * header in the CtsVerifierReportLog file.
+         * @return The name of the file to store the (suite of) ReportLog information.
          */
-        String getReportSectionName();
+        public String getReportFileName();
+
+        /**
+         * @return A unique name to serve as a section header in the CtsVerifierReportLog file.
+         * Tests need to conform to the underscore_delineated_name standard for use with
+         * the protobuff/json ReportLog parsing in Google3
+         */
+        public String getReportSectionName();
 
         /**
          * Test subclasses can override this to record their CtsVerifierReportLogs.
@@ -138,7 +147,7 @@
         private final TestResultHistoryCollection mHistoryCollection;
 
         public Activity() {
-            this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, getReportSectionName());
+            this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
             this.mHistoryCollection = new TestResultHistoryCollection();
         }
 
@@ -202,9 +211,15 @@
             return mReportLog;
         }
 
+        /**
+         * @return The name of the file to store the (suite of) ReportLog information.
+         */
         @Override
-        public final String getReportSectionName() {
-            return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
+        public String getReportFileName() { return GENERAL_TESTS_REPORT_LOG_NAME; }
+
+        @Override
+        public String getReportSectionName() {
+            return setTestNameSuffix(sCurrentDisplayMode, SECTION_UNDEFINED);
         }
 
         @Override
@@ -240,7 +255,7 @@
         private final TestResultHistoryCollection mHistoryCollection;
 
         public ListActivity() {
-            this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, getReportSectionName());
+            this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
             this.mHistoryCollection = new TestResultHistoryCollection();
         }
 
@@ -286,9 +301,15 @@
             return mReportLog;
         }
 
+        /**
+         * @return The name of the file to store the (suite of) ReportLog information.
+         */
         @Override
-        public final String getReportSectionName() {
-            return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
+        public String getReportFileName() { return GENERAL_TESTS_REPORT_LOG_NAME; }
+
+        @Override
+        public String getReportSectionName() {
+            return setTestNameSuffix(sCurrentDisplayMode, SECTION_UNDEFINED);
         }
 
         @Override
@@ -326,9 +347,7 @@
         public TestListActivity() {
             // TODO(b/186555602): temporary hack^H^H^H^H workaround to fix crash
             // This DOES NOT in fact fix that bug.
-            // if (true) this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, "42"); else
-
-            this.mReportLog = new CtsVerifierReportLog(REPORT_LOG_NAME, getReportSectionName());
+            this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
         }
 
         @Override
@@ -373,11 +392,18 @@
             return mReportLog;
         }
 
+        /**
+         * @return The name of the file to store the (suite of) ReportLog information.
+         */
         @Override
-        public final String getReportSectionName() {
-            return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
+        public String getReportFileName() { return GENERAL_TESTS_REPORT_LOG_NAME; }
+
+        @Override
+        public String getReportSectionName() {
+            return setTestNameSuffix(sCurrentDisplayMode, SECTION_UNDEFINED);
         }
 
+
         /**
          * Get existing test history to aggregate.
          */
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
index fe314c42..495c3ef 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
@@ -23,6 +23,7 @@
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
+import android.util.Log;
 
 import com.android.compatibility.common.util.FileUtil;
 import com.android.compatibility.common.util.IInvocationResult;
@@ -49,6 +50,8 @@
  * Background task to generate a report and save it to external storage.
  */
 class ReportExporter extends AsyncTask<Void, Void, String> {
+    private static final String TAG = ReportExporter.class.getSimpleName();
+    private static final boolean DEBUG = true;
 
     public static final String REPORT_DIRECTORY = "VerifierReports";
     public static final String LOGS_DIRECTORY = "ReportLogFiles";
@@ -76,11 +79,15 @@
     // so that they will get ZIPped into the transmitted file.
     //
     private void copyReportFiles(File tempDir) {
+        if (DEBUG) {
+            Log.d(TAG, "copyReportFiles(" + tempDir.getAbsolutePath() + ")");
+        }
+
         File externalStorageDirectory = Environment.getExternalStorageDirectory();
         File reportLogFolder =
                 new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                         + File.separator
-                        + REPORT_DIRECTORY);
+                        + LOGS_DIRECTORY);
         File[] reportLogFiles = reportLogFolder.listFiles();
 
         // if no ReportLog files have been created (i.e. the folder doesn't exist)
@@ -156,6 +163,9 @@
     }
 
     private void saveReportOnInternalStorage(File reportZipFile) {
+        if (DEBUG) {
+            Log.d(TAG, "---- saveReportOnInternalStorage(" + reportZipFile.getAbsolutePath() + ")");
+        }
         try {
             ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
                     reportZipFile, ParcelFileDescriptor.MODE_READ_ONLY);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
index 4ce2a12..6233f2c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
@@ -400,6 +400,8 @@
             }
         }
 
+        reportHeadsetPort(mHeadsetDeviceInfo != null);
+
         showConnectedDevice();
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java
deleted file mode 100644
index 78e34d3..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackBaseActivity.java
+++ /dev/null
@@ -1,686 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.audio;
-
-import android.app.AlertDialog;
-import android.media.AudioDeviceCallback;
-import android.media.AudioDeviceInfo;
-import android.media.AudioManager;
-import android.media.MediaRecorder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.audio.audiolib.AudioSystemFlags;
-import com.android.cts.verifier.audio.audiolib.StatUtils;
-import com.android.cts.verifier.audio.audiolib.AudioUtils;
-import com.android.cts.verifier.CtsVerifierReportLog;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
-import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
-
-/**
- * Base class for testing activitiees that require audio loopback hardware..
- */
-public class AudioLoopbackBaseActivity extends PassFailButtons.Activity {
-    private static final String TAG = "AudioLoopbackBaseActivity";
-
-    // JNI load
-    static {
-        try {
-            System.loadLibrary("audioloopback_jni");
-        } catch (UnsatisfiedLinkError e) {
-            Log.e(TAG, "Error loading Audio Loopback JNI library");
-            Log.e(TAG, "e: " + e);
-            e.printStackTrace();
-        }
-
-        /* TODO: gracefully fail/notify if the library can't be loaded */
-    }
-    protected AudioManager mAudioManager;
-
-    // UI
-    TextView mInputDeviceTxt;
-    TextView mOutputDeviceTxt;
-
-    TextView mAudioLevelText;
-    SeekBar mAudioLevelSeekbar;
-
-    TextView mTestPathTxt;
-
-    TextView mResultText;
-    ProgressBar mProgressBar;
-    int mMaxLevel;
-
-    OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
-    protected Button mTestButton;
-
-    String mYesString;
-    String mNoString;
-
-    // These flags determine the maximum allowed latency
-    private boolean mClaimsProAudio;
-    private boolean mClaimsOutput;
-    private boolean mClaimsInput;
-
-    // Useful info
-    private boolean mSupportsMMAP = AudioUtils.isMMapSupported();
-    private boolean mSupportsMMAPExclusive = AudioUtils.isMMapExclusiveSupported();
-
-    // Peripheral(s)
-    boolean mIsPeripheralAttached;  // CDD ProAudio section C-1-3
-    AudioDeviceInfo mOutputDevInfo;
-    AudioDeviceInfo mInputDevInfo;
-
-    protected static final int TESTPERIPHERAL_INVALID       = -1;
-    protected static final int TESTPERIPHERAL_NONE          = 0;
-    protected static final int TESTPERIPHERAL_ANALOG_JACK   = 1;
-    protected static final int TESTPERIPHERAL_USB           = 2;
-    protected static final int TESTPERIPHERAL_DEVICE        = 3; // device speaker + mic
-    protected int mTestPeripheral = TESTPERIPHERAL_NONE;
-
-    // Loopback Logic
-    NativeAnalyzerThread mNativeAnalyzerThread = null;
-
-    protected static final int NUM_TEST_PHASES = 5;
-    protected int mTestPhase = 0;
-
-    protected double[] mLatencyMillis = new double[NUM_TEST_PHASES];
-    protected double[] mConfidence = new double[NUM_TEST_PHASES];
-
-    protected double mMeanLatencyMillis;
-    protected double mMeanAbsoluteDeviation;
-    protected double mMeanConfidence;
-
-    protected static final double CONFIDENCE_THRESHOLD = 0.6;
-    // impossibly low latencies (indicating something in the test went wrong).
-    protected static final float EPSILON = 1.0f;
-    protected static final double PROAUDIO_RECOMMENDED_LATENCY_MS = 20.0;
-    protected static final double PROAUDIO_RECOMMENDED_USB_LATENCY_MS = 25.0;
-    protected static final double PROAUDIO_MUST_LATENCY_MS = 20.0;
-    protected static final double BASIC_RECOMMENDED_LATENCY_MS = 50.0;
-    protected static final double BASIC_MUST_LATENCY_MS = 800.0;
-    protected double mMustLatency;
-    protected double mRecommendedLatency;
-
-    // The audio stream callback threads should stop and close
-    // in less than a few hundred msec. This is a generous timeout value.
-    private static final int STOP_TEST_TIMEOUT_MSEC = 2 * 1000;
-
-    //
-    // Common UI Handling
-    //
-    private void connectLoopbackUI() {
-        // Connected Device
-        mInputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackInputLbl));
-        mOutputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackOutputLbl));
-
-        mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
-        mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
-        mMaxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
-        mAudioLevelSeekbar.setMax(mMaxLevel);
-        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0);
-        refreshLevel();
-
-        mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
-            @Override
-            public void onStopTrackingTouch(SeekBar seekBar) {}
-
-            @Override
-            public void onStartTrackingTouch(SeekBar seekBar) {}
-
-            @Override
-            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-                mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
-                        progress, 0);
-                Log.i(TAG,"Level set to: " + progress);
-                refreshLevel();
-            }
-        });
-
-        mResultText = (TextView)findViewById(R.id.audio_loopback_results_text);
-        mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar);
-        showWait(false);
-    }
-
-    //
-    // Peripheral Connection Logic
-    //
-    protected void scanPeripheralList(AudioDeviceInfo[] devices) {
-        // CDD Section C-1-3: USB port, host-mode support
-
-        // Can't just use the first record because then we will only get
-        // Source OR sink, not both even on devices that are both.
-        mOutputDevInfo = null;
-        mInputDevInfo = null;
-
-        // Any valid peripherals
-        // Do we leave in the Headset test to support a USB-Dongle?
-        for (AudioDeviceInfo devInfo : devices) {
-            if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || // USB Peripheral
-                    devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET || // USB dongle+LBPlug
-                    devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
-                    devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
-                if (devInfo.isSink()) {
-                    mOutputDevInfo = devInfo;
-                }
-                if (devInfo.isSource()) {
-                    mInputDevInfo = devInfo;
-                }
-            }  else {
-                handleDeviceConnection(devInfo);
-            }
-        }
-
-        // need BOTH input and output to test
-        mIsPeripheralAttached = mOutputDevInfo != null && mInputDevInfo != null;
-        calculateTestPeripheral();
-        showConnectedAudioPeripheral();
-        calculateLatencyThresholds();
-        displayLatencyThresholds();
-    }
-
-    protected void handleDeviceConnection(AudioDeviceInfo deviceInfo) {
-        // NOP
-    }
-
-    private class ConnectListener extends AudioDeviceCallback {
-        /*package*/ ConnectListener() {}
-
-        //
-        // AudioDevicesManager.OnDeviceConnectionListener
-        //
-        @Override
-        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
-            scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
-        }
-
-        @Override
-        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
-            scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
-        }
-    }
-
-    private void calculateTestPeripheral() {
-        if (!mIsPeripheralAttached) {
-            if ((mOutputDevInfo != null && mInputDevInfo == null)
-                || (mOutputDevInfo == null && mInputDevInfo != null)) {
-                mTestPeripheral = TESTPERIPHERAL_INVALID;
-            } else {
-                mTestPeripheral = TESTPERIPHERAL_DEVICE;
-            }
-        } else if (!areIODevicesOnePeripheral()) {
-            mTestPeripheral = TESTPERIPHERAL_INVALID;
-        } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE ||
-                mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
-            mTestPeripheral = TESTPERIPHERAL_USB;
-        } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET ||
-                mInputDevInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) {
-            mTestPeripheral = TESTPERIPHERAL_ANALOG_JACK;
-        } else {
-            // Huh?
-            Log.e(TAG, "No valid peripheral found!?");
-            mTestPeripheral = TESTPERIPHERAL_NONE;
-        }
-    }
-
-    private boolean isPeripheralValidForTest() {
-        return mTestPeripheral == TESTPERIPHERAL_ANALOG_JACK
-                    || mTestPeripheral == TESTPERIPHERAL_USB;
-    }
-
-    private void showConnectedAudioPeripheral() {
-        mInputDeviceTxt.setText(
-                mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
-                        : "Not connected");
-        mOutputDeviceTxt.setText(
-                mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
-                        : "Not connected");
-
-        String pathName;
-        switch (mTestPeripheral) {
-            case TESTPERIPHERAL_INVALID:
-                pathName = "Invalid Test Peripheral";
-                break;
-
-            case TESTPERIPHERAL_ANALOG_JACK:
-                pathName = "Headset Jack";
-                break;
-
-            case TESTPERIPHERAL_USB:
-                pathName = "USB";
-                break;
-
-            case TESTPERIPHERAL_DEVICE:
-                pathName = "Device Speaker + Microphone";
-                break;
-
-            case TESTPERIPHERAL_NONE:
-            default:
-                pathName = "Error. Unknown Test Path";
-                break;
-        }
-        mTestPathTxt.setText(pathName);
-        mTestButton.setEnabled(
-                mTestPeripheral != TESTPERIPHERAL_INVALID && mTestPeripheral != TESTPERIPHERAL_NONE);
-
-    }
-
-    private boolean areIODevicesOnePeripheral() {
-        if (mOutputDevInfo == null || mInputDevInfo == null) {
-            return false;
-        }
-
-        return mOutputDevInfo.getProductName().toString().equals(
-                mInputDevInfo.getProductName().toString());
-    }
-
-    private void calculateLatencyThresholds() {
-        switch (mTestPeripheral) {
-            case TESTPERIPHERAL_ANALOG_JACK:
-                mRecommendedLatency = mClaimsProAudio
-                        ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
-                mMustLatency =  mClaimsProAudio
-                        ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_MUST_LATENCY_MS;
-                break;
-
-            case TESTPERIPHERAL_USB:
-                mRecommendedLatency = mClaimsProAudio
-                        ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
-                mMustLatency = mClaimsProAudio
-                        ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_MUST_LATENCY_MS;
-                break;
-
-            case TESTPERIPHERAL_DEVICE:
-                // This isn't a valid case so we won't pass it, but it can be run
-                mRecommendedLatency = mClaimsProAudio
-                        ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
-                mMustLatency = mClaimsProAudio
-                        ? PROAUDIO_RECOMMENDED_LATENCY_MS :BASIC_MUST_LATENCY_MS;
-                break;
-
-            case TESTPERIPHERAL_NONE:
-            default:
-                mRecommendedLatency = BASIC_RECOMMENDED_LATENCY_MS;
-                mMustLatency = BASIC_MUST_LATENCY_MS;
-                break;
-        }
-    }
-
-    private void displayLatencyThresholds() {
-        if (isPeripheralValidForTest()) {
-            ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText("" + mMustLatency);
-            ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(
-                    "" + mRecommendedLatency);
-        } else {
-            String naStr = getResources().getString(R.string.audio_proaudio_NA);
-            ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText(naStr);
-            ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(naStr);
-        }
-    }
-
-    /**
-     * refresh Audio Level seekbar and text
-     */
-    private void refreshLevel() {
-        int currentLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
-        mAudioLevelSeekbar.setProgress(currentLevel);
-
-        String levelText = String.format("%s: %d/%d",
-                getResources().getString(R.string.audio_loopback_level_text),
-                currentLevel, mMaxLevel);
-        mAudioLevelText.setText(levelText);
-    }
-
-    //
-    // show active progress bar
-    //
-    protected void showWait(boolean show) {
-        mProgressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
-    }
-
-    //
-    // Common loging
-    //
-    // Schema
-    private static final String KEY_LATENCY = "latency";
-    private static final String KEY_CONFIDENCE = "confidence";
-    private static final String KEY_SAMPLE_RATE = "sample_rate";
-    private static final String KEY_IS_PRO_AUDIO = "is_pro_audio";
-    private static final String KEY_IS_LOW_LATENCY = "is_low_latency";
-    private static final String KEY_IS_PERIPHERAL_ATTACHED = "is_peripheral_attached";
-    private static final String KEY_INPUT_PERIPHERAL_NAME = "input_peripheral";
-    private static final String KEY_OUTPUT_PERIPHERAL_NAME = "output_peripheral";
-    private static final String KEY_TEST_PERIPHERAL = "test_peripheral";
-    private static final String KEY_TEST_MMAP = "supports_mmap";
-    private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive";
-    private static final String KEY_LEVEL = "level";
-    private static final String KEY_BUFFER_SIZE = "buffer_size_in_frames";
-
-    @Override
-    public String getTestId() {
-        return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
-    }
-
-    //
-    // Subclasses should call this explicitly. SubClasses should call submit() after their logs
-    //
-    @Override
-    public void recordTestResults() {
-        if (mNativeAnalyzerThread == null) {
-            return; // no results to report
-        }
-
-        CtsVerifierReportLog reportLog = getReportLog();
-        reportLog.addValue(
-                KEY_LATENCY,
-                mMeanLatencyMillis,
-                ResultType.LOWER_BETTER,
-                ResultUnit.MS);
-
-        reportLog.addValue(
-                KEY_CONFIDENCE,
-                mMeanConfidence,
-                ResultType.HIGHER_BETTER,
-                ResultUnit.NONE);
-
-        reportLog.addValue(
-                KEY_SAMPLE_RATE,
-                mNativeAnalyzerThread.getSampleRate(),
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        reportLog.addValue(
-                KEY_IS_LOW_LATENCY,
-                mNativeAnalyzerThread.isLowLatencyStream(),
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        reportLog.addValue(
-                KEY_IS_PERIPHERAL_ATTACHED,
-                mIsPeripheralAttached,
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        reportLog.addValue(
-                KEY_IS_PRO_AUDIO,
-                mClaimsProAudio,
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        reportLog.addValue(
-                KEY_TEST_PERIPHERAL,
-                mTestPeripheral,
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        reportLog.addValue(
-                KEY_TEST_MMAP,
-                mSupportsMMAP,
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        reportLog.addValue(
-                KEY_TEST_MMAPEXCLUSIVE ,
-                mSupportsMMAPExclusive,
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        if (mIsPeripheralAttached) {
-            reportLog.addValue(
-                    KEY_INPUT_PERIPHERAL_NAME,
-                    mInputDevInfo != null ? mInputDevInfo.getProductName().toString() : "None",
-                    ResultType.NEUTRAL,
-                    ResultUnit.NONE);
-
-            reportLog.addValue(
-                    KEY_OUTPUT_PERIPHERAL_NAME,
-                    mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString() : "None",
-                    ResultType.NEUTRAL,
-                    ResultUnit.NONE);
-        }
-
-        int audioLevel = mAudioLevelSeekbar.getProgress();
-        reportLog.addValue(
-                KEY_LEVEL,
-                audioLevel,
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        reportLog.submit();
-    }
-
-    private void startAudioTest(Handler messageHandler) {
-        getPassButton().setEnabled(false);
-
-        mTestPhase = 0;
-        java.util.Arrays.fill(mLatencyMillis, 0.0);
-        java.util.Arrays.fill(mConfidence, 0.0);
-
-        mNativeAnalyzerThread = new NativeAnalyzerThread(this);
-        if (mNativeAnalyzerThread != null) {
-            mNativeAnalyzerThread.setMessageHandler(messageHandler);
-            // This value matches AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
-            mNativeAnalyzerThread.setInputPreset(MediaRecorder.AudioSource.VOICE_RECOGNITION);
-            startTestPhase();
-        } else {
-            Log.e(TAG, "Couldn't allocate native analyzer thread");
-            mResultText.setText(getResources().getString(R.string.audio_loopback_failure));
-        }
-    }
-
-    private void startTestPhase() {
-        if (mNativeAnalyzerThread != null) {
-            mNativeAnalyzerThread.startTest();
-
-            // what is this for?
-            try {
-                Thread.sleep(200);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    private void handleTestCompletion() {
-        mMeanLatencyMillis = StatUtils.calculateMean(mLatencyMillis);
-        mMeanAbsoluteDeviation =
-                StatUtils.calculateMeanAbsoluteDeviation(mMeanLatencyMillis, mLatencyMillis);
-        mMeanConfidence = StatUtils.calculateMean(mConfidence);
-
-        boolean pass = isPeripheralValidForTest()
-                && mMeanConfidence >= CONFIDENCE_THRESHOLD
-                && mMeanLatencyMillis > EPSILON
-                && mMeanLatencyMillis < mMustLatency;
-
-        getPassButton().setEnabled(pass);
-
-        String result;
-        if (mMeanConfidence < CONFIDENCE_THRESHOLD) {
-            result = String.format(
-                    "Test Finished\nInsufficient Confidence (%.2f < %.2f). No Results.",
-                    mMeanConfidence, CONFIDENCE_THRESHOLD);
-        } else {
-            result = String.format(
-                    "Test Finished - %s\nMean Latency:%.2f ms (required:%.2f)\n" +
-                            "Mean Absolute Deviation: %.2f\n" +
-                            " Confidence: %.2f\n" +
-                            " Low Latency Path: %s",
-                    (pass ? "PASS" : "FAIL"),
-                    mMeanLatencyMillis,
-                    mMustLatency,
-                    mMeanAbsoluteDeviation,
-                    mMeanConfidence,
-                    mNativeAnalyzerThread.isLowLatencyStream() ? mYesString : mNoString);
-        }
-
-        // Make sure the test thread is finished. It should already be done.
-        if (mNativeAnalyzerThread != null) {
-            try {
-                mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-        }
-        mResultText.setText(result);
-
-        recordTestResults();
-
-        showWait(false);
-        mTestButton.setEnabled(true);
-    }
-
-    private void handleTestPhaseCompletion() {
-        if (mNativeAnalyzerThread != null && mTestPhase < NUM_TEST_PHASES) {
-            mLatencyMillis[mTestPhase] = mNativeAnalyzerThread.getLatencyMillis();
-            mConfidence[mTestPhase] = mNativeAnalyzerThread.getConfidence();
-
-            String result = String.format(
-                    "Test %d Finished\nLatency: %.2f ms\nConfidence: %.2f\n",
-                    mTestPhase,
-                    mLatencyMillis[mTestPhase],
-                    mConfidence[mTestPhase]);
-
-            mResultText.setText(result);
-            try {
-                mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
-                // Thread.sleep(/*STOP_TEST_TIMEOUT_MSEC*/500);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-
-            mTestPhase++;
-            if (mTestPhase >= NUM_TEST_PHASES) {
-                handleTestCompletion();
-            } else {
-                startTestPhase();
-            }
-        }
-    }
-
-    /**
-     * handler for messages from audio thread
-     */
-    private Handler mMessageHandler = new Handler() {
-        public void handleMessage(Message msg) {
-            super.handleMessage(msg);
-            switch(msg.what) {
-                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED:
-                    Log.v(TAG,"got message native rec started!!");
-                    showWait(true);
-                    mResultText.setText(String.format("[phase: %d] - Test Running...",
-                            (mTestPhase + 1)));
-                    break;
-                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR:
-                    Log.v(TAG,"got message native rec can't start!!");
-                    mResultText.setText("Test Error opening streams.");
-                    handleTestCompletion();
-                    break;
-                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR:
-                    Log.v(TAG,"got message native rec can't start!!");
-                    mResultText.setText("Test Error while recording.");
-                    handleTestCompletion();
-                    break;
-                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS:
-                    mResultText.setText("Test FAILED due to errors.");
-                    handleTestCompletion();
-                    break;
-                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING:
-                    Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING");
-                    mResultText.setText(String.format("[phase: %d] - Analyzing ...",
-                            mTestPhase + 1));
-                    break;
-                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE:
-                    Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE");
-                    handleTestPhaseCompletion();
-                    break;
-                default:
-                    break;
-            }
-        }
-    };
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.audio_loopback_latency_activity);
-
-        setPassFailButtonClickListeners();
-        getPassButton().setEnabled(false);
-        setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1);
-
-        mClaimsOutput = AudioSystemFlags.claimsOutput(this);
-        mClaimsInput = AudioSystemFlags.claimsInput(this);
-        mClaimsProAudio = AudioSystemFlags.claimsProAudio(this);
-
-        mYesString = getResources().getString(R.string.audio_general_yes);
-        mNoString = getResources().getString(R.string.audio_general_no);
-
-        // Pro Audio
-        ((TextView)findViewById(R.id.audio_loopback_pro_audio)).setText(
-                "" + (mClaimsProAudio ? mYesString : mNoString));
-
-        // MMAP
-        ((TextView)findViewById(R.id.audio_loopback_mmap)).setText(
-                "" + (mSupportsMMAP ? mYesString : mNoString));
-        ((TextView)findViewById(R.id.audio_loopback_mmap_exclusive)).setText(
-                "" + (mSupportsMMAPExclusive ? mYesString : mNoString));
-
-        // Low Latency
-        ((TextView)findViewById(R.id.audio_loopback_low_latency)).setText(
-                "" + (AudioSystemFlags.claimsLowLatencyAudio(this) ? mYesString : mNoString));
-
-        mTestPathTxt = ((TextView)findViewById(R.id.audio_loopback_testpath));
-
-        mTestButton = (Button)findViewById(R.id.audio_loopback_test_btn);
-        mTestButton.setOnClickListener(mBtnClickListener);
-
-        mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
-
-        mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
-
-        connectLoopbackUI();
-
-        calculateLatencyThresholds();
-        displayLatencyThresholds();
-    }
-
-    private class OnBtnClickListener implements OnClickListener {
-        @Override
-        public void onClick(View v) {
-            switch (v.getId()) {
-                case R.id.audio_loopback_test_btn:
-                    Log.i(TAG, "audio loopback test");
-                    startAudioTest(mMessageHandler);
-                    break;
-            }
-        }
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
index 9490091..8ee3446 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,679 @@
 
 package com.android.cts.verifier.audio;
 
+import android.app.AlertDialog;
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.MediaRecorder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.cts.verifier.audio.audiolib.AudioSystemFlags;
+import com.android.cts.verifier.audio.audiolib.StatUtils;
+import com.android.cts.verifier.audio.audiolib.AudioUtils;
+import com.android.cts.verifier.CtsVerifierReportLog;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
+import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
+
 /**
- * Tests Audio Device roundtrip latency by using a loopback plug.
+ * CtsVerifier Audio Loopback Latency Test
  */
-public class AudioLoopbackLatencyActivity extends AudioLoopbackBaseActivity {
-    private static final String TAG = AudioLoopbackLatencyActivity.class.getSimpleName();
+public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity {
+    private static final String TAG = "AudioLoopbackLatencyActivity";
+
+    // JNI load
+    static {
+        try {
+            System.loadLibrary("audioloopback_jni");
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(TAG, "Error loading Audio Loopback JNI library");
+            Log.e(TAG, "e: " + e);
+            e.printStackTrace();
+        }
+
+        /* TODO: gracefully fail/notify if the library can't be loaded */
+    }
+    protected AudioManager mAudioManager;
+
+    // UI
+    TextView mInputDeviceTxt;
+    TextView mOutputDeviceTxt;
+
+    TextView mAudioLevelText;
+    SeekBar mAudioLevelSeekbar;
+
+    TextView mTestPathTxt;
+
+    TextView mResultText;
+    ProgressBar mProgressBar;
+    int mMaxLevel;
+
+    OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+    protected Button mTestButton;
+
+    String mYesString;
+    String mNoString;
+
+    // These flags determine the maximum allowed latency
+    private boolean mClaimsProAudio;
+    private boolean mClaimsOutput;
+    private boolean mClaimsInput;
+
+    // Useful info
+    private boolean mSupportsMMAP = AudioUtils.isMMapSupported();
+    private boolean mSupportsMMAPExclusive = AudioUtils.isMMapExclusiveSupported();
+
+    // Peripheral(s)
+    boolean mIsPeripheralAttached;  // CDD ProAudio section C-1-3
+    AudioDeviceInfo mOutputDevInfo;
+    AudioDeviceInfo mInputDevInfo;
+
+    protected static final int TESTPERIPHERAL_INVALID       = -1;
+    protected static final int TESTPERIPHERAL_NONE          = 0;
+    protected static final int TESTPERIPHERAL_ANALOG_JACK   = 1;
+    protected static final int TESTPERIPHERAL_USB           = 2;
+    protected static final int TESTPERIPHERAL_DEVICE        = 3; // device speaker + mic
+    protected int mTestPeripheral = TESTPERIPHERAL_NONE;
+
+    // Loopback Logic
+    NativeAnalyzerThread mNativeAnalyzerThread = null;
+
+    protected static final int NUM_TEST_PHASES = 5;
+    protected int mTestPhase = 0;
+
+    protected double[] mLatencyMillis = new double[NUM_TEST_PHASES];
+    protected double[] mConfidence = new double[NUM_TEST_PHASES];
+
+    protected double mMeanLatencyMillis;
+    protected double mMeanAbsoluteDeviation;
+    protected double mMeanConfidence;
+
+    protected static final double CONFIDENCE_THRESHOLD = 0.6;
+    // impossibly low latencies (indicating something in the test went wrong).
+    protected static final float EPSILON = 1.0f;
+    protected static final double PROAUDIO_RECOMMENDED_LATENCY_MS = 20.0;
+    protected static final double PROAUDIO_RECOMMENDED_USB_LATENCY_MS = 25.0;
+    protected static final double PROAUDIO_MUST_LATENCY_MS = 20.0;
+    protected static final double BASIC_RECOMMENDED_LATENCY_MS = 50.0;
+    protected static final double BASIC_MUST_LATENCY_MS = 800.0;
+    protected double mMustLatency;
+    protected double mRecommendedLatency;
+
+    // The audio stream callback threads should stop and close
+    // in less than a few hundred msec. This is a generous timeout value.
+    private static final int STOP_TEST_TIMEOUT_MSEC = 2 * 1000;
+
+    //
+    // Common UI Handling
+    //
+    private void connectLoopbackUI() {
+        // Connected Device
+        mInputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackInputLbl));
+        mOutputDeviceTxt = ((TextView)findViewById(R.id.audioLoopbackOutputLbl));
+
+        mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
+        mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
+        mMaxLevel = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+        mAudioLevelSeekbar.setMax(mMaxLevel);
+        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0);
+        refreshLevel();
+
+        mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {}
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {}
+
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
+                        progress, 0);
+                Log.i(TAG,"Level set to: " + progress);
+                refreshLevel();
+            }
+        });
+
+        mResultText = (TextView)findViewById(R.id.audio_loopback_results_text);
+        mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar);
+        showWait(false);
+    }
+
+    //
+    // Peripheral Connection Logic
+    //
+    protected void scanPeripheralList(AudioDeviceInfo[] devices) {
+        // CDD Section C-1-3: USB port, host-mode support
+
+        // Can't just use the first record because then we will only get
+        // Source OR sink, not both even on devices that are both.
+        mOutputDevInfo = null;
+        mInputDevInfo = null;
+
+        // Any valid peripherals
+        // Do we leave in the Headset test to support a USB-Dongle?
+        for (AudioDeviceInfo devInfo : devices) {
+            if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE || // USB Peripheral
+                    devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET || // USB dongle+LBPlug
+                    devInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET || // Loopback Plug?
+                    devInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) { // Aux-cable loopback?
+                if (devInfo.isSink()) {
+                    mOutputDevInfo = devInfo;
+                }
+                if (devInfo.isSource()) {
+                    mInputDevInfo = devInfo;
+                }
+            }  else {
+                handleDeviceConnection(devInfo);
+            }
+        }
+
+        // need BOTH input and output to test
+        mIsPeripheralAttached = mOutputDevInfo != null && mInputDevInfo != null;
+        calculateTestPeripheral();
+        showConnectedAudioPeripheral();
+        calculateLatencyThresholds();
+        displayLatencyThresholds();
+    }
+
+    protected void handleDeviceConnection(AudioDeviceInfo deviceInfo) {
+        // NOP
+    }
+
+    private class ConnectListener extends AudioDeviceCallback {
+        /*package*/ ConnectListener() {}
+
+        //
+        // AudioDevicesManager.OnDeviceConnectionListener
+        //
+        @Override
+        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+            scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+        }
+
+        @Override
+        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+            scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL));
+        }
+    }
+
+    private void calculateTestPeripheral() {
+        if (!mIsPeripheralAttached) {
+            if ((mOutputDevInfo != null && mInputDevInfo == null)
+                || (mOutputDevInfo == null && mInputDevInfo != null)) {
+                mTestPeripheral = TESTPERIPHERAL_INVALID;
+            } else {
+                mTestPeripheral = TESTPERIPHERAL_DEVICE;
+            }
+        } else if (!areIODevicesOnePeripheral()) {
+            mTestPeripheral = TESTPERIPHERAL_INVALID;
+        } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_DEVICE ||
+                mInputDevInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) {
+            mTestPeripheral = TESTPERIPHERAL_USB;
+        } else if (mInputDevInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET ||
+                mInputDevInfo.getType() == AudioDeviceInfo.TYPE_AUX_LINE) {
+            mTestPeripheral = TESTPERIPHERAL_ANALOG_JACK;
+        } else {
+            // Huh?
+            Log.e(TAG, "No valid peripheral found!?");
+            mTestPeripheral = TESTPERIPHERAL_NONE;
+        }
+    }
+
+    private boolean isPeripheralValidForTest() {
+        return mTestPeripheral == TESTPERIPHERAL_ANALOG_JACK
+                    || mTestPeripheral == TESTPERIPHERAL_USB;
+    }
+
+    private void showConnectedAudioPeripheral() {
+        mInputDeviceTxt.setText(
+                mInputDevInfo != null ? mInputDevInfo.getProductName().toString()
+                        : "Not connected");
+        mOutputDeviceTxt.setText(
+                mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString()
+                        : "Not connected");
+
+        String pathName;
+        switch (mTestPeripheral) {
+            case TESTPERIPHERAL_INVALID:
+                pathName = "Invalid Test Peripheral";
+                break;
+
+            case TESTPERIPHERAL_ANALOG_JACK:
+                pathName = "Headset Jack";
+                break;
+
+            case TESTPERIPHERAL_USB:
+                pathName = "USB";
+                break;
+
+            case TESTPERIPHERAL_DEVICE:
+                pathName = "Device Speaker + Microphone";
+                break;
+
+            case TESTPERIPHERAL_NONE:
+            default:
+                pathName = "Error. Unknown Test Path";
+                break;
+        }
+        mTestPathTxt.setText(pathName);
+        mTestButton.setEnabled(
+                mTestPeripheral != TESTPERIPHERAL_INVALID && mTestPeripheral != TESTPERIPHERAL_NONE);
+
+    }
+
+    private boolean areIODevicesOnePeripheral() {
+        if (mOutputDevInfo == null || mInputDevInfo == null) {
+            return false;
+        }
+
+        return mOutputDevInfo.getProductName().toString().equals(
+                mInputDevInfo.getProductName().toString());
+    }
+
+    private void calculateLatencyThresholds() {
+        switch (mTestPeripheral) {
+            case TESTPERIPHERAL_ANALOG_JACK:
+                mRecommendedLatency = mClaimsProAudio
+                        ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
+                mMustLatency =  mClaimsProAudio
+                        ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_MUST_LATENCY_MS;
+                break;
+
+            case TESTPERIPHERAL_USB:
+                mRecommendedLatency = mClaimsProAudio
+                        ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
+                mMustLatency = mClaimsProAudio
+                        ? PROAUDIO_RECOMMENDED_USB_LATENCY_MS : BASIC_MUST_LATENCY_MS;
+                break;
+
+            case TESTPERIPHERAL_DEVICE:
+                // This isn't a valid case so we won't pass it, but it can be run
+                mRecommendedLatency = mClaimsProAudio
+                        ? PROAUDIO_RECOMMENDED_LATENCY_MS : BASIC_RECOMMENDED_LATENCY_MS;
+                mMustLatency = mClaimsProAudio
+                        ? PROAUDIO_RECOMMENDED_LATENCY_MS :BASIC_MUST_LATENCY_MS;
+                break;
+
+            case TESTPERIPHERAL_NONE:
+            default:
+                mRecommendedLatency = BASIC_RECOMMENDED_LATENCY_MS;
+                mMustLatency = BASIC_MUST_LATENCY_MS;
+                break;
+        }
+    }
+
+    private void displayLatencyThresholds() {
+        if (isPeripheralValidForTest()) {
+            ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText("" + mMustLatency);
+            ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(
+                    "" + mRecommendedLatency);
+        } else {
+            String naStr = getResources().getString(R.string.audio_proaudio_NA);
+            ((TextView) findViewById(R.id.audio_loopback_must_latency)).setText(naStr);
+            ((TextView) findViewById(R.id.audio_loopback_recommended_latency)).setText(naStr);
+        }
+    }
+
+    /**
+     * refresh Audio Level seekbar and text
+     */
+    private void refreshLevel() {
+        int currentLevel = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+        mAudioLevelSeekbar.setProgress(currentLevel);
+
+        String levelText = String.format("%s: %d/%d",
+                getResources().getString(R.string.audio_loopback_level_text),
+                currentLevel, mMaxLevel);
+        mAudioLevelText.setText(levelText);
+    }
+
+    //
+    // show active progress bar
+    //
+    protected void showWait(boolean show) {
+        mProgressBar.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    //
+    // Common logging
+    //
+    // Schema
+    private static final String KEY_LATENCY = "latency";
+    private static final String KEY_CONFIDENCE = "confidence";
+    private static final String KEY_SAMPLE_RATE = "sample_rate";
+    private static final String KEY_IS_PRO_AUDIO = "is_pro_audio";
+    private static final String KEY_IS_LOW_LATENCY = "is_low_latency";
+    private static final String KEY_IS_PERIPHERAL_ATTACHED = "is_peripheral_attached";
+    private static final String KEY_INPUT_PERIPHERAL_NAME = "input_peripheral";
+    private static final String KEY_OUTPUT_PERIPHERAL_NAME = "output_peripheral";
+    private static final String KEY_TEST_PERIPHERAL = "test_peripheral";
+    private static final String KEY_TEST_MMAP = "supports_mmap";
+    private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive";
+    private static final String KEY_LEVEL = "level";
+    private static final String KEY_BUFFER_SIZE = "buffer_size_in_frames";
+
+    @Override
+    public String getTestId() {
+        return setTestNameSuffix(sCurrentDisplayMode, getClass().getName());
+    }
+
+    @Override
+    public String getReportFileName() { return PassFailButtons.AUDIO_TESTS_REPORT_LOG_NAME; }
+
+    @Override
+    public final String getReportSectionName() {
+        return setTestNameSuffix(sCurrentDisplayMode, "audio_loopback_latency_activity");
+    }
+
+    //
+    // Subclasses should call this explicitly. SubClasses should call submit() after their logs
+    //
+    @Override
+    public void recordTestResults() {
+        if (mNativeAnalyzerThread == null) {
+            return; // no results to report
+        }
+
+        CtsVerifierReportLog reportLog = getReportLog();
+        reportLog.addValue(
+                KEY_LATENCY,
+                mMeanLatencyMillis,
+                ResultType.LOWER_BETTER,
+                ResultUnit.MS);
+
+        reportLog.addValue(
+                KEY_CONFIDENCE,
+                mMeanConfidence,
+                ResultType.HIGHER_BETTER,
+                ResultUnit.NONE);
+
+        reportLog.addValue(
+                KEY_SAMPLE_RATE,
+                mNativeAnalyzerThread.getSampleRate(),
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+
+        reportLog.addValue(
+                KEY_IS_LOW_LATENCY,
+                mNativeAnalyzerThread.isLowLatencyStream(),
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+
+        reportLog.addValue(
+                KEY_IS_PERIPHERAL_ATTACHED,
+                mIsPeripheralAttached,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+
+        reportLog.addValue(
+                KEY_IS_PRO_AUDIO,
+                mClaimsProAudio,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+
+        reportLog.addValue(
+                KEY_TEST_PERIPHERAL,
+                mTestPeripheral,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+
+        reportLog.addValue(
+                KEY_TEST_MMAP,
+                mSupportsMMAP,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+
+        reportLog.addValue(
+                KEY_TEST_MMAPEXCLUSIVE ,
+                mSupportsMMAPExclusive,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+
+        if (mIsPeripheralAttached) {
+            reportLog.addValue(
+                    KEY_INPUT_PERIPHERAL_NAME,
+                    mInputDevInfo != null ? mInputDevInfo.getProductName().toString() : "None",
+                    ResultType.NEUTRAL,
+                    ResultUnit.NONE);
+
+            reportLog.addValue(
+                    KEY_OUTPUT_PERIPHERAL_NAME,
+                    mOutputDevInfo != null ? mOutputDevInfo.getProductName().toString() : "None",
+                    ResultType.NEUTRAL,
+                    ResultUnit.NONE);
+        }
+
+        int audioLevel = mAudioLevelSeekbar.getProgress();
+        reportLog.addValue(
+                KEY_LEVEL,
+                audioLevel,
+                ResultType.NEUTRAL,
+                ResultUnit.NONE);
+
+        reportLog.submit();
+    }
+
+    private void startAudioTest(Handler messageHandler) {
+        getPassButton().setEnabled(false);
+
+        mTestPhase = 0;
+        java.util.Arrays.fill(mLatencyMillis, 0.0);
+        java.util.Arrays.fill(mConfidence, 0.0);
+
+        mNativeAnalyzerThread = new NativeAnalyzerThread(this);
+        if (mNativeAnalyzerThread != null) {
+            mNativeAnalyzerThread.setMessageHandler(messageHandler);
+            // This value matches AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
+            mNativeAnalyzerThread.setInputPreset(MediaRecorder.AudioSource.VOICE_RECOGNITION);
+            startTestPhase();
+        } else {
+            Log.e(TAG, "Couldn't allocate native analyzer thread");
+            mResultText.setText(getResources().getString(R.string.audio_loopback_failure));
+        }
+    }
+
+    private void startTestPhase() {
+        if (mNativeAnalyzerThread != null) {
+            mNativeAnalyzerThread.startTest();
+
+            // what is this for?
+            try {
+                Thread.sleep(200);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void handleTestCompletion() {
+        mMeanLatencyMillis = StatUtils.calculateMean(mLatencyMillis);
+        mMeanAbsoluteDeviation =
+                StatUtils.calculateMeanAbsoluteDeviation(mMeanLatencyMillis, mLatencyMillis);
+        mMeanConfidence = StatUtils.calculateMean(mConfidence);
+
+        boolean pass = isPeripheralValidForTest()
+                && mMeanConfidence >= CONFIDENCE_THRESHOLD
+                && mMeanLatencyMillis > EPSILON
+                && mMeanLatencyMillis < mMustLatency;
+
+        getPassButton().setEnabled(pass);
+
+        String result;
+        if (mMeanConfidence < CONFIDENCE_THRESHOLD) {
+            result = String.format(
+                    "Test Finished\nInsufficient Confidence (%.2f < %.2f). No Results.",
+                    mMeanConfidence, CONFIDENCE_THRESHOLD);
+        } else {
+            result = String.format(
+                    "Test Finished - %s\nMean Latency:%.2f ms (required:%.2f)\n" +
+                            "Mean Absolute Deviation: %.2f\n" +
+                            " Confidence: %.2f\n" +
+                            " Low Latency Path: %s",
+                    (pass ? "PASS" : "FAIL"),
+                    mMeanLatencyMillis,
+                    mMustLatency,
+                    mMeanAbsoluteDeviation,
+                    mMeanConfidence,
+                    mNativeAnalyzerThread.isLowLatencyStream() ? mYesString : mNoString);
+        }
+
+        // Make sure the test thread is finished. It should already be done.
+        if (mNativeAnalyzerThread != null) {
+            try {
+                mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        mResultText.setText(result);
+
+        recordTestResults();
+
+        showWait(false);
+        mTestButton.setEnabled(true);
+    }
+
+    private void handleTestPhaseCompletion() {
+        if (mNativeAnalyzerThread != null && mTestPhase < NUM_TEST_PHASES) {
+            mLatencyMillis[mTestPhase] = mNativeAnalyzerThread.getLatencyMillis();
+            mConfidence[mTestPhase] = mNativeAnalyzerThread.getConfidence();
+
+            String result = String.format(
+                    "Test %d Finished\nLatency: %.2f ms\nConfidence: %.2f\n",
+                    mTestPhase,
+                    mLatencyMillis[mTestPhase],
+                    mConfidence[mTestPhase]);
+
+            mResultText.setText(result);
+            try {
+                mNativeAnalyzerThread.stopTest(STOP_TEST_TIMEOUT_MSEC);
+                // Thread.sleep(/*STOP_TEST_TIMEOUT_MSEC*/500);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+
+            mTestPhase++;
+            if (mTestPhase >= NUM_TEST_PHASES) {
+                handleTestCompletion();
+            } else {
+                startTestPhase();
+            }
+        }
+    }
+
+    /**
+     * handler for messages from audio thread
+     */
+    private Handler mMessageHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+            switch(msg.what) {
+                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED:
+                    Log.v(TAG,"got message native rec started!!");
+                    showWait(true);
+                    mResultText.setText(String.format("[phase: %d] - Test Running...",
+                            (mTestPhase + 1)));
+                    break;
+                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_OPEN_ERROR:
+                    Log.v(TAG,"got message native rec can't start!!");
+                    mResultText.setText("Test Error opening streams.");
+                    handleTestCompletion();
+                    break;
+                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR:
+                    Log.v(TAG,"got message native rec can't start!!");
+                    mResultText.setText("Test Error while recording.");
+                    handleTestCompletion();
+                    break;
+                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS:
+                    mResultText.setText("Test FAILED due to errors.");
+                    handleTestCompletion();
+                    break;
+                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING:
+                    Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_ANALYZING");
+                    mResultText.setText(String.format("[phase: %d] - Analyzing ...",
+                            mTestPhase + 1));
+                    break;
+                case NativeAnalyzerThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE:
+                    Log.i(TAG, "NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE");
+                    handleTestPhaseCompletion();
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.audio_loopback_latency_activity);
+
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1);
+
+        mClaimsOutput = AudioSystemFlags.claimsOutput(this);
+        mClaimsInput = AudioSystemFlags.claimsInput(this);
+        mClaimsProAudio = AudioSystemFlags.claimsProAudio(this);
+
+        mYesString = getResources().getString(R.string.audio_general_yes);
+        mNoString = getResources().getString(R.string.audio_general_no);
+
+        // Pro Audio
+        ((TextView)findViewById(R.id.audio_loopback_pro_audio)).setText(
+                "" + (mClaimsProAudio ? mYesString : mNoString));
+
+        // MMAP
+        ((TextView)findViewById(R.id.audio_loopback_mmap)).setText(
+                "" + (mSupportsMMAP ? mYesString : mNoString));
+        ((TextView)findViewById(R.id.audio_loopback_mmap_exclusive)).setText(
+                "" + (mSupportsMMAPExclusive ? mYesString : mNoString));
+
+        // Low Latency
+        ((TextView)findViewById(R.id.audio_loopback_low_latency)).setText(
+                "" + (AudioSystemFlags.claimsLowLatencyAudio(this) ? mYesString : mNoString));
+
+        mTestPathTxt = ((TextView)findViewById(R.id.audio_loopback_testpath));
+
+        mTestButton = (Button)findViewById(R.id.audio_loopback_test_btn);
+        mTestButton.setOnClickListener(mBtnClickListener);
+
+        mAudioManager = getSystemService(AudioManager.class);
+
+        mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler());
+
+        connectLoopbackUI();
+
+        calculateLatencyThresholds();
+        displayLatencyThresholds();
+    }
+
+    private class OnBtnClickListener implements OnClickListener {
+        @Override
+        public void onClick(View v) {
+            switch (v.getId()) {
+                case R.id.audio_loopback_test_btn:
+                    Log.i(TAG, "audio loopback test");
+                    startAudioTest(mMessageHandler);
+                    break;
+            }
+        }
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
index d32749f..9804cd3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
@@ -67,6 +67,7 @@
     private static final String INFO_DIALOG_MESSAGE_ID = "infoDialogMessageId";
 
     // ReportLog Schema
+    private static final String SECTION_PRO_AUDIO_ACTIVITY = "pro_audio_activity";
     private static final String KEY_CLAIMS_PRO = "claims_pro_audio";
     private static final String KEY_CLAIMS_LOW_LATENCY = "claims_low_latency_audio";
     private static final String KEY_CLAIMS_MIDI = "claims_midi";
@@ -232,6 +233,14 @@
     // PassFailButtons Overrides
     //
     @Override
+    public String getReportFileName() { return PassFailButtons.AUDIO_TESTS_REPORT_LOG_NAME; }
+
+    @Override
+    public final String getReportSectionName() {
+        return setTestNameSuffix(sCurrentDisplayMode, SECTION_PRO_AUDIO_ACTIVITY);
+    }
+
+    @Override
     public void recordTestResults() {
 
         CtsVerifierReportLog reportLog = getReportLog();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BackgroundRfcommTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BackgroundRfcommTestActivity.java
new file mode 100644
index 0000000..feb8ce5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BackgroundRfcommTestActivity.java
@@ -0,0 +1,157 @@
+/*
+ * 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.cts.verifier.bluetooth;
+
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothSocket;
+import android.bluetooth.BluetoothStatusCodes;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.UUID;
+
+/**
+ * Activity for verifying the handoff of RFCOMM sockets from the bluetooth manager to the app
+ * holding the car projection role. This test expects a second device to run the {@link
+ * BackgroundRfcommTestClientActivity}.
+ */
+public class BackgroundRfcommTestActivity extends PassFailButtons.Activity {
+    private static final String TAG = "BT.CarProjectionTest";
+    private static final String ACTION = "BT_BACKGROUND_RFCOMM_TEST_ACTION";
+
+    private BluetoothAdapter mBluetoothAdapter;
+    private UUID mUuid;
+    private String mTestMessage;
+    private BroadcastReceiver mReceiver;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.bt_background_rfcomm);
+        getPassButton().setEnabled(false);
+
+        BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class);
+        mBluetoothAdapter = bluetoothManager.getAdapter();
+        mUuid = UUID.fromString(getString(R.string.bt_background_rfcomm_test_uuid));
+        mTestMessage = getString(R.string.bt_background_rfcomm_test_message);
+        mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                writeAndReadRfcommData();
+            }
+        };
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION);
+
+        registerReceiver(mReceiver, intentFilter);
+
+        Intent intent = new Intent(ACTION);
+        intent.putExtra(BluetoothAdapter.EXTRA_RFCOMM_LISTENER_ID, mUuid.toString());
+
+        PendingIntent pendingIntent =
+                PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+
+        mBluetoothAdapter.closeRfcommServer(mUuid);
+
+        if (mBluetoothAdapter.startRfcommServer("TestBackgroundRfcomm", mUuid, pendingIntent)
+                != BluetoothStatusCodes.SUCCESS) {
+            Log.e(TAG, "Failed to start RFCOMM listener");
+            setTestResultAndFinish(false);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        mBluetoothAdapter.closeRfcommServer(mUuid);
+        if (mReceiver != null) {
+            unregisterReceiver(mReceiver);
+            mReceiver = null;
+        }
+        super.onDestroy();
+    }
+
+    private void writeAndReadRfcommData() {
+        BluetoothSocket bluetoothSocket = mBluetoothAdapter.retrieveConnectedRfcommSocket(mUuid);
+
+        if (bluetoothSocket == null) {
+            Log.e(TAG, "Failed to retrieve incoming RFCOMM socket connection");
+            setTestResultAndFinish(false);
+            return;
+        }
+
+        updateInstructions(R.string.bt_background_rfcomm_test_socket_received);
+
+        try {
+            updateInstructions(R.string.bt_background_rfcomm_test_sending_message);
+            bluetoothSocket.getOutputStream().write(mTestMessage.getBytes(StandardCharsets.UTF_8));
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to write test message to RFCOMM socket", e);
+            setTestResultAndFinish(false);
+            return;
+        }
+
+        int offset = 0;
+        int length = mTestMessage.length();
+        ByteBuffer buf = ByteBuffer.allocate(length);
+
+        try {
+            updateInstructions(R.string.bt_background_rfcomm_test_waiting_for_message);
+            while (length > 0) {
+                int numRead = bluetoothSocket.getInputStream().read(buf.array(), offset, length);
+                if (numRead == -1) {
+                    break;
+                }
+
+                offset += numRead;
+                length -= numRead;
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "RFCOMM read failed", e);
+            setTestResultAndFinish(false);
+            return;
+        }
+
+        String receivedMessage = new String(buf.array());
+
+        if (receivedMessage.equals(mTestMessage)) {
+            setTestResultAndFinish(true);
+        } else {
+            Log.e(TAG, "Incorrect RFCOMM message received from client");
+            setTestResultAndFinish(false);
+        }
+    }
+
+
+    private void updateInstructions(int id) {
+        TextView textView = findViewById(R.id.bt_background_rfcomm_text);
+        textView.setText(id);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BackgroundRfcommTestClientActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BackgroundRfcommTestClientActivity.java
new file mode 100644
index 0000000..c957355
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BackgroundRfcommTestClientActivity.java
@@ -0,0 +1,184 @@
+/*
+ * 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.cts.verifier.bluetooth;
+
+import static android.bluetooth.BluetoothDevice.ACTION_UUID;
+import static android.bluetooth.BluetoothDevice.EXTRA_UUID;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothSocket;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.ParcelUuid;
+import android.os.Parcelable;
+import android.util.Log;
+import android.widget.TextView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Activity which connects to an RFCOMM server which is advertising a sample UUID which is known for
+ * car projection. This test should be run in conjunction with another device which is bonded to
+ * this one and is running the {@link BackgroundRfcommTestActivity}.
+ */
+public class BackgroundRfcommTestClientActivity extends PassFailButtons.Activity {
+    private static final String TAG = "BT.CarProjectionClient";
+
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothSocket mBluetoothSocket;
+    private BroadcastReceiver mSdpReceiver;
+    private String mTestMessage;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.bt_background_rfcomm_client);
+        getPassButton().setEnabled(false);
+
+        mBluetoothAdapter = getSystemService(BluetoothManager.class).getAdapter();
+        mTestMessage = getString(R.string.bt_background_rfcomm_test_message);
+
+        UUID uuid = UUID.fromString(getString(R.string.bt_background_rfcomm_test_uuid));
+        List<BluetoothDevice> connectedDevices =
+                mBluetoothAdapter.getMostRecentlyConnectedDevices();
+
+        if (connectedDevices.isEmpty()) {
+            Log.e(TAG, "No bluetooth devices connected");
+            setTestResultAndFinish(false);
+            return;
+        }
+
+        BluetoothDevice connectedDevice = connectedDevices.get(0);
+
+        mSdpReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent.hasExtra(EXTRA_UUID)) {
+                    Parcelable[] uuids = intent.getParcelableArrayExtra(EXTRA_UUID);
+
+                    for (Parcelable parcelUuid : uuids) {
+                        if (((ParcelUuid) parcelUuid).getUuid().equals(uuid)) {
+                            try {
+                                updateInstructions(
+                                        R.string.bt_background_rfcomm_test_connecting_to_server);
+                                mBluetoothSocket =
+                                        connectedDevice.createRfcommSocketToServiceRecord(uuid);
+                            } catch (IOException e) {
+                                Log.e(TAG, "Failed to create RFCOMM socket connection", e);
+                            }
+
+                            readAndWriteRfcommData();
+                            return;
+                        }
+                    }
+                }
+
+                Log.e(TAG, "Expected UUID not found for device.");
+                setTestResultAndFinish(false);
+            }
+        };
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION_UUID);
+
+        registerReceiver(mSdpReceiver, intentFilter);
+
+        updateInstructions(R.string.bt_background_rfcomm_test_doing_sdp);
+        connectedDevice.fetchUuidsWithSdp();
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mSdpReceiver != null) {
+            unregisterReceiver(mSdpReceiver);
+        }
+
+        if (mBluetoothSocket != null && mBluetoothSocket.isConnected()) {
+            try {
+                mBluetoothSocket.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Failed to close RFCOMM socket connection", e);
+            }
+        }
+        super.onDestroy();
+    }
+
+    private void readAndWriteRfcommData() {
+        if (mBluetoothSocket == null) {
+            setTestResultAndFinish(false);
+            return;
+        }
+
+        int offset = 0;
+        int length = mTestMessage.length();
+        ByteBuffer buf = ByteBuffer.allocate(length);
+
+        try {
+            updateInstructions(R.string.bt_background_rfcomm_test_waiting_for_message);
+            mBluetoothSocket.connect();
+            while (length > 0) {
+                int numRead = mBluetoothSocket.getInputStream().read(buf.array(), offset, length);
+                if (numRead == -1) {
+                    break;
+                }
+
+                offset += numRead;
+                length -= numRead;
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "RFCOMM read failed", e);
+            setTestResultAndFinish(false);
+            return;
+        }
+
+        String receivedMessage = new String(buf.array());
+
+        boolean success = receivedMessage.equals(mTestMessage);
+
+        if (success) {
+            try {
+                updateInstructions(R.string.bt_background_rfcomm_test_sending_message);
+                mBluetoothSocket.getOutputStream().write(
+                        mTestMessage.getBytes(StandardCharsets.UTF_8));
+            } catch (IOException e) {
+                success = false;
+                Log.e(TAG, "RFCOMM write failed", e);
+            }
+        } else {
+            Log.e(TAG, "Incorrect RFCOMM message received from server");
+        }
+
+        setTestResultAndFinish(success);
+    }
+
+    private void updateInstructions(int id) {
+        TextView textView = findViewById(R.id.bt_background_rfcomm_client_text);
+        textView.setText(id);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java
new file mode 100644
index 0000000..e02b122
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import static android.bluetooth.le.AdvertisingSetCallback.ADVERTISE_SUCCESS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertisingSet;
+import android.bluetooth.le.AdvertisingSetCallback;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ListView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests {@link AdvertisingSet} and {@link AdvertisingSetCallback}.
+ */
+public class BleAdvertisingSetTestActivity extends PassFailButtons.Activity {
+
+    private static final String TAG = "BleAdvertisingSetTestActivity";
+    private static final int TIMEOUT_MS = 5000;
+
+    private static final int PASS_FLAG_START = 0x1;
+    private static final int PASS_FLAG_ENABLE_DISABLE = 0x2;
+    private static final int PASS_FLAG_SET_ADVERTISING_DATA = 0x4;
+    private static final int PASS_FLAG_SET_ADVERTISING_PARAMS = 0x8;
+    private static final int PASS_FLAG_SET_PERIODIC_ADVERTISING_DATA = 0x10;
+    private static final int PASS_FLAG_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED = 0x20;
+    private static final int PASS_FLAG_SET_PERIODIC_ADVERTISING_PARAMS = 0x40;
+    private static final int PASS_FLAG_SET_SCAN_RESPONSE_DATA = 0x80;
+    private static final int PASS_FLAG_STOP = 0x100;
+    private static final int PASS_FLAG_ALL = 0x1FF;
+
+    private static final int TEST_ADAPTER_INDEX_START = 0;
+    private static final int TEST_ADAPTER_INDEX_ENABLE_DISABLE = 1;
+    private static final int TEST_ADAPTER_INDEX_SET_ADVERTISING_DATA = 2;
+    private static final int TEST_ADAPTER_INDEX_SET_ADVERTISING_PARAMS = 3;
+    private static final int TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_DATA = 4;
+    private static final int TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED = 5;
+    private static final int TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_PARAMS = 6;
+    private static final int TEST_ADAPTER_INDEX_SET_SCAN_RESPONSE_DATA = 7;
+    private static final int TEST_ADAPTER_INDEX_STOP = 8;
+
+    private static final AdvertisingSetParameters ADVERTISING_SET_PARAMETERS =
+            new AdvertisingSetParameters.Builder().setLegacyMode(true).build();
+
+    private BluetoothManager mBluetoothManager;
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothLeAdvertiser mAdvertiser;
+    private TestAdvertisingSetCallback mCallback;
+
+    private TestAdapter mTestAdapter;
+    private Button mStartTestButton;
+
+    private long mAllTestsPassed;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_advertising_set);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_advertising_set_test_name,
+                R.string.ble_advertising_set_test_info, -1);
+        getPassButton().setEnabled(false);
+
+        mTestAdapter = new TestAdapter(this, setupTestList());
+        ListView listView = findViewById(R.id.ble_advertising_set_tests);
+        listView.setAdapter(mTestAdapter);
+
+        mStartTestButton = findViewById(R.id.ble_advertising_set_start_test);
+        mStartTestButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mStartTestButton.setEnabled(false);
+                mStartTestButton.setText(R.string.ble_advertising_set_running_test);
+
+                HandlerThread testHandlerThread = new HandlerThread("TestHandlerThread");
+                testHandlerThread.start();
+                Handler handler = new Handler(testHandlerThread.getLooper());
+                handler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (!mBluetoothAdapter.isEnabled()) {
+                            assertTrue(BtAdapterUtils.enableAdapter(mBluetoothAdapter,
+                                    BleAdvertisingSetTestActivity.this));
+                            // If BluetoothAdapter was previously not enabled, we need to get the
+                            // BluetoothLeAdvertiser instance again.
+                            mAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
+                        }
+
+                        try {
+                            startAdvertisingSet();
+                            testEnableAndDisableAdvertising();
+                            testSetAdvertisingData();
+                            testSetAdvertisingParameters();
+                            testPeriodicAdvertising();
+                            testSetScanResponseData();
+                            stopAdvertisingSet();
+                        } catch (InterruptedException e) {
+                            Log.e(TAG, "Interrupted while running tests", e);
+                        } catch (AssertionError e) {
+                            Log.e(TAG, "Test failed", e);
+                        }
+
+                        // Disable bluetooth adapter
+                        assertTrue(BtAdapterUtils.disableAdapter(mBluetoothAdapter,
+                                BleAdvertisingSetTestActivity.this));
+
+                        BleAdvertisingSetTestActivity.this.runOnUiThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                mStartTestButton.setText(
+                                        R.string.ble_advertising_set_finished_test);
+
+                                // Update test list to reflect whether the tests passed or not.
+                                mTestAdapter.notifyDataSetChanged();
+
+                                if (mAllTestsPassed == PASS_FLAG_ALL) {
+                                    getPassButton().setEnabled(true);
+                                }
+                            }
+                        });
+                    }
+                });
+            }
+        });
+
+        mAllTestsPassed = 0;
+
+        mBluetoothManager = getSystemService(BluetoothManager.class);
+        mBluetoothAdapter = mBluetoothManager.getAdapter();
+        mAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
+        mCallback = new TestAdvertisingSetCallback();
+    }
+
+    private void startAdvertisingSet() throws InterruptedException {
+        mAdvertiser.startAdvertisingSet(ADVERTISING_SET_PARAMETERS,
+                /* advertiseData= */ null, /* scanResponse= */ null,
+                /* periodicParameters= */ null, /* periodicData= */ null,
+                mCallback);
+        assertTrue(mCallback.mAdvertisingSetStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingSetStartedStatus.get());
+        assertNotNull(mCallback.mAdvertisingSet);
+
+        mAllTestsPassed |= PASS_FLAG_START;
+        mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_START);
+    }
+
+    private void testEnableAndDisableAdvertising() throws InterruptedException {
+        mCallback.reset();
+
+        mCallback.mAdvertisingSet.get().enableAdvertising(/* enable= */ true, /* duration= */ 1,
+                /* maxExtendedAdvertisingEvents= */ 1);
+        assertTrue(mCallback.mAdvertisingEnabledLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingEnabledStatus.get());
+
+        mCallback.mAdvertisingSet.get().enableAdvertising(/* enable= */ false, /* duration= */ 1,
+                /* maxExtendedAdvertisingEvents= */ 1);
+        assertTrue(mCallback.mAdvertisingDisabledLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingDisabledStatus.get());
+
+        mAllTestsPassed |= PASS_FLAG_ENABLE_DISABLE;
+        mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_ENABLE_DISABLE);
+    }
+
+    private void testSetAdvertisingData() throws InterruptedException {
+        mCallback.reset();
+
+        mCallback.mAdvertisingSet.get().setAdvertisingData(new AdvertiseData.Builder().build());
+        assertTrue(mCallback.mAdvertisingDataSetLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingDataSetStatus.get());
+
+        mAllTestsPassed |= PASS_FLAG_SET_ADVERTISING_DATA;
+        mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_ADVERTISING_DATA);
+    }
+
+    private void testSetScanResponseData() throws InterruptedException {
+        mCallback.reset();
+
+        mCallback.mAdvertisingSet.get().setScanResponseData(new AdvertiseData.Builder().build());
+        assertTrue(mCallback.mScanResponseDataSetLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mScanResponseDataSetStatus.get());
+
+        mAllTestsPassed |= PASS_FLAG_SET_SCAN_RESPONSE_DATA;
+        mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_SCAN_RESPONSE_DATA);
+    }
+
+    private void testSetAdvertisingParameters() throws InterruptedException {
+        mCallback.reset();
+
+        mCallback.mAdvertisingSet.get().enableAdvertising(false, /* duration= */ 1,
+                /* maxExtendedAdvertisingEvents= */1);
+        assertTrue(mCallback.mAdvertisingDisabledLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingDisabledStatus.get());
+
+        mCallback.mAdvertisingSet.get().setAdvertisingParameters(
+                new AdvertisingSetParameters.Builder()
+                        .setLegacyMode(true)
+                        .setScannable(false)
+                        .build());
+        assertTrue(mCallback.mAdvertisingParametersUpdatedLatch.await(TIMEOUT_MS,
+                TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingParametersUpdatedStatus.get());
+
+        mAllTestsPassed |= PASS_FLAG_SET_ADVERTISING_PARAMS;
+        mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_ADVERTISING_PARAMS);
+    }
+
+    // The following order of commands follows the diagram of Bluetooth Core Specification,
+    // Version 5.3, Vol 6, Part D, Figure 3.7: Periodic advertising.
+    private void testPeriodicAdvertising() throws InterruptedException {
+        mCallback.reset();
+
+        mCallback.mAdvertisingSet.get().setAdvertisingParameters(
+                new AdvertisingSetParameters.Builder().build());
+        assertTrue(mCallback.mAdvertisingParametersUpdatedLatch.await(TIMEOUT_MS,
+                TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingParametersUpdatedStatus.get());
+
+        mCallback.mAdvertisingSet.get().setPeriodicAdvertisingParameters(
+                new PeriodicAdvertisingParameters.Builder().build());
+        assertTrue(mCallback.mPeriodicAdvertisingParamsUpdatedLatch.await(TIMEOUT_MS,
+                TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mPeriodicAdvertisingParamsUpdatedStatus.get());
+
+        mAllTestsPassed |= PASS_FLAG_SET_PERIODIC_ADVERTISING_PARAMS;
+        mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_PARAMS);
+
+        mCallback.mAdvertisingSet.get().setPeriodicAdvertisingEnabled(true);
+        assertTrue(mCallback.mPeriodicAdvertisingEnabledLatch.await(TIMEOUT_MS,
+                TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mPeriodicAdvertisingEnabledStatus.get());
+
+        mCallback.mAdvertisingSet.get().setPeriodicAdvertisingData(new AdvertiseData.Builder()
+                .build());
+        assertTrue(mCallback.mPeriodicAdvertisingDataSetLatch.await(TIMEOUT_MS,
+                TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mPeriodicAdvertisingDataSetStatus.get());
+
+        mAllTestsPassed |= PASS_FLAG_SET_PERIODIC_ADVERTISING_DATA;
+        mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_DATA);
+
+        mCallback.mAdvertisingSet.get().setPeriodicAdvertisingEnabled(false);
+        assertTrue(mCallback.mPeriodicAdvertisingDisabledLatch.await(TIMEOUT_MS,
+                TimeUnit.MILLISECONDS));
+        assertEquals(ADVERTISE_SUCCESS, mCallback.mPeriodicAdvertisingDisabledStatus.get());
+
+        mAllTestsPassed |= PASS_FLAG_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED;
+        mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED);
+    }
+
+    private void stopAdvertisingSet() throws InterruptedException {
+        mAdvertiser.stopAdvertisingSet(mCallback);
+        assertTrue(mCallback.mAdvertisingSetStoppedLatch.await(TIMEOUT_MS,
+                TimeUnit.MILLISECONDS));
+
+        mAllTestsPassed |= PASS_FLAG_STOP;
+        mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_STOP);
+    }
+
+    private List<Integer> setupTestList() {
+        List<Integer> testList = new ArrayList<>();
+        testList.add(R.string.ble_advertising_set_start);
+        testList.add(R.string.ble_advertising_set_enable_disable);
+        testList.add(R.string.ble_advertising_set_advertising_data);
+        testList.add(R.string.ble_advertising_set_advertising_params);
+        testList.add(R.string.ble_advertising_set_periodic_advertising_data);
+        testList.add(R.string.ble_advertising_set_periodic_advertising_enabled_disabled);
+        testList.add(R.string.ble_advertising_set_periodic_advertising_params);
+        testList.add(R.string.ble_advertising_set_scan_response_data);
+        testList.add(R.string.ble_advertising_set_stop);
+        return testList;
+    }
+
+    private static class TestAdvertisingSetCallback extends AdvertisingSetCallback {
+        public CountDownLatch mAdvertisingSetStartedLatch = new CountDownLatch(1);
+        public CountDownLatch mAdvertisingEnabledLatch = new CountDownLatch(1);
+        public CountDownLatch mAdvertisingDisabledLatch = new CountDownLatch(1);
+        public CountDownLatch mAdvertisingParametersUpdatedLatch = new CountDownLatch(1);
+        public CountDownLatch mAdvertisingDataSetLatch = new CountDownLatch(1);
+        public CountDownLatch mScanResponseDataSetLatch = new CountDownLatch(1);
+        public CountDownLatch mAdvertisingSetStoppedLatch = new CountDownLatch(1);
+        public CountDownLatch mPeriodicAdvertisingEnabledLatch = new CountDownLatch(1);
+        public CountDownLatch mPeriodicAdvertisingDisabledLatch = new CountDownLatch(1);
+        public CountDownLatch mPeriodicAdvertisingParamsUpdatedLatch = new CountDownLatch(1);
+        public CountDownLatch mPeriodicAdvertisingDataSetLatch = new CountDownLatch(1);
+
+        public AtomicInteger mAdvertisingSetStartedStatus = new AtomicInteger();
+        public AtomicInteger mAdvertisingEnabledStatus = new AtomicInteger();
+        public AtomicInteger mAdvertisingDisabledStatus = new AtomicInteger();
+        public AtomicInteger mAdvertisingParametersUpdatedStatus = new AtomicInteger();
+        public AtomicInteger mAdvertisingDataSetStatus = new AtomicInteger();
+        public AtomicInteger mScanResponseDataSetStatus = new AtomicInteger();
+        public AtomicInteger mPeriodicAdvertisingEnabledStatus = new AtomicInteger();
+        public AtomicInteger mPeriodicAdvertisingDisabledStatus = new AtomicInteger();
+        public AtomicInteger mPeriodicAdvertisingParamsUpdatedStatus = new AtomicInteger();
+        public AtomicInteger mPeriodicAdvertisingDataSetStatus = new AtomicInteger();
+
+        public AtomicReference<AdvertisingSet> mAdvertisingSet = new AtomicReference();
+
+        @Override
+        public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower,
+                int status) {
+            super.onAdvertisingSetStarted(advertisingSet, txPower, status);
+            mAdvertisingSetStartedStatus.set(status);
+            mAdvertisingSet.set(advertisingSet);
+            mAdvertisingSetStartedLatch.countDown();
+        }
+
+        @Override
+        public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable,
+                int status) {
+            super.onAdvertisingEnabled(advertisingSet, enable, status);
+            if (enable) {
+                mAdvertisingEnabledStatus.set(status);
+                mAdvertisingEnabledLatch.countDown();
+            } else {
+                mAdvertisingDisabledStatus.set(status);
+                mAdvertisingDisabledLatch.countDown();
+            }
+        }
+
+        @Override
+        public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet, int txPower,
+                int status) {
+            super.onAdvertisingParametersUpdated(advertisingSet, txPower, status);
+            mAdvertisingParametersUpdatedStatus.set(status);
+            mAdvertisingParametersUpdatedLatch.countDown();
+        }
+
+        @Override
+        public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {
+            super.onAdvertisingDataSet(advertisingSet, status);
+            mAdvertisingDataSetStatus.set(status);
+            mAdvertisingDataSetLatch.countDown();
+        }
+
+        @Override
+        public void onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet,
+                int status) {
+            super.onPeriodicAdvertisingParametersUpdated(advertisingSet, status);
+            mPeriodicAdvertisingParamsUpdatedStatus.set(status);
+            mPeriodicAdvertisingParamsUpdatedLatch.countDown();
+        }
+
+        @Override
+        public void onPeriodicAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {
+            super.onPeriodicAdvertisingDataSet(advertisingSet, status);
+            mPeriodicAdvertisingDataSetStatus.set(status);
+            mPeriodicAdvertisingDataSetLatch.countDown();
+        }
+
+        @Override
+        public void onPeriodicAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable,
+                int status) {
+            super.onPeriodicAdvertisingEnabled(advertisingSet, enable, status);
+            if (enable) {
+                mPeriodicAdvertisingEnabledStatus.set(status);
+                mPeriodicAdvertisingEnabledLatch.countDown();
+            } else {
+                mPeriodicAdvertisingDisabledStatus.set(status);
+                mPeriodicAdvertisingDisabledLatch.countDown();
+            }
+        }
+
+        @Override
+        public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {
+            super.onScanResponseDataSet(advertisingSet, status);
+            mScanResponseDataSetStatus.set(status);
+            mScanResponseDataSetLatch.countDown();
+        }
+
+        @Override
+        public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
+            super.onAdvertisingSetStopped(advertisingSet);
+            mAdvertisingSetStoppedLatch.countDown();
+        }
+
+        public void reset() {
+            mAdvertisingSetStartedLatch = new CountDownLatch(1);
+            mAdvertisingEnabledLatch = new CountDownLatch(1);
+            mAdvertisingParametersUpdatedLatch = new CountDownLatch(1);
+            mAdvertisingDataSetLatch = new CountDownLatch(1);
+            mPeriodicAdvertisingParamsUpdatedLatch = new CountDownLatch(1);
+            mPeriodicAdvertisingDataSetLatch = new CountDownLatch(1);
+            mPeriodicAdvertisingEnabledLatch = new CountDownLatch(1);
+            mPeriodicAdvertisingDisabledLatch = new CountDownLatch(1);
+            mScanResponseDataSetLatch = new CountDownLatch(1);
+
+            mAdvertisingSetStartedStatus = new AtomicInteger();
+            mAdvertisingEnabledStatus = new AtomicInteger();
+            mAdvertisingDisabledStatus = new AtomicInteger();
+            mAdvertisingParametersUpdatedStatus = new AtomicInteger();
+            mAdvertisingDataSetStatus = new AtomicInteger();
+            mPeriodicAdvertisingParamsUpdatedStatus = new AtomicInteger();
+            mPeriodicAdvertisingDataSetStatus = new AtomicInteger();
+            mPeriodicAdvertisingEnabledStatus = new AtomicInteger();
+            mPeriodicAdvertisingDisabledStatus = new AtomicInteger();
+            mScanResponseDataSetStatus = new AtomicInteger();
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
index 95f9764..746c894 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
@@ -323,7 +323,7 @@
     private static final int SERVICE_CHANGED_FLAG_TRIGGER_ACTION = 0x01;
     private static final int SERVICE_CHANGED_FLAG_ON_SERVICE_CHANGED = 0x02;
     private static final int SERVICE_CHANGED_FLAG_ALL = 0x03;
-    private static final int SERVOCE_CHANGED_FLAG_IGNORE = 0xFF;
+    private static final int SERVICE_CHANGED_FLAG_IGNORE = 0xFF;
     private int mServiceChangedFlag;
 
     private enum ReliableWriteState {
@@ -568,8 +568,13 @@
 
     private void writeCharacteristic(BluetoothGattCharacteristic characteristic, String writeValue) {
         if (characteristic != null) {
+            // Note: setValue() should not be necessary when using writeCharacteristic(byte[]) which
+            // is added on Android T, but here we call the method in order to make the test
+            // easier to read. Otherwise, we should verify the written value by calling
+            // readCharacteristic() but that makes the whole test hard to read.
             characteristic.setValue(writeValue);
-            mBluetoothGatt.writeCharacteristic(characteristic);
+            mBluetoothGatt.writeCharacteristic(characteristic, writeValue.getBytes(),
+                    characteristic.getWriteType());
         }
     }
 
@@ -590,8 +595,12 @@
     private void writeDescriptor(UUID uid, String writeValue) {
         BluetoothGattDescriptor descriptor = getDescriptor(uid);
         if (descriptor != null) {
+            // Note: setValue() should not be necessary when using writeDescriptor(byte[]) which
+            // is added on Android T, but here we call the method in order to make the test
+            // easier to read. Otherwise, we should verify the written value by calling
+            // readDescriptor() but that makes the whole test hard to read.
             descriptor.setValue(writeValue.getBytes());
-            mBluetoothGatt.writeDescriptor(descriptor);
+            mBluetoothGatt.writeDescriptor(descriptor, writeValue.getBytes());
         }
     }
 
@@ -605,8 +614,12 @@
     private void writeDescriptor(UUID cuid, UUID duid, String writeValue) {
         BluetoothGattDescriptor descriptor = getDescriptor(cuid, duid);
         if (descriptor != null) {
+            // Note: setValue() should not be necessary when using writeDescriptor(byte[]) which
+            // is added on Android T, but here we call the method in order to make the test
+            // easier to read. Otherwise, we should verify the written value by calling
+            // readDescriptor() but that makes the whole test hard to read.
             descriptor.setValue(writeValue.getBytes());
-            mBluetoothGatt.writeDescriptor(descriptor);
+            mBluetoothGatt.writeDescriptor(descriptor, writeValue.getBytes());
         }
     }
 
@@ -644,15 +657,15 @@
         synchronized (mServiceChangedLock) {
             mServiceChangedFlag |= flag;
             if (mServiceChangedFlag == SERVICE_CHANGED_FLAG_ALL) {
-                mServiceChangedFlag |= SERVOCE_CHANGED_FLAG_IGNORE;
+                mServiceChangedFlag |= SERVICE_CHANGED_FLAG_IGNORE;
                 shouldSend = true;
             }
         }
 
         if (shouldSend) {
+            // This is to send result to the connected GATT server.
             writeCharacteristic(getCharacteristic(CHARACTERISTIC_RESULT_UUID),
                 SERVICE_CHANGED_VALUE);
-            notifyServiceChanged();
         }
     }
 
@@ -1068,7 +1081,20 @@
             }
 
             if (BLE_CLIENT_ACTION_TRIGGER_SERVICE_CHANGED.equals(mCurrentAction)) {
-                sendServiceChangedEventIfReady(SERVICE_CHANGED_FLAG_TRIGGER_ACTION);
+                if (SERVICE_CHANGED_VALUE.equals(value)) {
+                    if (status == BluetoothGatt.GATT_SUCCESS) {
+                        // Waits until the GATT server sends the response, we can then do notify.
+                        notifyServiceChanged();
+                    } else {
+                        notifyError("Failed to send result for service changed event");
+                    }
+                } else {
+                    // The reason not to check the status code is that we know there is a service
+                    // changed event coming later, sometimes the status code will be modified by
+                    // bt stack (133), but it's ok, as long as onServiceChanged is called, we then
+                    // know the request is successfully sent during this test session.
+                    sendServiceChangedEventIfReady(SERVICE_CHANGED_FLAG_TRIGGER_ACTION);
+                }
             } else if (BLE_CLIENT_ACTION_REQUEST_MTU_512.equals(mCurrentAction) ||
                     BLE_CLIENT_ACTION_REQUEST_MTU_23.equals(mCurrentAction)) {
                 if (status == BluetoothGatt.GATT_SUCCESS) {
@@ -1157,18 +1183,30 @@
         @Override
         public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
             super.onCharacteristicRead(gatt, characteristic, status);
+            // Note: Both this method and onCharacteristicRead(byte[]) will be called.
             UUID uid = characteristic.getUuid();
             if (DEBUG) {
-                Log.d(TAG, "onCharacteristicRead: status=" + status);
+                Log.d(TAG, "onCharacteristicRead (deprecated): status=" + status);
             }
+        }
+
+        @Override
+        public void onCharacteristicRead(BluetoothGatt gatt,
+                BluetoothGattCharacteristic characteristic, byte[] value, int status) {
+            super.onCharacteristicRead(gatt, characteristic, value, status);
+            UUID uid = characteristic.getUuid();
+            if (DEBUG) {
+                Log.d(TAG, "onCharacteristicRead (memory safe version): status=" + status);
+            }
+
             if (status == BluetoothGatt.GATT_SUCCESS) {
-                String value = characteristic.getStringValue(0);
+                String stringValue = new String(value);
                 if (characteristic.getUuid().equals(CHARACTERISTIC_NEED_ENCRYPTED_READ_UUID)) {
-                    notifyCharacteristicReadNeedEncrypted(value);
+                    notifyCharacteristicReadNeedEncrypted(stringValue);
                 } else {
                     // verify
-                    if (BleServerService.WRITE_VALUE.equals(value)) {
-                        notifyCharacteristicRead(value);
+                    if (BleServerService.WRITE_VALUE.equals(stringValue)) {
+                        notifyCharacteristicRead(stringValue);
                     } else {
                         notifyError("Read data is not correct");
                     }
@@ -1225,21 +1263,32 @@
         @Override
         public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
             super.onDescriptorRead(gatt, descriptor, status);
+            // Note: Both this method and onDescriptorRead(byte[]) will be called.
             if (DEBUG) {
-                Log.d(TAG, "onDescriptorRead");
+                Log.d(TAG, "onDescriptorRead (deprecated)");
+            }
+        }
+
+        @Override
+        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+                int status, byte[] value) {
+            super.onDescriptorRead(gatt, descriptor, status, value);
+            if (DEBUG) {
+                Log.d(TAG, "onDescriptorRead (memory safe version)");
             }
 
             UUID uid = descriptor.getUuid();
+            String stringValue = new String(value);
             if ((status == BluetoothGatt.GATT_SUCCESS)) {
                 if ((uid != null) && (uid.equals(DESCRIPTOR_UUID))) {
                     // verify
-                    if (Arrays.equals(BleServerService.WRITE_VALUE.getBytes(), descriptor.getValue())) {
-                        notifyDescriptorRead(new String(descriptor.getValue()));
+                    if (BleServerService.WRITE_VALUE.equals(stringValue)) {
+                        notifyDescriptorRead(stringValue);
                     } else {
                         notifyError("Read data is not correct");
                     }
                 } else if (uid.equals(DESCRIPTOR_NEED_ENCRYPTED_READ_UUID)) {
-                    notifyDescriptorReadNeedEncrypted(new String(descriptor.getValue()));
+                    notifyDescriptorReadNeedEncrypted(stringValue);
                 }
             } else if (status == BluetoothGatt.GATT_READ_NOT_PERMITTED) {
                 if (uid.equals(DESCRIPTOR_NO_READ_UUID)) {
@@ -1257,14 +1306,28 @@
         public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
             super.onCharacteristicChanged(gatt, characteristic);
             UUID uid = characteristic.getUuid();
+            // Note: Both this method and onCharacteristicChanged(byte[]) will be called.
             if (DEBUG) {
-                Log.d(TAG, "onCharacteristicChanged: " + uid);
+                Log.d(TAG, "onCharacteristicChanged (deprecated): uid=" + uid);
             }
+        }
+
+        @Override
+        public void onCharacteristicChanged(BluetoothGatt gatt,
+                BluetoothGattCharacteristic characteristic, byte[] value) {
+            super.onCharacteristicChanged(gatt, characteristic, value);
+            UUID uid = characteristic.getUuid();
+            if (DEBUG) {
+                Log.d(TAG, "onCharacteristicChanged (memory safe version): uid=" + uid);
+            }
+
+            String stringValue = new String(value);
             if (uid != null) {
-                if (uid.equals(INDICATE_CHARACTERISTIC_UUID)) {
+                if (uid.equals(INDICATE_CHARACTERISTIC_UUID)
+                        && BleServerService.INDICATE_VALUE.equals(stringValue)) {
                     setNotification(characteristic, false);
                     notifyCharacteristicIndicated();
-                } else {
+                } else if (BleServerService.NOTIFY_VALUE.equals(stringValue)) {
                     mNotifyCount--;
                     setNotification(characteristic, false);
                     if (mNotifyCount == 0) {
@@ -1318,6 +1381,17 @@
         }
 
         @Override
+        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
+            // TODO: Currently this is not called when BluetoothGatt.setPreferredPhy() is called.
+            // It is because the path is not wired in native code. (acl_legacy_interface.cc)
+            // Add a proper implementation and related test.
+            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
+            if (DEBUG) {
+                Log.d(TAG, "onPhyUpdate");
+            }
+        }
+
+        @Override
         public void onServiceChanged(BluetoothGatt gatt) {
             super.onServiceChanged(gatt);
             if (DEBUG) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java
index 8177ca2..1cd3093 100755
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerService.java
@@ -41,11 +41,7 @@
 
 import com.android.cts.verifier.R;
 
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Set;
 import java.util.Timer;
 import java.util.UUID;
@@ -212,8 +208,8 @@
     private static final long NOTIFICATION_DELAY_OF_SECURE_TEST_FAILURE = 5 * 1000;
 
     public static final String WRITE_VALUE = "SERVER_TEST";
-    private static final String NOTIFY_VALUE = "NOTIFY_TEST";
-    private static final String INDICATE_VALUE = "INDICATE_TEST";
+    public static final String NOTIFY_VALUE = "NOTIFY_TEST";
+    public static final String INDICATE_VALUE = "INDICATE_TEST";
     public static final String READ_NO_PERMISSION = "READ_NO_CHAR";
     public static final String WRITE_NO_PERMISSION = "WRITE_NO_CHAR";
     public static final String DESCRIPTOR_READ_NO_PERMISSION = "READ_NO_DESC";
@@ -1160,27 +1156,28 @@
                 Log.d(TAG, "   characteristic uuid = " + uid);
             }
 
-            descriptor.setValue(value);
             UUID duid = descriptor.getUuid();
             // If there is a written request to the CCCD for Notify.
             if (duid.equals(UPDATE_DESCRIPTOR_UUID)) {
                 if (Arrays.equals(value, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) {
-                    mGattServer.notifyCharacteristicChanged(mDevice, descriptor.getCharacteristic(), false);
+                    mGattServer.notifyCharacteristicChanged(
+                            mDevice, descriptor.getCharacteristic(), false, value);
                     mIndicated = false;
                 } else if (Arrays.equals(value, BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)) {
-                    mGattServer.notifyCharacteristicChanged(mDevice, descriptor.getCharacteristic(), true);
+                    mGattServer.notifyCharacteristicChanged(
+                            mDevice, descriptor.getCharacteristic(), true, value);
                     mIndicated = true;
                 }
             } else if (duid.equals(DESCRIPTOR_NEED_ENCRYPTED_WRITE_UUID)) {
                 // verify
-                if (Arrays.equals(BleClientService.WRITE_VALUE.getBytes(), descriptor.getValue())) {
+                if (Arrays.equals(BleClientService.WRITE_VALUE.getBytes(), value)) {
                     notifyDescriptorWriteRequestNeedEncrypted();
                 } else {
                     showMessage("Written data is not correct");
                 }
             } else {
                 // verify
-                if (Arrays.equals(BleClientService.WRITE_VALUE.getBytes(), descriptor.getValue())) {
+                if (Arrays.equals(BleClientService.WRITE_VALUE.getBytes(), value)) {
                     notifyDescriptorWriteRequest();
                 } else {
                     showMessage("Written data is not correct");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BtAdapterUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BtAdapterUtils.java
new file mode 100644
index 0000000..1576d34
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BtAdapterUtils.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Utility for controlling the Bluetooth adapter from CTS test.
+ *
+ * Code mostly copied from android.bluetooth.cts.BTAdapterUtils class.
+ */
+public class BtAdapterUtils {
+    private static final String TAG = "BtAdapterUtils";
+
+    // ADAPTER_ENABLE_TIMEOUT_MS = AdapterState.BLE_START_TIMEOUT_DELAY +
+    //                              AdapterState.BREDR_START_TIMEOUT_DELAY
+    private static final int ADAPTER_ENABLE_TIMEOUT_MS = 8000;
+    // ADAPTER_DISABLE_TIMEOUT_MS = AdapterState.BLE_STOP_TIMEOUT_DELAY +
+    //                                  AdapterState.BREDR_STOP_TIMEOUT_DELAY
+    private static final int ADAPTER_DISABLE_TIMEOUT_MS = 5000;
+
+    private static BroadcastReceiver sAdapterIntentReceiver;
+
+    private static Condition sConditionAdapterIsEnabled;
+    private static ReentrantLock sAdapterStateEnablingLock;
+
+    private static Condition sConditionAdapterIsDisabled;
+    private static ReentrantLock sAdapterStateDisablingLock;
+    private static boolean sAdapterVarsInitialized;
+
+    private static HandlerThread sHandlerThread;
+    private static Looper sLooper;
+    private static Handler sHandler;
+
+    private static class AdapterIntentReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+                int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
+                int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+                Log.d(TAG, "Previous state: " + previousState + " New state: " + newState);
+
+                if (newState == BluetoothAdapter.STATE_ON) {
+                    sAdapterStateEnablingLock.lock();
+                    try {
+                        Log.d(TAG, "Signaling to mConditionAdapterIsEnabled");
+                        sConditionAdapterIsEnabled.signal();
+                    } finally {
+                        sAdapterStateEnablingLock.unlock();
+                    }
+                } else if (newState == BluetoothAdapter.STATE_OFF) {
+                    sAdapterStateDisablingLock.lock();
+                    try {
+                        Log.d(TAG, "Signaling to mConditionAdapterIsDisabled");
+                        sConditionAdapterIsDisabled.signal();
+                    } finally {
+                        sAdapterStateDisablingLock.unlock();
+                    }
+                }
+            }
+        }
+    }
+
+    /** Enables the Bluetooth Adapter. Return true if it is already enabled or is enabled. */
+    public static boolean enableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
+        if (!sAdapterVarsInitialized) {
+            initAdapterStateVariables(context);
+        }
+        registerIntentReceiver(context);
+
+        if (bluetoothAdapter.isEnabled()) return true;
+
+        bluetoothAdapter.enable();
+        sAdapterStateEnablingLock.lock();
+        try {
+            // Wait for the Adapter to be enabled
+            while (!bluetoothAdapter.isEnabled()) {
+                if (!sConditionAdapterIsEnabled.await(
+                        ADAPTER_ENABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    // Timeout
+                    Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter enable");
+                    break;
+                } // else spurious wakeups
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "enableAdapter: interrupted");
+        } finally {
+            sAdapterStateEnablingLock.unlock();
+        }
+        return bluetoothAdapter.isEnabled();
+    }
+
+    /** Disable the Bluetooth Adapter. Return true if it is already disabled or is disabled. */
+    public static boolean disableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
+        if (!sAdapterVarsInitialized) {
+            initAdapterStateVariables(context);
+        }
+        registerIntentReceiver(context);
+
+        if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
+
+        bluetoothAdapter.disable();
+        sAdapterStateDisablingLock.lock();
+        try {
+            // Wait for the Adapter to be disabled
+            while (bluetoothAdapter.getState() != BluetoothAdapter.STATE_OFF) {
+                if (!sConditionAdapterIsDisabled.await(
+                        ADAPTER_DISABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                    // Timeout
+                    Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter disable");
+                    break;
+                } // else spurious wakeups
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "disableAdapter: interrupted");
+        } finally {
+            sAdapterStateDisablingLock.unlock();
+        }
+        return bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF;
+    }
+
+    private static void registerIntentReceiver(Context context) {
+        sAdapterIntentReceiver = new AdapterIntentReceiver();
+        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+        context.registerReceiver(sAdapterIntentReceiver, filter);
+    }
+
+    // Initialize variables required for TestUtils#enableAdapter and TestUtils#disableAdapter
+    private static void initAdapterStateVariables(Context context) {
+        sAdapterStateEnablingLock = new ReentrantLock();
+        sConditionAdapterIsEnabled = sAdapterStateEnablingLock.newCondition();
+        sAdapterStateDisablingLock = new ReentrantLock();
+        sConditionAdapterIsDisabled = sAdapterStateDisablingLock.newCondition();
+
+        sAdapterVarsInitialized = true;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/HidDeviceActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/HidDeviceActivity.java
index c5cffc9..b661d5a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/HidDeviceActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/HidDeviceActivity.java
@@ -25,11 +25,11 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
-
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
+
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
@@ -96,9 +96,16 @@
     private BluetoothHidDevice.Callback mCallback = new BluetoothHidDevice.Callback() {
         @Override
         public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
+            super.onAppStatusChanged(pluggedDevice, registered);
             Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered="
                     + registered);
         }
+
+        @Override
+        public void onConnectionStateChanged(BluetoothDevice device, int state) {
+            super.onConnectionStateChanged(device, state);
+            Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
+        }
     };
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 69a63f2..63d9687 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -737,9 +737,9 @@
                     doCheckStreamCombination(cmdObj);
                 } else if ("isCameraPrivacyModeSupported".equals(cmdObj.getString("cmdName"))) {
                     doCheckCameraPrivacyModeSupport();
-                } else if ("getPerformanceClassLevel".equals(cmdObj.getString("cmdName"))) {
+                } else if ("isPerformanceClassPrimaryCamera".equals(cmdObj.getString("cmdName"))) {
                     String cameraId = cmdObj.getString("cameraId");
-                    doGetPerformanceClassLevel(cameraId);
+                    doCheckPerformanceClassPrimaryCamera(cameraId);
                 } else if ("measureCameraLaunchMs".equals(cmdObj.getString("cmdName"))) {
                     String cameraId = cmdObj.getString("cameraId");
                     doMeasureCameraLaunchMs(cameraId);
@@ -1082,9 +1082,9 @@
                 hasPrivacySupport ? "true" : "false");
     }
 
-    private void doGetPerformanceClassLevel(String cameraId) throws ItsException {
-        boolean isSPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S);
-        boolean isRPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R);
+    private void doCheckPerformanceClassPrimaryCamera(String cameraId) throws ItsException {
+        boolean  isPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S
+                || Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R);
 
         if (mItsCameraIdList == null) {
             mItsCameraIdList = ItsUtils.getItsCompatibleCameraIds(mCameraManager);
@@ -1116,9 +1116,8 @@
             throw new ItsException("Failed to get camera characteristics", e);
         }
 
-        mSocketRunnableObj.sendResponse("performanceClassLevel",
-                (isSPerfClass && isPrimaryCamera) ? "12" :
-                ((isRPerfClass && isPrimaryCamera) ? "11" : "0"));
+        mSocketRunnableObj.sendResponse("performanceClassPrimaryCamera",
+                (isPerfClass && isPrimaryCamera) ? "true" : "false");
     }
 
     private double invokeCameraPerformanceTest(Class testClass, String testName,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index dbe2c14..83d00a9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -290,6 +290,10 @@
                 return (hasSettingsActivity(context, Settings.ACTION_DISPLAY_SETTINGS)
                     && !pm.hasSystemFeature(PackageManager.FEATURE_WATCH));
             case UserManager.DISALLOW_CONFIG_CELL_BROADCASTS:
+                if (context.getResources().getBoolean(context.getResources()
+                        .getIdentifier("config_disable_all_cb_messages", "bool", "android"))) {
+                    return false;
+                }
                 final TelephonyManager tm =
                     context.getSystemService(TelephonyManager.class);
                 if (!tm.isSmsCapable()) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/IdentityCredentialAuthenticationMultiDocument.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/IdentityCredentialAuthenticationMultiDocument.java
new file mode 100644
index 0000000..d68c5b6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/IdentityCredentialAuthenticationMultiDocument.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.security;
+
+import android.Manifest;
+import android.app.KeyguardManager;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback;
+import android.hardware.biometrics.BiometricPrompt.AuthenticationResult;
+import android.hardware.biometrics.BiometricPrompt.CryptoObject;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.security.identity.AccessControlProfile;
+import android.security.identity.AccessControlProfileId;
+import android.security.identity.CredentialDataRequest;
+import android.security.identity.CredentialDataResult;
+import android.security.identity.IdentityCredential;
+import android.security.identity.IdentityCredentialStore;
+import android.security.identity.PersonalizationData;
+import android.security.identity.PresentationSession;
+import android.security.identity.WritableIdentityCredential;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * @hide
+ */
+public class IdentityCredentialAuthenticationMultiDocument extends PassFailButtons.Activity {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "IdentityCredentialAuthenticationMultiDocument";
+
+    private static final int BIOMETRIC_REQUEST_PERMISSION_CODE = 0;
+
+    private BiometricManager mBiometricManager;
+    private KeyguardManager mKeyguardManager;
+
+    protected int getTitleRes() {
+        return R.string.sec_identity_credential_authentication_multi_document_test;
+    }
+
+    private int getDescriptionRes() {
+        return R.string.sec_identity_credential_authentication_multi_document_test_info;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.sec_screen_lock_keys_main);
+        setPassFailButtonClickListeners();
+        setInfoResources(getTitleRes(), getDescriptionRes(), -1);
+        getPassButton().setEnabled(false);
+        requestPermissions(new String[]{Manifest.permission.USE_BIOMETRIC},
+                BIOMETRIC_REQUEST_PERMISSION_CODE);
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] state) {
+        if (requestCode == BIOMETRIC_REQUEST_PERMISSION_CODE
+                && state[0] == PackageManager.PERMISSION_GRANTED) {
+            mBiometricManager = getSystemService(BiometricManager.class);
+            mKeyguardManager = getSystemService(KeyguardManager.class);
+            Button startTestButton = findViewById(R.id.sec_start_test_button);
+
+            if (!mKeyguardManager.isKeyguardSecure()) {
+                // Show a message that the user hasn't set up a lock screen.
+                showToast("Secure lock screen hasn't been set up.\n Go to "
+                          + "'Settings -> Security -> Screen lock' to set up a lock screen");
+                startTestButton.setEnabled(false);
+                return;
+            }
+
+            startTestButton.setOnClickListener(v -> startTest());
+        }
+    }
+
+    protected void showToast(String message) {
+        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+        Log.i(TAG, "Showing Toast: " + message);
+    }
+
+    private void provisionCredential(IdentityCredentialStore store,
+                                     String credentialName) throws Exception {
+        store.deleteCredentialByName(credentialName);
+        WritableIdentityCredential wc = store.createCredential(
+                credentialName, "org.iso.18013-5.2019.mdl");
+
+        // 'Bar' encoded as CBOR tstr
+        byte[] barCbor = {0x63, 0x42, 0x61, 0x72};
+
+        AccessControlProfile acp = new AccessControlProfile.Builder(new AccessControlProfileId(0))
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationTimeout(0)
+                .build();
+        LinkedList<AccessControlProfileId> idsProfile0 = new LinkedList<AccessControlProfileId>();
+        idsProfile0.add(new AccessControlProfileId(0));
+        PersonalizationData pd = new PersonalizationData.Builder()
+                                 .addAccessControlProfile(acp)
+                                 .putEntry("org.iso.18013-5.2019", "Foo", idsProfile0, barCbor)
+                                 .build();
+        byte[] proofOfProvisioningSignature = wc.personalize(pd);
+
+        // Create authentication keys.
+        IdentityCredential credential = store.getCredentialByName(credentialName,
+                IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+        credential.setAvailableAuthenticationKeys(1, 10);
+        Collection<X509Certificate> dynAuthKeyCerts = credential.getAuthKeysNeedingCertification();
+        credential.storeStaticAuthenticationData(dynAuthKeyCerts.iterator().next(), new byte[0]);
+    }
+
+    private int getFooStatus(PresentationSession session, String credentialName) throws Exception {
+        Map<String, Collection<String>> isEntriesToRequest = new LinkedHashMap<>();
+        isEntriesToRequest.put("org.iso.18013-5.2019", Arrays.asList("Foo"));
+
+        CredentialDataResult rd = session.getCredentialData(
+                credentialName,
+                new CredentialDataRequest.Builder()
+                .setIssuerSignedEntriesToRequest(isEntriesToRequest)
+                .build());
+        return rd.getIssuerSignedEntries().getStatus("org.iso.18013-5.2019", "Foo");
+    }
+
+    protected void startTest() {
+        IdentityCredentialStore store = IdentityCredentialStore.getInstance(this);
+        if (store == null) {
+            showToast("No Identity Credential support, test passed.");
+            getPassButton().setEnabled(true);
+            return;
+        }
+
+        final int result = mBiometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG);
+        switch (result) {
+            case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
+                showToast("No biometrics enrolled.\n"
+                        + "Go to 'Settings -> Security' to enroll");
+                Button startTestButton = findViewById(R.id.sec_start_test_button);
+                startTestButton.setEnabled(false);
+                return;
+            case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
+                showToast("No strong biometrics, test passed.");
+                showToast("No Identity Credential support, test passed.");
+                getPassButton().setEnabled(true);
+                return;
+        }
+
+        try {
+            provisionCredential(store, "credential1");
+            provisionCredential(store, "credential2");
+
+            PresentationSession session = store.createPresentationSession(
+                    IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+
+            // First, check that Foo cannot be retrieved without authentication.
+            //
+            int status = getFooStatus(session, "credential1");
+            if (status != CredentialDataResult.Entries.STATUS_USER_AUTHENTICATION_FAILED) {
+                showToast("Unexpected credential1 pre-auth status "
+                          + status + " expected STATUS_USER_AUTHENTICATION_FAILED");
+                return;
+            }
+            status = getFooStatus(session, "credential2");
+            if (status != CredentialDataResult.Entries.STATUS_USER_AUTHENTICATION_FAILED) {
+                showToast("Unexpected credential2 pre-auth status "
+                          + status + " expected STATUS_USER_AUTHENTICATION_FAILED");
+                return;
+            }
+
+            // Try one more time, this time with a CryptoObject that we'll use with
+            // BiometricPrompt. This should work.
+            //
+            CryptoObject cryptoObject = new BiometricPrompt.CryptoObject(session);
+            BiometricPrompt.Builder builder = new BiometricPrompt.Builder(this);
+            builder.setAllowedAuthenticators(Authenticators.BIOMETRIC_STRONG);
+            builder.setTitle("Identity Credential");
+            builder.setDescription("Authenticate to unlock multiple credentials.");
+            builder.setNegativeButton("Cancel",
+                    getMainExecutor(),
+                    (dialogInterface, i) -> showToast("Canceled biometric prompt."));
+            final BiometricPrompt prompt = builder.build();
+            final AuthenticationCallback callback = new AuthenticationCallback() {
+                @Override
+                public void onAuthenticationSucceeded(AuthenticationResult authResult) {
+                    try {
+                        // Check that Foo can be retrieved because we used
+                        // the CryptoObject to auth with.
+                        int status = getFooStatus(session, "credential1");
+                        if (status != CredentialDataResult.Entries.STATUS_OK) {
+                            showToast("Unexpected credential1 post-auth status "
+                                      + status + " expected STATUS_OK");
+                            return;
+                        }
+                        status = getFooStatus(session, "credential2");
+                        if (status != CredentialDataResult.Entries.STATUS_OK) {
+                            showToast("Unexpected credential2 post-auth status "
+                                      + status + " expected STATUS_OK");
+                            return;
+                        }
+
+                        // Finally, check that Foo cannot be retrieved again from another session
+                        PresentationSession anotherSession = store.createPresentationSession(
+                                IdentityCredentialStore
+                                    .CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+                        status = getFooStatus(anotherSession, "credential1");
+                        if (status
+                                != CredentialDataResult.Entries.STATUS_USER_AUTHENTICATION_FAILED) {
+                            showToast("Unexpected credential1 other session status "
+                                      + status + " expected STATUS_USER_AUTHENTICATION_FAILED");
+                            return;
+                        }
+                        status = getFooStatus(anotherSession, "credential2");
+                        if (status
+                                != CredentialDataResult.Entries.STATUS_USER_AUTHENTICATION_FAILED) {
+                            showToast("Unexpected credential2 other session status "
+                                      + status + " expected STATUS_USER_AUTHENTICATION_FAILED");
+                            return;
+                        }
+
+                        showToast("Test passed.");
+                        getPassButton().setEnabled(true);
+
+                    } catch (Exception e) {
+                        showToast("Unexpected exception " + e);
+                    }
+                }
+            };
+
+            prompt.authenticate(cryptoObject, new CancellationSignal(), getMainExecutor(),
+                    callback);
+        } catch (Exception e) {
+            showToast("Unexpection exception " + e);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/security/OWNERS
index 40ff6060..c0eb5b3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/OWNERS
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/OWNERS
@@ -1,8 +1,10 @@
 # Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204 = per-file CA*.java, Ca*.java, KeyChainTest.java
 # Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204 = per-file LockConfirmBypassTest.java, SetNewPasswordComplexityTest.java
 # Bug template url: https://b.corp.google.com/issues/new?component=746324&template=1398789 = per-file: SecurityModeFeatureVerifierActivity.java
-# Bug component: 189335 = per-file FingerprintBoundKeysTest.java, IdentityCredentialAuthentication.java, ProtectedConfirmationTest.java, ScreenLockBoundKeysTest.java
+# Bug component: 189335 = per-file FingerprintBoundKeysTest.java, ProtectedConfirmationTest.java, ScreenLockBoundKeysTest.java
+# Bug component: 1084909 = per-file IdentityCredential*.java
 per-file CA*.java, Ca*.java, KeyChainTest.java = file:platform/frameworks/base:/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS
 per-file LockConfirmBypassTest.java, SetNewPasswordComplexityTest.java, CredentialManagementAppActivity.java = file:platform/frameworks/base:/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS
-per-file FingerprintBoundKeysTest.java, IdentityCredentialAuthentication.java, ProtectedConfirmationTest.java, ScreenLockBoundKeysTest.java = swillden@google.com
+per-file FingerprintBoundKeysTest.java, ProtectedConfirmationTest.java, ScreenLockBoundKeysTest.java = swillden@google.com
 per-file SecurityModeFeatureVerifierActivity.java = jjoslin@google.com, tomcherry@google.com
+per-file IdentityCredential*.java = zeuthen@google.com
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
index faee07a..e68983b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/usb/device/UsbDeviceTestActivity.java
@@ -84,6 +84,7 @@
     private Thread mTestThread;
     private TextView mStatus;
     private ProgressBar mProgress;
+    private UsbDevice mDevice;
 
     /**
      * Some N and older accessories do not send a zero sized package after a request that is a
@@ -156,7 +157,6 @@
                         case ACTION_USB_PERMISSION:
                             boolean granted = intent.getBooleanExtra(
                                     UsbManager.EXTRA_PERMISSION_GRANTED, false);
-
                             if (granted) {
                                 if (!AoapInterface.isDeviceInAoapMode(device)) {
                                     mStatus.setText(R.string.usb_device_test_step3);
@@ -194,7 +194,6 @@
                 }
             }
         };
-
         registerReceiver(mUsbDeviceConnectionReceiver, filter);
     }
 
@@ -1557,6 +1556,15 @@
         connection.close();
     }
 
+    private void syncReconnectDevice(@NonNull UsbDevice device) {
+        this.mDevice = device;
+    }
+
+    private UsbDevice getReconnectDevice() {
+        return mDevice;
+    }
+
+
     /**
      * <p> This attachedtask the requests and does not care about them anymore after the
      * system took them. The {@link attachedTask} handles the test after the main test done.
@@ -1600,6 +1608,8 @@
             public void onReceive(Context context, Intent intent) {
                 synchronized (UsbDeviceTestActivity.this) {
                     UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+                    syncReconnectDevice(device);
+
                     switch (intent.getAction()) {
                         case ACTION_USB_PERMISSION:
                             boolean granted = intent.getBooleanExtra(
@@ -2267,6 +2277,12 @@
             // If reconnect timeout make the test fail
             assertEquals(0, errors.size());
 
+            // Update connection handle after reconnect
+            device = getReconnectDevice();
+            assertNotNull(device);
+            connection = mUsbManager.openDevice(device);
+            assertNotNull(connection);
+
             connection.close();
 
             // We should not be able to communicate with the device anymore
diff --git a/hostsidetests/appcompat/strictjavapackages/Android.bp b/hostsidetests/appcompat/strictjavapackages/Android.bp
index fcb20e8..9ab8a83 100644
--- a/hostsidetests/appcompat/strictjavapackages/Android.bp
+++ b/hostsidetests/appcompat/strictjavapackages/Android.bp
@@ -33,6 +33,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-mainline-infra",
     ],
 }
diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
index d6a3548..f9030fd 100644
--- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
+++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
@@ -92,6 +92,7 @@
                     "Landroid/annotation/DrawableRes;",
                     "Landroid/annotation/DurationMillisLong;",
                     "Landroid/annotation/ElapsedRealtimeLong;",
+                    "Landroid/annotation/EnforcePermission;",
                     "Landroid/annotation/FloatRange;",
                     "Landroid/annotation/FontRes;",
                     "Landroid/annotation/FractionRes;",
diff --git a/hostsidetests/devicepolicy/Android.bp b/hostsidetests/devicepolicy/Android.bp
index 3432898..b09e4db 100644
--- a/hostsidetests/devicepolicy/Android.bp
+++ b/hostsidetests/devicepolicy/Android.bp
@@ -32,7 +32,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     static_libs: [
         "cts-statsd-atom-host-test-utils",
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Android.bp b/hostsidetests/devicepolicy/app/AccountCheck/Android.bp
index defd5f6..cde714b 100644
--- a/hostsidetests/devicepolicy/app/AccountCheck/Android.bp
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Android.bp
@@ -23,7 +23,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src-owner/**/*.java"],
     resource_dirs: ["TestOnlyOwner/res"],
@@ -44,7 +44,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src-owner/**/*.java"],
     resource_dirs: ["NonTestOnlyOwner/res"],
@@ -65,7 +65,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src-owner/**/*.java"],
     resource_dirs: ["TestOnlyOwnerUpdate/res"],
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.bp b/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.bp
index 7117a4c..3a04c96 100644
--- a/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.bp
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/Android.bp
@@ -23,7 +23,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src/**/*.java"],
     static_libs: [
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Tester/Android.bp b/hostsidetests/devicepolicy/app/AccountCheck/Tester/Android.bp
index 3d524bb..76f664b 100644
--- a/hostsidetests/devicepolicy/app/AccountCheck/Tester/Android.bp
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Tester/Android.bp
@@ -23,7 +23,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src/**/*.java"],
     static_libs: [
diff --git a/hostsidetests/devicepolicy/app/AccountManagement/Android.bp b/hostsidetests/devicepolicy/app/AccountManagement/Android.bp
index 8ab3eb8..a1e9a3f 100644
--- a/hostsidetests/devicepolicy/app/AccountManagement/Android.bp
+++ b/hostsidetests/devicepolicy/app/AccountManagement/Android.bp
@@ -24,7 +24,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src/**/*.java"],
     static_libs: [
diff --git a/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.bp b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.bp
index c9688e8..3b740ff 100644
--- a/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.bp
@@ -27,7 +27,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     static_libs: [
         "DpmWrapper",
diff --git a/hostsidetests/devicepolicy/app/Assistant/Android.bp b/hostsidetests/devicepolicy/app/Assistant/Android.bp
index 7ef13dc..4eede47 100644
--- a/hostsidetests/devicepolicy/app/Assistant/Android.bp
+++ b/hostsidetests/devicepolicy/app/Assistant/Android.bp
@@ -26,7 +26,7 @@
         "general-tests",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     static_libs: [
         "androidx.legacy_legacy-support-v4",
diff --git a/hostsidetests/devicepolicy/app/AutofillApp/Android.bp b/hostsidetests/devicepolicy/app/AutofillApp/Android.bp
index f78e06a..97218f8 100644
--- a/hostsidetests/devicepolicy/app/AutofillApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/AutofillApp/Android.bp
@@ -25,7 +25,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     sdk_version: "current",
 }
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/Android.bp b/hostsidetests/devicepolicy/app/CertInstaller/Android.bp
index 83e8380..d178a9e 100644
--- a/hostsidetests/devicepolicy/app/CertInstaller/Android.bp
+++ b/hostsidetests/devicepolicy/app/CertInstaller/Android.bp
@@ -39,6 +39,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/ContactDirectoryProvider/Android.bp b/hostsidetests/devicepolicy/app/ContactDirectoryProvider/Android.bp
index f4fbd66..bbd5cf6 100644
--- a/hostsidetests/devicepolicy/app/ContactDirectoryProvider/Android.bp
+++ b/hostsidetests/devicepolicy/app/ContactDirectoryProvider/Android.bp
@@ -24,7 +24,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     sdk_version: "current",
 }
diff --git a/hostsidetests/devicepolicy/app/ContentCaptureApp/Android.bp b/hostsidetests/devicepolicy/app/ContentCaptureApp/Android.bp
index fffaa33..1b960b3 100644
--- a/hostsidetests/devicepolicy/app/ContentCaptureApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/ContentCaptureApp/Android.bp
@@ -25,7 +25,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     sdk_version: "system_current",
 }
diff --git a/hostsidetests/devicepolicy/app/ContentCaptureService/Android.bp b/hostsidetests/devicepolicy/app/ContentCaptureService/Android.bp
index 09554c2..c69414d 100644
--- a/hostsidetests/devicepolicy/app/ContentCaptureService/Android.bp
+++ b/hostsidetests/devicepolicy/app/ContentCaptureService/Android.bp
@@ -25,7 +25,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     sdk_version: "system_current",
 }
diff --git a/hostsidetests/devicepolicy/app/ContentSuggestionsApp/Android.bp b/hostsidetests/devicepolicy/app/ContentSuggestionsApp/Android.bp
index e98b934..18fc28e 100644
--- a/hostsidetests/devicepolicy/app/ContentSuggestionsApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/ContentSuggestionsApp/Android.bp
@@ -25,7 +25,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     sdk_version: "system_current",
 }
diff --git a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/Android.bp b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/Android.bp
index 8ee2426..1bd51cc 100644
--- a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/Android.bp
+++ b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/Android.bp
@@ -46,7 +46,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
 
@@ -75,7 +75,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     aaptflags: [
         "--rename-manifest-package",
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/Android.bp
index dc719d4..07fa05e 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/Android.bp
@@ -35,6 +35,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsWithNoPermissionTest/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsWithNoPermissionTest/Android.bp
index ec78ab2..c137ad2 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsWithNoPermissionTest/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsWithNoPermissionTest/Android.bp
@@ -34,6 +34,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledApp/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledApp/Android.bp
index f7d31e3..ffca3b3 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledApp/Android.bp
@@ -34,6 +34,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledNoPermsApp/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledNoPermsApp/Android.bp
index 8cb5d00..30ce152 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledNoPermsApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledNoPermsApp/Android.bp
@@ -34,6 +34,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileNotEnabledApp/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileNotEnabledApp/Android.bp
index cdb5335..49ca07f 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileNotEnabledApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileNotEnabledApp/Android.bp
@@ -34,6 +34,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileUserEnabledApp/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileUserEnabledApp/Android.bp
index 071cc2e..323b377 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileUserEnabledApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileUserEnabledApp/Android.bp
@@ -34,6 +34,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/Android.bp
index ba0c260..5ea1218 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/ModifyQuietModeEnabledApp/Android.bp
@@ -34,6 +34,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/CustomizationApp/Android.bp b/hostsidetests/devicepolicy/app/CustomizationApp/Android.bp
index 64303d7..3ace6d7 100644
--- a/hostsidetests/devicepolicy/app/CustomizationApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/CustomizationApp/Android.bp
@@ -23,7 +23,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src/**/*.java"],
     static_libs: [
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/Android.bp b/hostsidetests/devicepolicy/app/DelegateApp/Android.bp
index 93b5304..ab815d5 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/DelegateApp/Android.bp
@@ -39,6 +39,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/Android.bp b/hostsidetests/devicepolicy/app/DeviceAdmin/Android.bp
index 5f8d9ce..a4c32bc 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/Android.bp
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/Android.bp
@@ -36,7 +36,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "api23/AndroidManifest.xml",
 }
@@ -63,7 +63,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "api24/AndroidManifest.xml",
 }
@@ -88,7 +88,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "api29/AndroidManifest.xml",
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAdminService/Android.bp b/hostsidetests/devicepolicy/app/DeviceAdminService/Android.bp
index b723aca..d43959f 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdminService/Android.bp
+++ b/hostsidetests/devicepolicy/app/DeviceAdminService/Android.bp
@@ -31,7 +31,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "package1/AndroidManifest.xml",
 }
@@ -51,7 +51,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "package2/AndroidManifest.xml",
 }
@@ -71,7 +71,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "package3/AndroidManifest.xml",
 }
@@ -91,7 +91,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "package4/AndroidManifest.xml",
 }
@@ -111,7 +111,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "packageb/AndroidManifest.xml",
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.bp b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.bp
index b91fec2..b73b888 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.bp
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.bp
@@ -43,7 +43,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "api23/AndroidManifest.xml",
 }
@@ -78,7 +78,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "api25/AndroidManifest.xml",
 }
@@ -111,7 +111,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "api30/AndroidManifest.xml",
 }
@@ -148,7 +148,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "latest/AndroidManifest.xml",
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
index 75bcdf1..974ab7b 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
@@ -115,7 +115,7 @@
     }
 
     public void testUserRestrictionDisallowConfigDateTimeIsNotPersisted() throws Exception {
-        final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(30);
+        final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(60);
         while (System.nanoTime() <= deadline) {
             if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME)) {
                 return;
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp b/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp
index 99ebd9f..0fc8b16 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp
@@ -50,7 +50,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
index 6898a9d..dc3476e 100644
--- a/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/HasLauncherActivityApp/Android.bp
@@ -32,7 +32,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     sdk_version: "current",
 }
@@ -52,7 +52,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "no_launcher_activity_AndroidManifest.xml",
     sdk_version: "current",
@@ -73,7 +73,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "no_permission_AndroidManifest.xml",
     sdk_version: "current",
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/Android.bp b/hostsidetests/devicepolicy/app/IntentReceiver/Android.bp
index 728fb98..81dc7b6 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/Android.bp
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/Android.bp
@@ -35,6 +35,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/IntentSender/Android.bp b/hostsidetests/devicepolicy/app/IntentSender/Android.bp
index 081ccec..0777be0 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/Android.bp
+++ b/hostsidetests/devicepolicy/app/IntentSender/Android.bp
@@ -36,6 +36,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/Android.bp b/hostsidetests/devicepolicy/app/LauncherTests/Android.bp
index b6f4cd2..ce091ea 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/Android.bp
+++ b/hostsidetests/devicepolicy/app/LauncherTests/Android.bp
@@ -36,6 +36,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/LauncherTestsSupport/Android.bp b/hostsidetests/devicepolicy/app/LauncherTestsSupport/Android.bp
index dd061a8..f77dd24 100644
--- a/hostsidetests/devicepolicy/app/LauncherTestsSupport/Android.bp
+++ b/hostsidetests/devicepolicy/app/LauncherTestsSupport/Android.bp
@@ -28,6 +28,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/Android.bp b/hostsidetests/devicepolicy/app/ManagedProfile/Android.bp
index a515d6b..df56c4d 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/Android.bp
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/Android.bp
@@ -42,7 +42,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     platform_apis: true,
 }
diff --git a/hostsidetests/devicepolicy/app/MeteredDataTestApp/Android.bp b/hostsidetests/devicepolicy/app/MeteredDataTestApp/Android.bp
index 14d899f..495d285 100644
--- a/hostsidetests/devicepolicy/app/MeteredDataTestApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/MeteredDataTestApp/Android.bp
@@ -25,7 +25,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     sdk_version: "current",
 }
diff --git a/hostsidetests/devicepolicy/app/NotificationSender/Android.bp b/hostsidetests/devicepolicy/app/NotificationSender/Android.bp
index 037e794..c113414 100644
--- a/hostsidetests/devicepolicy/app/NotificationSender/Android.bp
+++ b/hostsidetests/devicepolicy/app/NotificationSender/Android.bp
@@ -24,7 +24,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     sdk_version: "current",
 }
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/Android.bp b/hostsidetests/devicepolicy/app/PackageInstaller/Android.bp
index 92181a9..5e119c2 100644
--- a/hostsidetests/devicepolicy/app/PackageInstaller/Android.bp
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/Android.bp
@@ -36,6 +36,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/PasswordComplexity/Android.bp b/hostsidetests/devicepolicy/app/PasswordComplexity/Android.bp
index 1fe0b28..09cb166 100644
--- a/hostsidetests/devicepolicy/app/PasswordComplexity/Android.bp
+++ b/hostsidetests/devicepolicy/app/PasswordComplexity/Android.bp
@@ -34,6 +34,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/PrintingApp/Android.bp b/hostsidetests/devicepolicy/app/PrintingApp/Android.bp
index a359538..a8caa82 100644
--- a/hostsidetests/devicepolicy/app/PrintingApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/PrintingApp/Android.bp
@@ -26,6 +26,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/ProfileOwner/Android.bp b/hostsidetests/devicepolicy/app/ProfileOwner/Android.bp
index 4f141c4..83aacd7 100644
--- a/hostsidetests/devicepolicy/app/ProfileOwner/Android.bp
+++ b/hostsidetests/devicepolicy/app/ProfileOwner/Android.bp
@@ -37,6 +37,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.bp b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.bp
index 3ac997b..b007c23 100644
--- a/hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.bp
+++ b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.bp
@@ -38,6 +38,6 @@
         "vts10",
         "general-tests",
 	"sts",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/SharingApps/Android.bp b/hostsidetests/devicepolicy/app/SharingApps/Android.bp
index 9729aaf..110c0bd 100644
--- a/hostsidetests/devicepolicy/app/SharingApps/Android.bp
+++ b/hostsidetests/devicepolicy/app/SharingApps/Android.bp
@@ -37,7 +37,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "sharingapp1/AndroidManifest.xml",
 }
@@ -63,7 +63,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "sharingapp2/AndroidManifest.xml",
 }
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/Android.bp b/hostsidetests/devicepolicy/app/SimpleApp/Android.bp
index b73f38a..af1f731 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/SimpleApp/Android.bp
@@ -25,7 +25,7 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
     sdk_version: "current",
diff --git a/hostsidetests/devicepolicy/app/SimplePreMApp/Android.bp b/hostsidetests/devicepolicy/app/SimplePreMApp/Android.bp
index dbf0ffc..8848fdb 100644
--- a/hostsidetests/devicepolicy/app/SimplePreMApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/SimplePreMApp/Android.bp
@@ -28,6 +28,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp b/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp
index 2327596cd..05c8016 100644
--- a/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp
@@ -28,6 +28,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/SingleAdminApp/Android.bp b/hostsidetests/devicepolicy/app/SingleAdminApp/Android.bp
index a0a7ac3..2e138fd 100644
--- a/hostsidetests/devicepolicy/app/SingleAdminApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/SingleAdminApp/Android.bp
@@ -36,6 +36,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/TestApps/Android.bp b/hostsidetests/devicepolicy/app/TestApps/Android.bp
index 2bfbf45..a5b8b52 100644
--- a/hostsidetests/devicepolicy/app/TestApps/Android.bp
+++ b/hostsidetests/devicepolicy/app/TestApps/Android.bp
@@ -37,7 +37,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "testapp1/AndroidManifest.xml",
 }
@@ -63,7 +63,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "testapp2/AndroidManifest.xml",
 }
@@ -89,7 +89,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "testapp3/AndroidManifest.xml",
 }
@@ -115,7 +115,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "testapp4/AndroidManifest.xml",
 }
@@ -127,7 +127,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "shareduidapp1/AndroidManifest.xml",
 }
@@ -139,7 +139,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     manifest: "shareduidapp2/AndroidManifest.xml",
 }
diff --git a/hostsidetests/devicepolicy/app/TestIme/Android.bp b/hostsidetests/devicepolicy/app/TestIme/Android.bp
index 9998a48..a450ff7 100644
--- a/hostsidetests/devicepolicy/app/TestIme/Android.bp
+++ b/hostsidetests/devicepolicy/app/TestIme/Android.bp
@@ -28,6 +28,6 @@
         "arcts",
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.bp b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.bp
index 48c61d3..b588d83 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/Android.bp
@@ -38,6 +38,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.bp b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.bp
index deda73f..7735d8d 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/Android.bp
@@ -38,6 +38,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/WidgetProvider/Android.bp b/hostsidetests/devicepolicy/app/WidgetProvider/Android.bp
index d691fe4..363c471 100644
--- a/hostsidetests/devicepolicy/app/WidgetProvider/Android.bp
+++ b/hostsidetests/devicepolicy/app/WidgetProvider/Android.bp
@@ -25,6 +25,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/app/WifiConfigCreator/Android.bp b/hostsidetests/devicepolicy/app/WifiConfigCreator/Android.bp
index 440b6da..4b3eb4e 100644
--- a/hostsidetests/devicepolicy/app/WifiConfigCreator/Android.bp
+++ b/hostsidetests/devicepolicy/app/WifiConfigCreator/Android.bp
@@ -28,6 +28,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
index 77e1838..0d11e0a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfilePasswordTest.java
@@ -127,7 +127,7 @@
     @LockSettingsTest
     @Test
     public void testUnlockWorkProfile_deviceWidePassword() throws Exception {
-        assumeHasSecureLockScreenFeature();
+        assumeHasFileBasedEncryptionAndSecureLockScreenFeatures();
 
         try {
             // Add a device password after the work profile has been created.
diff --git a/hostsidetests/gputools/Android.bp b/hostsidetests/gputools/Android.bp
index f919a8c..28730ae 100644
--- a/hostsidetests/gputools/Android.bp
+++ b/hostsidetests/gputools/Android.bp
@@ -14,12 +14,7 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-MIT
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 java_test_host {
diff --git a/hostsidetests/gputools/layers/Android.bp b/hostsidetests/gputools/layers/Android.bp
index 8d294d6..b007cb5 100644
--- a/hostsidetests/gputools/layers/Android.bp
+++ b/hostsidetests/gputools/layers/Android.bp
@@ -14,12 +14,16 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-MIT
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_hostsidetests_gputools_layers_vulkan_license",
+    ],
+}
+
+license {
+    name: "cts_hostsidetests_gputools_layers_vulkan_license",
+    license_kinds: ["SPDX-license-identifier-MIT"],
+    license_text: ["LICENSE_MIT"]
 }
 
 cc_test_library {
diff --git a/hostsidetests/gputools/layers/LICENSE_MIT b/hostsidetests/gputools/layers/LICENSE_MIT
new file mode 100644
index 0000000..fe3c973
--- /dev/null
+++ b/hostsidetests/gputools/layers/LICENSE_MIT
@@ -0,0 +1,18 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and/or associated documentation files (the "Materials"), to
+deal in the Materials without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Materials, and to permit persons to whom the Materials are
+furnished to do so, subject to the following conditions:
+
+The above copyright notice(s) and this permission notice shall be included in
+all copies or substantial portions of the Materials.
+
+THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
+USE OR OTHER DEALINGS IN THE MATERIALS.
diff --git a/hostsidetests/graphics/gpumetrics/OWNERS b/hostsidetests/graphics/gpumetrics/OWNERS
new file mode 100644
index 0000000..959ca7c
--- /dev/null
+++ b/hostsidetests/graphics/gpumetrics/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 653544
+paulthomson@google.com
+pbaiget@google.com
+chrisforbes@google.com
+lpy@google.com
+lfy@google.com
+alecmouri@google.com
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
index 67c4033..f4961d8 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
@@ -46,6 +46,7 @@
 public class BaseHdmiCecCtsTest extends BaseHostJUnit4Test {
 
     public static final String PROPERTY_LOCALE = "persist.sys.locale";
+    private static final String POWER_CONTROL_MODE = "power_control_mode";
 
     /** Enum contains the list of possible address types. */
     private enum AddressType {
@@ -377,7 +378,7 @@
         checkDeviceAsleep();
     }
 
-    private void waitForTransitionTo(int finalState) throws Exception {
+    public void waitForTransitionTo(int finalState) throws Exception {
         int powerStatus;
         int waitTimeSeconds = 0;
         LogicalAddress cecClientDevice = hdmiCecClient.getSelfDevice();
@@ -408,10 +409,14 @@
         }
     }
 
-    public void sendDeviceToSleep() throws Exception {
+    public void sendDeviceToSleepWithoutWait() throws Exception {
         ITestDevice device = getDevice();
         WakeLockHelper.acquirePartialWakeLock(device);
         device.executeShellCommand("input keyevent KEYCODE_SLEEP");
+    }
+
+    public void sendDeviceToSleep() throws Exception {
+        sendDeviceToSleepWithoutWait();
         waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
     }
 
@@ -431,4 +436,36 @@
         ITestDevice device = getDevice();
         device.executeShellCommand("cmd hdmi_control onetouchplay");
     }
+
+    public String setPowerControlMode(String valToSet) throws Exception {
+        String val = getSettingsValue(POWER_CONTROL_MODE);
+        setSettingsValue(POWER_CONTROL_MODE, valToSet);
+        return val;
+    }
+
+    public boolean isDeviceActiveSource(ITestDevice device) throws DumpsysParseException {
+        final String activeSource = "activeSource";
+        final String pattern =
+                "(.*?)"
+                        + "(isActiveSource\\(\\): )"
+                        + "(?<"
+                        + activeSource
+                        + ">\\btrue\\b|\\bfalse\\b)"
+                        + "(.*?)";
+        try {
+            Pattern p = Pattern.compile(pattern);
+            String dumpsys = device.executeShellCommand("dumpsys hdmi_control");
+            BufferedReader reader = new BufferedReader(new StringReader(dumpsys));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                Matcher matcher = p.matcher(line);
+                if (matcher.matches()) {
+                    return matcher.group(activeSource).equals("true");
+                }
+            }
+        } catch (IOException | DeviceNotAvailableException e) {
+            throw new DumpsysParseException("Could not fetch 'dumpsys hdmi_control' output.", e);
+        }
+        throw new DumpsysParseException("Could not parse isActiveSource() from dumpsys.");
+    }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
index 55409a2..3de26f1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
@@ -316,6 +316,31 @@
         }
     }
 
+    public void sendMultipleUserControlPressAndRelease(
+            LogicalAddress source, List<Integer> keycodes) throws CecClientWrapperException {
+        try {
+            for (int keycode : keycodes) {
+                String key = String.format("%02x", keycode);
+                mOutputConsole.write(
+                        "tx "
+                                + source
+                                + targetDevice
+                                + ":"
+                                + CecOperand.USER_CONTROL_PRESSED
+                                + ":"
+                                + key);
+                mOutputConsole.newLine();
+                mOutputConsole.write(
+                        "tx " + source + targetDevice + ":" + CecOperand.USER_CONTROL_RELEASED);
+                mOutputConsole.newLine();
+                mOutputConsole.flush();
+                TimeUnit.MILLISECONDS.sleep(200);
+            }
+        } catch (InterruptedException | IOException ioe) {
+            throw new CecClientWrapperException(ErrorCodes.WriteConsole, ioe);
+        }
+    }
+
     /**
      * Sends a <USER_CONTROL_PRESSED> and <USER_CONTROL_RELEASED> from source to device through the
      * output console of the cec-communication channel with the mentioned keycode.
@@ -345,6 +370,37 @@
     }
 
     /**
+     * Sends a {@code <UCP>} with and additional param. This is used to check that the DUT ignores
+     * additional params in an otherwise correct message.
+     */
+    public void sendUserControlPressAndReleaseWithAdditionalParams(
+            LogicalAddress source, LogicalAddress destination, int keyCode, int additionalParam)
+            throws CecClientWrapperException {
+        String key = String.format("%02x", keyCode);
+        String command =
+                "tx "
+                        + source
+                        + destination
+                        + ":"
+                        + CecOperand.USER_CONTROL_PRESSED
+                        + ":"
+                        + key
+                        + ":"
+                        + additionalParam;
+
+        try {
+            mOutputConsole.write(command);
+            mOutputConsole.newLine();
+            mOutputConsole.write(
+                    "tx " + source + destination + ":" + CecOperand.USER_CONTROL_RELEASED);
+            mOutputConsole.newLine();
+            mOutputConsole.flush();
+        } catch (IOException ioe) {
+            throw new CecClientWrapperException(ErrorCodes.WriteConsole, ioe);
+        }
+    }
+
+    /**
      * Sends a <UCP> message from source to destination through the output console of the
      * cec-communication channel with the mentioned keycode. If holdKey is true, the method will
      * send multiple <UCP> messages to simulate a long press. No <UCR> will be sent.
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
index 2f1298d..a8426cc 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
@@ -29,6 +29,8 @@
     public static final int TIMEOUT_CEC_REINIT_SECONDS = 5;
     public static final int TIMEOUT_SAFETY_MS = 500;
 
+    public static final int INVALID_VENDOR_ID = 0xFFFFFF;
+
     // Standard delay to allow the DUT to react to a CEC message or ADB command
     public static final int DEVICE_WAIT_TIME_SECONDS = 5;
     public static final int DEVICE_WAIT_TIME_MS = 5000;
@@ -38,19 +40,52 @@
     public static final int TV_PHYSICAL_ADDRESS = 0x0000;
     public static final int PHYSICAL_ADDRESS_LENGTH = 4; /* Num nibbles in CEC message */
 
-    public static final int CEC_CONTROL_SELECT = 0x0;
-    public static final int CEC_CONTROL_UP = 0x1;
-    public static final int CEC_CONTROL_DOWN = 0x2;
-    public static final int CEC_CONTROL_LEFT = 0x3;
-    public static final int CEC_CONTROL_RIGHT = 0x4;
-    public static final int CEC_CONTROL_BACK = 0xd;
-    public static final int CEC_CONTROL_POWER = 0x40;
-    public static final int CEC_CONTROL_VOLUME_UP = 0x41;
-    public static final int CEC_CONTROL_VOLUME_DOWN = 0x42;
-    public static final int CEC_CONTROL_MUTE = 0x43;
-    public static final int CEC_CONTROL_POWER_TOGGLE_FUNCTION = 0x6B;
-    public static final int CEC_CONTROL_POWER_OFF_FUNCTION = 0x6C;
-    public static final int CEC_CONTROL_POWER_ON_FUNCTION = 0x6D;
+    public static final int CEC_KEYCODE_SELECT = 0x00;
+    public static final int CEC_KEYCODE_UP = 0x01;
+    public static final int CEC_KEYCODE_DOWN = 0x02;
+    public static final int CEC_KEYCODE_LEFT = 0x03;
+    public static final int CEC_KEYCODE_RIGHT = 0x04;
+    public static final int CEC_KEYCODE_ROOT_MENU = 0x09;
+    public static final int CEC_KEYCODE_SETUP_MENU = 0x0A;
+    public static final int CEC_KEYCODE_CONTENTS_MENU = 0x0B;
+    public static final int CEC_KEYCODE_BACK = 0x0D;
+    public static final int CEC_KEYCODE_MEDIA_TOP_MENU = 0x10;
+    public static final int CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU = 0x11;
+    public static final int CEC_KEYCODE_NUMBER_0_OR_NUMBER_10 = 0x20;
+    public static final int CEC_KEYCODE_NUMBERS_1 = 0x21;
+    public static final int CEC_KEYCODE_NUMBERS_2 = 0x22;
+    public static final int CEC_KEYCODE_NUMBERS_3 = 0x23;
+    public static final int CEC_KEYCODE_NUMBERS_4 = 0x24;
+    public static final int CEC_KEYCODE_NUMBERS_5 = 0x25;
+    public static final int CEC_KEYCODE_NUMBERS_6 = 0x26;
+    public static final int CEC_KEYCODE_NUMBERS_7 = 0x27;
+    public static final int CEC_KEYCODE_NUMBERS_8 = 0x28;
+    public static final int CEC_KEYCODE_NUMBERS_9 = 0x29;
+    public static final int CEC_KEYCODE_CHANNEL_UP = 0x30;
+    public static final int CEC_KEYCODE_CHANNEL_DOWN = 0x31;
+    public static final int CEC_KEYCODE_PREVIOUS_CHANNEL = 0x32;
+    public static final int CEC_KEYCODE_DISPLAY_INFORMATION = 0x35;
+    public static final int CEC_KEYCODE_POWER = 0x40;
+    public static final int CEC_KEYCODE_VOLUME_UP = 0x41;
+    public static final int CEC_KEYCODE_VOLUME_DOWN = 0x42;
+    public static final int CEC_KEYCODE_MUTE = 0x43;
+    public static final int CEC_KEYCODE_PLAY = 0x44;
+    public static final int CEC_KEYCODE_STOP = 0x45;
+    public static final int CEC_KEYCODE_PAUSE = 0x46;
+    public static final int CEC_KEYCODE_RECORD = 0x47;
+    public static final int CEC_KEYCODE_REWIND = 0x48;
+    public static final int CEC_KEYCODE_FAST_FORWARD = 0x49;
+    public static final int CEC_KEYCODE_EJECT = 0x4A;
+    public static final int CEC_KEYCODE_FORWARD = 0x4B;
+    public static final int CEC_KEYCODE_BACKWARD = 0x4C;
+    public static final int CEC_KEYCODE_POWER_TOGGLE_FUNCTION = 0x6B;
+    public static final int CEC_KEYCODE_POWER_OFF_FUNCTION = 0x6C;
+    public static final int CEC_KEYCODE_POWER_ON_FUNCTION = 0x6D;
+    public static final int CEC_KEYCODE_F1_BLUE = 0x71;
+    public static final int CEC_KEYCODE_F2_RED = 0x72;
+    public static final int CEC_KEYCODE_F3_GREEN = 0x73;
+    public static final int CEC_KEYCODE_F4_YELLOW = 0x74;
+    public static final int CEC_KEYCODE_DATA = 0x76;
 
     public static final int UNRECOGNIZED_OPCODE = 0x0;
 
@@ -95,6 +130,9 @@
     public static final int CEC_POWER_STATUS_IN_TRANSITION_TO_ON = 0x2;
     public static final int CEC_POWER_STATUS_IN_TRANSITION_TO_STANDBY = 0x3;
 
+    /** Poll Message Success */
+    public static final String POLL_SUCCESS = "POLL message sent";
+
     // CEC Device feature list
     public static final String HDMI_CEC_FEATURE = "feature:android.hardware.hdmi.cec";
     public static final String LEANBACK_FEATURE = "feature:android.software.leanback";
@@ -107,4 +145,13 @@
      */
     public static final File CEC_MAP_FOLDER =
             new File(System.getProperty("java.io.tmpdir"), "cec-cts-temp");
+
+
+    // Power Control Modes for source devices
+    public static final String POWER_CONTROL_MODE_BROADCAST = "broadcast";
+    public static final String POWER_CONTROL_MODE_NONE = "none";
+    public static final String POWER_CONTROL_MODE_TV = "to_tv";
+
+    // CEC 2.0 Report Feature Bits
+    public static final int FEATURES_SINK_SUPPORTS_ARC_TX_BIT = 0x4;
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/RemoteControlPassthrough.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/RemoteControlPassthrough.java
index 6486313..c12bae1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/RemoteControlPassthrough.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/RemoteControlPassthrough.java
@@ -18,6 +18,8 @@
 
 import com.android.tradefed.device.ITestDevice;
 
+import java.util.HashMap;
+
 /** Helper class with methods to test the remote control passthrough functionality */
 public final class RemoteControlPassthrough {
 
@@ -33,6 +35,9 @@
     /** The command to clear the main activity. */
     private static final String CLEAR_COMMAND = String.format("pm clear %s", PACKAGE);
 
+    private static final HashMap<Integer, String> mUserControlPressKeys_20 =
+            createUserControlPressKeys_20();
+
     /**
      * Tests that the device responds correctly to a {@code <USER_CONTROL_PRESSED>} message followed
      * immediately by a {@code <USER_CONTROL_RELEASED>} message.
@@ -50,23 +55,23 @@
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_UP, false);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_UP, false);
         LogHelper.assertLog(device, CLASS, "Short press KEYCODE_DPAD_UP");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_DOWN, false);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_DOWN, false);
         LogHelper.assertLog(device, CLASS, "Short press KEYCODE_DPAD_DOWN");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_LEFT, false);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_LEFT, false);
         LogHelper.assertLog(device, CLASS, "Short press KEYCODE_DPAD_LEFT");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_RIGHT, false);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_RIGHT, false);
         LogHelper.assertLog(device, CLASS, "Short press KEYCODE_DPAD_RIGHT");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_SELECT, false);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_SELECT, false);
         LogHelper.assertLog(
                 device, CLASS, "Short press KEYCODE_DPAD_CENTER", "Short press KEYCODE_ENTER");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_BACK, false);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_BACK, false);
         LogHelper.assertLog(device, CLASS, "Short press KEYCODE_BACK");
     }
 
@@ -87,23 +92,23 @@
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_UP, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_UP, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_UP");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_DOWN, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_DOWN, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_DOWN");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_LEFT, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_LEFT, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_LEFT");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_RIGHT, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_RIGHT, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_RIGHT");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_SELECT, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_SELECT, true);
         LogHelper.assertLog(
                 device, CLASS, "Long press KEYCODE_DPAD_CENTER", "Long press KEYCODE_ENTER");
         hdmiCecClient.sendUserControlPressAndRelease(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_BACK, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_BACK, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_BACK");
     }
 
@@ -124,22 +129,22 @@
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
         hdmiCecClient.sendUserControlPress(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_UP, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_UP, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_UP");
         hdmiCecClient.sendUserControlPress(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_DOWN, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_DOWN, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_DOWN");
         hdmiCecClient.sendUserControlPress(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_LEFT, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_LEFT, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_LEFT");
         hdmiCecClient.sendUserControlPress(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_RIGHT, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_RIGHT, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_RIGHT");
         hdmiCecClient.sendUserControlPress(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_SELECT, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_SELECT, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_CENTER");
         hdmiCecClient.sendUserControlPress(
-                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_CONTROL_BACK, true);
+                sourceDevice, dutLogicalAddress, HdmiCecConstants.CEC_KEYCODE_BACK, true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_BACK");
     }
 
@@ -163,44 +168,160 @@
         hdmiCecClient.sendUserControlInterruptedPressAndHold(
                 sourceDevice,
                 dutLogicalAddress,
-                HdmiCecConstants.CEC_CONTROL_UP,
-                HdmiCecConstants.CEC_CONTROL_BACK,
+                HdmiCecConstants.CEC_KEYCODE_UP,
+                HdmiCecConstants.CEC_KEYCODE_BACK,
                 true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_UP");
         hdmiCecClient.sendUserControlInterruptedPressAndHold(
                 sourceDevice,
                 dutLogicalAddress,
-                HdmiCecConstants.CEC_CONTROL_DOWN,
-                HdmiCecConstants.CEC_CONTROL_UP,
+                HdmiCecConstants.CEC_KEYCODE_DOWN,
+                HdmiCecConstants.CEC_KEYCODE_UP,
                 true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_DOWN");
         hdmiCecClient.sendUserControlInterruptedPressAndHold(
                 sourceDevice,
                 dutLogicalAddress,
-                HdmiCecConstants.CEC_CONTROL_LEFT,
-                HdmiCecConstants.CEC_CONTROL_DOWN,
+                HdmiCecConstants.CEC_KEYCODE_LEFT,
+                HdmiCecConstants.CEC_KEYCODE_DOWN,
                 true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_LEFT");
         hdmiCecClient.sendUserControlInterruptedPressAndHold(
                 sourceDevice,
                 dutLogicalAddress,
-                HdmiCecConstants.CEC_CONTROL_RIGHT,
-                HdmiCecConstants.CEC_CONTROL_LEFT,
+                HdmiCecConstants.CEC_KEYCODE_RIGHT,
+                HdmiCecConstants.CEC_KEYCODE_LEFT,
                 true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_RIGHT");
         hdmiCecClient.sendUserControlInterruptedPressAndHold(
                 sourceDevice,
                 dutLogicalAddress,
-                HdmiCecConstants.CEC_CONTROL_SELECT,
-                HdmiCecConstants.CEC_CONTROL_RIGHT,
+                HdmiCecConstants.CEC_KEYCODE_SELECT,
+                HdmiCecConstants.CEC_KEYCODE_RIGHT,
                 true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_DPAD_CENTER");
         hdmiCecClient.sendUserControlInterruptedPressAndHold(
                 sourceDevice,
                 dutLogicalAddress,
-                HdmiCecConstants.CEC_CONTROL_BACK,
-                HdmiCecConstants.CEC_CONTROL_SELECT,
+                HdmiCecConstants.CEC_KEYCODE_BACK,
+                HdmiCecConstants.CEC_KEYCODE_SELECT,
                 true);
         LogHelper.assertLog(device, CLASS, "Long press KEYCODE_BACK");
     }
+
+    /**
+     * Tests that the device responds correctly to a {@code <User Control Pressed> [keyCode]} press
+     * and release operation when it has an additional parameter following the keyCode.
+     */
+    public static void checkUserControlPressAndReleaseWithAdditionalParams(
+            HdmiCecClientWrapper hdmiCecClient,
+            ITestDevice device,
+            LogicalAddress sourceDevice,
+            LogicalAddress dutLogicalAddress)
+            throws Exception {
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // Clear logcat.
+        device.executeAdbCommand("logcat", "-c");
+        // Start the APK and wait for it to complete.
+        device.executeShellCommand(START_COMMAND);
+        hdmiCecClient.sendUserControlPressAndReleaseWithAdditionalParams(
+                sourceDevice,
+                dutLogicalAddress,
+                HdmiCecConstants.CEC_KEYCODE_UP,
+                HdmiCecConstants.CEC_KEYCODE_DOWN);
+        LogHelper.assertLog(device, CLASS, "Short press KEYCODE_DPAD_UP");
+    }
+
+    /**
+     * Tests that the device that support cec version 2.0 responds correctly to a
+     * {@code <USER_CONTROL_PRESSED>} message followed immediately by a
+     * {@code <USER_CONTROL_RELEASED>} message.
+     */
+    public static void checkUserControlPressAndRelease_20(
+            HdmiCecClientWrapper hdmiCecClient,
+            ITestDevice device,
+            LogicalAddress sourceDevice,
+            LogicalAddress dutLogicalAddress)
+            throws Exception {
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // Clear logcat.
+        device.executeAdbCommand("logcat", "-c");
+        // Start the APK and wait for it to complete.
+        device.executeShellCommand(START_COMMAND);
+
+        for (Integer userControlPressKey : mUserControlPressKeys_20.keySet()) {
+            hdmiCecClient.sendUserControlPressAndRelease(
+                    sourceDevice, dutLogicalAddress, userControlPressKey, false);
+            LogHelper.assertLog(
+                    device,
+                    CLASS,
+                    "Short press KEYCODE_" + mUserControlPressKeys_20.get(userControlPressKey));
+        }
+    }
+
+    private static HashMap<Integer, String> createUserControlPressKeys_20() {
+        HashMap<Integer, String> userControlPressKeys = new HashMap<Integer, String>();
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_UP,"DPAD_UP");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_DOWN,"DPAD_DOWN");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_LEFT,"DPAD_LEFT");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_RIGHT,"DPAD_RIGHT");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_ROOT_MENU,"MENU");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_SETUP_MENU,"SETTINGS");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_CONTENTS_MENU,"TV_CONTENTS_MENU");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_BACK,"BACK");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_MEDIA_TOP_MENU,"MEDIA_TOP_MENU");
+        userControlPressKeys.put(
+                HdmiCecConstants.CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU,"TV_MEDIA_CONTEXT_MENU");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBER_0_OR_NUMBER_10,"0");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBERS_1,"1");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBERS_2,"2");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBERS_3,"3");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBERS_4,"4");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBERS_5,"5");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBERS_6,"6");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBERS_7,"7");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBERS_8,"8");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_NUMBERS_9,"9");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_CHANNEL_UP,"CHANNEL_UP");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_CHANNEL_DOWN,"CHANNEL_DOWN");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_PREVIOUS_CHANNEL,"LAST_CHANNEL");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_DISPLAY_INFORMATION,"INFO");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_PLAY,"MEDIA_PLAY");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_STOP,"MEDIA_STOP");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_PAUSE,"MEDIA_PAUSE");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_RECORD,"MEDIA_RECORD");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_REWIND,"MEDIA_REWIND");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_FAST_FORWARD,"MEDIA_FAST_FORWARD");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_EJECT,"MEDIA_EJECT");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_FORWARD,"MEDIA_NEXT");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_BACKWARD,"MEDIA_PREVIOUS");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_F1_BLUE,"PROG_BLUE");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_F2_RED,"PROG_RED");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_F3_GREEN,"PROG_GREEN");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_F4_YELLOW,"PROG_YELLOW");
+        userControlPressKeys.put(HdmiCecConstants.CEC_KEYCODE_DATA,"TV_DATA_SERVICE");
+        return userControlPressKeys;
+    }
+
+    public static void checkUserControlPressAndRelease(
+            HdmiCecClientWrapper hdmiCecClient,
+            ITestDevice device,
+            LogicalAddress sourceDevice,
+            LogicalAddress dutLogicalAddress,
+            int cecKeycode,
+            String androidKeycode)
+            throws Exception {
+        // Clear activity
+        device.executeShellCommand(CLEAR_COMMAND);
+        // Clear logcat.
+        device.executeAdbCommand("logcat", "-c");
+        // Start the APK and wait for it to complete.
+        device.executeShellCommand(START_COMMAND);
+
+        hdmiCecClient.sendUserControlPressAndRelease(
+                sourceDevice, dutLogicalAddress, cecKeycode, false);
+        LogHelper.assertLog(device, CLASS, "Short press KEYCODE_" + androidKeycode);
+    }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java
index 0b217b8..e27ae7e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecSystemAudioModeTest.java
@@ -251,7 +251,7 @@
                 CecMessage.formatParams(HdmiCecConstants.TV_PHYSICAL_ADDRESS,
                     HdmiCecConstants.PHYSICAL_ADDRESS_LENGTH));
         hdmiCecClient.sendUserControlPressAndRelease(LogicalAddress.TV, AUDIO_DEVICE,
-                HdmiCecConstants.CEC_CONTROL_MUTE, false);
+                HdmiCecConstants.CEC_KEYCODE_MUTE, false);
         assertWithMessage("Device is not muted")
                 .that(AudioManagerHelper.isDeviceMuted(getDevice()))
                 .isTrue();
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecGeneralProtocolTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecGeneralProtocolTest.java
new file mode 100644
index 0000000..140c113
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecGeneralProtocolTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.hdmicec.cts.common;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hdmicec.cts.BaseHdmiCecCtsTest;
+import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+import android.hdmicec.cts.RemoteControlPassthrough;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/** HDMI CEC 2.0 general protocol tests */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class HdmiCecGeneralProtocolTest extends BaseHdmiCecCtsTest {
+
+    @Rule
+    public RuleChain ruleChain =
+            RuleChain.outerRule(CecRules.requiresCec(this))
+                    .around(CecRules.requiresLeanback(this))
+                    .around(hdmiCecClient);
+
+    /**
+     * Test HF4-2-5, HF4-2-6 (CEC 2.0)
+     *
+     * <p>Tests that the device ignores any additional trailing parameters in an otherwise correct
+     * CEC message.
+     *
+     * <p>e.g. If {@code 14:44:01:02 (<UCP>[KEYCODE_DPAD_UP])} is sent to the DUT, the DUT should
+     * ignore the last byte of the parameter and treat it as {@code <UCP>[KEYCODE_DPAD_UP]}
+     */
+    @Test
+    public void cect_hf_ignoreAdditionalParams() throws Exception {
+        setCec20();
+        RemoteControlPassthrough.checkUserControlPressAndReleaseWithAdditionalParams(
+                hdmiCecClient, getDevice(), LogicalAddress.RECORDER_1, getTargetLogicalAddress());
+    }
+
+    /**
+     * Test HF4-2-15 (CEC 2.0)
+     *
+     * <p>Tests that the DUT responds to mandatory messages in both on and standby states
+     */
+    @Test
+    public void cect_hf_4_2_15() throws Exception {
+        setCec20();
+        int cecVersion = HdmiCecConstants.CEC_VERSION_2_0;
+        int physicalAddressParams;
+        int features = 0;
+        String osdName;
+
+        sendDeviceToSleep();
+        try {
+            // Check POLL message response
+            hdmiCecClient.sendPoll();
+            if (!hdmiCecClient.checkConsoleOutput(HdmiCecConstants.POLL_SUCCESS)) {
+                throw new Exception("Device did not respond to Poll");
+            }
+
+            // Check CEC version
+            hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.GET_CEC_VERSION);
+            String message =
+                    hdmiCecClient.checkExpectedOutput(
+                            hdmiCecClient.getSelfDevice(), CecOperand.CEC_VERSION);
+            assertThat(CecMessage.getParams(message)).isEqualTo(cecVersion);
+
+            // Give physical address
+            hdmiCecClient.sendCecMessage(
+                    hdmiCecClient.getSelfDevice(), CecOperand.GIVE_PHYSICAL_ADDRESS);
+            message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+            physicalAddressParams = CecMessage.getParams(message);
+
+            // Give features
+            hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.GIVE_FEATURES);
+            message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_FEATURES);
+            features = CecMessage.getParams(message);
+
+            // Give power status
+            hdmiCecClient.sendCecMessage(
+                    hdmiCecClient.getSelfDevice(), CecOperand.GIVE_POWER_STATUS);
+            message =
+                    hdmiCecClient.checkExpectedOutput(
+                            hdmiCecClient.getSelfDevice(), CecOperand.REPORT_POWER_STATUS);
+            assertThat(CecMessage.getParams(message))
+                    .isEqualTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
+
+            // Give OSD name
+            hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.GIVE_OSD_NAME);
+            message =
+                    hdmiCecClient.checkExpectedOutput(
+                            hdmiCecClient.getSelfDevice(), CecOperand.SET_OSD_NAME);
+            osdName = CecMessage.getParamsAsString(message);
+        } finally {
+            wakeUpDevice();
+        }
+
+        // Repeat the above with DUT not in standby, and verify that the responses are the same,
+        // except the <Report Power Status> message
+        // Check POLL message response
+        hdmiCecClient.sendPoll();
+        if (!hdmiCecClient.checkConsoleOutput(HdmiCecConstants.POLL_SUCCESS)) {
+            throw new Exception("Device did not respond to Poll");
+        }
+
+        // Check CEC version
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.GET_CEC_VERSION);
+        String message =
+                hdmiCecClient.checkExpectedOutput(
+                        hdmiCecClient.getSelfDevice(), CecOperand.CEC_VERSION);
+        assertThat(CecMessage.getParams(message)).isEqualTo(cecVersion);
+
+        // Give physical address
+        hdmiCecClient.sendCecMessage(
+                hdmiCecClient.getSelfDevice(), CecOperand.GIVE_PHYSICAL_ADDRESS);
+        message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_PHYSICAL_ADDRESS);
+        assertThat(CecMessage.getParams(message)).isEqualTo(physicalAddressParams);
+
+        // Give features
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.GIVE_FEATURES);
+        message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_FEATURES);
+        assertThat(CecMessage.getParams(message)).isEqualTo(features);
+
+        // Give power status
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.GIVE_POWER_STATUS);
+        message =
+                hdmiCecClient.checkExpectedOutput(
+                        hdmiCecClient.getSelfDevice(), CecOperand.REPORT_POWER_STATUS);
+        assertThat(CecMessage.getParams(message)).isEqualTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
+
+        // Give OSD name
+        hdmiCecClient.sendCecMessage(hdmiCecClient.getSelfDevice(), CecOperand.GIVE_OSD_NAME);
+        message =
+                hdmiCecClient.checkExpectedOutput(
+                        hdmiCecClient.getSelfDevice(), CecOperand.SET_OSD_NAME);
+        assertThat(CecMessage.getParamsAsString(message)).isEqualTo(osdName);
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecInvalidMessagesTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecInvalidMessagesTest.java
index ce4c4a1..fd7f10a 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecInvalidMessagesTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecInvalidMessagesTest.java
@@ -180,7 +180,7 @@
         // Start the APK and wait for it to complete.
         device.executeShellCommand(START_COMMAND);
         hdmiCecClient.sendUserControlPressAndRelease(
-                source, LogicalAddress.BROADCAST, HdmiCecConstants.CEC_CONTROL_UP, false);
+                source, LogicalAddress.BROADCAST, HdmiCecConstants.CEC_KEYCODE_UP, false);
         LogHelper.assertLogDoesNotContain(getDevice(), CLASS, "Short press KEYCODE_DPAD_UP");
     }
 
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java
index 966173a..50b57de 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecPowerStatusTest.java
@@ -34,6 +34,7 @@
 import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -124,6 +125,123 @@
     }
 
     /**
+     * Test HF4-6-22 (CEC 2.0)
+     *
+     * <p>Verifies that the DUT notifies the transition back to Standby state if an ongoing
+     * transition from standby state to power on state is interrupted.
+     */
+    @Test
+    public void cect_hf4_6_22_interruptedPowerOn() throws Exception {
+        ITestDevice device = getDevice();
+        setCec20();
+
+        try {
+            // Turn device off
+            sendDeviceToSleep();
+
+            List<Integer> keycodes = new ArrayList<>();
+            keycodes.add(HdmiCecConstants.CEC_KEYCODE_POWER_ON_FUNCTION);
+            keycodes.add(HdmiCecConstants.CEC_KEYCODE_POWER_OFF_FUNCTION);
+
+            // Send a <UCP>[Power On] immediately followed by a <UCP>[Power Off]
+            hdmiCecClient.sendMultipleUserControlPressAndRelease(LogicalAddress.TV, keycodes);
+
+            String reportPowerStatus =
+                    hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_POWER_STATUS);
+
+            switch (CecMessage.getParams(reportPowerStatus)) {
+                case HdmiCecConstants.CEC_POWER_STATUS_STANDBY:
+                    // No further messages are expected, check for 5s outside the switch case.
+                    break;
+                case HdmiCecConstants.CEC_POWER_STATUS_IN_TRANSITION_TO_STANDBY:
+                    reportPowerStatus =
+                            hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_POWER_STATUS);
+                    assertThat(CecMessage.getParams(reportPowerStatus))
+                            .isEqualTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
+                    break;
+                case HdmiCecConstants.CEC_POWER_STATUS_IN_TRANSITION_TO_ON:
+                case HdmiCecConstants.CEC_POWER_STATUS_ON:
+                    reportPowerStatus =
+                            hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_POWER_STATUS);
+                    int powerState = CecMessage.getParams(reportPowerStatus);
+                    if (powerState == HdmiCecConstants.CEC_POWER_STATUS_IN_TRANSITION_TO_STANDBY) {
+                        // If it is in transition, wait for another <Report Power Status>[Power Off]
+                        reportPowerStatus =
+                                hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_POWER_STATUS);
+                        powerState = CecMessage.getParams(reportPowerStatus);
+                    }
+                    // If no <Report Power Status>[Power Off] is received, fail the test
+                    assertThat(powerState).isEqualTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
+                    break;
+            }
+            // Make sure there are no further <Report Power Status> for 5s
+            hdmiCecClient.checkOutputDoesNotContainMessage(
+                    LogicalAddress.BROADCAST, CecOperand.REPORT_POWER_STATUS, 5000);
+        } finally {
+            wakeUpDevice();
+        }
+    }
+
+    /**
+     * Test HF4-6-23 (CEC 2.0)
+     *
+     * <p>Verifies that the DUT notifies the transition back to On state if an ongoing transition
+     * from On state to Standby state is interrupted.
+     */
+    @Test
+    public void cect_hf4_6_23_interruptedStandby() throws Exception {
+        ITestDevice device = getDevice();
+        setCec20();
+
+        try {
+            // Turn device off
+            wakeUpDevice();
+            WakeLockHelper.acquirePartialWakeLock(getDevice());
+
+            List<Integer> keycodes = new ArrayList<>();
+            keycodes.add(HdmiCecConstants.CEC_KEYCODE_POWER_OFF_FUNCTION);
+            keycodes.add(HdmiCecConstants.CEC_KEYCODE_POWER_ON_FUNCTION);
+
+            // Send a <UCP>[Power Off] immediately followed by a <UCP>[Power On]
+            hdmiCecClient.sendMultipleUserControlPressAndRelease(LogicalAddress.TV, keycodes);
+
+            String reportPowerStatus =
+                    hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_POWER_STATUS);
+
+            switch (CecMessage.getParams(reportPowerStatus)) {
+                case HdmiCecConstants.CEC_POWER_STATUS_ON:
+                    // No further messages are expected, check for 5s outside the switch case.
+                    break;
+                case HdmiCecConstants.CEC_POWER_STATUS_IN_TRANSITION_TO_ON:
+                    reportPowerStatus =
+                            hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_POWER_STATUS);
+                    assertThat(CecMessage.getParams(reportPowerStatus))
+                            .isEqualTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
+                    break;
+                case HdmiCecConstants.CEC_POWER_STATUS_IN_TRANSITION_TO_STANDBY:
+                case HdmiCecConstants.CEC_POWER_STATUS_STANDBY:
+                    reportPowerStatus =
+                            hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_POWER_STATUS);
+                    int powerState = CecMessage.getParams(reportPowerStatus);
+                    if (powerState == HdmiCecConstants.CEC_POWER_STATUS_IN_TRANSITION_TO_ON) {
+                        // If it is in transition, wait for another <Report Power Status>[Power On]
+                        reportPowerStatus =
+                                hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_POWER_STATUS);
+                        powerState = CecMessage.getParams(reportPowerStatus);
+                    }
+                    // If no <Report Power Status>[Power On] is received, fail the test
+                    assertThat(powerState).isEqualTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
+                    break;
+            }
+            // Make sure there are no further <Report Power Status> for 5s
+            hdmiCecClient.checkOutputDoesNotContainMessage(
+                    LogicalAddress.BROADCAST, CecOperand.REPORT_POWER_STATUS, 5000);
+        } finally {
+            wakeUpDevice();
+        }
+    }
+
+    /**
      * Test 11.1.14-1, 11.2.14-1
      *
      * <p>Tests that the device sends a {@code <REPORT_POWER_STATUS>} with params 0x0 when the
@@ -186,9 +304,9 @@
     @Test
     public void cect_hf4_6_8_userControlPressed_powerOn() throws Exception {
         ITestDevice device = getDevice();
-        List<Integer> powerControlOperands = Arrays.asList(HdmiCecConstants.CEC_CONTROL_POWER,
-                HdmiCecConstants.CEC_CONTROL_POWER_ON_FUNCTION,
-                HdmiCecConstants.CEC_CONTROL_POWER_TOGGLE_FUNCTION);
+        List<Integer> powerControlOperands = Arrays.asList(HdmiCecConstants.CEC_KEYCODE_POWER,
+                HdmiCecConstants.CEC_KEYCODE_POWER_ON_FUNCTION,
+                HdmiCecConstants.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
 
         LogicalAddress source = hasDeviceType(HdmiCecConstants.CEC_DEVICE_TYPE_TV)
                 ? LogicalAddress.PLAYBACK_1
@@ -224,8 +342,8 @@
     public void cect_hf4_6_10_userControlPressed_powerOff() throws Exception {
         ITestDevice device = getDevice();
         List<Integer> powerControlOperands = Arrays.asList(
-                HdmiCecConstants.CEC_CONTROL_POWER_OFF_FUNCTION,
-                HdmiCecConstants.CEC_CONTROL_POWER_TOGGLE_FUNCTION);
+                HdmiCecConstants.CEC_KEYCODE_POWER_OFF_FUNCTION,
+                HdmiCecConstants.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
 
         LogicalAddress source = hasDeviceType(HdmiCecConstants.CEC_DEVICE_TYPE_TV)
                 ? LogicalAddress.PLAYBACK_1
@@ -252,4 +370,97 @@
             }
         }
     }
+
+    /**
+     * Test HF4-6-26
+     *
+     * <p> Verify that, when a Source device is put to Standby by the user, it does not broadcast a
+     * system {@code <Standby>} message unless explicitly requested by the user.
+     */
+    @Test
+    public void cect_hf4_6_26_standby_noBroadcast_20() throws Exception {
+        ITestDevice device = getDevice();
+        setCec20();
+        String previousPowerControlMode =
+                setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_NONE);
+        try {
+            sendDeviceToSleep();
+            hdmiCecClient.checkOutputDoesNotContainMessage(
+                    LogicalAddress.BROADCAST, CecOperand.STANDBY);
+        } finally {
+            wakeUpDevice();
+            setPowerControlMode(previousPowerControlMode);
+        }
+    }
+
+    /*
+     * Test HF4-6-28
+     *
+     * <p>Tests that the DUT handles {@code <User Control Pressed>} of any power related buttons
+     * correctly
+     */
+    @Test
+    public void cect_hf_4_6_28_testPowerUcpHandling() throws Exception {
+        setCec20();
+        int waitSeconds = 10;
+        ITestDevice device = getDevice();
+        // Ensure device is awake.
+        wakeUpDevice();
+
+        // Acquire the wakelock.
+        WakeLockHelper.acquirePartialWakeLock(device);
+        try {
+            // All <UCP> commands will be sent from TV.
+            LogicalAddress source = LogicalAddress.TV;
+            hdmiCecClient.sendUserControlPressAndRelease(
+                    source, HdmiCecConstants.CEC_KEYCODE_POWER_TOGGLE_FUNCTION, false);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
+
+            // Toggle power again, DUT should wakeup.
+            hdmiCecClient.sendUserControlPressAndRelease(
+                    source, HdmiCecConstants.CEC_KEYCODE_POWER_TOGGLE_FUNCTION, false);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
+
+            // Send <UCP>[Power On]. DUT should remain in ON state, check for 10s.
+            hdmiCecClient.sendUserControlPressAndRelease(
+                    source, HdmiCecConstants.CEC_KEYCODE_POWER_ON_FUNCTION, false);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
+            TimeUnit.SECONDS.sleep(waitSeconds);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
+
+            // Send <UCP>[Power Off]. DUT should got to OFF state, check for 10s.
+            hdmiCecClient.sendUserControlPressAndRelease(
+                    source, HdmiCecConstants.CEC_KEYCODE_POWER_OFF_FUNCTION, false);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
+            TimeUnit.SECONDS.sleep(waitSeconds);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
+
+            // Send <UCP>[Power Off] again. DUT should stay in OFF state, check for 10s.
+            hdmiCecClient.sendUserControlPressAndRelease(
+                    source, HdmiCecConstants.CEC_KEYCODE_POWER_OFF_FUNCTION, false);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
+            TimeUnit.SECONDS.sleep(waitSeconds);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
+
+            // Send <UCP>[Power On]. DUT should go to ON state, check for 10s.
+            hdmiCecClient.sendUserControlPressAndRelease(
+                    source, HdmiCecConstants.CEC_KEYCODE_POWER_ON_FUNCTION, false);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
+            TimeUnit.SECONDS.sleep(waitSeconds);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
+
+            // Send <UCP> [Power]. DUT should go to standby.
+            hdmiCecClient.sendUserControlPressAndRelease(
+                    source, HdmiCecConstants.CEC_KEYCODE_POWER, false);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_STANDBY);
+
+            // Send <UCP> [Power]. DUT should wakeup.
+            hdmiCecClient.sendUserControlPressAndRelease(
+                    source, HdmiCecConstants.CEC_KEYCODE_POWER, false);
+            waitForTransitionTo(HdmiCecConstants.CEC_POWER_STATUS_ON);
+        } finally {
+            // Wake up the device. This will also release the wakelock.
+            wakeUpDevice();
+        }
+    }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemAudioControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemAudioControlTest.java
index 5a7cda1..0eebc18 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemAudioControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemAudioControlTest.java
@@ -97,7 +97,7 @@
         String message =
                 hdmiCecClient.checkExpectedOutput(
                         hdmiCecClient.getSelfDevice(), CecOperand.USER_CONTROL_PRESSED);
-        assertThat(CecMessage.getParams(message)).isEqualTo(HdmiCecConstants.CEC_CONTROL_VOLUME_UP);
+        assertThat(CecMessage.getParams(message)).isEqualTo(HdmiCecConstants.CEC_KEYCODE_VOLUME_UP);
         hdmiCecClient.checkExpectedOutput(
                 hdmiCecClient.getSelfDevice(), CecOperand.USER_CONTROL_RELEASED);
         /* TODO: b/174733146  For TV devices, assert that the volume level has not changed. */
@@ -107,7 +107,7 @@
                 hdmiCecClient.checkExpectedOutput(
                         hdmiCecClient.getSelfDevice(), CecOperand.USER_CONTROL_PRESSED);
         assertThat(CecMessage.getParams(message))
-                .isEqualTo(HdmiCecConstants.CEC_CONTROL_VOLUME_DOWN);
+                .isEqualTo(HdmiCecConstants.CEC_KEYCODE_VOLUME_DOWN);
         hdmiCecClient.checkExpectedOutput(
                 hdmiCecClient.getSelfDevice(), CecOperand.USER_CONTROL_RELEASED);
         /* TODO: b/174733146  For TV devices, assert that the volume level has not changed. */
@@ -138,7 +138,7 @@
         String message =
                 hdmiCecClient.checkExpectedOutput(
                         hdmiCecClient.getSelfDevice(), CecOperand.USER_CONTROL_PRESSED);
-        assertThat(CecMessage.getParams(message)).isEqualTo(HdmiCecConstants.CEC_CONTROL_MUTE);
+        assertThat(CecMessage.getParams(message)).isEqualTo(HdmiCecConstants.CEC_KEYCODE_MUTE);
         hdmiCecClient.checkExpectedOutput(
                 hdmiCecClient.getSelfDevice(), CecOperand.USER_CONTROL_RELEASED);
         /* TODO: b/174733146  For TV devices, assert that the volume level has not changed. */
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemInformationTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemInformationTest.java
index 15eca65..33f705e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemInformationTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemInformationTest.java
@@ -198,4 +198,28 @@
             wakeUpDevice();
         }
     }
+
+    /**
+     * Test HF4-2-16 (CEC 2.0)
+     *
+     * <p>Tests that the DUT responds to a {@code <Give Device Vendor Id>} with a {@code <Device
+     * Vendor ID>} message or a {@code <Feature Abort>[Unrecognized Opcode]}
+     */
+    @Test
+    public void cect_hf_4_2_16_GiveDeviceVendorId() throws Exception {
+        ITestDevice device = getDevice();
+        setCec20();
+        hdmiCecClient.sendCecMessage(
+                hdmiCecClient.getSelfDevice(), CecOperand.GIVE_DEVICE_VENDOR_ID);
+        String message =
+                hdmiCecClient.checkExpectedOutputOrFeatureAbort(
+                        LogicalAddress.BROADCAST,
+                        CecOperand.DEVICE_VENDOR_ID,
+                        CecOperand.GIVE_DEVICE_VENDOR_ID,
+                        HdmiCecConstants.ABORT_UNRECOGNIZED_MODE);
+        if (CecMessage.getOperand(message) == CecOperand.GIVE_DEVICE_VENDOR_ID) {
+            assertThat(CecMessage.getParams(message))
+                    .isNotEqualTo(HdmiCecConstants.INVALID_VENDOR_ID);
+        }
+    }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemStandbyTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemStandbyTest.java
index 19eff6c..6541ca6 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemStandbyTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecSystemStandbyTest.java
@@ -40,10 +40,9 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecSystemStandbyTest extends BaseHdmiCecCtsTest {
 
-    private static final String HDMI_CONTROL_DEVICE_AUTO_OFF =
-            "hdmi_control_auto_device_off_enabled";
-    private static final String POWER_CONTROL_MODE = "send_standby_on_sleep";
-    private static final String POWER_CONTROL_MODE_NONE = "none";
+    private static final String TV_SEND_STANDBY_ON_SLEEP = "tv_send_standby_on_sleep";
+    private static final String TV_SEND_STANDBY_ON_SLEEP_ENABLED = "1";
+    private static final String TV_SEND_STANDBY_ON_SLEEP_DISABLED = "0";
 
     public List<LogicalAddress> mLogicalAddresses = new ArrayList<>();
     public boolean previousDeviceAutoOff;
@@ -59,7 +58,7 @@
     public void initialTestSetup() throws Exception {
         defineLogicalAddressList();
         previousDeviceAutoOff = setHdmiControlDeviceAutoOff(false);
-        previousPowerControlMode = setPowerControlMode(POWER_CONTROL_MODE_NONE);
+        previousPowerControlMode = setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_NONE);
     }
 
     @After
@@ -140,19 +139,9 @@
     }
 
     private boolean setHdmiControlDeviceAutoOff(boolean turnOn) throws Exception {
-        ITestDevice device = getDevice();
-        String val = device.executeShellCommand("settings get global " +
-                HDMI_CONTROL_DEVICE_AUTO_OFF).trim();
-        String valToSet = turnOn ? "1" : "0";
-        device.executeShellCommand("settings put global "
-                + HDMI_CONTROL_DEVICE_AUTO_OFF + " " + valToSet);
-        device.executeShellCommand("settings get global " + HDMI_CONTROL_DEVICE_AUTO_OFF);
-        return val.equals("1");
-    }
-
-    private String setPowerControlMode(String valToSet) throws Exception {
-        String val = getSettingsValue(POWER_CONTROL_MODE);
-        setSettingsValue(POWER_CONTROL_MODE, valToSet);
-        return val;
+        String val = getSettingsValue(TV_SEND_STANDBY_ON_SLEEP);
+        setSettingsValue(TV_SEND_STANDBY_ON_SLEEP, turnOn ? TV_SEND_STANDBY_ON_SLEEP_ENABLED
+                                                          : TV_SEND_STANDBY_ON_SLEEP_DISABLED);
+        return val == TV_SEND_STANDBY_ON_SLEEP_ENABLED;
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
index 5dd4b9d..408d719 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecPowerStatusTest.java
@@ -52,16 +52,16 @@
 
     private static final List<String> UCP_POWER_MSGS = new ArrayList<>(Arrays.asList(
             CecMessage.buildCecMessage(LogicalAddress.PLAYBACK_1, LogicalAddress.TV,
-                    CecOperand.USER_CONTROL_PRESSED, HdmiCecConstants.CEC_CONTROL_POWER),
+                    CecOperand.USER_CONTROL_PRESSED, HdmiCecConstants.CEC_KEYCODE_POWER),
             CecMessage.buildCecMessage(LogicalAddress.PLAYBACK_1, LogicalAddress.TV,
                     CecOperand.USER_CONTROL_PRESSED,
-                    HdmiCecConstants.CEC_CONTROL_POWER_TOGGLE_FUNCTION),
+                    HdmiCecConstants.CEC_KEYCODE_POWER_TOGGLE_FUNCTION),
             CecMessage.buildCecMessage(LogicalAddress.PLAYBACK_1, LogicalAddress.TV,
                     CecOperand.USER_CONTROL_PRESSED,
-                    HdmiCecConstants.CEC_CONTROL_POWER_OFF_FUNCTION),
+                    HdmiCecConstants.CEC_KEYCODE_POWER_OFF_FUNCTION),
             CecMessage.buildCecMessage(LogicalAddress.PLAYBACK_1, LogicalAddress.TV,
                     CecOperand.USER_CONTROL_PRESSED,
-                    HdmiCecConstants.CEC_CONTROL_POWER_ON_FUNCTION)));
+                    HdmiCecConstants.CEC_KEYCODE_POWER_ON_FUNCTION)));
 
     private static final List<CecOperand> VIEW_ON_MSGS =
             new ArrayList<>(Arrays.asList(CecOperand.TEXT_VIEW_ON, CecOperand.IMAGE_VIEW_ON));
@@ -140,4 +140,55 @@
             wakeUpDevice();
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * Test HF4-6-16
+     *
+     * <p>Verify that the DUT initially sends a {@code <Standby>} message to the TV when system
+     * standby feature is enabled, before sending any {@code <User Control Pressed>} with
+     * power-related operands. (Ref section 11.5.1 in CEC 2.1 specification)
+     */
+    @Test
+    public void cect_hf4_6_16_standby_tvBeforeUcp_20() throws Exception {
+        ITestDevice device = getDevice();
+        setCec20();
+        String previousPowerControlMode =
+                setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
+
+        try {
+            sendDeviceToSleepWithoutWait();
+            hdmiCecClient.checkMessagesInOrder(
+                    LogicalAddress.TV,
+                    new ArrayList<>(Arrays.asList(CecOperand.STANDBY)),
+                    UCP_POWER_MSGS);
+        } finally {
+            wakeUpDevice();
+            setPowerControlMode(previousPowerControlMode);
+        }
+    }
+
+    /**
+     * Test HF4-6-19
+     *
+     * <p>Verify that the DUT initially broadcasts a {@code <Standby>} message when the system
+     * standby feature is enabled, before sending any {@code <User Control Pressed>} with
+     * power-related operands.
+     */
+    @Test
+    public void cect_hf4_6_19_standby_broadcastBeforeUcp_20() throws Exception {
+        ITestDevice device = getDevice();
+        setCec20();
+        String previousPowerControlMode =
+                setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_BROADCAST);
+        try {
+            sendDeviceToSleepWithoutWait();
+            hdmiCecClient.checkMessagesInOrder(
+                    LogicalAddress.BROADCAST,
+                    new ArrayList<>(Arrays.asList(CecOperand.STANDBY)),
+                    UCP_POWER_MSGS);
+        } finally {
+            wakeUpDevice();
+            setPowerControlMode(previousPowerControlMode);
+        }
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
index c0ebfb8..cc7d5fd 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRemoteControlPassThroughTest.java
@@ -17,6 +17,8 @@
 package android.hdmicec.cts.playback;
 
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
+import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
 import android.hdmicec.cts.HdmiCecConstants;
 import android.hdmicec.cts.LogicalAddress;
 import android.hdmicec.cts.RemoteControlPassthrough;
@@ -74,4 +76,69 @@
         RemoteControlPassthrough.checkUserControlPressAndHold(
                 hdmiCecClient, getDevice(), LogicalAddress.TV, dutLogicalAddress);
     }
+
+    /**
+     * HF 4-8-4
+     *
+     * <p>Verify that the device that support cec version 2.0 accepts {@code <USER_CONTROL_PRESSED>}
+     * messages and maps to appropriate internal action.
+     *
+     * No Android keycode defined for {@code <CEC_KEYCODE_FAVORITE_MENU>},
+     * {@code <CEC_KEYCODE_STOP_RECORD>} and {@code <CEC_KEYCODE_PAUSE_RECORD>}
+     *
+     * The UI commands Audio Description, internet and 3D mode are introduced in CEC 2.0 devices but
+     * they haven't been implemented yet.
+     * TODO: Add these UI commands once they are implemented.
+     */
+    @Test
+    public void cect_4_8_4_UserControlPressAndRelease_20() throws Exception {
+        setCec20();
+        LogicalAddress dutLogicalAddress = getTargetLogicalAddress(getDevice(), DUT_DEVICE_TYPE);
+        RemoteControlPassthrough.checkUserControlPressAndRelease_20(
+                hdmiCecClient, getDevice(), LogicalAddress.TV, dutLogicalAddress);
+    }
+
+    /**
+     * Test HF4-8-12
+     *
+     * <p>Tests that device sends the UCP Commands related to menus (Device Root Menu, Device Setup
+     * Menu, Contents Menu, Media Top Menu, Media Context-Sensitive Menu) in the operand [RC Profile
+     * Source] that is sent in the <Report Features> message and verifies that device reacts to sent
+     * UCP commands.
+     */
+    @Test
+    public void cect_hf4_8_12_UCPForRcProfileSearchOperand() throws Exception {
+        setCec20();
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_FEATURES);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_FEATURES);
+        int remoteControlProfileSource = CecMessage.getParams(message, 4, 6);
+        if ((remoteControlProfileSource & 0x01) == 0x01) {
+            sendUcpMenuCommand(
+                    HdmiCecConstants.CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU,
+                    "TV_MEDIA_CONTEXT_MENU");
+        }
+        if ((remoteControlProfileSource & 0x02) == 0x02) {
+            sendUcpMenuCommand(HdmiCecConstants.CEC_KEYCODE_MEDIA_TOP_MENU, "MEDIA_TOP_MENU");
+        }
+        if ((remoteControlProfileSource & 0x04) == 0x04) {
+            sendUcpMenuCommand(HdmiCecConstants.CEC_KEYCODE_CONTENTS_MENU, "TV_CONTENTS_MENU");
+        }
+        if ((remoteControlProfileSource & 0x08) == 0x08) {
+            sendUcpMenuCommand(HdmiCecConstants.CEC_KEYCODE_SETUP_MENU, "SETTINGS");
+        }
+        if ((remoteControlProfileSource & 0x10) == 0x10) {
+            sendUcpMenuCommand(HdmiCecConstants.CEC_KEYCODE_ROOT_MENU, "MENU");
+        }
+    }
+
+    private void sendUcpMenuCommand(int cecKeycode, String androidKeycode) throws Exception {
+        LogicalAddress dutLogicalAddress = getTargetLogicalAddress(getDevice(), DUT_DEVICE_TYPE);
+        RemoteControlPassthrough.checkUserControlPressAndRelease(
+                hdmiCecClient,
+                getDevice(),
+                LogicalAddress.TV,
+                dutLogicalAddress,
+                cecKeycode,
+                androidKeycode);
+    }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
index 75d73ea..e715f91 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
@@ -39,10 +39,6 @@
 public final class HdmiCecRoutingControlTest extends BaseHdmiCecCtsTest {
 
     private static final int PHYSICAL_ADDRESS = 0x1000;
-    private static final String POWER_CONTROL_MODE =
-            "power_control_mode";
-    private static final String POWER_CONTROL_MODE_NONE =
-            "none";
 
     public HdmiCecRoutingControlTest() {
         super(HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
@@ -57,12 +53,6 @@
                                     this, HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE))
                     .around(hdmiCecClient);
 
-    private String setPowerControlMode(String valToSet) throws Exception {
-        String val = getSettingsValue(POWER_CONTROL_MODE);
-        setSettingsValue(POWER_CONTROL_MODE, valToSet);
-        return val;
-    }
-
     /**
      * Test 11.1.2-2, HF4-7-2
      *
@@ -143,7 +133,8 @@
     @Test
     public void cect_11_2_2_4_InactiveSourceOnStandby() throws Exception {
         ITestDevice device = getDevice();
-        String previousPowerControlMode = setPowerControlMode(POWER_CONTROL_MODE_NONE);
+        String previousPowerControlMode =
+                setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_NONE);
         try {
             int dumpsysPhysicalAddress = getDumpsysPhysicalAddress();
             hdmiCecClient.sendCecMessage(
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStandbyTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStandbyTest.java
new file mode 100644
index 0000000..d9f0b95
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecStandbyTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts.playback;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.hdmicec.cts.BaseHdmiCecCtsTest;
+import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.runner.RunWith;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+
+import java.util.concurrent.TimeUnit;
+
+/** Tests that check Standby behaviour of playback devices */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class HdmiCecStandbyTest extends BaseHdmiCecCtsTest {
+
+    @Rule
+    public RuleChain ruleChain =
+            RuleChain.outerRule(CecRules.requiresCec(this))
+                    .around(CecRules.requiresLeanback(this))
+                    .around(
+                            CecRules.requiresDeviceType(
+                                    this, HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE))
+                    .around(hdmiCecClient);
+
+    private void sendStandbyAndCheckNoStandbySent(LogicalAddress destAddress) throws Exception {
+        hdmiCecClient.broadcastActiveSource(LogicalAddress.TV);
+        TimeUnit.SECONDS.sleep(HdmiCecConstants.DEVICE_WAIT_TIME_SECONDS);
+        assertWithMessage("Device should not have been active source!")
+                .that(isDeviceActiveSource(getDevice()))
+                .isFalse();
+
+        try {
+            sendDeviceToSleep();
+            hdmiCecClient.checkOutputDoesNotContainMessage(destAddress, CecOperand.STANDBY);
+        } finally {
+            wakeUpDevice();
+        }
+    }
+
+    /**
+     * Tests that the DUT does not send a {@code <STANDBY>} to the TV when it is turned off, and is
+     * not the active source.
+     */
+    @Test
+    public void cectNoTvStandbyWhenNotActiveSource() throws Exception {
+        String prevMode = setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
+        sendStandbyAndCheckNoStandbySent(LogicalAddress.TV);
+        setPowerControlMode(prevMode);
+    }
+
+    /**
+     * Tests that the DUT does not broadcast a {@code <STANDBY>} when it is turned off, and is not
+     * the active source.
+     */
+    @Test
+    public void cectNoBroadcastStandbyWhenNotActiveSource() throws Exception {
+        String prevMode = setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_BROADCAST);
+        sendStandbyAndCheckNoStandbySent(LogicalAddress.BROADCAST);
+        setPowerControlMode(prevMode);
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
index fc9c543..963d5e4 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
@@ -154,4 +154,19 @@
             setSystemLocale(locale);
         }
     }
+
+    /**
+     * Test HF4-11-4 (CEC 2.0)
+     *
+     * <p>Tests that the DUT responds to {@code <Give Features>} with "Sink supports ARC Tx" bit not
+     * set.
+     */
+    @Test
+    public void cect_hf_4_11_4_SinkArcTxBitReset() throws Exception {
+        setCec20();
+        hdmiCecClient.sendCecMessage(LogicalAddress.TV, CecOperand.GIVE_FEATURES);
+        String message = hdmiCecClient.checkExpectedOutput(CecOperand.REPORT_FEATURES);
+        int params = CecMessage.getParams(message, 6, 8);
+        assertThat(params & HdmiCecConstants.FEATURES_SINK_SUPPORTS_ARC_TX_BIT).isEqualTo(0);
+    }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java
index 141cb63..7f666e4 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecTvPowerToggleTest.java
@@ -46,8 +46,6 @@
     private static final int OFF = 0x1;
 
     private static final LogicalAddress PLAYBACK_DEVICE = LogicalAddress.PLAYBACK_1;
-    private static final String POWER_CONTROL_MODE =
-            "hdmi_control_send_standby_on_sleep";
 
     @Rule
     public RuleChain ruleChain =
@@ -62,15 +60,6 @@
         super(HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
     }
 
-    private String setPowerControlMode(String valToSet) throws Exception {
-        ITestDevice device = getDevice();
-        String val = device.executeShellCommand("settings get global " +
-                POWER_CONTROL_MODE).trim();
-        device.executeShellCommand("settings put global "
-                + POWER_CONTROL_MODE + " " + valToSet);
-        return val;
-    }
-
     /**
      * Tests that KEYCODE_TV_POWER functions as a TV power toggle.
      * Device is awake and not active source. TV is on.
@@ -80,7 +69,8 @@
         ITestDevice device = getDevice();
         // Make sure the device is not booting up/in standby
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        String previousPowerControlMode = setPowerControlMode("to_tv");
+        String previousPowerControlMode =
+                setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
         try {
             device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
             device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
@@ -116,7 +106,8 @@
         ITestDevice device = getDevice();
         // Make sure the device is not booting up/in standby
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        String previousPowerControlMode = setPowerControlMode("to_tv");
+        String previousPowerControlMode =
+                setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
         try {
             device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
             device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
@@ -151,7 +142,8 @@
         ITestDevice device = getDevice();
         // Make sure the device is not booting up/in standby
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        String previousPowerControlMode = setPowerControlMode("to_tv");
+        String previousPowerControlMode =
+                setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
         try {
             device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
             device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
@@ -184,7 +176,8 @@
         ITestDevice device = getDevice();
         // Make sure the device is not booting up/in standby
         device.waitForBootComplete(HdmiCecConstants.REBOOT_TIMEOUT);
-        String previousPowerControlMode = setPowerControlMode("to_tv");
+        String previousPowerControlMode =
+                setPowerControlMode(HdmiCecConstants.POWER_CONTROL_MODE_TV);
         try {
             device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 0");
             device.executeShellCommand("cmd hdmi_control cec_setting set hdmi_cec_enabled 1");
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java
index e15d634..ff2ff00 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java
@@ -40,12 +40,16 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /** HDMI CEC test to check Remote Control Pass Through behaviour (Sections 11.1.13) */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecRemoteControlPassThroughTest extends BaseHdmiCecCtsTest {
 
+    private static final int WAIT_TIME_MS = 300;
+
     private HashMap<String, Integer> remoteControlKeys = new HashMap<String, Integer>();
+    private HashMap<String, Integer> remoteControlAudioKeys = new HashMap<String, Integer>();
 
     @Rule
     public RuleChain ruleChain =
@@ -80,6 +84,11 @@
                  */
                 hdmiCecClient.broadcastActiveSource(
                         LogicalAddress.RECORDER_1, hdmiCecClient.getPhysicalAddress());
+                try {
+                    TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
+                } catch (InterruptedException ex) {
+                    // Do nothing
+                }
             }
         }
     }
@@ -94,7 +103,8 @@
     public void cect_11_1_13_1_RemoteControlMessagesToRecorder() throws Exception {
         hdmiCecClient.broadcastActiveSource(
                 LogicalAddress.RECORDER_1, hdmiCecClient.getPhysicalAddress());
-        validateKeyeventToUserControlPress(LogicalAddress.RECORDER_1);
+        TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
+        validateKeyeventToUserControlPress(LogicalAddress.RECORDER_1, remoteControlKeys);
     }
 
     /**
@@ -107,7 +117,8 @@
     public void cect_11_1_13_2_RemoteControlMessagesToPlayback() throws Exception {
         hdmiCecClient.broadcastActiveSource(
                 LogicalAddress.PLAYBACK_1, hdmiCecClient.getPhysicalAddress());
-        validateKeyeventToUserControlPress(LogicalAddress.PLAYBACK_1);
+        TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
+        validateKeyeventToUserControlPress(LogicalAddress.PLAYBACK_1, remoteControlKeys);
     }
 
     /**
@@ -120,7 +131,8 @@
     public void cect_11_1_13_3_RemoteControlMessagesToTuner() throws Exception {
         hdmiCecClient.broadcastActiveSource(
                 LogicalAddress.TUNER_1, hdmiCecClient.getPhysicalAddress());
-        validateKeyeventToUserControlPress(LogicalAddress.TUNER_1);
+        TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
+        validateKeyeventToUserControlPress(LogicalAddress.TUNER_1, remoteControlKeys);
     }
 
     /**
@@ -133,7 +145,8 @@
     public void cect_11_1_13_4_RemoteControlMessagesToAudioSystem() throws Exception {
         hdmiCecClient.broadcastActiveSource(
                 LogicalAddress.AUDIO_SYSTEM, hdmiCecClient.getPhysicalAddress());
-        validateKeyeventToUserControlPress(LogicalAddress.AUDIO_SYSTEM);
+        TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
+        validateKeyeventToUserControlPress(LogicalAddress.AUDIO_SYSTEM, remoteControlAudioKeys);
     }
 
     /**
@@ -151,19 +164,23 @@
     }
 
     private void mapRemoteControlKeys() {
-        remoteControlKeys.put("DPAD_UP", HdmiCecConstants.CEC_CONTROL_UP);
-        remoteControlKeys.put("DPAD_DOWN", HdmiCecConstants.CEC_CONTROL_DOWN);
-        remoteControlKeys.put("DPAD_LEFT", HdmiCecConstants.CEC_CONTROL_LEFT);
-        remoteControlKeys.put("DPAD_RIGHT", HdmiCecConstants.CEC_CONTROL_RIGHT);
+        remoteControlKeys.put("DPAD_UP", HdmiCecConstants.CEC_KEYCODE_UP);
+        remoteControlKeys.put("DPAD_DOWN", HdmiCecConstants.CEC_KEYCODE_DOWN);
+        remoteControlKeys.put("DPAD_LEFT", HdmiCecConstants.CEC_KEYCODE_LEFT);
+        remoteControlKeys.put("DPAD_RIGHT", HdmiCecConstants.CEC_KEYCODE_RIGHT);
+        remoteControlAudioKeys.put("VOLUME_UP", HdmiCecConstants.CEC_KEYCODE_VOLUME_UP);
+        remoteControlAudioKeys.put("VOLUME_DOWN", HdmiCecConstants.CEC_KEYCODE_VOLUME_DOWN);
+        remoteControlAudioKeys.put("VOLUME_MUTE", HdmiCecConstants.CEC_KEYCODE_MUTE);
     }
 
-    private void validateKeyeventToUserControlPress(LogicalAddress toDevice) throws Exception {
+    private void validateKeyeventToUserControlPress(LogicalAddress toDevice
+            , HashMap<String, Integer> keyMaps) throws Exception {
         ITestDevice device = getDevice();
-        for (String remoteKey : remoteControlKeys.keySet()) {
+        for (String remoteKey : keyMaps.keySet()) {
             device.executeShellCommand("input keyevent KEYCODE_" + remoteKey);
             String message =
                     hdmiCecClient.checkExpectedOutput(toDevice, CecOperand.USER_CONTROL_PRESSED);
-            assertThat(CecMessage.getParams(message)).isEqualTo(remoteControlKeys.get(remoteKey));
+            assertThat(CecMessage.getParams(message)).isEqualTo(keyMaps.get(remoteKey));
             hdmiCecClient.checkExpectedOutput(toDevice, CecOperand.USER_CONTROL_RELEASED);
         }
     }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java
index a588ba1..1775f5e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java
@@ -41,6 +41,8 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecRoutingControlTest extends BaseHdmiCecCtsTest {
 
+    private static final int WAIT_TIME_MS = 300;
+
     @Rule
     public RuleChain ruleChain =
             RuleChain.outerRule(CecRules.requiresCec(this))
@@ -73,6 +75,11 @@
                  */
                 hdmiCecClient.broadcastActiveSource(
                         hdmiCecClient.getSelfDevice(), hdmiCecClient.getPhysicalAddress());
+                try {
+                    TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
+                } catch (InterruptedException ex) {
+                    // Do nothing
+                }
             }
         }
     }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemAudioControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemAudioControlTest.java
index 586a7d4..54c296e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemAudioControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemAudioControlTest.java
@@ -59,6 +59,9 @@
      */
     @Test
     public void cect_11_1_15_1_DutSendsSystemAudioModeRequest() throws Exception {
+        // Ensure that system audio mode is off before testing 11.1.15-5.
+        setSystemAudioMode(false);
+
         hdmiCecClient.broadcastReportPhysicalAddress(LogicalAddress.AUDIO_SYSTEM);
         hdmiCecClient.broadcastReportPhysicalAddress(
                 LogicalAddress.RECORDER_1, hdmiCecClient.getPhysicalAddress());
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java
index 13c0381..4aba289 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java
@@ -128,6 +128,7 @@
              */
             hdmiCecClient.broadcastActiveSource(
                     LogicalAddress.RECORDER_1, hdmiCecClient.getPhysicalAddress());
+            TimeUnit.MILLISECONDS.sleep(WAIT_TIME_MS);
         }
         // Make the TV device the active source.
         HdmiControlManagerUtility.setActiveSource(
diff --git a/hostsidetests/multidevice/AndroidTest.xml b/hostsidetests/multidevice/AndroidTest.xml
deleted file mode 100644
index e77c72e..0000000
--- a/hostsidetests/multidevice/AndroidTest.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-          http://www.apache.org/licenses/LICENSE-2.0
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Config for CTS Sample multi-device test cases">
-    <option name="test-suite-tag" value="cts" />
-    <option name="config-descriptor:metadata" key="component" value="misc" />
-    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
-    <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
-    <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
-
-    <device name="device1">
-        <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-            <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
-            <option name="run-command" value="wm dismiss-keyguard" />
-        </target_preparer>
-    </device>
-    <device name="device2">
-        <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-            <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
-            <option name="run-command" value="wm dismiss-keyguard" />
-        </target_preparer>
-    </device>
-
-    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
-        <option name="class" value="android.sample.cts.SampleMultiDeviceTest" />
-    </test>
-</configuration>
-
diff --git a/hostsidetests/multidevice/OWNERS b/hostsidetests/multidevice/OWNERS
deleted file mode 100644
index bcb1758..0000000
--- a/hostsidetests/multidevice/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 44752
-jdesprez@google.com
-frankfeng@google.com
-murj@google.com
\ No newline at end of file
diff --git a/hostsidetests/multidevice/src/android/sample/cts/SampleMultiDeviceTest.java b/hostsidetests/multidevice/src/android/sample/cts/SampleMultiDeviceTest.java
deleted file mode 100644
index f15bdb4..0000000
--- a/hostsidetests/multidevice/src/android/sample/cts/SampleMultiDeviceTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.sample.cts;
-
-import static org.junit.Assert.assertNotNull;
-
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import com.google.common.truth.Truth;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test that interacts with multiple devices.
- */
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class SampleMultiDeviceTest extends BaseHostJUnit4Test {
-
-    /**
-     * Sample tests that showcase that we are receiving the multiple devices in the test side.
-     */
-    @Test
-    public void testMultiDeviceTest() throws Exception {
-        // Validate we are actually multi-devices
-        Truth.assertThat(getListDevices().size()).isGreaterThan(1);
-        for (ITestDevice device : getListDevices()) {
-            CLog.i("device '%s' is available.", device.getSerialNumber());
-            String buildId = device.getProperty("ro.vendor.build.id");
-            assertNotNull(buildId);
-        }
-    }
-
-}
-
diff --git a/hostsidetests/multidevices/README.md b/hostsidetests/multidevices/README.md
new file mode 100644
index 0000000..2f42104
--- /dev/null
+++ b/hostsidetests/multidevices/README.md
@@ -0,0 +1,5 @@
+## CTS Multi-device Modules
+
+Refer to go/cts-multi-device-module for step-by-step instructions on creating
+CTS multi-device modules.
+
diff --git a/hostsidetests/sample-mobly/Android.bp b/hostsidetests/multidevices/wifi_aware/Android.bp
similarity index 80%
rename from hostsidetests/sample-mobly/Android.bp
rename to hostsidetests/multidevices/wifi_aware/Android.bp
index 3673d3f..2226f8d 100644
--- a/hostsidetests/sample-mobly/Android.bp
+++ b/hostsidetests/multidevices/wifi_aware/Android.bp
@@ -17,9 +17,9 @@
 }
 
 python_test_host {
-    name: "CtsSampleMoblyTestCases",
-    main: "multidevice_test.py",
-    srcs: ["multidevice_test.py",],
+    name: "CtsWifiAwareTestCases",
+    main: "wifi_aware_test.py",
+    srcs: ["wifi_aware_test.py"],
     test_suites: [
         "cts",
         "general-tests",
@@ -27,5 +27,8 @@
     test_options: {
         unit_test: false,
     },
-    data: ["CtsSampleMoblyTestCases_config.yaml"],
+    data: [
+        // Package the snippet with the mobly test
+        ":wifi_aware_snippet",
+    ],
 }
diff --git a/hostsidetests/sample-mobly/AndroidTest.xml b/hostsidetests/multidevices/wifi_aware/AndroidTest.xml
similarity index 65%
rename from hostsidetests/sample-mobly/AndroidTest.xml
rename to hostsidetests/multidevices/wifi_aware/AndroidTest.xml
index fe86171..352333a 100644
--- a/hostsidetests/sample-mobly/AndroidTest.xml
+++ b/hostsidetests/multidevices/wifi_aware/AndroidTest.xml
@@ -10,23 +10,33 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Config for CTS Sample multi-device test cases">
+<configuration description="Config for CTS Wifi Aware test cases">
     <option name="test-suite-tag" value="cts" />
-    <option name="config-descriptor:metadata" key="component" value="misc" />
+    <option name="config-descriptor:metadata" key="component" value="wifi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
 
     <device name="device1">
+        <!-- For coverage to work, the APK should not be uninstalled until after coverage is pulled.
+             So it's a lot easier to install APKs outside the python code.
+        -->
+        <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+            <option name="test-file-name" value="wifi_aware_snippet.apk" />
+        </target_preparer>
         <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
             <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
             <option name="run-command" value="wm dismiss-keyguard" />
         </target_preparer>
         <target_preparer class="com.android.tradefed.targetprep.PythonVirtualenvPreparer">
+          <!-- Any python dependencies can be specified and will be installed with pip -->
           <option name="dep-module" value="mobly" />
         </target_preparer>
     </device>
     <device name="device2">
+        <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+            <option name="test-file-name" value="wifi_aware_snippet.apk" />
+        </target_preparer>
         <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
             <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
             <option name="run-command" value="wm dismiss-keyguard" />
@@ -34,9 +44,10 @@
     </device>
 
     <test class="com.android.tradefed.testtype.mobly.MoblyBinaryHostTest">
-      <option name="mobly-par-file-name" value="CtsSampleMoblyTestCases" />
-      <option name="mobly-config-file-name" value="CtsSampleMoblyTestCases_config.yaml" />
-      <option name="mobly-test-timeout" value="1800000" />
+      <!-- The mobly-par-file-name should match the module name -->
+      <option name="mobly-par-file-name" value="CtsWifiAwareTestCases" />
+      <!-- Timeout limit in milliseconds for all test cases of the python binary -->
+      <option name="mobly-test-timeout" value="60000" />
     </test>
 </configuration>
 
diff --git a/hostsidetests/multidevices/wifi_aware/OWNERS b/hostsidetests/multidevices/wifi_aware/OWNERS
new file mode 100644
index 0000000..bde7824
--- /dev/null
+++ b/hostsidetests/multidevices/wifi_aware/OWNERS
@@ -0,0 +1,7 @@
+# Bug component: 33618
+include platform/packages/modules/Wifi:/WIFI_OWNERS
+
+# Engprod - Not owner of the test but help maintaining the module as an example
+jdesprez@google.com
+frankfeng@google.com
+murj@google.com
diff --git a/hostsidetests/sample-mobly/Android.bp b/hostsidetests/multidevices/wifi_aware/snippet/Android.bp
similarity index 64%
copy from hostsidetests/sample-mobly/Android.bp
copy to hostsidetests/multidevices/wifi_aware/snippet/Android.bp
index 3673d3f..f6af7c4 100644
--- a/hostsidetests/sample-mobly/Android.bp
+++ b/hostsidetests/multidevices/wifi_aware/snippet/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Android Open Source Project
+// Copyright (C) 2022 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,16 +16,18 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-python_test_host {
-    name: "CtsSampleMoblyTestCases",
-    main: "multidevice_test.py",
-    srcs: ["multidevice_test.py",],
-    test_suites: [
-        "cts",
-        "general-tests",
+android_test {
+    name: "wifi_aware_snippet",
+    sdk_version: "current",
+    srcs: [
+        "CallbackUtils.java",
+        "WifiAwareSnippet.java",
     ],
-    test_options: {
-        unit_test: false,
-    },
-    data: ["CtsSampleMoblyTestCases_config.yaml"],
+    manifest: "AndroidManifest.xml",
+    static_libs: [
+        "androidx.test.runner",
+        "guava",
+        "mobly-snippet-lib",
+    ],
 }
+
diff --git a/hostsidetests/multidevices/wifi_aware/snippet/AndroidManifest.xml b/hostsidetests/multidevices/wifi_aware/snippet/AndroidManifest.xml
new file mode 100644
index 0000000..87ed372
--- /dev/null
+++ b/hostsidetests/multidevices/wifi_aware/snippet/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.snippet">
+  <!-- Declare the minimum Android SDK version and internet permission,
+       which are required by Mobly Snippet Lib since it uses network socket. -->
+  <uses-sdk
+      android:minSdkVersion="16"
+      android:targetSdkVersion="21"/>
+  <uses-permission android:name="android.permission.INTERNET" />
+  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+  <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+  <application>
+    <!-- Add any classes that implement the Snippet interface as meta-data, whose
+         value is a comma-separated string, each section being the package path
+         of a snippet class -->
+    <meta-data
+        android:name="mobly-snippets"
+        android:value="com.google.snippet.WifiAwareSnippet" />
+  </application>
+  <!-- Add an instrumentation tag so that the app can be launched through an
+       instrument command. The runner `com.google.android.mobly.snippet.SnippetRunner`
+       is derived from `AndroidJUnitRunner`, and is required to use the
+       Mobly Snippet Lib. -->
+  <instrumentation
+      android:name="com.google.android.mobly.snippet.SnippetRunner"
+      android:targetPackage="com.google.snippet" />
+</manifest>
diff --git a/hostsidetests/multidevices/wifi_aware/snippet/CallbackUtils.java b/hostsidetests/multidevices/wifi_aware/snippet/CallbackUtils.java
new file mode 100644
index 0000000..a23d619
--- /dev/null
+++ b/hostsidetests/multidevices/wifi_aware/snippet/CallbackUtils.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.snippet;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.wifi.aware.AttachCallback;
+import android.net.wifi.aware.DiscoverySessionCallback;
+import android.net.wifi.aware.IdentityChangedListener;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.PublishDiscoverySession;
+import android.net.wifi.aware.SubscribeDiscoverySession;
+import android.net.wifi.aware.WifiAwareSession;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.util.Log;
+import android.util.Pair;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+/** Blocking callbacks for Wi-Fi Aware and Connectivity Manager. */
+public final class CallbackUtils {
+  private static final String TAG = "CallbackUtils";
+
+  public static final int CALLBACK_TIMEOUT_SEC = 15;
+
+  /**
+   * Utility AttachCallback - provides mechanism to block execution with the waitForAttach method.
+   */
+  public static class AttachCb extends AttachCallback {
+
+    /** Callback codes. */
+    public static enum CallbackCode {
+      TIMEOUT,
+      ON_ATTACHED,
+      ON_ATTACH_FAILED
+    };
+
+    private final CountDownLatch blocker = new CountDownLatch(1);
+    private CallbackCode callbackCode = CallbackCode.TIMEOUT;
+    private WifiAwareSession wifiAwareSession = null;
+
+    @Override
+    public void onAttached(WifiAwareSession session) {
+      callbackCode = CallbackCode.ON_ATTACHED;
+      wifiAwareSession = session;
+      blocker.countDown();
+    }
+
+    @Override
+    public void onAttachFailed() {
+      callbackCode = CallbackCode.ON_ATTACH_FAILED;
+      blocker.countDown();
+    }
+
+    /**
+     * Wait (blocks) for any AttachCallback callback or timeout.
+     *
+     * @return A pair of values: the callback constant (or TIMEOUT) and the WifiAwareSession created
+     *     when attach successful - null otherwise (attach failure or timeout).
+     */
+    public Pair<CallbackCode, WifiAwareSession> waitForAttach() throws InterruptedException {
+      if (blocker.await(CALLBACK_TIMEOUT_SEC, SECONDS)) {
+        return new Pair<>(callbackCode, wifiAwareSession);
+      }
+
+      return new Pair<>(CallbackCode.TIMEOUT, null);
+    }
+  }
+
+  /**
+   * Utility IdentityChangedListener - provides mechanism to block execution with the
+   * waitForIdentity method. Single shot listener - only listens for the first triggered callback.
+   */
+  public static class IdentityListenerSingleShot extends IdentityChangedListener {
+    private final CountDownLatch blocker = new CountDownLatch(1);
+    private byte[] mac = null;
+
+    @Override
+    public void onIdentityChanged(byte[] mac) {
+      if (this.mac != null) {
+        return;
+      }
+
+      this.mac = mac;
+      blocker.countDown();
+    }
+
+    /**
+     * Wait (blocks) for the onIdentityChanged callback or a timeout.
+     *
+     * @return The MAC address returned by the onIdentityChanged() callback, or null on timeout.
+     */
+    public byte[] waitForMac() throws InterruptedException {
+      if (blocker.await(CALLBACK_TIMEOUT_SEC, SECONDS)) {
+        return mac;
+      }
+
+      return null;
+    }
+  }
+
+  /**
+   * Utility NetworkCallback - provides mechanism for blocking/serializing access with the
+   * waitForNetwork method.
+   */
+  public static class NetworkCb extends ConnectivityManager.NetworkCallback {
+    private final CountDownLatch blocker = new CountDownLatch(1);
+    private Network network = null;
+    private NetworkCapabilities networkCapabilities = null;
+
+    @Override
+    public void onUnavailable() {
+      networkCapabilities = null;
+      blocker.countDown();
+    }
+
+    @Override
+    public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
+      this.network = network;
+      this.networkCapabilities = networkCapabilities;
+      blocker.countDown();
+    }
+
+    /**
+     * Wait (blocks) for Capabilities Changed callback - or timesout.
+     *
+     * @return Network + NetworkCapabilities (pair) if occurred, null otherwise.
+     */
+    public Pair<Network, NetworkCapabilities> waitForNetworkCapabilities()
+        throws InterruptedException {
+      if (blocker.await(CALLBACK_TIMEOUT_SEC, SECONDS)) {
+        return Pair.create(network, networkCapabilities);
+      }
+      return null;
+    }
+  }
+
+  /**
+   * Utility DiscoverySessionCallback - provides mechanism to block/serialize Aware discovery
+   * operations using the waitForCallbacks() method.
+   */
+  public static class DiscoveryCb extends DiscoverySessionCallback {
+    /** Callback codes. */
+    public static enum CallbackCode {
+      TIMEOUT,
+      ON_PUBLISH_STARTED,
+      ON_SUBSCRIBE_STARTED,
+      ON_SESSION_CONFIG_UPDATED,
+      ON_SESSION_CONFIG_FAILED,
+      ON_SESSION_TERMINATED,
+      ON_SERVICE_DISCOVERED,
+      ON_MESSAGE_SEND_SUCCEEDED,
+      ON_MESSAGE_SEND_FAILED,
+      ON_MESSAGE_RECEIVED,
+      ON_SERVICE_DISCOVERED_WITH_RANGE,
+    };
+
+    /**
+     * Data container for all parameters which can be returned by any DiscoverySessionCallback
+     * callback.
+     */
+    public static class CallbackData {
+      public CallbackData(CallbackCode callbackCode) {
+        this.callbackCode = callbackCode;
+      }
+
+      public CallbackCode callbackCode;
+
+      public PublishDiscoverySession publishDiscoverySession;
+      public SubscribeDiscoverySession subscribeDiscoverySession;
+      public PeerHandle peerHandle;
+      public byte[] serviceSpecificInfo;
+      public List<byte[]> matchFilter;
+      public int messageId;
+      public int distanceMm;
+    }
+
+    private CountDownLatch blocker = null;
+    private Set<CallbackCode> waitForCallbackCodes = ImmutableSet.of();
+
+    private final Object lock = new Object();
+    private final ArrayDeque<CallbackData> callbackQueue = new ArrayDeque<>();
+
+    private void processCallback(CallbackData callbackData) {
+      synchronized (lock) {
+        callbackQueue.addLast(callbackData);
+        if (blocker != null && waitForCallbackCodes.contains(callbackData.callbackCode)) {
+          blocker.countDown();
+        }
+      }
+    }
+
+    private CallbackData getAndRemoveFirst(Set<CallbackCode> callbackCodes) {
+      synchronized (lock) {
+        for (CallbackData cbd : callbackQueue) {
+          if (callbackCodes.contains(cbd.callbackCode)) {
+            callbackQueue.remove(cbd);
+            return cbd;
+          }
+        }
+      }
+
+      return null;
+    }
+
+    private CallbackData waitForCallbacks(Set<CallbackCode> callbackCodes, boolean timeout)
+        throws InterruptedException {
+      synchronized (lock) {
+        CallbackData cbd = getAndRemoveFirst(callbackCodes);
+        if (cbd != null) {
+          return cbd;
+        }
+
+        waitForCallbackCodes = callbackCodes;
+        blocker = new CountDownLatch(1);
+      }
+
+      boolean finishedNormally = true;
+      if (timeout) {
+        finishedNormally = blocker.await(CALLBACK_TIMEOUT_SEC, SECONDS);
+      } else {
+        blocker.await();
+      }
+      if (finishedNormally) {
+        CallbackData cbd = getAndRemoveFirst(callbackCodes);
+        if (cbd != null) {
+          return cbd;
+        }
+
+        Log.wtf(
+            TAG,
+            "DiscoveryCb.waitForCallback: callbackCodes="
+                + callbackCodes
+                + ": did not time-out but doesn't have any of the requested callbacks in "
+                + "the stack!?");
+        // falling-through to TIMEOUT
+      }
+
+      return new CallbackData(CallbackCode.TIMEOUT);
+    }
+
+    /**
+     * Wait for the specified callbacks - a bitmask of any of the ON_* constants. Returns the
+     * CallbackData structure whose CallbackData.callback specifies the callback which was
+     * triggered. The callback may be TIMEOUT.
+     *
+     * <p>Note: other callbacks happening while while waiting for the specified callback(s) will be
+     * queued.
+     */
+    public CallbackData waitForCallbacks(Set<CallbackCode> callbackCodes)
+        throws InterruptedException {
+      return waitForCallbacks(callbackCodes, true);
+    }
+
+    /**
+     * Wait for the specified callbacks - a bitmask of any of the ON_* constants. Returns the
+     * CallbackData structure whose CallbackData.callback specifies the callback which was
+     * triggered.
+     *
+     * <p>This call will not timeout - it can be interrupted though (which results in a thrown
+     * exception).
+     *
+     * <p>Note: other callbacks happening while while waiting for the specified callback(s) will be
+     * queued.
+     */
+    public CallbackData waitForCallbacksNoTimeout(Set<CallbackCode> callbackCodes)
+        throws InterruptedException {
+      return waitForCallbacks(callbackCodes, false);
+    }
+
+    @Override
+    public void onPublishStarted(PublishDiscoverySession session) {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_PUBLISH_STARTED);
+      callbackData.publishDiscoverySession = session;
+      processCallback(callbackData);
+    }
+
+    @Override
+    public void onSubscribeStarted(SubscribeDiscoverySession session) {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_SUBSCRIBE_STARTED);
+      callbackData.subscribeDiscoverySession = session;
+      processCallback(callbackData);
+    }
+
+    @Override
+    public void onSessionConfigUpdated() {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_SESSION_CONFIG_UPDATED);
+      processCallback(callbackData);
+    }
+
+    @Override
+    public void onSessionConfigFailed() {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_SESSION_CONFIG_FAILED);
+      processCallback(callbackData);
+    }
+
+    @Override
+    public void onSessionTerminated() {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_SESSION_TERMINATED);
+      processCallback(callbackData);
+    }
+
+    @Override
+    public void onServiceDiscovered(
+        PeerHandle peerHandle, byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_SERVICE_DISCOVERED);
+      callbackData.peerHandle = peerHandle;
+      callbackData.serviceSpecificInfo = serviceSpecificInfo;
+      callbackData.matchFilter = matchFilter;
+      processCallback(callbackData);
+    }
+
+    @Override
+    public void onServiceDiscoveredWithinRange(
+        PeerHandle peerHandle,
+        byte[] serviceSpecificInfo,
+        List<byte[]> matchFilter,
+        int distanceMm) {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_SERVICE_DISCOVERED_WITH_RANGE);
+      callbackData.peerHandle = peerHandle;
+      callbackData.serviceSpecificInfo = serviceSpecificInfo;
+      callbackData.matchFilter = matchFilter;
+      callbackData.distanceMm = distanceMm;
+      processCallback(callbackData);
+    }
+
+    @Override
+    public void onMessageSendSucceeded(int messageId) {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_MESSAGE_SEND_SUCCEEDED);
+      callbackData.messageId = messageId;
+      processCallback(callbackData);
+    }
+
+    @Override
+    public void onMessageSendFailed(int messageId) {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_MESSAGE_SEND_FAILED);
+      callbackData.messageId = messageId;
+      processCallback(callbackData);
+    }
+
+    @Override
+    public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
+      CallbackData callbackData = new CallbackData(CallbackCode.ON_MESSAGE_RECEIVED);
+      callbackData.peerHandle = peerHandle;
+      callbackData.serviceSpecificInfo = message;
+      processCallback(callbackData);
+    }
+  }
+
+  /**
+   * Utility RangingResultCallback - provides mechanism for blocking/serializing access with the
+   * waitForRangingResults method.
+   */
+  public static class RangingCb extends RangingResultCallback {
+    public static final int TIMEOUT = -1;
+    public static final int ON_FAILURE = 0;
+    public static final int ON_RESULTS = 1;
+
+    private final CountDownLatch blocker = new CountDownLatch(1);
+    private int status = TIMEOUT;
+    private List<RangingResult> results = null;
+
+    /**
+     * Wait (blocks) for Ranging results callbacks - or times-out.
+     *
+     * @return Pair of status & Ranging results if succeeded, null otherwise.
+     */
+    public Pair<Integer, List<RangingResult>> waitForRangingResults() throws InterruptedException {
+      if (blocker.await(CALLBACK_TIMEOUT_SEC, SECONDS)) {
+        return new Pair<>(status, results);
+      }
+      return new Pair<>(TIMEOUT, null);
+    }
+
+    @Override
+    public void onRangingFailure(int code) {
+      status = ON_FAILURE;
+      blocker.countDown();
+    }
+
+    @Override
+    public void onRangingResults(List<RangingResult> results) {
+      status = ON_RESULTS;
+      this.results = results;
+      blocker.countDown();
+    }
+  }
+
+  private CallbackUtils() {}
+}
diff --git a/hostsidetests/multidevices/wifi_aware/snippet/WifiAwareSnippet.java b/hostsidetests/multidevices/wifi_aware/snippet/WifiAwareSnippet.java
new file mode 100644
index 0000000..38f70fd
--- /dev/null
+++ b/hostsidetests/multidevices/wifi_aware/snippet/WifiAwareSnippet.java
@@ -0,0 +1,257 @@
+package com.google.snippet;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.content.Context;
+import android.net.wifi.aware.DiscoverySession;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.WifiAwareManager;
+import android.net.wifi.aware.WifiAwareSession;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+import android.util.Pair;
+import androidx.test.platform.app.InstrumentationRegistry;
+import com.google.android.mobly.snippet.Snippet;
+import com.google.android.mobly.snippet.rpc.Rpc;
+import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/** An example snippet class with a simple Rpc. */
+public class WifiAwareSnippet implements Snippet {
+
+  private static class WifiAwareSnippetException extends Exception {
+    private static final long SERIAL_VERSION_UID = 1;
+
+    public WifiAwareSnippetException(String msg) {
+      super(msg);
+    }
+
+    public WifiAwareSnippetException(String msg, Throwable err) {
+      super(msg, err);
+    }
+  }
+
+  private static final String TAG = "WifiAwareSnippet";
+
+  private static final String SERVICE_NAME = "CtsVerifierTestService";
+  private static final byte[] MATCH_FILTER_BYTES = "bytes used for matching".getBytes(UTF_8);
+  private static final byte[] PUB_SSI = "Extra bytes in the publisher discovery".getBytes(UTF_8);
+  private static final byte[] SUB_SSI =
+      "Arbitrary bytes for the subscribe discovery".getBytes(UTF_8);
+  private static final int LARGE_ENOUGH_DISTANCE = 100000; // 100 meters
+
+  private final WifiAwareManager wifiAwareManager;
+
+  private final Context context;
+
+  private final HandlerThread handlerThread;
+
+  private final Handler handler;
+
+  private WifiAwareSession wifiAwareSession;
+  private DiscoverySession wifiAwareDiscoverySession;
+  private CallbackUtils.DiscoveryCb discoveryCb;
+  private PeerHandle peerHandle;
+
+  public WifiAwareSnippet() {
+    context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    wifiAwareManager = context.getSystemService(WifiAwareManager.class);
+    handlerThread = new HandlerThread("Snippet-Aware");
+    handlerThread.start();
+    handler = new Handler(handlerThread.getLooper());
+  }
+
+  @Rpc(description = "Execute attach.")
+  public void attach() throws InterruptedException, WifiAwareSnippetException {
+    CallbackUtils.AttachCb attachCb = new CallbackUtils.AttachCb();
+    wifiAwareManager.attach(attachCb, handler);
+    Pair<CallbackUtils.AttachCb.CallbackCode, WifiAwareSession> results = attachCb.waitForAttach();
+    if (results.first != CallbackUtils.AttachCb.CallbackCode.ON_ATTACHED) {
+      throw new WifiAwareSnippetException(String.format("executeTest: attach %s", results.first));
+    }
+    wifiAwareSession = results.second;
+    if (wifiAwareSession == null) {
+      throw new WifiAwareSnippetException(
+          "executeTest: attach callback succeeded but null session returned!?");
+    }
+  }
+
+  @Rpc(description = "Execute subscribe.")
+  public void subscribe(Boolean isUnsolicited, Boolean isRangingRequired)
+      throws InterruptedException, WifiAwareSnippetException {
+    discoveryCb = new CallbackUtils.DiscoveryCb();
+
+    List<byte[]> matchFilter = new ArrayList<>();
+    matchFilter.add(MATCH_FILTER_BYTES);
+    SubscribeConfig.Builder builder =
+        new SubscribeConfig.Builder()
+            .setServiceName(SERVICE_NAME)
+            .setServiceSpecificInfo(SUB_SSI)
+            .setMatchFilter(matchFilter)
+            .setSubscribeType(
+                isUnsolicited
+                    ? SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE
+                    : SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE)
+            .setTerminateNotificationEnabled(true);
+
+    if (isRangingRequired) {
+      // set up a distance that will always trigger - i.e. that we're already in that range
+      builder.setMaxDistanceMm(LARGE_ENOUGH_DISTANCE);
+    }
+    SubscribeConfig subscribeConfig = builder.build();
+    Log.d(TAG, "executeTestSubscriber: subscribeConfig=" + subscribeConfig);
+    wifiAwareSession.subscribe(subscribeConfig, discoveryCb, handler);
+
+    // wait for results - subscribe session
+    CallbackUtils.DiscoveryCb.CallbackData callbackData =
+        discoveryCb.waitForCallbacks(
+            ImmutableSet.of(
+                CallbackUtils.DiscoveryCb.CallbackCode.ON_SUBSCRIBE_STARTED,
+                CallbackUtils.DiscoveryCb.CallbackCode.ON_SESSION_CONFIG_FAILED));
+    if (callbackData.callbackCode != CallbackUtils.DiscoveryCb.CallbackCode.ON_SUBSCRIBE_STARTED) {
+      throw new WifiAwareSnippetException(
+          String.format("executeTestSubscriber: subscribe %s", callbackData.callbackCode));
+    }
+    wifiAwareDiscoverySession = callbackData.subscribeDiscoverySession;
+    if (wifiAwareDiscoverySession == null) {
+      throw new WifiAwareSnippetException(
+          "executeTestSubscriber: subscribe succeeded but null session returned");
+    }
+    Log.d(TAG, "executeTestSubscriber: subscribe succeeded");
+
+    // 3. wait for discovery
+    callbackData =
+        discoveryCb.waitForCallbacks(
+            ImmutableSet.of(
+                isRangingRequired
+                    ? CallbackUtils.DiscoveryCb.CallbackCode.ON_SERVICE_DISCOVERED_WITH_RANGE
+                    : CallbackUtils.DiscoveryCb.CallbackCode.ON_SERVICE_DISCOVERED));
+
+    if (callbackData.callbackCode == CallbackUtils.DiscoveryCb.CallbackCode.TIMEOUT) {
+      throw new WifiAwareSnippetException("executeTestSubscriber: waiting for discovery TIMEOUT");
+    }
+    peerHandle = callbackData.peerHandle;
+    if (!isRangingRequired) {
+      Log.d(TAG, "executeTestSubscriber: discovery");
+    } else {
+      Log.d(TAG, "executeTestSubscriber: discovery with range=" + callbackData.distanceMm);
+    }
+
+    if (!Arrays.equals(PUB_SSI, callbackData.serviceSpecificInfo)) {
+      throw new WifiAwareSnippetException(
+          "executeTestSubscriber: discovery but SSI mismatch: rx='"
+              + new String(callbackData.serviceSpecificInfo, UTF_8)
+              + "'");
+    }
+    if (callbackData.matchFilter.size() != 1
+        || !Arrays.equals(MATCH_FILTER_BYTES, callbackData.matchFilter.get(0))) {
+      StringBuilder sb = new StringBuilder();
+      sb.append("size=").append(callbackData.matchFilter.size());
+      for (byte[] mf : callbackData.matchFilter) {
+        sb.append(", e='").append(new String(mf, UTF_8)).append("'");
+      }
+      throw new WifiAwareSnippetException(
+          "executeTestSubscriber: discovery but matchFilter mismatch: " + sb);
+    }
+    if (peerHandle == null) {
+      throw new WifiAwareSnippetException("executeTestSubscriber: discovery but null peerHandle");
+    }
+  }
+
+  @Rpc(description = "Send message.")
+  public void sendMessage(int messageId, String message)
+      throws InterruptedException, WifiAwareSnippetException {
+    // 4. send message & wait for send status
+    wifiAwareDiscoverySession.sendMessage(peerHandle, messageId, message.getBytes(UTF_8));
+    CallbackUtils.DiscoveryCb.CallbackData callbackData =
+        discoveryCb.waitForCallbacks(
+            ImmutableSet.of(
+                CallbackUtils.DiscoveryCb.CallbackCode.ON_MESSAGE_SEND_SUCCEEDED,
+                CallbackUtils.DiscoveryCb.CallbackCode.ON_MESSAGE_SEND_FAILED));
+
+    if (callbackData.callbackCode
+        != CallbackUtils.DiscoveryCb.CallbackCode.ON_MESSAGE_SEND_SUCCEEDED) {
+      throw new WifiAwareSnippetException(
+          String.format("executeTestSubscriber: sendMessage %s", callbackData.callbackCode));
+    }
+    Log.d(TAG, "executeTestSubscriber: send message succeeded");
+    if (callbackData.messageId != messageId) {
+      throw new WifiAwareSnippetException(
+          "executeTestSubscriber: send message message ID mismatch: " + callbackData.messageId);
+    }
+  }
+
+  @Rpc(description = "Create publish session.")
+  public void publish(Boolean isUnsolicited, Boolean isRangingRequired)
+      throws WifiAwareSnippetException, InterruptedException {
+    discoveryCb = new CallbackUtils.DiscoveryCb();
+
+    // 2. publish
+    List<byte[]> matchFilter = new ArrayList<>();
+    matchFilter.add(MATCH_FILTER_BYTES);
+    PublishConfig publishConfig =
+        new PublishConfig.Builder()
+            .setServiceName(SERVICE_NAME)
+            .setServiceSpecificInfo(PUB_SSI)
+            .setMatchFilter(matchFilter)
+            .setPublishType(
+                isUnsolicited
+                    ? PublishConfig.PUBLISH_TYPE_UNSOLICITED
+                    : PublishConfig.PUBLISH_TYPE_SOLICITED)
+            .setTerminateNotificationEnabled(true)
+            .setRangingEnabled(isRangingRequired)
+            .build();
+    Log.d(TAG, "executeTestPublisher: publishConfig=" + publishConfig);
+    wifiAwareSession.publish(publishConfig, discoveryCb, handler);
+
+    //    wait for results - publish session
+    CallbackUtils.DiscoveryCb.CallbackData callbackData =
+        discoveryCb.waitForCallbacks(
+            ImmutableSet.of(
+                CallbackUtils.DiscoveryCb.CallbackCode.ON_PUBLISH_STARTED,
+                CallbackUtils.DiscoveryCb.CallbackCode.ON_SESSION_CONFIG_FAILED));
+    if (callbackData.callbackCode != CallbackUtils.DiscoveryCb.CallbackCode.ON_PUBLISH_STARTED) {
+      throw new WifiAwareSnippetException(
+          String.format("executeTestPublisher: publish %s", callbackData.callbackCode));
+    }
+    wifiAwareDiscoverySession = callbackData.publishDiscoverySession;
+    if (wifiAwareDiscoverySession == null) {
+      throw new WifiAwareSnippetException(
+          "executeTestPublisher: publish succeeded but null session returned");
+    }
+    Log.d(TAG, "executeTestPublisher: publish succeeded");
+  }
+
+  @Rpc(description = "Receive message.")
+  public String receiveMessage() throws WifiAwareSnippetException, InterruptedException {
+    // 3. wait to receive message.
+    CallbackUtils.DiscoveryCb.CallbackData callbackData =
+        discoveryCb.waitForCallbacks(
+            ImmutableSet.of(CallbackUtils.DiscoveryCb.CallbackCode.ON_MESSAGE_RECEIVED));
+    peerHandle = callbackData.peerHandle;
+    Log.d(TAG, "executeTestPublisher: received message");
+
+    if (peerHandle == null) {
+      throw new WifiAwareSnippetException(
+          "executeTestPublisher: received message but peerHandle is null!?");
+    }
+    return new String(callbackData.serviceSpecificInfo, UTF_8);
+  }
+
+  @Override
+  public void shutdown() {
+    if (wifiAwareDiscoverySession != null) {
+      wifiAwareDiscoverySession.close();
+      wifiAwareDiscoverySession = null;
+    }
+    if (wifiAwareSession != null) {
+      wifiAwareSession.close();
+      wifiAwareSession = null;
+    }
+  }
+}
diff --git a/hostsidetests/multidevices/wifi_aware/wifi_aware_test.py b/hostsidetests/multidevices/wifi_aware/wifi_aware_test.py
new file mode 100644
index 0000000..d2ab7df
--- /dev/null
+++ b/hostsidetests/multidevices/wifi_aware/wifi_aware_test.py
@@ -0,0 +1,69 @@
+# Lint as: python3
+"""CTS-V Wifi test reimplemented in Mobly."""
+import sys
+import os.path
+
+import logging
+logging.basicConfig(filename="/tmp/wifi_aware_test_log.txt", level=logging.INFO)
+
+from mobly import asserts
+from mobly import base_test
+from mobly import test_runner
+from mobly import utils
+from mobly.controllers import android_device
+
+WIFI_AWARE_SNIPPET_PATH = 'wifi_aware_snippet.apk'
+
+WIFI_AWARE_SNIPPET_PACKAGE = 'com.google.snippet'
+
+TEST_MESSAGE = 'test message!'
+
+MESSAGE_ID = 1234
+
+
+class WifiAwareTest(base_test.BaseTestClass):
+
+  def setup_class(self):
+    # Declare that two Android devices are needed.
+    self.publisher, self.subscriber = self.register_controller(
+      android_device, min_number=2)
+
+    def setup_device(device):
+      # Expect wifi_aware apk to be installed as it is configured to install
+      # with the module configuration AndroidTest.xml on both devices.
+      device.adb.shell([
+          'pm', 'grant', WIFI_AWARE_SNIPPET_PACKAGE,
+          'android.permission.ACCESS_FINE_LOCATION'
+      ])
+      device.load_snippet('wifi_aware_snippet', WIFI_AWARE_SNIPPET_PACKAGE)
+
+    # Sets up devices in parallel to save time.
+    utils.concurrent_exec(
+        setup_device, ((self.publisher,), (self.subscriber,)),
+        max_workers=2,
+        raise_on_exception=True)
+
+  def test_discovery_base_test_case(self):
+    is_unsolicited = True
+    is_ranging_required = False
+    self.subscriber.wifi_aware_snippet.attach()
+    self.publisher.wifi_aware_snippet.attach()
+
+    self.publisher.wifi_aware_snippet.publish(is_unsolicited,
+                                              is_ranging_required)
+    self.subscriber.wifi_aware_snippet.subscribe(is_unsolicited,
+                                                 is_ranging_required)
+
+    self.subscriber.wifi_aware_snippet.sendMessage(MESSAGE_ID, TEST_MESSAGE)
+    received_message = self.publisher.wifi_aware_snippet.receiveMessage()
+    asserts.assert_equal(
+        received_message, TEST_MESSAGE,
+        'Message received by publisher does not match the message sent by subscriber.'
+    )
+
+if __name__ == '__main__':
+  # Take test args
+  index = sys.argv.index('--')
+  sys.argv = sys.argv[:1] + sys.argv[index + 1:]
+
+  test_runner.main()
diff --git a/hostsidetests/packagemanager/dynamicmime/OWNERS b/hostsidetests/packagemanager/dynamicmime/OWNERS
index 3f48dcf..7dd198a 100644
--- a/hostsidetests/packagemanager/dynamicmime/OWNERS
+++ b/hostsidetests/packagemanager/dynamicmime/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 36137
 tantoshchuk@google.com
+mhasank@google.com
diff --git a/hostsidetests/sample-mobly/CtsSampleMoblyTestCases_config.yaml b/hostsidetests/sample-mobly/CtsSampleMoblyTestCases_config.yaml
deleted file mode 100644
index 314d865..0000000
--- a/hostsidetests/sample-mobly/CtsSampleMoblyTestCases_config.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
-TestBeds:
-- Name: SampleTestBed
-  Controllers:
-    AndroidDevice:
-    - serial: serial_1
-    - serial: serial_2
-MoblyParams: {LogPath: ./}
diff --git a/hostsidetests/sample-mobly/OWNERS b/hostsidetests/sample-mobly/OWNERS
deleted file mode 100644
index bcb1758..0000000
--- a/hostsidetests/sample-mobly/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 44752
-jdesprez@google.com
-frankfeng@google.com
-murj@google.com
\ No newline at end of file
diff --git a/hostsidetests/sample-mobly/multidevice_test.py b/hostsidetests/sample-mobly/multidevice_test.py
deleted file mode 100644
index 887c1a0..0000000
--- a/hostsidetests/sample-mobly/multidevice_test.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/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 sys
-
-from mobly import base_test
-from mobly import test_runner
-from mobly.controllers import android_device
-
-
-class MultiDeviceTest(base_test.BaseTestClass):
-
-  def setup_class(self):
-    # Registering android_device controller module declares the test's
-    # dependency on Android device hardware.
-    self.ads = self.register_controller(android_device)
-
-  def test_multidevice(self):
-    # Verify 2 devices are allocated.
-    assert len(self.ads) == 2, "Failed to get multiple devices"
-
-
-if __name__ == '__main__':
-  index = sys.argv.index('--')
-  sys.argv = sys.argv[:1] + sys.argv[index + 1:]
-  test_runner.main()
diff --git a/hostsidetests/security/Android.bp b/hostsidetests/security/Android.bp
index a6e0d8e..4551750 100644
--- a/hostsidetests/security/Android.bp
+++ b/hostsidetests/security/Android.bp
@@ -36,8 +36,8 @@
         "compatibility-host-util",
     ],
     java_resources: [
-        ":plat_seapp_contexts_rule",
-        ":plat_seapp_neverallows_rule",
+        ":plat_seapp_contexts",
+        ":plat_seapp_neverallows",
         ":plat_file_contexts",
         ":plat_property_contexts",
         ":plat_service_contexts",
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 7b9afe2..039867b 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -95,7 +95,7 @@
 
     private static final Map<ITestDevice, File> cachedDevicePolicyFiles = new HashMap<>(1);
     private static final Map<ITestDevice, File> cachedDevicePlatFcFiles = new HashMap<>(1);
-    private static final Map<ITestDevice, File> cachedDeviceNonplatFcFiles = new HashMap<>(1);
+    private static final Map<ITestDevice, File> cachedDeviceVendorFcFiles = new HashMap<>(1);
     private static final Map<ITestDevice, File> cachedDeviceVendorManifest = new HashMap<>(1);
     private static final Map<ITestDevice, File> cachedDeviceVintfJson = new HashMap<>(1);
     private static final Map<ITestDevice, File> cachedDeviceSystemPolicy = new HashMap<>(1);
@@ -110,9 +110,9 @@
     private File devicePolicyFile;
     private File deviceSystemPolicyFile;
     private File devicePlatSeappFile;
-    private File deviceNonplatSeappFile;
+    private File deviceVendorSeappFile;
     private File devicePlatFcFile;
-    private File deviceNonplatFcFile;
+    private File deviceVendorFcFile;
     private File devicePcFile;
     private File deviceSvcFile;
     private File seappNeverAllowFile;
@@ -171,20 +171,14 @@
         if (isSepolicySplit(mDevice)) {
             devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
                     "/system/etc/selinux/plat_file_contexts", "plat_file_contexts");
-            if (mDevice.doesFileExist("/vendor/etc/selinux/nonplat_file_contexts")){
-                // Old nonplat_* naming can be present if a framework-only OTA was done.
-                deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
-                        "/vendor/etc/selinux/nonplat_file_contexts", "nonplat_file_contexts");
-            } else {
-                deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
-                        "/vendor/etc/selinux/vendor_file_contexts", "vendor_file_contexts");
-            }
+            deviceVendorFcFile = getDeviceFile(mDevice, cachedDeviceVendorFcFiles,
+                    "/vendor/etc/selinux/vendor_file_contexts", "vendor_file_contexts");
             deviceSystemPolicyFile =
                     android.security.cts.SELinuxHostTest.getDeviceSystemPolicyFile(mDevice);
         } else {
             devicePlatFcFile = getDeviceFile(mDevice, cachedDevicePlatFcFiles,
                     "/plat_file_contexts", "plat_file_contexts");
-            deviceNonplatFcFile = getDeviceFile(mDevice, cachedDeviceNonplatFcFiles,
+            deviceVendorFcFile = getDeviceFile(mDevice, cachedDeviceVendorFcFiles,
                     "/vendor_file_contexts", "vendor_file_contexts");
         }
     }
@@ -634,13 +628,13 @@
         /* obtain seapp_contexts file from running device */
         devicePlatSeappFile = File.createTempFile("plat_seapp_contexts", ".tmp");
         devicePlatSeappFile.deleteOnExit();
-        deviceNonplatSeappFile = File.createTempFile("nonplat_seapp_contexts", ".tmp");
-        deviceNonplatSeappFile.deleteOnExit();
+        deviceVendorSeappFile = File.createTempFile("vendor_seapp_contexts", ".tmp");
+        deviceVendorSeappFile.deleteOnExit();
         if (mDevice.pullFile("/system/etc/selinux/plat_seapp_contexts", devicePlatSeappFile)) {
-            mDevice.pullFile("/vendor/etc/selinux/nonplat_seapp_contexts", deviceNonplatSeappFile);
+            mDevice.pullFile("/vendor/etc/selinux/vendor_seapp_contexts", deviceVendorSeappFile);
         }else {
             mDevice.pullFile("/plat_seapp_contexts", devicePlatSeappFile);
-            mDevice.pullFile("/nonplat_seapp_contexts", deviceNonplatSeappFile);
+            mDevice.pullFile("/vendor_seapp_contexts", deviceVendorSeappFile);
 	}
 
         /* retrieve the checkseapp executable from jar */
@@ -655,7 +649,7 @@
                 "-p", devicePolicyFile.getAbsolutePath(),
                 seappNeverAllowFile.getAbsolutePath(),
                 devicePlatSeappFile.getAbsolutePath(),
-                deviceNonplatSeappFile.getAbsolutePath());
+                deviceVendorSeappFile.getAbsolutePath());
         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
         pb.redirectErrorStream(true);
         Process p = pb.start();
@@ -806,11 +800,11 @@
         checkFc = copyResourceToTempFile("/checkfc");
         checkFc.setExecutable(true);
 
-        /* combine plat and nonplat policies for testing */
+        /* combine plat and vendor policies for testing */
         File combinedFcFile = File.createTempFile("combined_file_context", ".tmp");
         combinedFcFile.deleteOnExit();
         appendTo(combinedFcFile.getAbsolutePath(), devicePlatFcFile.getAbsolutePath());
-        appendTo(combinedFcFile.getAbsolutePath(), deviceNonplatFcFile.getAbsolutePath());
+        appendTo(combinedFcFile.getAbsolutePath(), deviceVendorFcFile.getAbsolutePath());
 
         /* run checkfc sepolicy file_contexts */
         ProcessBuilder pb = new ProcessBuilder(checkFc.getAbsolutePath(),
@@ -941,8 +935,6 @@
 
         List<String> args = new ArrayList<String>();
         args.add(sepolicyTests.getAbsolutePath());
-        args.add("-l");
-        args.add(libsepolwrap.getAbsolutePath());
         args.add("-f");
         args.add(devicePlatFcFile.getAbsolutePath());
         args.add("--test");
@@ -950,7 +942,7 @@
 
         if (includeVendorSepolicy) {
             args.add("-f");
-            args.add(deviceNonplatFcFile.getAbsolutePath());
+            args.add(deviceVendorFcFile.getAbsolutePath());
             args.add("-p");
             args.add(devicePolicyFile.getAbsolutePath());
         } else {
diff --git a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java
index ef24a7a..ab43151 100644
--- a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java
+++ b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java
@@ -29,6 +29,7 @@
 import android.app.PendingIntent;
 import android.app.job.JobInfo;
 import android.app.job.JobScheduler;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.le.BluetoothLeScanner;
 import android.bluetooth.le.ScanCallback;
@@ -63,6 +64,7 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -986,6 +988,23 @@
         doGenerateNetworkTraffic(context, NetworkCapabilities.TRANSPORT_CELLULAR);
     }
 
+    /**
+     * Force poll NetworkStatsService to get most updated network stats from lower layer.
+     */
+    @Test
+    public void testForcePollNetworkStats() throws Exception {
+        final Context context = InstrumentationRegistry.getContext();
+        final NetworkStatsManager nsm = context.getSystemService(NetworkStatsManager.class);
+        try {
+            nsm.setPollForce(true);
+            // This query is for triggering force poll NetworkStatsService.
+            nsm.querySummaryForUser(ConnectivityManager.TYPE_WIFI, null, Long.MIN_VALUE,
+                    Long.MAX_VALUE);
+        } catch (RemoteException e) {
+            Log.e(TAG, "doPollNetworkStats failed with " + e);
+        }
+    }
+
     // Constants which are locally used by doGenerateNetworkTraffic.
     private static final int NETWORK_TIMEOUT_MILLIS = 15000;
     private static final String HTTPS_HOST_URL =
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/net/BytesTransferredTest.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/net/BytesTransferredTest.java
index 089843a..a340700d4 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/net/BytesTransferredTest.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/net/BytesTransferredTest.java
@@ -65,20 +65,23 @@
         mCtsBuild = buildInfo;
     }
 
-    // TODO: inline the contents of doTestUsageBytesTransferEnable
     public void testDataUsageBytesTransfer() throws Throwable {
-        final boolean oldSubtypeCombined = getNetworkStatsCombinedSubTypeEnabled();
+        doTestMobileBytesTransferThat(Atom.DATA_USAGE_BYTES_TRANSFER_FIELD_NUMBER, /*isUidAtom=*/
+                false, (atom) -> {
+                    final AtomsProto.DataUsageBytesTransfer data =
+                            atom.getDataUsageBytesTransfer();
+                    final boolean ratTypeGreaterThanUnknown =
+                            (data.getRatType() > NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
 
-        doTestDataUsageBytesTransferEnabled(true);
-
-        // Remove old configs from disk and clear any pending statsd reports to clear history.
-        ConfigUtils.removeConfig(getDevice());
-        ReportUtils.clearReports(getDevice());
-
-        doTestDataUsageBytesTransferEnabled(false);
-
-        // Restore to original default value.
-        setNetworkStatsCombinedSubTypeEnabled(oldSubtypeCombined);
+                    if (ratTypeGreaterThanUnknown) {
+                        // Assert that subscription info is valid.
+                        assertSubscriptionInfo(data);
+                        // DataUsageBytesTransferred atom does not report app uid.
+                        return new TransferredBytes(data.getRxBytes(), data.getTxBytes(),
+                                data.getRxPackets(), data.getTxPackets(), /*appUid=*/-1);
+                    }
+                    return null;
+                });
     }
 
     public void testMobileBytesTransfer() throws Throwable {
@@ -152,32 +155,6 @@
         TransferredBytes accept(S s) throws T;
     }
 
-    private void doTestDataUsageBytesTransferEnabled(boolean enable) throws Throwable {
-        // Set value to enable/disable combine subtype.
-        setNetworkStatsCombinedSubTypeEnabled(enable);
-
-        doTestMobileBytesTransferThat(Atom.DATA_USAGE_BYTES_TRANSFER_FIELD_NUMBER, /*isUidAtom=*/
-                false, (atom) -> {
-                    final AtomsProto.DataUsageBytesTransfer data =
-                            atom.getDataUsageBytesTransfer();
-                    final boolean ratTypeEqualsToUnknown =
-                            (data.getRatType() == NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
-                    final boolean ratTypeGreaterThanUnknown =
-                            (data.getRatType() > NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
-
-                    if ((data.getState() == 1) // NetworkStats.SET_FOREGROUND
-                            && ((enable && ratTypeEqualsToUnknown)
-                            || (!enable && ratTypeGreaterThanUnknown))) {
-                        // Assert that subscription info is valid.
-                        assertSubscriptionInfo(data);
-                        // DataUsageBytesTransferred atom does not report app uid.
-                        return new TransferredBytes(data.getRxBytes(), data.getTxBytes(),
-                                data.getRxPackets(), data.getTxPackets(), /*appUid=*/-1);
-                    }
-                    return null;
-                });
-    }
-
     private void doTestMobileBytesTransferThat(int atomId, boolean isUidAtom,
             ThrowingPredicate<Atom, Exception> p)
             throws Throwable {
@@ -197,10 +174,10 @@
                 "testGenerateMobileTraffic");
         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
         // Force poll NetworkStatsService to get most updated network stats from lower layer.
-        DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
-                "PollNetworkStatsActivity",
-                /*actionKey=*/null, /*actionValue=*/null);
+        DeviceUtils.runDeviceTests(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, ".AtomTests",
+                "testForcePollNetworkStats");
         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
+
         // Trigger atom pull.
         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
@@ -245,15 +222,4 @@
         assertThat(data.getSimMnc()).matches("^\\d{2,3}$");
         assertThat(data.getCarrierId()).isNotEqualTo(-1); // TelephonyManager#UNKNOWN_CARRIER_ID
     }
-
-    private boolean getNetworkStatsCombinedSubTypeEnabled() throws Exception {
-        final String output = getDevice().executeShellCommand(
-                "settings get global netstats_combine_subtype_enabled").trim();
-        return output.equals("1");
-    }
-
-    private void setNetworkStatsCombinedSubTypeEnabled(boolean enable) throws Exception {
-        getDevice().executeShellCommand("settings put global netstats_combine_subtype_enabled "
-                + (enable ? "1" : "0"));
-    }
 }
diff --git a/hostsidetests/multidevice/Android.bp b/hostsidetests/tagging/sdk_30_memtag/Android.bp
similarity index 71%
rename from hostsidetests/multidevice/Android.bp
rename to hostsidetests/tagging/sdk_30_memtag/Android.bp
index fc36c60..f21ce4a 100644
--- a/hostsidetests/multidevice/Android.bp
+++ b/hostsidetests/tagging/sdk_30_memtag/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2021 The Android Open Source Project
+// Copyright (C) 2020 The Android Open Source Project
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,19 +16,13 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-java_test_host {
-    name: "CtsSampleMultiDeviceTestCases",
-    defaults: ["cts_defaults"],
+android_test_helper_app {
+    name: "CtsHostsideTaggingSdk30MemtagApp",
+    defaults: ["cts_tagging_app_defaults"],
     srcs: ["src/**/*.java"],
-    // tag this module as a cts test artifact
     test_suites: [
         "cts",
         "general-tests",
     ],
-    libs: [
-        "cts-tradefed",
-        "tradefed",
-        "compatibility-host-util",
-    ],
+    static_libs: ["compatibility-device-util-axt"],
 }
-
diff --git a/hostsidetests/tagging/sdk_30_memtag/AndroidManifest.xml b/hostsidetests/tagging/sdk_30_memtag/AndroidManifest.xml
new file mode 100644
index 0000000..fe9413a
--- /dev/null
+++ b/hostsidetests/tagging/sdk_30_memtag/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.cts.tagging.sdk30memtag">
+
+    <uses-sdk android:targetSdkVersion="30" />
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+
+    <application android:debuggable="true"
+                 android:memtagMode="sync"
+                 android:zygotePreloadName=".ZygotePreload">
+      <uses-library android:name="android.test.runner" />
+
+      <activity android:name=".ServiceRunnerActivity" />
+
+      <service android:name=".CrashAppZygoteService"
+               android:process=":CrashIsolatedProcess"
+               android:useAppZygote="true"
+               android:isolatedProcess="true"
+               android:exported="false" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.cts.tagging.sdk30memtag" />
+</manifest>
diff --git a/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/CrashAppZygoteService.java b/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/CrashAppZygoteService.java
new file mode 100644
index 0000000..5fdc532
--- /dev/null
+++ b/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/CrashAppZygoteService.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.tagging.sdk30memtag;
+
+import android.app.Service;
+import android.content.Intent;
+import android.cts.tagging.Utils;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+public class CrashAppZygoteService extends Service {
+    private static String TAG = CrashAppZygoteService.class.getName();
+
+    private Messenger mClient;
+
+    class IncomingHandler extends Handler {
+        private CrashAppZygoteService mService;
+
+        IncomingHandler(CrashAppZygoteService service) {
+            mService = service;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what != ServiceRunnerActivity.MSG_START_TEST) {
+                Log.e(TAG, "CrashAppZygoteService received bad message: " + msg.what);
+                super.handleMessage(msg);
+                return;
+            }
+            mService.mClient = msg.replyTo;
+            mService.startTests();
+        }
+    }
+
+    final Messenger mMessenger = new Messenger(new IncomingHandler(this));
+
+    public CrashAppZygoteService() {
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+
+    private void notifyClientOfResult(int result) {
+        try {
+          mClient.send(
+              Message.obtain(null, ServiceRunnerActivity.MSG_NOTIFY_TEST_RESULT, result, 0, null));
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to send message back to client.");
+        }
+    }
+
+    private void notifyClientOfSuccess() {
+      notifyClientOfResult(ServiceRunnerActivity.RESULT_TEST_SUCCESS);
+    }
+
+    private void startTests() {
+      Utils.accessMistaggedPointer();
+      notifyClientOfSuccess();
+    }
+}
diff --git a/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/ServiceRunnerActivity.java b/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/ServiceRunnerActivity.java
new file mode 100644
index 0000000..51bc1c2
--- /dev/null
+++ b/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/ServiceRunnerActivity.java
@@ -0,0 +1,140 @@
+/*
+ * 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.cts.tagging.sdk30memtag;
+
+import android.app.Activity;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+public class ServiceRunnerActivity extends Activity {
+  private static String TAG = ServiceRunnerActivity.class.getName();
+
+  // Message received from the client.
+  public static final int MSG_START_TEST = 1;
+
+  public static final int RESULT_TEST_UNKNOWN = RESULT_FIRST_USER + 1;
+  public static final int RESULT_TEST_SUCCESS = RESULT_FIRST_USER + 2;
+  public static final int RESULT_TEST_IGNORED = RESULT_FIRST_USER + 3;
+  public static final int RESULT_TEST_FAILED = RESULT_FIRST_USER + 4;
+  public static final int RESULT_TEST_CRASHED = RESULT_FIRST_USER + 5;
+
+  // Messages sent to the client.
+  public static final int MSG_NOTIFY_TEST_RESULT = 2;
+
+  private Messenger mService;
+  private boolean mIsBound;
+
+  private int mResult;
+  private final Object mFinishEvent = new Object();
+
+  public synchronized int getResult() { return mResult; }
+
+  // Handler of incoming messages from service.
+  class IncomingHandler extends Handler {
+    private ServiceRunnerActivity mActivity;
+
+    IncomingHandler(ServiceRunnerActivity activity) { mActivity = activity; }
+
+    @Override
+    public void handleMessage(Message msg) {
+      switch (msg.what) {
+        case MSG_NOTIFY_TEST_RESULT:
+          synchronized (mActivity.mFinishEvent) {
+            mActivity.mResult = msg.arg1;
+            mFinishEvent.notify();
+          }
+          doUnbindService();
+          break;
+        default:
+          super.handleMessage(msg);
+          return;
+      }
+    }
+  }
+
+  // Target we publish for clients to send messages to IncomingHandler.
+  final Messenger mMessenger = new Messenger(new IncomingHandler(this));
+
+  private ServiceConnection mConnection = new ServiceConnection() {
+    @Override
+    public void onServiceConnected(ComponentName className, IBinder service) {
+      mService = new Messenger(service);
+
+      // Send a message to the service to register.
+      try {
+        Message msg = Message.obtain(null, MSG_START_TEST);
+        msg.replyTo = mMessenger;
+        mService.send(msg);
+      } catch (RemoteException e) {
+        // In this case the service has crashed before we could even do anything.
+        Log.e(TAG, "Failed to send start message to service.");
+      }
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName className) {
+      // This is called when the connection with the service has been unexpectedly
+      // disconnected -- that is, its process crashed.
+      Log.i(TAG, "Service disconnected.");
+      mService = null;
+      synchronized (mFinishEvent) {
+        mResult = RESULT_TEST_CRASHED;
+        mFinishEvent.notify();
+      }
+    }
+  };
+
+  void doBindService(Class<?> cls) {
+    bindService(new Intent(this, cls), mConnection, Context.BIND_AUTO_CREATE);
+    mIsBound = true;
+  }
+
+  void doUnbindService() {
+    // Detach our existing connection.
+    unbindService(mConnection);
+    mIsBound = false;
+  }
+
+  void runService(Class<?> cls) throws Exception {
+    mResult = RESULT_TEST_UNKNOWN;
+    doBindService(cls);
+    Thread thread = new Thread() {
+      @Override
+      public void run() {
+        synchronized (mFinishEvent) {
+          while (mResult == RESULT_TEST_UNKNOWN) {
+            try {
+              mFinishEvent.wait();
+            } catch (InterruptedException e) {
+            }
+          }
+        }
+      }
+    };
+    thread.start();
+    thread.join(50000 /* millis */);
+  }
+}
diff --git a/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/TaggingTest.java b/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/TaggingTest.java
new file mode 100644
index 0000000..2dccca0
--- /dev/null
+++ b/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/TaggingTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.cts.tagging.sdk30memtag;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Rule;
+import org.junit.Test;
+
+import com.android.compatibility.common.util.DropBoxReceiver;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TaggingTest {
+    @Rule
+    public ActivityTestRule<ServiceRunnerActivity> mTestActivityRule = new ActivityTestRule<>(
+        ServiceRunnerActivity.class, false /*initialTouchMode*/, true /*launchActivity*/);
+
+    @Test
+    public void testAppZygoteMemtagSyncService() throws Exception {
+      ServiceRunnerActivity activity = mTestActivityRule.getActivity();
+      activity.runService(CrashAppZygoteService.class);
+      assertEquals(ServiceRunnerActivity.RESULT_TEST_CRASHED, activity.getResult());
+    }
+}
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java b/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/ZygotePreload.java
similarity index 65%
copy from tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
copy to hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/ZygotePreload.java
index e3fb1b5..85c7023 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
+++ b/hostsidetests/tagging/sdk_30_memtag/src/android/cts/tagging/sdk30memtag/ZygotePreload.java
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-package android.media.audio.cts;
+package android.cts.tagging.sdk30memtag;
 
-import android.media.cts.WorkDirBase;
+import android.content.pm.ApplicationInfo;
+import android.util.Log;
 
-class WorkDir extends WorkDirBase {
-    public static final String getMediaDirString() {
-        return getMediaDirString("CtsMediaTestCases-1.4");
+public class ZygotePreload implements android.app.ZygotePreload {
+    static final String TAG = "ZygotePreload";
+
+    @Override
+    public void doPreload(ApplicationInfo appInfo) {
+      Log.i(TAG, "preload called");
     }
 }
diff --git a/hostsidetests/tagging/src/com/android/cts/tagging/TaggingSdk30MemtagTest.java b/hostsidetests/tagging/src/com/android/cts/tagging/TaggingSdk30MemtagTest.java
new file mode 100644
index 0000000..dfb556c
--- /dev/null
+++ b/hostsidetests/tagging/src/com/android/cts/tagging/TaggingSdk30MemtagTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.tagging;
+
+import com.google.common.collect.ImmutableSet;
+
+public class TaggingSdk30MemtagTest extends TaggingBaseTest {
+    protected static final String TEST_APK = "CtsHostsideTaggingSdk30MemtagApp.apk";
+    protected static final String TEST_PKG = "android.cts.tagging.sdk30memtag";
+    private static final String TEST_RUNNER = "androidx.test.runner.AndroidJUnitRunner";
+
+    private static final long NATIVE_MEMTAG_ASYNC_CHANGE_ID = 135772972;
+    private static final long NATIVE_MEMTAG_SYNC_CHANGE_ID = 177438394;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        installPackage(TEST_APK, true);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        uninstallPackage(TEST_PKG, true);
+        super.tearDown();
+    }
+
+    public void testAppZygoteMemtagSyncService() throws Exception {
+        if (!deviceSupportsMemoryTagging) {
+            return;
+        }
+        runDeviceCompatTest(TEST_PKG, ".TaggingTest", "testAppZygoteMemtagSyncService",
+                /*enabledChanges*/ ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+    }
+}
diff --git a/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java b/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java
index d37b3cf..787bfdb 100644
--- a/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java
+++ b/libs/install/testapp/src/com/android/cts/install/lib/testapp/ProcessUserData.java
@@ -21,11 +21,14 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Process;
+import android.system.ErrnoException;
+import android.system.Os;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
 import java.util.Scanner;
 
 /**
@@ -109,9 +112,20 @@
                 throw new UserDataException("User handle expected to be: " + userHandle
                         + ", but was actually " + readUserHandle);
             }
+
+            int xattrVersion = Integer.valueOf(
+                    new String(Os.getxattr(versionFile.getAbsolutePath(), "user.test")));
+
+            if (xattrVersion > appVersion) {
+                throw new UserDataException("xattr data is from version " + xattrVersion
+                        + ", which is not compatible with this version " + appVersion
+                        + " of the RollbackTestApp");
+            }
         } catch (FileNotFoundException e) {
             // No problem. This is a fresh install of the app or the user data
             // has been wiped.
+        } catch (ErrnoException e) {
+            throw new UserDataException("Error while retrieving xattr.", e);
         }
 
         // Record the current version of the app in the user data.
@@ -120,8 +134,12 @@
             pw.println(appVersion);
             pw.println(userHandle);
             pw.close();
+            Os.setxattr(versionFile.getAbsolutePath(), "user.test",
+                    Integer.toString(appVersion).getBytes(StandardCharsets.UTF_8), 0);
         } catch (IOException e) {
             throw new UserDataException("Unable to write user data.", e);
+        } catch (ErrnoException e) {
+            throw new UserDataException("Unable to set xattr.", e);
         }
     }
 
diff --git a/libs/json/fuzzers/Android.bp b/libs/json/fuzzers/Android.bp
new file mode 100644
index 0000000..6434e9a
--- /dev/null
+++ b/libs/json/fuzzers/Android.bp
@@ -0,0 +1,24 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_fuzz_host {
+    name: "json-reader-fuzzer",
+    srcs: [
+        "JsonReaderFuzzer.java",
+    ],
+    static_libs: [
+        "jazzer",
+        "jsonlib",
+    ],
+}
\ No newline at end of file
diff --git a/libs/json/fuzzers/JsonReaderFuzzer.java b/libs/json/fuzzers/JsonReaderFuzzer.java
new file mode 100644
index 0000000..171808d
--- /dev/null
+++ b/libs/json/fuzzers/JsonReaderFuzzer.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.android.json.stream.JsonReader;
+
+import com.code_intelligence.jazzer.api.FuzzedDataProvider;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+/**
+ * JsonReaderFuzzer contains fuzzerTestOneInput(...) method to fuzz JsonReader
+ * using the jazzer fuzzing engine.
+ */
+public class JsonReaderFuzzer {
+    /**
+     * fuzzerTestOneInput(FuzzedDataProvider data) is called by the jazzer
+     * fuzzing engine repeatedly with random inputs to try and crash the code
+     * in JsonReader.
+     * @param data
+     * argument of type FuzzedDataProvider to provide easy access to various
+     * data types to feed into the fuzzer program.
+     */
+    public static void fuzzerTestOneInput(FuzzedDataProvider data) {
+        String initString = data.consumeRemainingAsString();
+        Reader in = new StringReader(initString);
+        JsonReader jsonReader = new JsonReader(in);
+        boolean hasNext = true;
+        while (hasNext) {
+            try {
+                hasNext = jsonReader.hasNext();
+            } catch (IOException e) {
+                break;
+            }
+            try {
+                jsonReader.nextString();
+            } catch (IOException | IllegalStateException e) {
+                break;
+            }
+        }
+    }
+}
diff --git a/libs/testserver/Android.bp b/libs/testserver/Android.bp
index 56a0921..295c286 100644
--- a/libs/testserver/Android.bp
+++ b/libs/testserver/Android.bp
@@ -14,12 +14,17 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-BSD
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "cts_libs_testserver_license", // BSD
+        "Android-Apache-2.0",
+    ],
+}
+
+license {
+    name: "cts_libs_testserver_license",
+    package_name: "Android Testserver CTS",
+    license_kinds: ["SPDX-license-identifier-BSD"],
+    license_text: ["LICENSE_BSD"],
 }
 
 java_library {
diff --git a/libs/testserver/LICENSE_BSD b/libs/testserver/LICENSE_BSD
new file mode 100644
index 0000000..063941f
--- /dev/null
+++ b/libs/testserver/LICENSE_BSD
@@ -0,0 +1,25 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+   * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+   * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_long.mp4 b/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_long.mp4
index 6b37153..c8d4b8f 100755
--- a/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_long.mp4
+++ b/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_long.mp4
Binary files differ
diff --git a/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_medium.mp4 b/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_medium.mp4
index 207530c..96cf7b1 100755
--- a/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_medium.mp4
+++ b/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_medium.mp4
Binary files differ
diff --git a/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_small.mp4 b/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_small.mp4
index f2aa045..b2b0f5d 100755
--- a/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_small.mp4
+++ b/tests/MediaProviderTranscode/res/raw/testVideo_HEVC_small.mp4
Binary files differ
diff --git a/tests/MediaProviderTranscode/res/raw/testVideo_Legacy.mp4 b/tests/MediaProviderTranscode/res/raw/testVideo_Legacy.mp4
index 1c74ffa..001667d3 100755
--- a/tests/MediaProviderTranscode/res/raw/testVideo_Legacy.mp4
+++ b/tests/MediaProviderTranscode/res/raw/testVideo_Legacy.mp4
Binary files differ
diff --git a/tests/MediaProviderTranscode/res/raw/testvideo_HEVC.mp4 b/tests/MediaProviderTranscode/res/raw/testvideo_HEVC.mp4
index 8a3dba2..fcc1bb4 100644
--- a/tests/MediaProviderTranscode/res/raw/testvideo_HEVC.mp4
+++ b/tests/MediaProviderTranscode/res/raw/testvideo_HEVC.mp4
Binary files differ
diff --git a/tests/accessibility/OWNERS b/tests/accessibility/OWNERS
index 0d2264c..edcf656 100644
--- a/tests/accessibility/OWNERS
+++ b/tests/accessibility/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 44214
-pweaver@google.com
-rhedjao@google.com
-qasid@google.com
 ryanlwlin@google.com
+sallyyuen@google.com
+pweaver@google.com
diff --git a/tests/accessibilityservice/OWNERS b/tests/accessibilityservice/OWNERS
index 0d2264c..edcf656 100644
--- a/tests/accessibilityservice/OWNERS
+++ b/tests/accessibilityservice/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 44214
-pweaver@google.com
-rhedjao@google.com
-qasid@google.com
 ryanlwlin@google.com
+sallyyuen@google.com
+pweaver@google.com
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index ecdab0f..eb9f037 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -912,15 +912,16 @@
             classNameAndTextList.add("android.widget.LinearLayout");
             classNameAndTextList.add("android.widget.LinearLayout");
             classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.ButtonB1");
-            classNameAndTextList.add("android.widget.ButtonB2");
-            classNameAndTextList.add("android.widget.ButtonB3");
-            classNameAndTextList.add("android.widget.ButtonB4");
-            classNameAndTextList.add("android.widget.ButtonB5");
-            classNameAndTextList.add("android.widget.ButtonB6");
-            classNameAndTextList.add("android.widget.ButtonB7");
-            classNameAndTextList.add("android.widget.ButtonB8");
-            classNameAndTextList.add("android.widget.ButtonB9");
+            final String btnClass = "android.widget.Button";
+            classNameAndTextList.add(btnClass.concat(mActivity.getString(R.string.button1)));
+            classNameAndTextList.add(btnClass.concat(mActivity.getString(R.string.button2)));
+            classNameAndTextList.add(btnClass.concat(mActivity.getString(R.string.button3)));
+            classNameAndTextList.add(btnClass.concat(mActivity.getString(R.string.button4)));
+            classNameAndTextList.add(btnClass.concat(mActivity.getString(R.string.button5)));
+            classNameAndTextList.add(btnClass.concat(mActivity.getString(R.string.button6)));
+            classNameAndTextList.add(btnClass.concat(mActivity.getString(R.string.button7)));
+            classNameAndTextList.add(btnClass.concat(mActivity.getString(R.string.button8)));
+            classNameAndTextList.add(btnClass.concat(mActivity.getString(R.string.button9)));
 
             boolean verifyContent = false;
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
index 074b422..f821d68 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DuplicateIdActivityTest.java
@@ -19,6 +19,7 @@
 import static android.autofillservice.cts.activities.DuplicateIdActivity.DUPLICATE_ID;
 import static android.autofillservice.cts.testcore.CannedFillResponse.NO_RESPONSE;
 import static android.autofillservice.cts.testcore.Helper.assertEqualsIgnoreSession;
+import static android.autofillservice.cts.testcore.Helper.getActivityTitle;
 
 import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
 
@@ -34,12 +35,15 @@
 import android.autofillservice.cts.testcore.CannedFillResponse;
 import android.autofillservice.cts.testcore.Helper;
 import android.autofillservice.cts.testcore.InstrumentedAutoFillService;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.autofill.AutofillId;
 import android.widget.EditText;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -77,9 +81,17 @@
      * @return An array containing the two tested views
      */
     private AssistStructure.ViewNode[] findViews(InstrumentedAutoFillService.FillRequest request,
-            int expectedCount) {
-        assertThat(request.structure.getWindowNodeCount()).isEqualTo(1);
-        AssistStructure.WindowNode windowNode = request.structure.getWindowNodeAt(0);
+            int expectedCount, String activityTitle) {
+        AssistStructure.WindowNode windowNode = null;
+        int count = 0;
+        final int nodes = request.structure.getWindowNodeCount();
+        for (int i = 0; i < nodes; ++i) {
+            if (TextUtils.equals(request.structure.getWindowNodeAt(i).getTitle(), activityTitle)) {
+                windowNode = request.structure.getWindowNodeAt(i);
+                count++;
+            }
+        }
+        assertThat(count).isEqualTo(1);
 
         AssistStructure.ViewNode rootNode = windowNode.getRootViewNode();
 
@@ -110,7 +122,9 @@
         final InstrumentedAutoFillService.FillRequest request1 = sReplier.getNextFillRequest();
         Log.v(TAG, "request1: " + request1);
 
-        final AssistStructure.ViewNode[] views1 = findViews(request1, 2);
+        final String activityTitle = getActivity().getPackageName() + "/"
+                + getActivityTitle(InstrumentationRegistry.getInstrumentation(), getActivity());
+        final AssistStructure.ViewNode[] views1 = findViews(request1, 2, activityTitle);
         final AssistStructure.ViewNode view1 = views1[0];
         final AssistStructure.ViewNode view2 = views1[1];
         final AutofillId id1 = view1.getAutofillId();
@@ -148,7 +162,7 @@
 
         final InstrumentedAutoFillService.FillRequest request3 = sReplier.getNextFillRequest();
         Log.v(TAG, "request3: " + request3);
-        final AssistStructure.ViewNode[] views2 = findViews(request3, 3);
+        final AssistStructure.ViewNode[] views2 = findViews(request3, 3, activityTitle);
         final AssistStructure.ViewNode recreatedView1 = views2[0];
         final AssistStructure.ViewNode recreatedView2 = views2[1];
         final AssistStructure.ViewNode newView1 = views2[2];
diff --git a/tests/autofillservice/src/android/autofillservice/cts/dropdown/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/dropdown/LoginActivityTest.java
index d4c9863..1880ed3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/dropdown/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/dropdown/LoginActivityTest.java
@@ -31,7 +31,7 @@
 import static android.autofillservice.cts.testcore.Helper.ID_USERNAME_LABEL;
 import static android.autofillservice.cts.testcore.Helper.allowOverlays;
 import static android.autofillservice.cts.testcore.Helper.assertHasFlags;
-import static android.autofillservice.cts.testcore.Helper.assertNumberOfChildren;
+import static android.autofillservice.cts.testcore.Helper.assertNumberOfChildrenWithWindowTitle;
 import static android.autofillservice.cts.testcore.Helper.assertTextAndValue;
 import static android.autofillservice.cts.testcore.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.testcore.Helper.assertTextOnly;
@@ -41,6 +41,7 @@
 import static android.autofillservice.cts.testcore.Helper.dumpStructure;
 import static android.autofillservice.cts.testcore.Helper.findAutofillIdByResourceId;
 import static android.autofillservice.cts.testcore.Helper.findNodeByResourceId;
+import static android.autofillservice.cts.testcore.Helper.getActivityTitle;
 import static android.autofillservice.cts.testcore.Helper.isAutofillWindowFullScreen;
 import static android.autofillservice.cts.testcore.Helper.setUserComplete;
 import static android.autofillservice.cts.testcore.InstrumentedAutoFillService.SERVICE_CLASS;
@@ -116,6 +117,7 @@
 import android.widget.EditText;
 import android.widget.RemoteViews;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 
 import com.android.compatibility.common.util.RetryableException;
@@ -2095,7 +2097,10 @@
         // But it also has an intermediate container (for username) that should be included because
         // it has a resource id.
 
-        assertNumberOfChildren(fillRequest.structure, 12);
+        // get activity title
+        final CharSequence activityTitle = mActivity.getPackageName() + "/"
+                + getActivityTitle(InstrumentationRegistry.getInstrumentation(), mActivity);
+        assertNumberOfChildrenWithWindowTitle(fillRequest.structure, 12, activityTitle);
 
         // Make sure container with a resource id was included:
         final ViewNode usernameContainer = findNodeByResourceId(fillRequest.structure,
diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
index c2dfb13..21befa5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
@@ -33,6 +33,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.app.Activity;
+import android.app.Instrumentation;
 import android.app.PendingIntent;
 import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.ViewNode;
@@ -758,10 +759,9 @@
     /**
      * Asserts the number of children in the Assist structure.
      */
-    public static void assertNumberOfChildren(AssistStructure structure, int expected) {
-        assertWithMessage("wrong number of nodes").that(structure.getWindowNodeCount())
-                .isEqualTo(1);
-        final int actual = getNumberNodes(structure);
+    public static void assertNumberOfChildrenWithWindowTitle(AssistStructure structure,
+            int expected, CharSequence windowTitle) {
+        final int actual = getNumberNodes(structure, windowTitle);
         if (actual != expected) {
             dumpStructure("assertNumberOfChildren()", structure);
             throw new AssertionError("assertNumberOfChildren() for structure failed: expected "
@@ -772,18 +772,31 @@
     /**
      * Gets the total number of nodes in an structure.
      */
-    public static int getNumberNodes(AssistStructure structure) {
+    public static int getNumberNodes(AssistStructure structure,
+            CharSequence windowTitle) {
         int count = 0;
         final int nodes = structure.getWindowNodeCount();
         for (int i = 0; i < nodes; i++) {
             final WindowNode windowNode = structure.getWindowNodeAt(i);
-            final ViewNode rootNode = windowNode.getRootViewNode();
-            count += getNumberNodes(rootNode);
+            if (windowNode.getTitle().equals(windowTitle)) {
+                final ViewNode rootNode = windowNode.getRootViewNode();
+                count += getNumberNodes(rootNode);
+            }
         }
         return count;
     }
 
     /**
+     * Gets the activity title.
+     */
+    public static CharSequence getActivityTitle(Instrumentation instrumentation,
+            Activity activity) {
+        final StringBuilder titleBuilder = new StringBuilder();
+        instrumentation.runOnMainSync(() -> titleBuilder.append(activity.getTitle()));
+        return titleBuilder;
+    }
+
+    /**
      * Gets the total number of nodes in an node, including all descendants and the node itself.
      */
     public static int getNumberNodes(ViewNode node) {
diff --git a/tests/backup/app/Android.bp b/tests/backup/app/Android.bp
index da52a3e..b0ba975 100644
--- a/tests/backup/app/Android.bp
+++ b/tests/backup/app/Android.bp
@@ -33,7 +33,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     platform_apis: true,
     manifest: "fullbackup/AndroidManifest.xml"
@@ -57,7 +57,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     platform_apis: true,
     manifest: "keyvalue/AndroidManifest.xml"
@@ -81,7 +81,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     platform_apis: true,
     manifest: "permission/AndroidManifest.xml"
@@ -103,7 +103,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     platform_apis: true,
     manifest: "permission22/AndroidManifest.xml"
diff --git a/tests/camera/Android.bp b/tests/camera/Android.bp
index 28c0928..7558776 100644
--- a/tests/camera/Android.bp
+++ b/tests/camera/Android.bp
@@ -86,6 +86,7 @@
         "android.test.runner.stubs",
         "android.test.base.stubs",
     ],
+    per_testcase_directory: true,
 }
 
 genrule {
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 774477d..a7787af 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -287,11 +287,17 @@
         for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
             try {
                 Log.i(TAG, "Testing supported video size recording for camera " + mCameraIdsUnderTest[i]);
-                if (!mAllStaticInfo.get(mCameraIdsUnderTest[i]).isColorOutputSupported()) {
+                StaticMetadata staticInfo = mAllStaticInfo.get(mCameraIdsUnderTest[i]);
+                if (!staticInfo.isColorOutputSupported()) {
                     Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
                             " does not support color outputs, skipping");
                     continue;
                 }
+                if (staticInfo.isExternalCamera()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+                            " does not support CamcorderProfile, skipping");
+                    continue;
+                }
                 // Re-use the MediaRecorder object for the same camera device.
                 mMediaRecorder = new MediaRecorder();
                 openDevice(mCameraIdsUnderTest[i]);
@@ -574,6 +580,11 @@
                             " does not support color outputs, skipping");
                     continue;
                 }
+                if (staticInfo.isExternalCamera()) {
+                    Log.i(TAG, "Camera " + mCameraIdsUnderTest[i] +
+                            " does not support CamcorderProfile, skipping");
+                    continue;
+                }
                 // Re-use the MediaRecorder object for the same camera device.
                 mMediaRecorder = new MediaRecorder();
                 openDevice(mCameraIdsUnderTest[i]);
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java
index 81198f7..891aefa 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/BlankWithTitleActivity.java
@@ -15,23 +15,13 @@
  */
 package android.contentcaptureservice.cts;
 
-import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
-import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
-import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
-import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
-import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
-import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
-import static android.contentcaptureservice.cts.Assertions.assertViewsOptionallyDisappeared;
-
-import static com.google.common.truth.Truth.assertThat;
 
 import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
 import android.util.Log;
 import android.view.View;
 import android.view.contentcapture.ContentCaptureEvent;
 import android.view.contentcapture.ContentCaptureSessionId;
-import android.view.contentcapture.ViewNode;
 
 import androidx.annotation.NonNull;
 
@@ -51,20 +41,13 @@
         final List<ContentCaptureEvent> events = session.getEvents();
         Log.v(TAG, "events(" + events.size() + "): " + events);
 
-        final int minEvents = 9; // TODO(b/122315042): disappeared not always sent
-        assertThat(events.size()).isAtLeast(minEvents);
-
-        assertSessionResumed(events, 0);
-        assertViewTreeStarted(events, 1);
-        assertDecorViewAppeared(events, 2, decorView);
-        // TODO(b/123540067): ignoring 3 intermediate parents
-        final ViewNode title = assertViewAppeared(events, 6).getViewNode();
-        assertThat(title.getText()).isEqualTo("Blanka");
-        assertViewTreeFinished(events, 7);
-        assertSessionPaused(events, 8);
-        if (false) { // TODO(b/123540067): disabled because it includes the parent
-            assertViewsOptionallyDisappeared(events, minEvents, decorView.getAutofillId(),
-                    title.getAutofillId());
-        }
+        new EventsAssertor(events)
+                .isAtLeast(9)
+                .assertSessionResumed()
+                .assertViewTreeStarted()
+                .assertDecorViewAppeared(decorView)
+                .assertViewAppeared("Blanka")
+                .assertViewTreeFinished()
+                .assertSessionPaused();
     }
 }
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
index c603f77..aa83153 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/ChildlessActivityTest.java
@@ -18,7 +18,6 @@
 import static android.contentcaptureservice.cts.Assertions.LifecycleOrder.CREATION;
 import static android.contentcaptureservice.cts.Assertions.LifecycleOrder.DESTRUCTION;
 import static android.contentcaptureservice.cts.Assertions.assertChildSessionContext;
-import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertLifecycleOrder;
 import static android.contentcaptureservice.cts.Assertions.assertMainSessionContext;
 import static android.contentcaptureservice.cts.Assertions.assertNoViewLevelEvents;
@@ -312,20 +311,20 @@
         final View grandpa2 = activity.getGrandGrandParent();
         final View decorView = activity.getDecorView();
 
-        // Assert just the relevant events
-        assertThat(events.size()).isAtLeast(12);
-        assertSessionResumed(events, 0);
-        assertViewTreeStarted(events, 1);
-        assertDecorViewAppeared(events, 2, decorView);
-        assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
-        assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
-        assertViewAppeared(events, 5, sessionId, rootView, grandpa1.getAutofillId());
-        assertViewAppeared(events, 6, sessionId, child, rootId);
-        assertViewTreeFinished(events, 7);
-        assertViewTreeStarted(events, 8);
-        assertViewDisappeared(events, 9, child.getAutofillId());
-        assertViewTreeFinished(events, 10);
-        assertSessionPaused(events, 11);
+        new EventsAssertor(events)
+                .isAtLeast(12)
+                .assertSessionResumed()
+                .assertViewTreeStarted()
+                .assertDecorViewAppeared(decorView)
+                .assertViewAppeared(grandpa2, decorView.getAutofillId())
+                .assertViewAppeared(grandpa1, grandpa2.getAutofillId())
+                .assertViewAppeared(sessionId, rootView, grandpa1.getAutofillId())
+                .assertViewAppeared(sessionId, child, rootId)
+                .assertViewTreeFinished()
+                .assertViewTreeStarted()
+                .assertViewDisappeared(child.getAutofillId())
+                .assertViewTreeFinished()
+                .assertSessionPaused();
 
         // TODO(b/122315042): assert parents disappeared
     }
@@ -358,15 +357,14 @@
         final View grandpa = activity.getGrandParent();
 
         // Assert just the relevant events
-
-        assertThat(events.size()).isAtLeast(6);
-        // TODO(b/122959591): figure out the child is coming first
-        assertSessionResumed(events, 0);
-        assertViewTreeStarted(events, 1);
-        assertViewAppeared(events, 2, sessionId, child, rootView.getAutofillId());
-        assertViewAppeared(events, 3, sessionId, rootView, grandpa.getAutofillId());
-        assertViewTreeFinished(events, 4);
-        assertSessionPaused(events, 5);
+        new EventsAssertor(events)
+                .isAtLeast(6)
+                .assertSessionResumed()
+                .assertViewTreeStarted()
+                .assertViewAppeared(sessionId, child, rootView.getAutofillId())
+                .assertViewAppeared(sessionId, rootView, grandpa.getAutofillId())
+                .assertViewTreeFinished()
+                .assertSessionPaused();
     }
 
     @Test
@@ -408,22 +406,25 @@
         final List<ContentCaptureEvent> mainEvents = mainTestSession.getEvents();
         Log.v(TAG, "mainEvents(" + mainEvents.size() + "): " + mainEvents);
 
-        assertThat(mainEvents.size()).isAtLeast(5);
-        assertSessionResumed(mainEvents, 0);
-        assertViewTreeStarted(mainEvents, 1);
-        assertViewAppeared(mainEvents, 2, mainSessionId, rootView, grandpa.getAutofillId());
-        assertViewTreeFinished(mainEvents, 3);
-        assertSessionPaused(mainEvents, 4);
+        new EventsAssertor(mainEvents)
+                .isAtLeast(5)
+                .assertSessionResumed()
+                .assertViewTreeStarted()
+                .assertViewAppeared(mainSessionId, rootView, grandpa.getAutofillId())
+                .assertViewTreeFinished()
+                .assertSessionPaused();
 
         final Session childTestSession = service.getFinishedSession(childSessionId);
         assertChildSessionContext(childTestSession, "child");
         final List<ContentCaptureEvent> childEvents = childTestSession.getEvents();
         Log.v(TAG, "childEvents(" + childEvents.size() + "): " + childEvents);
         final int minEvents = 3;
-        assertThat(childEvents.size()).isAtLeast(minEvents);
-        assertViewTreeStarted(childEvents, 0);
-        assertViewAppeared(childEvents, 1, childSessionId, child, rootView.getAutofillId());
-        assertViewTreeFinished(childEvents, 2);
+        new EventsAssertor(childEvents)
+                .isAtLeast(3)
+                .assertViewTreeStarted()
+                .assertViewAppeared(childSessionId, child, rootView.getAutofillId())
+                .assertViewTreeFinished();
+
         // TODO(b/122315042): assert parents disappeared
     }
 
@@ -726,37 +727,40 @@
         final AutofillId rootId = rootView.getAutofillId();
         final View grandpa = activity.getGrandParent();
 
-        assertThat(mainEvents).hasSize(8);
-        assertSessionResumed(mainEvents, 0);
-        assertViewTreeStarted(mainEvents, 1);
-        assertViewAppeared(mainEvents, 2, rootView, grandpa.getAutofillId());
-        assertViewTreeFinished(mainEvents, 3);
-        assertSessionPaused(mainEvents, 4); // TODO(b/122959591): investigate why
-        assertViewTreeStarted(mainEvents, 5);
-        assertViewDisappeared(mainEvents, 6, rootId);
-        assertViewTreeFinished(mainEvents, 7);
+        new EventsAssertor(mainEvents)
+                .isAtLeast(8)
+                .assertSessionResumed()
+                .assertViewTreeStarted()
+                .assertViewAppeared(rootView, grandpa.getAutofillId())
+                .assertViewTreeFinished()
+                .assertSessionPaused()
+                .assertViewTreeStarted()
+                .assertViewDisappeared(rootId)
+                .assertViewTreeFinished();
 
-        assertThat(events1).hasSize(3);
-        assertViewTreeStarted(events1, 0);
-        assertViewAppeared(events1, 1, s1c1, rootId);
-        assertViewTreeFinished(events1, 2);
+        new EventsAssertor(events1)
+                .isAtLeast(3)
+                .assertViewTreeStarted()
+                .assertViewAppeared(s1c1, rootId)
+                .assertViewTreeFinished();
 
-        assertThat(events2.size()).isAtLeast(4);
-        assertViewTreeStarted(events2, 0);
-        assertViewAppeared(events2, 1, s2c1, rootId);
-        assertViewAppeared(events2, 2, s2c2, rootId);
-        assertViewTreeFinished(events2, 3);
-        // TODO(b/122315042): assert parents disappeared
+        new EventsAssertor(events2)
+                .isAtLeast(4)
+                .assertViewTreeStarted()
+                .assertViewAppeared(s2c1, rootId)
+                .assertViewAppeared(s2c2, rootId)
+                .assertViewTreeFinished();
 
-        assertThat(events3).hasSize(8);
-        assertViewTreeStarted(events3, 0);
-        assertViewAppeared(events3, 1, s3c1, rootId);
-        assertViewAppeared(events3, 2, s3c2, rootId);
-        assertViewTreeFinished(events3, 3);
-        assertViewTreeStarted(events3, 4);
-        assertViewDisappeared(events3, 5, s3c1.getAutofillId());
-        assertViewAppeared(events3, 6, s3c3, rootId);
-        assertViewTreeFinished(events3, 7);
+        new EventsAssertor(events3)
+                .isAtLeast(8)
+                .assertViewTreeStarted()
+                .assertViewAppeared(s3c1, rootId)
+                .assertViewAppeared(s3c2, rootId)
+                .assertViewTreeFinished()
+                .assertViewTreeStarted()
+                .assertViewDisappeared(s3c1.getAutofillId())
+                .assertViewAppeared(s3c3, rootId)
+                .assertViewTreeFinished();
 
         final Session childTestSession4 = service.getFinishedSession(childSessionId4);
         assertChildSessionContext(childTestSession4, "session4");
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
index 74429de..ad79e02 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/CustomViewActivityTest.java
@@ -15,17 +15,9 @@
  */
 package android.contentcaptureservice.cts;
 
-import static android.contentcaptureservice.cts.Assertions.assertDecorViewAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertRightActivity;
-import static android.contentcaptureservice.cts.Assertions.assertSessionPaused;
-import static android.contentcaptureservice.cts.Assertions.assertSessionResumed;
-import static android.contentcaptureservice.cts.Assertions.assertViewAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertViewTextChanged;
-import static android.contentcaptureservice.cts.Assertions.assertViewTreeFinished;
-import static android.contentcaptureservice.cts.Assertions.assertViewTreeStarted;
-import static android.contentcaptureservice.cts.Assertions.assertViewWithUnknownParentAppeared;
 import static android.contentcaptureservice.cts.Assertions.assertVirtualViewAppeared;
-import static android.contentcaptureservice.cts.Assertions.assertVirtualViewDisappeared;
 import static android.contentcaptureservice.cts.Assertions.assertVirtualViewsDisappeared;
 import static android.contentcaptureservice.cts.Helper.MY_PACKAGE;
 import static android.contentcaptureservice.cts.Helper.NO_ACTIVITIES;
@@ -149,24 +141,19 @@
         Log.v(TAG, "events(" + events.size() + "): " + events);
         final int additionalEvents = 2;
 
-        assertThat(events.size()).isAtLeast(CustomViewActivity.MIN_EVENTS + additionalEvents);
-
-        // Assert just the relevant events
-        assertSessionResumed(events, 0);
-        assertViewTreeStarted(events, 1);
-        assertDecorViewAppeared(events, 2, decorView);
-        assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
-        assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
-
-        // Assert for session lifecycle events.
-        assertSessionResumed(events, 5);
-        assertSessionPaused(events, 6);
-
-        assertViewWithUnknownParentAppeared(events, 7, session.id, customViewRef.get());
-        assertViewTreeFinished(events, 8);
-        assertSessionPaused(events, 9);
-
-        activity.assertInitialViewsDisappeared(events, additionalEvents);
+        new EventsAssertor(events)
+                .isAtLeast(CustomViewActivity.MIN_EVENTS + additionalEvents)
+                .assertSessionResumed()
+                .assertViewTreeStarted()
+                .assertDecorViewAppeared(decorView)
+                .assertViewAppeared(grandpa2, decorView.getAutofillId())
+                .assertViewAppeared(grandpa1, grandpa2.getAutofillId())
+                // Assert for session lifecycle events.
+                .assertSessionResumed()
+                .assertSessionPaused()
+                .assertViewAppeared(session.id, customViewRef.get())
+                .assertViewTreeFinished()
+                .assertSessionPaused();
     }
 
     /**
@@ -218,27 +205,20 @@
         final List<ContentCaptureEvent> events = session.getEvents();
         Log.v(TAG, "events(" + events.size() + "): " + events);
         final int additionalEvents = 2;
-
-        assertThat(events.size()).isAtLeast(CustomViewActivity.MIN_EVENTS + additionalEvents);
-
-        // Assert just the relevant events
-        assertSessionResumed(events, 0);
-        assertViewTreeStarted(events, 1);
-        assertDecorViewAppeared(events, 2, decorView);
-        assertViewAppeared(events, 3, grandpa2, decorView.getAutofillId());
-        assertViewAppeared(events, 4, grandpa1, grandpa2.getAutofillId());
-
         final ContentCaptureSession mainSession = activity.mCustomView.getContentCaptureSession();
-        assertVirtualViewAppeared(events, 5, mainSession, customViewId, 1, "child");
-        assertVirtualViewDisappeared(events, 6, customViewId, mainSession, 1);
 
-        // This is the "wrong" part - the parent is notified last
-        assertViewWithUnknownParentAppeared(events, 7, session.id, activity.mCustomView);
-
-        assertViewTreeFinished(events, 8);
-        assertSessionPaused(events, 9);
-
-        activity.assertInitialViewsDisappeared(events, additionalEvents);
+        new EventsAssertor(events)
+                .isAtLeast(CustomViewActivity.MIN_EVENTS + additionalEvents)
+                .assertSessionResumed()
+                .assertViewTreeStarted()
+                .assertDecorViewAppeared(decorView)
+                .assertViewAppeared(grandpa2, decorView.getAutofillId())
+                .assertViewAppeared(grandpa1, grandpa2.getAutofillId())
+                .assertVirtualViewAppeared(mainSession, customViewId, 1, "child")
+                .assertVirtualViewDisappeared(customViewId, mainSession, 1)
+                .assertViewAppeared(session.id, activity.mCustomView)
+                .assertViewTreeFinished()
+                .assertSessionPaused();
     }
 
     /**
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/EventsAssertor.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/EventsAssertor.java
new file mode 100644
index 0000000..d00b031
--- /dev/null
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/EventsAssertor.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.contentcaptureservice.cts;
+
+import static android.contentcaptureservice.cts.Assertions.assertSessionId;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.contentcapture.ContentCaptureEvent;
+import android.view.contentcapture.ContentCaptureSession;
+import android.view.contentcapture.ContentCaptureSessionId;
+import android.view.contentcapture.ViewNode;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Used to assert Content Capture events by the event order.
+ *
+ * When the type is the same, the assertor will assert the content of the event.
+ * If the current event is different type, this event will be skipped, and then
+ * try to assert the next event. Through all events, the assertor will report an
+ * "not found" error.
+ */
+public class EventsAssertor {
+    private static final String TAG = "CtsContentCapture";
+    private final List<ContentCaptureEvent> mEvents;
+    private int mNextEvent = 0;
+
+    public EventsAssertor(@NonNull List<ContentCaptureEvent> events) {
+        mEvents = Collections.unmodifiableList(events);
+    }
+
+    @NonNull
+    public EventsAssertor isAtLeast(int size) {
+        assertThat(mEvents.size()).isAtLeast(size);
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_SESSION_RESUMED} event.
+     */
+    @NonNull
+    public EventsAssertor assertSessionResumed() {
+        assertNextEvent((event) -> assertSessionLevelEvent(event),
+                ContentCaptureEvent.TYPE_SESSION_RESUMED, "no SESSION_RESUMED event");
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_SESSION_PAUSED} event.
+     */
+    @NonNull
+    public EventsAssertor assertSessionPaused() {
+        assertNextEvent((event) -> assertSessionLevelEvent(event),
+                ContentCaptureEvent.TYPE_SESSION_PAUSED, "no SESSION_PAUSED event");
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_TREE_APPEARING} event.
+     */
+    @NonNull
+    public EventsAssertor assertViewTreeStarted() {
+        assertNextEvent((event) -> assertSessionLevelEvent(event),
+                ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING, "no VIEW_TREE_APPEARING event");
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_TREE_APPEARED} event.
+     */
+    @NonNull
+    public EventsAssertor assertViewTreeFinished() {
+        assertNextEvent((event) -> assertSessionLevelEvent(event),
+                ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED,
+                "no VIEW_TREE_APPEARED event");
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_APPEARED}
+     * event for a decor view.
+     *
+     * <P>The decor view is typically internal, so there isn't much we can assert,
+     * other than its autofill id.
+     */
+    @NonNull
+    public EventsAssertor assertDecorViewAppeared(@NonNull View expectedDecorView) {
+        assertNextEvent((event) -> assertEventId(event, expectedDecorView),
+                ContentCaptureEvent.TYPE_VIEW_APPEARED,
+                "no VIEW_APPEARED event for decor view");
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_APPEARED}
+     * event, without checking for parent id.
+     */
+    @NonNull
+    public EventsAssertor assertViewAppeared(@NonNull View expectedView) {
+        assertNextEvent((event) -> assertViewEvent(event, expectedView),
+                ContentCaptureEvent.TYPE_VIEW_APPEARED,
+                String.format("no VIEW_APPEARED event for %s", expectedView));
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_APPEARED} event.
+     */
+    @NonNull
+    public EventsAssertor assertViewAppeared(@NonNull View expectedView,
+            @NonNull AutofillId expectedParentId) {
+        assertNextEvent((event) -> assertViewEvent(event, expectedView, expectedParentId),
+                ContentCaptureEvent.TYPE_VIEW_APPEARED,
+                String.format("no VIEW_APPEARED event for %s", expectedView));
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_APPEARED} event.
+     */
+    @NonNull
+    public EventsAssertor assertViewAppeared(@NonNull ContentCaptureSessionId expectedSessionId,
+            @NonNull View expectedView, @NonNull AutofillId expectedParentId) {
+        assertViewAppeared(expectedView, expectedParentId);
+        assertSessionId(expectedSessionId, expectedView);
+        return this;
+    }
+
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_APPEARED} event.
+     */
+    @NonNull
+    public EventsAssertor assertViewAppeared(@NonNull ContentCaptureSessionId expectedSessionId,
+            @NonNull View expectedView) {
+        assertViewAppeared(expectedView);
+        assertSessionId(expectedSessionId, expectedView);
+        return this;
+    }
+
+    /**
+     * Asserts the {@code text} of a {@link ContentCaptureEvent#TYPE_VIEW_APPEARED}
+     * event.
+     */
+    @NonNull
+    public EventsAssertor assertViewAppeared(@NonNull String text) {
+        assertNextEvent((event) -> assertEventText(event, text),
+                ContentCaptureEvent.TYPE_VIEW_APPEARED,
+                String.format("no TYPE_VIEW_APPEARED event with text: %s", text));
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_DISAPPEARED}
+     * event for multiple views.
+     */
+    @NonNull
+    public EventsAssertor assertViewDisappeared(@NonNull AutofillId autofillId) {
+        assertNextEvent((event) -> assertDisappearedEvent(event, autofillId),
+                ContentCaptureEvent.TYPE_VIEW_DISAPPEARED,
+                String.format("no VIEW_DISAPPEARED event for %s", autofillId));
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_DISAPPEARED}
+     * event for multiple views.
+     */
+    @NonNull
+    public EventsAssertor assertViewDisappeared(@NonNull AutofillId... autofillIds) {
+        assertNextEvent((event) -> assertDisappearedEvent(event, autofillIds),
+                ContentCaptureEvent.TYPE_VIEW_DISAPPEARED,
+                String.format("no VIEW_DISAPPEARED event for %s", Arrays.toString(autofillIds)));
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_APPEARED}
+     * event for a virtual node.
+     */
+    @NonNull
+    public EventsAssertor assertVirtualViewAppeared(@NonNull ContentCaptureSession session,
+            @NonNull AutofillId parentId, int childId, String expectedText) {
+        final AutofillId expectedId = session.newAutofillId(parentId, childId);
+        assertNextEvent((event) -> assertVirtualViewEvent(event, expectedId, expectedText),
+                ContentCaptureEvent.TYPE_VIEW_APPEARED,
+                String.format("no VIEW_APPEARED event for %s", expectedId.toString()));
+        return this;
+    }
+
+    /**
+     * Asserts the contents of a {@link ContentCaptureEvent#TYPE_VIEW_DISAPPEARED}
+     * event for a virtual node.
+     */
+    @NonNull
+    public EventsAssertor assertVirtualViewDisappeared(AutofillId parentId,
+            ContentCaptureSession session, int childId) {
+        return assertViewDisappeared(session.newAutofillId(parentId, childId));
+    }
+
+    @Nullable
+    private String assertVirtualViewEvent(@NonNull ContentCaptureEvent event,
+            @NonNull AutofillId expectedId, @Nullable String expectedText) {
+        final ViewNode node = event.getViewNode();
+        assertThat(node).isNotNull();
+        assertWithMessage("wrong autofill id on %s", event)
+                .that(node.getAutofillId()).isEqualTo(expectedId);
+        if (expectedText != null) {
+            assertWithMessage("wrong text on %s", event)
+                    .that(node.getText().toString()).isEqualTo(expectedText);
+        } else {
+            assertWithMessage("%s should not have text", node)
+                    .that(node.getText()).isNull();
+        }
+        return null;
+    }
+
+    @Nullable
+    private String assertDisappearedEvent(@NonNull ContentCaptureEvent event,
+            @NonNull AutofillId expectedId) {
+        assertCommonViewDisappearedProperties(event);
+        assertWithMessage("wrong autofillId on event %s", event)
+                .that(event.getId()).isEqualTo(expectedId);
+        assertWithMessage("event %s should not have autofillIds", event)
+                .that(event.getIds()).isNull();
+        return null;
+    }
+
+    @Nullable
+    private String assertDisappearedEvent(@NonNull ContentCaptureEvent event,
+            @NonNull AutofillId[] expectedIds) {
+        final List<AutofillId> ids = event.getIds();
+
+        assertCommonViewDisappearedProperties(event);
+        assertWithMessage("no autofillIds on event %s", event).that(ids)
+                .isNotNull();
+        assertWithMessage("wrong autofillId on event %s", event)
+                .that(ids).containsExactly((Object[]) expectedIds).inOrder();
+        assertWithMessage("event %s should not have autofillId", event)
+                .that(event.getId()).isNull();
+        return null;
+    }
+
+    private void assertCommonViewDisappearedProperties(@NonNull ContentCaptureEvent event) {
+        assertWithMessage("event %s should not have a ViewNode", event)
+                .that(event.getViewNode()).isNull();
+        assertWithMessage("event %s should not have text", event)
+                .that(event.getText()).isNull();
+    }
+
+    @Nullable
+    private String assertViewEvent(@NonNull ContentCaptureEvent event, @NonNull View expectedView) {
+        return assertViewEvent(event, expectedView, /* expectedParentId */ null);
+    }
+
+    @Nullable
+    private String assertViewEvent(@NonNull ContentCaptureEvent event, @NonNull View expectedView,
+            @Nullable AutofillId expectedParentId) {
+        assertEvent(event, expectedView, expectedParentId, /* expectedText */  null);
+        return null;
+    }
+
+    @Nullable
+    private String assertViewEvent(@NonNull ContentCaptureEvent event, @NonNull View expectedView,
+            AutofillId expectedParentId, String expectedText) {
+        assertEvent(event, expectedView, expectedParentId, expectedText);
+        return null;
+    }
+
+    private String assertEventId(@NonNull ContentCaptureEvent event, View expectedView) {
+        final ViewNode node = event.getViewNode();
+
+        if (node == null) {
+            return String.format("node is null at %s", event);
+        }
+
+        if (expectedView != null && !node.getAutofillId().equals(expectedView.getAutofillId())) {
+            return String.format("wrong event id (expected %s, actual is %s) at %s",
+                    expectedView.getAutofillId(), node.getAutofillId(), event);
+        }
+        return null;
+    }
+
+    private String assertEventText(@NonNull ContentCaptureEvent event, String expectedText) {
+        final ViewNode node = event.getViewNode();
+        if (node == null) {
+            return String.format("node is null at %s", event);
+        }
+
+        if (!TextUtils.equals(node.getText(), expectedText)) {
+            return String.format("wrong event text (expected %s, actual is %s) at %s",
+                    expectedText, node.getText(), event);
+        }
+        return null;
+    }
+
+    private void assertEvent(@NonNull ContentCaptureEvent event, @Nullable View expectedView,
+            @Nullable AutofillId expectedParentId, @Nullable String expectedText) {
+        final ViewNode node = event.getViewNode();
+
+        assertThat(node).isNotNull();
+        assertWithMessage("wrong class on %s", event).that(node.getClassName())
+                .isEqualTo(expectedView.getClass().getName());
+        assertWithMessage("wrong autofill id on %s", event).that(node.getAutofillId())
+                .isEqualTo(expectedView.getAutofillId());
+
+        if (expectedParentId != null) {
+            assertWithMessage("wrong parent autofill id on %s", event)
+                    .that(node.getParentAutofillId()).isEqualTo(expectedParentId);
+        }
+
+        if (expectedText != null) {
+            assertWithMessage("wrong text on %s", event).that(node.getText().toString())
+                    .isEqualTo(expectedText);
+        }
+    }
+
+    @Nullable
+    private String assertSessionLevelEvent(@NonNull ContentCaptureEvent event) {
+        assertWithMessage("event %s should not have a ViewNode", event)
+                .that(event.getViewNode()).isNull();
+        assertWithMessage("event %s should not have text", event)
+                .that(event.getText()).isNull();
+        assertWithMessage("event %s should not have an autofillId", event)
+                .that(event.getId()).isNull();
+        assertWithMessage("event %s should not have autofillIds", event)
+                .that(event.getIds()).isNull();
+        return null;
+    }
+
+    private void assertNextEvent(@NonNull EventAssertion assertion, int expectedType,
+            @NonNull String errorFormat, @Nullable Object... errorArgs) {
+        if (mNextEvent >= mEvents.size()) {
+            throw new AssertionError("Reached the end of the events: "
+                    + String.format(errorFormat, errorArgs) + "\n. Events("
+                    + mEvents.size() + "): " + mEvents);
+        }
+        do {
+            final int index = mNextEvent++;
+            final ContentCaptureEvent event = mEvents.get(index);
+            if (event.getType() != expectedType) {
+                Log.w(TAG, "assertNextEvent(): ignoring event #" + index + "(" + event + ")");
+                continue;
+            }
+            // If returns an error message from getErrorMessage(), means the error can be ignored.
+            // The test will assert nex event.
+            // If directly throws AssertionError or exception, means the error cannot be ignored.
+            // The test will stop immediately.
+            final String error = assertion.getErrorMessage(event);
+            if (error == null) {
+                Log.d(TAG, "assertNextEvent(): match event in #" + index + ".");
+                return;
+            }
+            Log.w(TAG, "assertNextEvent(): ignoring event #" + index + "(" + event + "): "
+                    + error);
+        } while (mNextEvent < mEvents.size());
+        throw new AssertionError(String.format(errorFormat, errorArgs) + "\n. Events("
+                + mEvents.size() + "): " + mEvents);
+    }
+
+    private interface EventAssertion {
+        @Nullable
+        String getErrorMessage(@NonNull ContentCaptureEvent event);
+    }
+}
diff --git a/tests/filesystem/AndroidTest.xml b/tests/filesystem/AndroidTest.xml
index df140d6..3f0ddc0 100644
--- a/tests/filesystem/AndroidTest.xml
+++ b/tests/filesystem/AndroidTest.xml
@@ -26,8 +26,8 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.filesystem.cts" />
         <option name="runtime-hint" value="14m48s" />
-        <!-- test-timeout unit is ms, value = 60 min -->
-        <option name="test-timeout" value="3600000" />
+        <!-- test-timeout unit is ms, value = 70 min -->
+        <option name="test-timeout" value="4200000" />
         <!-- shell-timeout unit is ms, value = 180 min for AlmostFullTest -->
         <option name="shell-timeout" value="10800000" />
         <!-- disable isolated storage so tests can write report log -->
diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java
index 35893a7..b2dbbd4 100644
--- a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java
+++ b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java
@@ -233,6 +233,7 @@
 
     @Test
     public void testSimpleBiometricAuth_convenience() throws Exception {
+        assumeTrue(Utils.isFirstApiLevel29orGreater());
         for (SensorProperties props : mSensorProperties) {
             if (props.getSensorStrength() != SensorProperties.STRENGTH_CONVENIENCE) {
                 continue;
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 4827246..210de2b 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -350,6 +350,7 @@
              android:configChanges="orientation|screenLayout|keyboard|keyboardHidden|navigation"
              android:showWhenLocked="true"/>
         <activity android:name="android.server.wm.WindowInsetsPolicyTest$TestActivity"
+             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
              android:turnScreenOn="true"
              android:showWhenLocked="true"/>
         <activity android:name="android.server.wm.WindowInsetsPolicyTest$FullscreenTestActivity"/>
@@ -357,8 +358,6 @@
         <activity android:name="android.server.wm.WindowInsetsPolicyTest$ImmersiveFullscreenTestActivity"
              android:documentLaunchMode="always"
              android:theme="@style/no_animation"/>
-        <activity android:name="android.server.wm.WindowInsetsPolicyTest$NaturalOrientationTestActivity"
-                  android:screenOrientation="nosensor"/>
         <activity android:name="android.server.wm.LayoutTests$TestActivity"
              android:theme="@style/no_animation"/>
         <activity android:name="android.server.wm.LocationOnScreenTests$TestActivity"
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/AssistantActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/AssistantActivity.java
index 82c3770..2258da2 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/AssistantActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/AssistantActivity.java
@@ -47,13 +47,14 @@
             final Intent launchIntent = new Intent();
             launchIntent.setComponent(launchActivity)
                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            final ActivityOptions activityOptions = ActivityOptions.makeBasic();
-            activityOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
             if (getIntent().hasExtra(EXTRA_ASSISTANT_DISPLAY_ID)) {
-                activityOptions.setLaunchDisplayId(Integer.parseInt(getIntent()
+                ActivityOptions displayOptions = ActivityOptions.makeBasic();
+                displayOptions.setLaunchDisplayId(Integer.parseInt(getIntent()
                         .getStringExtra(EXTRA_ASSISTANT_DISPLAY_ID)));
+                startActivity(launchIntent, displayOptions.toBundle());
+            } else {
+                startActivity(launchIntent);
             }
-            startActivity(launchIntent, activityOptions.toBundle());
         }
 
         // Enter pip if requested
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTestsActivity.java b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTestsActivity.java
index c051817..a4f62fe 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTestsActivity.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AlertWindowsAppOpsTestsActivity.java
@@ -35,14 +35,14 @@
     }
 
     public void hideSystemAlertWindow() {
-        getWindowManager().removeView(mContent);
+        if (mContent != null && mContent.isAttachedToWindow()) {
+            getWindowManager().removeView(mContent);
+        }
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        if (mContent != null) {
-            hideSystemAlertWindow();
-        }
+        hideSystemAlertWindow();
     }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
index 55ae8a0..0ec2bef 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
@@ -200,9 +200,9 @@
             removeRootTasksWithActivityTypes(ACTIVITY_TYPE_ASSISTANT);
         }
 
-        // Launch an assistant activity on top of an existing fullscreen activity, and ensure that
-        // the fullscreen activity is still visible and on top after the assistant activity finishes
-        launchActivityOnDisplay(TEST_ACTIVITY, WINDOWING_MODE_FULLSCREEN, mAssistantDisplayId);
+        // Launch an assistant activity on top of an existing activity, and ensure that the activity
+        // is still visible and on top after the assistant activity finishes
+        launchActivityOnDisplay(TEST_ACTIVITY, mAssistantDisplayId);
         try (final AssistantSession assistantSession = new AssistantSession()) {
             assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
 
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
index 1ba6672..6552273 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
@@ -171,6 +171,7 @@
         getLaunchActivityBuilder()
                 .setTargetActivity(LAUNCHING_ACTIVITY)
                 .setUseInstrumentation()
+                .setWaitForLaunched(false)
                 .execute();
 
         // make sure TEST_ACTIVITY is still on top and resumed
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
index a3b504e..ab057d7 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
@@ -18,8 +18,8 @@
 
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_90;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
@@ -88,11 +88,6 @@
             new ActivityTestRule<>(ImmersiveFullscreenTestActivity.class,
                     false /* initialTouchMode */, false /* launchActivity */);
 
-    @Rule
-    public final ActivityTestRule<NaturalOrientationTestActivity> mNaturalOrientationTestActivity =
-            new ActivityTestRule<>(NaturalOrientationTestActivity.class,
-                    false /* initialTouchMode */, false /* launchActivity */);
-
     @Before
     @Override
     public void setUp() throws Exception {
@@ -129,16 +124,20 @@
         assumeTrue("Skipping test: no split multi-window support",
                 supportsSplitScreenMultiWindow());
 
-        launchAndWait(mNaturalOrientationTestActivity);
-        mWmState.computeState(new ComponentName[] {});
-        final boolean naturalOrientationPortrait =
-                mWmState.getDisplay(DEFAULT_DISPLAY)
-                        .mFullConfiguration.orientation == ORIENTATION_PORTRAIT;
-
-        final RotationSession rotationSession = createManagedRotationSession();
-        rotationSession.set(naturalOrientationPortrait ? ROTATION_90 : ROTATION_0);
-
         final TestActivity activity = launchAndWait(mTestActivity);
+        final int rotation = activity.getDisplay().getRotation();
+        final boolean isPortrait = activity.getResources().getConfiguration()
+                .orientation == ORIENTATION_PORTRAIT;
+        final RotationSession rotationSession = createManagedRotationSession();
+        if (isPortrait) {
+            // Rotate to landscape.
+            rotationSession.set(rotation == ROTATION_0 || rotation == ROTATION_180
+                    ? ROTATION_90 : ROTATION_0);
+        } else {
+            // Keep in landscape.
+            rotationSession.set(rotation);
+        }
+
         mWmState.waitForValidState(mTestActivityComponentName);
         final int taskId = mWmState.getTaskByActivity(mTestActivityComponentName).mTaskId;
         launchActivityInPrimarySplit(LAUNCHING_ACTIVITY);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsActivityTests.java
index c656d22..6218e31 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsActivityTests.java
@@ -47,6 +47,7 @@
 import androidx.test.filters.FlakyTest;
 
 import org.junit.Test;
+import java.util.function.Supplier;
 
 /**
  * Tests that verify the behavior of {@link WindowMetrics} APIs on {@link Activity activities}.
@@ -203,7 +204,6 @@
         // Resize the freeform activity.
         resizeActivityTask(activity.getComponentName(), WINDOW_BOUNDS.left, WINDOW_BOUNDS.top,
                 WINDOW_BOUNDS.right, WINDOW_BOUNDS.bottom);
-        mWmState.computeState(activity.getComponentName());
 
         assertMetricsMatchesLayout(activity);
 
@@ -211,7 +211,6 @@
         resizeActivityTask(activity.getComponentName(), RESIZED_WINDOW_BOUNDS.left,
                 RESIZED_WINDOW_BOUNDS.top, RESIZED_WINDOW_BOUNDS.right,
                 RESIZED_WINDOW_BOUNDS.bottom);
-        mWmState.computeState(activity.getComponentName());
 
         assertMetricsMatchesLayout(activity);
 
@@ -219,7 +218,6 @@
         resizeActivityTask(activity.getComponentName(), MOVE_OFFSET + RESIZED_WINDOW_BOUNDS.left,
                 MOVE_OFFSET + RESIZED_WINDOW_BOUNDS.top, MOVE_OFFSET + RESIZED_WINDOW_BOUNDS.right,
                 MOVE_OFFSET + RESIZED_WINDOW_BOUNDS.bottom);
-        mWmState.computeState(activity.getComponentName());
 
         assertMetricsMatchesLayout(activity);
     }
@@ -261,19 +259,21 @@
         final OnLayoutChangeListener listener = activity.mListener;
         listener.waitForLayout();
 
-        final WindowMetrics currentMetrics = activity.getWindowManager().getCurrentWindowMetrics();
-        final WindowMetrics maxMetrics = activity.getWindowManager().getMaximumWindowMetrics();
+        final Supplier<WindowMetrics> currentMetrics =
+                () -> activity.getWindowManager().getCurrentWindowMetrics();
+        final Supplier<WindowMetrics> maxMetrics =
+                () -> activity.getWindowManager().getMaximumWindowMetrics();
 
         Condition.waitFor(new Condition<>("WindowMetrics must match layout metrics",
-                () -> currentMetrics.getBounds().equals(listener.getLayoutBounds()))
+                () -> currentMetrics.get().getBounds().equals(listener.getLayoutBounds()))
                 .setRetryIntervalMs(500).setRetryLimit(10)
                 .setOnFailure(unused -> fail("WindowMetrics must match layout metrics. Layout"
                         + "bounds is" + listener.getLayoutBounds() + ", while current window"
-                        + "metrics is " + currentMetrics.getBounds())));
+                        + "metrics is " + currentMetrics.get().getBounds())));
 
         final boolean isFreeForm = activity.getResources().getConfiguration().windowConfiguration
                 .getWindowingMode() == WINDOWING_MODE_FREEFORM;
-        WindowMetricsTestHelper.assertMetricsMatchesLayout(currentMetrics, maxMetrics,
+        WindowMetricsTestHelper.assertMetricsMatchesLayout(currentMetrics.get(), maxMetrics.get(),
                 listener.getLayoutBounds(), listener.getLayoutInsets(), isFreeForm);
     }
 
diff --git a/tests/libcore/jsr166/Android.bp b/tests/libcore/jsr166/Android.bp
index 0ab6329..1addbfd 100644
--- a/tests/libcore/jsr166/Android.bp
+++ b/tests/libcore/jsr166/Android.bp
@@ -37,6 +37,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-art",
     ],
 }
diff --git a/tests/libcore/jsr166/AndroidTest.xml b/tests/libcore/jsr166/AndroidTest.xml
index 93a2b76..2b5aa4f 100644
--- a/tests/libcore/jsr166/AndroidTest.xml
+++ b/tests/libcore/jsr166/AndroidTest.xml
@@ -54,4 +54,7 @@
         <!-- ART Mainline Module (external (AOSP) version). -->
         <option name="mainline-module-package-name" value="com.android.art" />
     </object>
+
+    <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
 </configuration>
diff --git a/tests/libcore/luni/AndroidTest.xml b/tests/libcore/luni/AndroidTest.xml
index 2173c92..13fd8d4 100644
--- a/tests/libcore/luni/AndroidTest.xml
+++ b/tests/libcore/luni/AndroidTest.xml
@@ -80,4 +80,7 @@
         <!-- ART Mainline Module (external (AOSP) version). -->
         <option name="mainline-module-package-name" value="com.android.art" />
     </object>
+
+    <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
 </configuration>
diff --git a/tests/libcore/ojluni/Android.bp b/tests/libcore/ojluni/Android.bp
index b11bc9b..1276621 100644
--- a/tests/libcore/ojluni/Android.bp
+++ b/tests/libcore/ojluni/Android.bp
@@ -41,6 +41,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-art",
     ],
 }
diff --git a/tests/libcore/ojluni/AndroidTest.xml b/tests/libcore/ojluni/AndroidTest.xml
index 86e04f6..b067445 100644
--- a/tests/libcore/ojluni/AndroidTest.xml
+++ b/tests/libcore/ojluni/AndroidTest.xml
@@ -58,4 +58,7 @@
         <!-- ART Mainline Module (external (AOSP) version). -->
         <option name="mainline-module-package-name" value="com.android.art" />
     </object>
+
+    <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
 </configuration>
diff --git a/tests/libcore/okhttp/MtsLibcoreOkHttpTestCases.xml b/tests/libcore/okhttp/MtsLibcoreOkHttpTestCases.xml
index 8219e38c..f60c81c 100644
--- a/tests/libcore/okhttp/MtsLibcoreOkHttpTestCases.xml
+++ b/tests/libcore/okhttp/MtsLibcoreOkHttpTestCases.xml
@@ -56,4 +56,7 @@
         <!-- ART Mainline Module (external (AOSP) version). -->
         <option name="mainline-module-package-name" value="com.android.art" />
     </object>
+
+    <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
 </configuration>
diff --git a/tests/libcore/runner/Android.bp b/tests/libcore/runner/Android.bp
index d54a198..4e0742f 100644
--- a/tests/libcore/runner/Android.bp
+++ b/tests/libcore/runner/Android.bp
@@ -31,6 +31,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-art",
     ],
 }
diff --git a/tests/libcore/wycheproof-bc/AndroidTest.xml b/tests/libcore/wycheproof-bc/AndroidTest.xml
index b0471d0..beb852c 100644
--- a/tests/libcore/wycheproof-bc/AndroidTest.xml
+++ b/tests/libcore/wycheproof-bc/AndroidTest.xml
@@ -52,4 +52,7 @@
         <!-- ART Mainline Module (external (AOSP) version). -->
         <option name="mainline-module-package-name" value="com.android.art" />
     </object>
+
+    <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
 </configuration>
diff --git a/tests/location/location_gnss/AndroidManifest.xml b/tests/location/location_gnss/AndroidManifest.xml
index f463c37..49f1368 100644
--- a/tests/location/location_gnss/AndroidManifest.xml
+++ b/tests/location/location_gnss/AndroidManifest.xml
@@ -25,6 +25,7 @@
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="android.permission.READ_SMS"/>
     <uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>
diff --git a/tests/location/location_gnss/src/android/location/cts/gnss/GnssStatusTest.java b/tests/location/location_gnss/src/android/location/cts/gnss/GnssStatusTest.java
index e23dd84..c872070 100644
--- a/tests/location/location_gnss/src/android/location/cts/gnss/GnssStatusTest.java
+++ b/tests/location/location_gnss/src/android/location/cts/gnss/GnssStatusTest.java
@@ -1,38 +1,67 @@
 package android.location.cts.gnss;
 
+
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.location.GnssStatus;
 import android.location.cts.common.GnssTestCase;
 import android.location.cts.common.SoftAssert;
 import android.location.cts.common.TestLocationListener;
 import android.location.cts.common.TestLocationManager;
 import android.location.cts.common.TestMeasurementUtil;
+import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
+import java.util.ArrayList;
+import java.util.List;
+
 public class GnssStatusTest extends GnssTestCase  {
 
     private static final String TAG = "GnssStatusTest";
     private static final int LOCATION_TO_COLLECT_COUNT = 1;
     private static final int STATUS_TO_COLLECT_COUNT = 3;
+    private UiAutomation mUiAutomation;
 
   @Override
   protected void setUp() throws Exception {
     super.setUp();
     mTestLocationManager = new TestLocationManager(getContext());
+    mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
   }
 
   /**
    * Tests that one can listen for {@link GnssStatus}.
    */
+  @AppModeFull(reason = "Instant apps cannot access package manager to scan for permissions")
   public void testGnssStatusChanges() throws Exception {
     // Checks if GPS hardware feature is present, skips test (pass) if not
     if (!TestMeasurementUtil.canTestRunOnCurrentDevice(mTestLocationManager, TAG)) {
       return;
     }
 
-    // Register Gps Status Listener.
-    TestGnssStatusCallback testGnssStatusCallback =
-        new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
-    checkGnssChange(testGnssStatusCallback);
+    // Revoke location permissions from packages before running GnssStatusTest stops
+    // active location requests, allowing this test to receive all necessary Gnss callbacks.
+    List<String> courseLocationPackages = revokePermissions(ACCESS_COARSE_LOCATION);
+    List<String> fineLocationPackages = revokePermissions(ACCESS_FINE_LOCATION);
+
+    try {
+        // Register Gps Status Listener.
+        TestGnssStatusCallback testGnssStatusCallback =
+            new TestGnssStatusCallback(TAG, STATUS_TO_COLLECT_COUNT);
+        checkGnssChange(testGnssStatusCallback);
+    } finally {
+        // For each location package, re-grant the permission
+        grantLocationPermissions(ACCESS_COARSE_LOCATION, courseLocationPackages);
+        grantLocationPermissions(ACCESS_FINE_LOCATION, fineLocationPackages);
+    }
   }
 
   private void checkGnssChange(TestGnssStatusCallback testGnssStatusCallback)
@@ -126,4 +155,55 @@
       Log.i(TAG, "usedInFix: " + status.usedInFix(i));
     }
   }
+
+  private List<String> getPackagesWithPermissions(String permission) {
+    Context context = InstrumentationRegistry.getTargetContext();
+    PackageManager pm = context.getPackageManager();
+
+    ArrayList<String> packagesWithPermission = new ArrayList<>();
+    List<ApplicationInfo> packages = pm.getInstalledApplications(/*flags=*/ 0);
+
+    for (ApplicationInfo applicationInfo : packages) {
+      String packageName = applicationInfo.packageName;
+      if (packageName.equals(context.getPackageName())) {
+        // Don't include this test package.
+        continue;
+      }
+
+      if (pm.checkPermission(permission, packageName) == PackageManager.PERMISSION_GRANTED) {
+        final int flags;
+        mUiAutomation.adoptShellPermissionIdentity("android.permission.GET_RUNTIME_PERMISSIONS");
+        try {
+          flags = pm.getPermissionFlags(permission, packageName,
+                    android.os.Process.myUserHandle());
+        } finally {
+          mUiAutomation.dropShellPermissionIdentity();
+        }
+
+        final boolean fixed = (flags & (PackageManager.FLAG_PERMISSION_USER_FIXED
+            | PackageManager.FLAG_PERMISSION_POLICY_FIXED
+            | PackageManager.FLAG_PERMISSION_SYSTEM_FIXED)) != 0;
+        if (!fixed) {
+          packagesWithPermission.add(packageName);
+        }
+      }
+    }
+    return packagesWithPermission;
+  }
+
+  private List<String> revokePermissions(String permission) {
+    List<String> packages = getPackagesWithPermissions(permission);
+    for (String packageWithPermission : packages) {
+      Log.i(TAG, "Revoking permissions from: " + packageWithPermission);
+      mUiAutomation.revokeRuntimePermission(packageWithPermission, permission);
+    }
+    return packages;
+  }
+
+  private void grantLocationPermissions(String permission, List<String> packages) {
+    for (String packageToGivePermission : packages) {
+      Log.i(TAG, "Granting permissions (back) to: " + packageToGivePermission);
+      mUiAutomation.grantRuntimePermission(packageToGivePermission, permission);
+    }
+  }
 }
diff --git a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java
index 528b417..f81c30a 100644
--- a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java
+++ b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java
@@ -49,14 +49,10 @@
     // The bitrates are configured to large values. The content has zero motion, so in-time the
     // qp of the encoded clips shall drop down to < 10. Further the color bands are aligned to 2,
     // so from downsampling rgb24 to yuv420p, even if bilinear filters are used as opposed to
-    // skipping samples, we may not see large color loss. Hence allowable tolerance is kept to 8.
-    // until QP stabilizes, the tolerance is set at 10.
-
-    // TODO (b/193192195) Initial delta values used in the tests were 7 and 5, but were increased
-    // to 10 and 8, as tests on emulator showed a higher delta. So the allowed delta values are
-    // increased for now
-    private final int TRANSIENT_STATE_COLOR_DELTA = 10;
-    private final int STEADY_STATE_COLOR_DELTA = 8;
+    // skipping samples, we may not see large color loss. Hence allowable tolerance is kept to 5.
+    // until QP stabilizes, the tolerance is set at 7.
+    private final int TRANSIENT_STATE_COLOR_DELTA = 7;
+    private final int STEADY_STATE_COLOR_DELTA = 5;
     private final int[][] mColorBars = new int[][]{
             {66, 133, 244},
             {219, 68, 55},
diff --git a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
index 6600af0..056f7a4 100644
--- a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
@@ -78,7 +78,7 @@
         // Verify minimum screen density and resolution
         assertMinDpiAndPixels(context, DENSITY_400, 1920, 1080);
         // Verify minimum memory
-        assertMinMemoryMb(context, 6 * 1024);
+        assertMinMemoryMb(context, Utils.MIN_MEMORY_PERF_CLASS_CANDIDATE_MB);
     }
 
     /** Asserts that the given values conform to the specs in CDD */
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
new file mode 100644
index 0000000..5b0178d
--- /dev/null
+++ b/tests/net/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// Contains tests for networking code that is in the platform and not in mainline modules.
+// These tests are run by CtsNetTestCases and CtsNetTestCasesLatestSdk, so like all the other tests
+// in those suites, they must pass on all Android versions supported by the modules.
+java_library {
+    name: "CtsNetTestsNonUpdatableLib",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules"
+    ],
+    libs: [
+        "net-tests-utils",
+    ],
+    platform_apis: true,
+    visibility: [
+        "//packages/modules/Connectivity/tests:__subpackages__",
+    ],
+}
diff --git a/tests/net/OWNERS b/tests/net/OWNERS
new file mode 100644
index 0000000..67e4fc9
--- /dev/null
+++ b/tests/net/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 31808
+set noparent
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
\ No newline at end of file
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
new file mode 100644
index 0000000..a6a02d5
--- /dev/null
+++ b/tests/net/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "packages/modules/Connectivity"
+    }
+  ]
+}
diff --git a/tests/net/src/android/net/cts/LocalSocketTest.java b/tests/net/src/android/net/cts/LocalSocketTest.java
new file mode 100644
index 0000000..39b5dbc
--- /dev/null
+++ b/tests/net/src/android/net/cts/LocalSocketTest.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+import static com.android.testutils.MiscAsserts.assertThrows;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.Credentials;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructTimeval;
+import android.system.UnixSocketAddress;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketAddress;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class LocalSocketTest {
+    private final static String ADDRESS_PREFIX = "com.android.net.LocalSocketTest";
+
+    @Rule
+    public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
+    @Test
+    public void testLocalConnections() throws IOException {
+        String address = ADDRESS_PREFIX + "_testLocalConnections";
+        // create client and server socket
+        LocalServerSocket localServerSocket = new LocalServerSocket(address);
+        LocalSocket clientSocket = new LocalSocket();
+
+        // establish connection between client and server
+        LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
+        assertFalse(clientSocket.isConnected());
+        clientSocket.connect(locSockAddr);
+        assertTrue(clientSocket.isConnected());
+
+        LocalSocket serverSocket = localServerSocket.accept();
+        assertTrue(serverSocket.isConnected());
+        assertTrue(serverSocket.isBound());
+        assertThrows(IOException.class, () -> {
+            serverSocket.bind(localServerSocket.getLocalSocketAddress());
+        });
+        assertThrows(IOException.class, () -> {
+            serverSocket.connect(locSockAddr);
+        });
+
+        Credentials credent = clientSocket.getPeerCredentials();
+        assertTrue(0 != credent.getPid());
+
+        // send data from client to server
+        OutputStream clientOutStream = clientSocket.getOutputStream();
+        clientOutStream.write(12);
+        InputStream serverInStream = serverSocket.getInputStream();
+        assertEquals(12, serverInStream.read());
+
+        //send data from server to client
+        OutputStream serverOutStream = serverSocket.getOutputStream();
+        serverOutStream.write(3);
+        InputStream clientInStream = clientSocket.getInputStream();
+        assertEquals(3, clientInStream.read());
+
+        // Test sending and receiving file descriptors
+        clientSocket.setFileDescriptorsForSend(new FileDescriptor[]{FileDescriptor.in});
+        clientOutStream.write(32);
+        assertEquals(32, serverInStream.read());
+
+        FileDescriptor[] out = serverSocket.getAncillaryFileDescriptors();
+        assertEquals(1, out.length);
+        FileDescriptor fd = clientSocket.getFileDescriptor();
+        assertTrue(fd.valid());
+
+        //shutdown input stream of client
+        clientSocket.shutdownInput();
+        assertEquals(-1, clientInStream.read());
+
+        //shutdown output stream of client
+        clientSocket.shutdownOutput();
+        assertThrows(IOException.class, () -> {
+            clientOutStream.write(10);
+        });
+
+        //shutdown input stream of server
+        serverSocket.shutdownInput();
+        assertEquals(-1, serverInStream.read());
+
+        //shutdown output stream of server
+        serverSocket.shutdownOutput();
+        assertThrows(IOException.class, () -> {
+            serverOutStream.write(10);
+        });
+
+        //close client socket
+        clientSocket.close();
+        assertThrows(IOException.class, () -> {
+            clientInStream.read();
+        });
+
+        //close server socket
+        serverSocket.close();
+        assertThrows(IOException.class, () -> {
+            serverInStream.read();
+        });
+    }
+
+    @Test
+    public void testAccessors() throws IOException {
+        String address = ADDRESS_PREFIX + "_testAccessors";
+        LocalSocket socket = new LocalSocket();
+        LocalSocketAddress addr = new LocalSocketAddress(address);
+
+        assertFalse(socket.isBound());
+        socket.bind(addr);
+        assertTrue(socket.isBound());
+        assertEquals(addr, socket.getLocalSocketAddress());
+
+        String str = socket.toString();
+        assertTrue(str.contains("impl:android.net.LocalSocketImpl"));
+
+        socket.setReceiveBufferSize(1999);
+        assertEquals(1999 << 1, socket.getReceiveBufferSize());
+
+        socket.setSendBufferSize(3998);
+        assertEquals(3998 << 1, socket.getSendBufferSize());
+
+        assertEquals(0, socket.getSoTimeout());
+        socket.setSoTimeout(1996);
+        assertTrue(socket.getSoTimeout() > 0);
+
+        assertThrows(UnsupportedOperationException.class, () -> {
+            socket.getRemoteSocketAddress();
+        });
+
+        assertThrows(UnsupportedOperationException.class, () -> {
+            socket.isClosed();
+        });
+
+        assertThrows(UnsupportedOperationException.class, () -> {
+            socket.isInputShutdown();
+        });
+
+        assertThrows(UnsupportedOperationException.class, () -> {
+            socket.isOutputShutdown();
+        });
+
+        assertThrows(UnsupportedOperationException.class, () -> {
+            socket.connect(addr, 2005);
+        });
+
+        socket.close();
+    }
+
+    // http://b/31205169
+    @Test
+    public void testSetSoTimeout_readTimeout() throws Exception {
+        String address = ADDRESS_PREFIX + "_testSetSoTimeout_readTimeout";
+
+        try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+            final LocalSocket clientSocket = socketPair.clientSocket;
+
+            // Set the timeout in millis.
+            int timeoutMillis = 1000;
+            clientSocket.setSoTimeout(timeoutMillis);
+
+            // Avoid blocking the test run if timeout doesn't happen by using a separate thread.
+            Callable<Result> reader = () -> {
+                try {
+                    clientSocket.getInputStream().read();
+                    return Result.noException("Did not block");
+                } catch (IOException e) {
+                    return Result.exception(e);
+                }
+            };
+            // Allow the configured timeout, plus some slop.
+            int allowedTime = timeoutMillis + 2000;
+            Result result = runInSeparateThread(allowedTime, reader);
+
+            // Check the message was a timeout, it's all we have to go on.
+            String expectedMessage = Os.strerror(OsConstants.EAGAIN);
+            result.assertThrewIOException(expectedMessage);
+        }
+    }
+
+    // http://b/31205169
+    @Test
+    public void testSetSoTimeout_writeTimeout() throws Exception {
+        String address = ADDRESS_PREFIX + "_testSetSoTimeout_writeTimeout";
+
+        try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+            final LocalSocket clientSocket = socketPair.clientSocket;
+
+            // Set the timeout in millis.
+            int timeoutMillis = 1000;
+            clientSocket.setSoTimeout(timeoutMillis);
+
+            // Set a small buffer size so we know we can flood it.
+            clientSocket.setSendBufferSize(100);
+            final int bufferSize = clientSocket.getSendBufferSize();
+
+            // Avoid blocking the test run if timeout doesn't happen by using a separate thread.
+            Callable<Result> writer = () -> {
+                try {
+                    byte[] toWrite = new byte[bufferSize * 2];
+                    clientSocket.getOutputStream().write(toWrite);
+                    return Result.noException("Did not block");
+                } catch (IOException e) {
+                    return Result.exception(e);
+                }
+            };
+            // Allow the configured timeout, plus some slop.
+            int allowedTime = timeoutMillis + 2000;
+
+            Result result = runInSeparateThread(allowedTime, writer);
+
+            // Check the message was a timeout, it's all we have to go on.
+            String expectedMessage = Os.strerror(OsConstants.EAGAIN);
+            result.assertThrewIOException(expectedMessage);
+        }
+    }
+
+    @Test
+    public void testAvailable() throws Exception {
+        String address = ADDRESS_PREFIX + "_testAvailable";
+
+        try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+            LocalSocket clientSocket = socketPair.clientSocket;
+            LocalSocket serverSocket = socketPair.serverSocket.accept();
+
+            OutputStream clientOutputStream = clientSocket.getOutputStream();
+            InputStream serverInputStream = serverSocket.getInputStream();
+            assertEquals(0, serverInputStream.available());
+
+            byte[] buffer = new byte[50];
+            clientOutputStream.write(buffer);
+            assertEquals(50, serverInputStream.available());
+
+            InputStream clientInputStream = clientSocket.getInputStream();
+            OutputStream serverOutputStream = serverSocket.getOutputStream();
+            assertEquals(0, clientInputStream.available());
+            serverOutputStream.write(buffer);
+            assertEquals(50, serverInputStream.available());
+
+            serverSocket.close();
+        }
+    }
+
+    // http://b/34095140
+    @Test @IgnoreUpTo(SC_V2)
+    public void testLocalSocketCreatedFromFileDescriptor() throws Exception {
+        String address = ADDRESS_PREFIX + "_testLocalSocketCreatedFromFileDescriptor";
+
+        // Establish connection between a local client and server to get a valid client socket file
+        // descriptor.
+        try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+            // Extract the client FileDescriptor we can use.
+            FileDescriptor fileDescriptor = socketPair.clientSocket.getFileDescriptor();
+            assertTrue(fileDescriptor.valid());
+
+            // Create the LocalSocket we want to test.
+            LocalSocket clientSocketCreatedFromFileDescriptor = new LocalSocket(fileDescriptor);
+            assertTrue(clientSocketCreatedFromFileDescriptor.isConnected());
+            assertTrue(clientSocketCreatedFromFileDescriptor.isBound());
+
+            // Test the LocalSocket can be used for communication.
+            LocalSocket serverSocket = socketPair.serverSocket.accept();
+            OutputStream clientOutputStream =
+                    clientSocketCreatedFromFileDescriptor.getOutputStream();
+            InputStream serverInputStream = serverSocket.getInputStream();
+
+            clientOutputStream.write(12);
+            assertEquals(12, serverInputStream.read());
+
+            // Closing clientSocketCreatedFromFileDescriptor does not close the file descriptor.
+            clientSocketCreatedFromFileDescriptor.close();
+            assertTrue(fileDescriptor.valid());
+
+            // .. while closing the LocalSocket that owned the file descriptor does.
+            socketPair.clientSocket.close();
+            assertFalse(fileDescriptor.valid());
+        }
+    }
+
+    @Test
+    public void testFlush() throws Exception {
+        String address = ADDRESS_PREFIX + "_testFlush";
+
+        try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+            LocalSocket clientSocket = socketPair.clientSocket;
+            LocalSocket serverSocket = socketPair.serverSocket.accept();
+
+            OutputStream clientOutputStream = clientSocket.getOutputStream();
+            InputStream serverInputStream = serverSocket.getInputStream();
+            testFlushWorks(clientOutputStream, serverInputStream);
+
+            OutputStream serverOutputStream = serverSocket.getOutputStream();
+            InputStream clientInputStream = clientSocket.getInputStream();
+            testFlushWorks(serverOutputStream, clientInputStream);
+
+            serverSocket.close();
+        }
+    }
+
+    private void testFlushWorks(OutputStream outputStream, InputStream inputStream)
+            throws Exception {
+        final int bytesToTransfer = 50;
+        StreamReader inputStreamReader = new StreamReader(inputStream, bytesToTransfer);
+
+        byte[] buffer = new byte[bytesToTransfer];
+        outputStream.write(buffer);
+        assertEquals(bytesToTransfer, inputStream.available());
+
+        // Start consuming the data.
+        inputStreamReader.start();
+
+        // This doesn't actually flush any buffers, it just polls until the reader has read all the
+        // bytes.
+        outputStream.flush();
+
+        inputStreamReader.waitForCompletion(5000);
+        inputStreamReader.assertBytesRead(bytesToTransfer);
+        assertEquals(0, inputStream.available());
+    }
+
+    private void sendAndReceiveBytes(LocalSocket s1, LocalSocket s2) throws Exception {
+        final Random random = new Random();
+        final byte[] sendBytes = new byte[random.nextInt(511) + 1];  // Avoid 0-byte writes.
+        random.nextBytes(sendBytes);
+        final int numBytes = sendBytes.length;
+        final OutputStream os = s1.getOutputStream();
+        os.write(sendBytes);
+        os.flush();
+
+        final InputStream is = s2.getInputStream();
+        final byte[] recvBytes = new byte[1024];
+        assertEquals(numBytes, is.read(recvBytes, 0, recvBytes.length));
+
+        final byte[] received = Arrays.copyOfRange(recvBytes, 0, numBytes);
+        assertArrayEquals(received, sendBytes);
+    }
+
+    /**
+     * Keeps track of the highest-numbered FD that is passed in.
+     */
+    private class MaxFdTracker{
+        private int mMax = -1;
+
+        public int get() {
+            return mMax;
+        }
+
+        private void noteFd(int fd) {
+            mMax = Math.max(mMax, fd);
+        }
+
+        public void noteFd(FileDescriptor fd) {
+            noteFd(fd.getInt$());
+        }
+
+        public void noteFd(LocalSocket s) {
+            noteFd(s.getFileDescriptor().getInt$());
+        }
+    }
+
+    @Test @IgnoreUpTo(SC_V2)
+    public void testCreateFromFd() throws Exception {
+        String address = ADDRESS_PREFIX + "_testClosingConnectedSocket";
+        LocalServerSocket server = new LocalServerSocket(address);
+
+        final int TIMEOUT_MS = 1000;
+
+        final int NUM_ITERATIONS = 1000;
+        int firstFd = -1;
+        MaxFdTracker maxFd = new MaxFdTracker();
+
+        for (int i = 0; i < NUM_ITERATIONS; i++) {
+            FileDescriptor fd = Os.socket(OsConstants.AF_UNIX, OsConstants.SOCK_STREAM, 0);
+            if (firstFd == -1) {
+                firstFd = fd.getInt$();
+            } else  {
+                maxFd.noteFd(fd);
+            }
+
+            // Ensure the test doesn't hang by setting a reasonably short timeout.
+            // This seems easier than polling on non-blocking socket.
+            Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
+                    StructTimeval.fromMillis(TIMEOUT_MS));
+            Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
+                    StructTimeval.fromMillis(TIMEOUT_MS));
+
+            final SocketAddress sockAddr = Os.getsockname(server.getFileDescriptor());
+            Os.connect(fd, sockAddr);
+
+            LocalSocket accepted = server.accept();
+            accepted.setSoTimeout(TIMEOUT_MS);
+            maxFd.noteFd(accepted);
+
+            LocalSocket ls = new LocalSocket(fd);
+            assertEquals(ls.getFileDescriptor().getInt$(), fd.getInt$());
+            maxFd.noteFd(ls);
+
+            sendAndReceiveBytes(accepted, ls);
+            sendAndReceiveBytes(ls, accepted);
+
+            accepted.close();
+            assertNull(accepted.getFileDescriptor());
+            Os.close(fd);
+        }
+        server.close();
+
+        assertTrue("No FDs created!", firstFd != -1);
+        assertTrue("Only one FD created?", maxFd.get() != -1);
+        int fdsConsumed = maxFd.get() - firstFd;
+        assertTrue(
+                "FD leak! Opened " + NUM_ITERATIONS + " sockets, FD int went up by " + fdsConsumed,
+            fdsConsumed < NUM_ITERATIONS / 2);
+    }
+
+    @Test @IgnoreUpTo(SC_V2)
+    public void testCreateFromFd_notConnected() throws Exception {
+        FileDescriptor fd = Os.socket(OsConstants.AF_UNIX, OsConstants.SOCK_STREAM, 0);
+        assertThrows(IllegalArgumentException.class, () -> {
+            LocalSocket ls = new LocalSocket(fd);
+        });
+    }
+
+    @Test @IgnoreUpTo(SC_V2)
+    public void testCreateFromFd_notSocket() throws Exception {
+        FileDescriptor fd = Os.open("/dev/null", 0 /* flags */, OsConstants.O_WRONLY);
+        assertThrows(IllegalArgumentException.class, () -> {
+            LocalSocket ls = new LocalSocket(fd);
+        });
+    }
+
+    private static class StreamReader extends Thread {
+        private final InputStream is;
+        private final int expectedByteCount;
+        private final CountDownLatch completeLatch = new CountDownLatch(1);
+
+        private volatile Exception exception;
+        private int bytesRead;
+
+        private StreamReader(InputStream is, int expectedByteCount) {
+            this.is = is;
+            this.expectedByteCount = expectedByteCount;
+        }
+
+        @Override
+        public void run() {
+            try {
+                byte[] buffer = new byte[10];
+                int readCount;
+                while ((readCount = is.read(buffer)) >= 0) {
+                    bytesRead += readCount;
+                    if (bytesRead >= expectedByteCount) {
+                        break;
+                    }
+                }
+            } catch (IOException e) {
+                exception = e;
+            } finally {
+                completeLatch.countDown();
+            }
+        }
+
+        public void waitForCompletion(long waitMillis) throws Exception {
+            if (!completeLatch.await(waitMillis, TimeUnit.MILLISECONDS)) {
+                fail("Timeout waiting for completion");
+            }
+            if (exception != null) {
+                throw new Exception("Read failed", exception);
+            }
+        }
+
+        public void assertBytesRead(int expected) {
+            assertEquals(expected, bytesRead);
+        }
+    }
+
+    private static class Result {
+        private final String type;
+        private final Exception e;
+
+        private Result(String type, Exception e) {
+            this.type = type;
+            this.e = e;
+        }
+
+        static Result noException(String description) {
+            return new Result(description, null);
+        }
+
+        static Result exception(Exception e) {
+            return new Result(e.getClass().getName(), e);
+        }
+
+        void assertThrewIOException(String expectedMessage) {
+            assertEquals("Unexpected result type", IOException.class.getName(), type);
+            assertEquals("Unexpected exception message", expectedMessage, e.getMessage());
+        }
+    }
+
+    private static Result runInSeparateThread(int allowedTime, final Callable<Result> callable)
+            throws Exception {
+        ExecutorService service = Executors.newSingleThreadScheduledExecutor();
+        Future<Result> future = service.submit(callable);
+        Result result = future.get(allowedTime, TimeUnit.MILLISECONDS);
+        if (!future.isDone()) {
+            fail("Worker thread appears blocked");
+        }
+        return result;
+    }
+
+    private static class LocalSocketPair implements AutoCloseable {
+        static LocalSocketPair createConnectedSocketPair(String address) throws Exception {
+            LocalServerSocket localServerSocket = new LocalServerSocket(address);
+            final LocalSocket clientSocket = new LocalSocket();
+
+            // Establish connection between client and server
+            LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
+            clientSocket.connect(locSockAddr);
+            assertTrue(clientSocket.isConnected());
+            return new LocalSocketPair(localServerSocket, clientSocket);
+        }
+
+        final LocalServerSocket serverSocket;
+        final LocalSocket clientSocket;
+
+        LocalSocketPair(LocalServerSocket serverSocket, LocalSocket clientSocket) {
+            this.serverSocket = serverSocket;
+            this.clientSocket = clientSocket;
+        }
+
+        public void close() throws Exception {
+            serverSocket.close();
+            clientSocket.close();
+        }
+    }
+}
diff --git a/tests/sample/src/android/sample/cts/SampleDeviceReportLogTest.java b/tests/sample/src/android/sample/cts/SampleDeviceReportLogTest.java
index a71fd97..0970b2a 100644
--- a/tests/sample/src/android/sample/cts/SampleDeviceReportLogTest.java
+++ b/tests/sample/src/android/sample/cts/SampleDeviceReportLogTest.java
@@ -23,6 +23,11 @@
 import com.android.compatibility.common.util.ResultUnit;
 
 /**
+ * WARNING: This sample test is out of date. Newer tests should be written using
+ * androidx testing libraries. See SampleJUnit4DeviceTest.java
+ *
+ * TODO(b/211678773): Update the test to use androidx testing libraries.
+ *
  * A simple compatibility test which includes results in the report.
  *
  * This class has 3 no-op tests that create report logs and log fake metrics.
diff --git a/tests/sample/src/android/sample/cts/SampleDeviceResultTest.java b/tests/sample/src/android/sample/cts/SampleDeviceResultTest.java
index 41384b0..8c4a97d 100644
--- a/tests/sample/src/android/sample/cts/SampleDeviceResultTest.java
+++ b/tests/sample/src/android/sample/cts/SampleDeviceResultTest.java
@@ -29,6 +29,13 @@
 import java.util.Random;
 
 /**
+ * WARNING: This sample test is out of date. Newer tests should be written using
+ * androidx testing libraries. See SampleJUnit4DeviceTest.java
+ *
+ * TODO(b/211678773): Update the test to use androidx testing libraries.
+ *
+ * A simple compatibility test which includes results in the report.
+ *
  * A simple compatibility test which includes results in the report.
  *
  * This test measures the time taken to run a workload and adds in the report.
diff --git a/tests/sample/src/android/sample/cts/SampleDeviceTest.java b/tests/sample/src/android/sample/cts/SampleDeviceTest.java
index 13b7ea6..653bb47 100644
--- a/tests/sample/src/android/sample/cts/SampleDeviceTest.java
+++ b/tests/sample/src/android/sample/cts/SampleDeviceTest.java
@@ -19,6 +19,11 @@
 import android.test.ActivityInstrumentationTestCase2;
 
 /**
+ * WARNING: This sample test is out of date. Newer tests should be written using
+ * androidx testing libraries. See SampleJUnit4DeviceTest.java
+ *
+ * TODO(b/211678773): This test can be probably removed.
+ *
  * A simple compatibility test which tests the SharedPreferences API.
  *
  * This test uses {@link android.test.ActivityInstrumentationTestCase2} to instrument the
diff --git a/tests/sensor/src/android/hardware/cts/helpers/SensorRatePermissionEventConnectionTestHelper.java b/tests/sensor/src/android/hardware/cts/helpers/SensorRatePermissionEventConnectionTestHelper.java
index 5094d17..952faab 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/SensorRatePermissionEventConnectionTestHelper.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/SensorRatePermissionEventConnectionTestHelper.java
@@ -36,7 +36,7 @@
  * A helper class to test sensor APIs related to sampling rates of SensorEventConnections.
  */
 public class SensorRatePermissionEventConnectionTestHelper {
-    public static final int CAPPED_SAMPLE_RATE_HZ = 220; // Capped rate 200 Hz + 10% headroom
+    public static final int CAPPED_SAMPLE_RATE_HZ = 270; // Capped rate 200 Hz + 10% headroom
     // Set of sensors that are throttled
     public static final ImmutableSet<Integer> CAPPED_SENSOR_TYPE_SET = ImmutableSet.of(
             Sensor.TYPE_ACCELEROMETER,
diff --git a/tests/signature/api-check/Android.bp b/tests/signature/api-check/Android.bp
index fd0315f..96f3a9a 100644
--- a/tests/signature/api-check/Android.bp
+++ b/tests/signature/api-check/Android.bp
@@ -92,7 +92,7 @@
 // Defaults for hiddenapi blocklist checks.
 java_defaults {
     name: "hiddenapi-blocklist-check-defaults",
-    defaults: ["signature-api-check-defaults"],
+    defaults: ["signature-api-check-dynamic-config-defaults"],
     java_resources: [
         ":platform-bootclasspath{hiddenapi-flags.csv}",
         ":cts-api-hiddenapi-filter-csv"
@@ -101,3 +101,12 @@
         "libcts_dexchecker",
     ],
 }
+
+// The CtsHiddenApiBlocklistApiDynamicConfig file is intended to be used by
+// multiple CtsHiddenApiBlocklist...TestCases.
+filegroup {
+    name: "CtsHiddenApiBlocklistApiDynamicConfig",
+    srcs: [
+      "CtsHiddenApiBlocklistApiDynamicConfig.dynamic",
+    ],
+}
diff --git a/tests/signature/api-check/CtsHiddenApiBlocklistApiDynamicConfig.dynamic b/tests/signature/api-check/CtsHiddenApiBlocklistApiDynamicConfig.dynamic
new file mode 100644
index 0000000..55d0fa0
--- /dev/null
+++ b/tests/signature/api-check/CtsHiddenApiBlocklistApiDynamicConfig.dynamic
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 Google Inc.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<dynamicConfig>
+    <entry key ="expected_failures">
+        <!--
+         ! Each value in this section identifies an expected failure and is of the
+         ! form:
+         !    <failure-type>:<signature of class/member>
+         !
+         ! These entries are loaded by AnnotationTest which uses them to construct
+         ! an ExpectedFailuresFilter which discards them.
+         !
+         ! e.g. If the test fails with the following error message:
+         ! repackaged.junit.framework.AssertionFailedError:
+         ! extra_class:	android.media.MediaParceledListSlice	Error: Class annotated with android.annotation.SystemApi does not exist in the documented API
+         ! extra_class:	android.media.MediaFrameworkInitializer	Error: Class annotated with android.annotation.SystemApi does not exist in the documented API
+         ! extra_interface:	android.media.MediaCommunicationManager$SessionCallback	Error: Class annotated with android.annotation.SystemApi does not exist in the documented API
+         ! extra_class:	android.media.MediaTranscodingManager	Error: Class annotated with android.annotation.SystemApi does not exist in the documented API
+         ! ClassLoader hierarchy
+         !
+         ! The first step is to check that the extra classes are expected (e.g.
+         ! because they have been annotated with the @SystemApi since this version
+         ! of the CTS tests were released and the tests are being run on an
+         ! Android system with a version of the mainline modules that includes
+         ! those changes.
+         !
+         ! If they are not expected then this must be caused by a partner
+         ! inadvertently adding something to the @SystemApi so the correct
+         ! response is for them to stop doing that.
+         !
+         ! If they are expected then additional entries should be added to this
+         ! section. That simply requires copying each error message into their own
+         ! <value></value> element and then removing the whitespace after the
+         ! first : and also removing the trailing " Error: ..." part (including
+         ! the leading white space).
+         !
+         ! See below for some examples.
+         !-->
+        <!-- Bug: 204723907 -->
+        <value>extra_field:int android.net.wifi.ScanResult.UNSPECIFIED</value>
+        <value>extra_method:boolean android.net.wifi.WifiInfo.isTrusted()</value>
+        <value>extra_method:long[] android.net.wifi.hotspot2.pps.HomeSp.getMatchAllOis()</value>
+        <value>extra_method:long[] android.net.wifi.hotspot2.pps.HomeSp.getMatchAnyOis()</value>
+        <value>extra_method:void android.net.wifi.hotspot2.pps.HomeSp.setMatchAllOis(long[])</value>
+        <value>extra_method:void android.net.wifi.hotspot2.pps.HomeSp.setMatchAnyOis(long[])</value>
+        <!--
+         ! Add any new entries before this.
+         !-->
+    </entry>
+</dynamicConfig>
diff --git a/tests/signature/api-check/hidden-api-blocklist-27-api/Android.bp b/tests/signature/api-check/hidden-api-blocklist-27-api/Android.bp
index 86cb1ee..10912bb 100644
--- a/tests/signature/api-check/hidden-api-blocklist-27-api/Android.bp
+++ b/tests/signature/api-check/hidden-api-blocklist-27-api/Android.bp
@@ -28,4 +28,11 @@
         "cts",
         "general-tests",
     ],
+
+    // Ensure that the default CtsHiddenApiBlocklistApiDynamicConfig provided for
+    // this test does not collide with the copy provided for other tests.
+    per_testcase_directory: true,
+    data: [
+        ":CtsHiddenApiBlocklistApiDynamicConfig",
+    ],
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-27-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-27-api/AndroidTest.xml
index fdf5bfa..ac0a3d8 100644
--- a/tests/signature/api-check/hidden-api-blocklist-27-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-27-api/AndroidTest.xml
@@ -16,8 +16,13 @@
 <configuration description="Config for CTS Hidden API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="device" />
+        <option name="config-filename" value="CtsHiddenApiBlocklistApiDynamicConfig" />
+        <option name="version" value="1.0" />
+    </target_preparer>
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -27,10 +32,13 @@
         <option name="package" value="android.signature.cts.api.hiddenapi_blocklist_api_27" />
         <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
         <option name="class" value="android.signature.cts.api.api27.HiddenApiTest" />
+        <option name="instrumentation-arg" key="dynamic-config-name" value="CtsHiddenApiBlocklistApiDynamicConfig" />
         <option name="instrumentation-arg" key="hiddenapi-files" value="hiddenapi-flags.csv" />
         <option name="instrumentation-arg" key="hiddenapi-test-flags" value="blocked" />
         <option name="instrumentation-arg" key="hiddenapi-filter-file" value="hiddenapi-filter.csv" />
         <option name="test-api-access" value="false" />
         <option name="runtime-hint" value="30s" />
+        <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
+        <option name="isolated-storage" value="false" />
     </test>
 </configuration>
diff --git a/tests/signature/api-check/hidden-api-blocklist-27-api/src/android/signature/cts/api/api27/HiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-27-api/src/android/signature/cts/api/api27/HiddenApiTest.java
index c940aac..13ea0f1 100644
--- a/tests/signature/api-check/hidden-api-blocklist-27-api/src/android/signature/cts/api/api27/HiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-27-api/src/android/signature/cts/api/api27/HiddenApiTest.java
@@ -16,5 +16,7 @@
 
 package android.signature.cts.api.api27;
 
-public class HiddenApiTest extends android.signature.cts.api.HiddenApiTest {
+import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
+
+public class HiddenApiTest extends DynamicConfigHiddenApiTest {
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-28-api/Android.bp b/tests/signature/api-check/hidden-api-blocklist-28-api/Android.bp
index 7c9f844..d65467c 100644
--- a/tests/signature/api-check/hidden-api-blocklist-28-api/Android.bp
+++ b/tests/signature/api-check/hidden-api-blocklist-28-api/Android.bp
@@ -28,4 +28,11 @@
         "cts",
         "general-tests",
     ],
+
+    // Ensure that the default CtsHiddenApiBlocklistApiDynamicConfig provided for
+    // this test does not collide with the copy provided for other tests.
+    per_testcase_directory: true,
+    data: [
+        ":CtsHiddenApiBlocklistApiDynamicConfig",
+    ],
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-28-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-28-api/AndroidTest.xml
index 30d29f0..9a8ba04 100644
--- a/tests/signature/api-check/hidden-api-blocklist-28-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-28-api/AndroidTest.xml
@@ -16,8 +16,13 @@
 <configuration description="Config for CTS Hidden API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="device" />
+        <option name="config-filename" value="CtsHiddenApiBlocklistApiDynamicConfig" />
+        <option name="version" value="1.0" />
+    </target_preparer>
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -27,10 +32,13 @@
         <option name="package" value="android.signature.cts.api.hiddenapi_blocklist_api_28" />
         <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
         <option name="class" value="android.signature.cts.api.api28.HiddenApiTest" />
+        <option name="instrumentation-arg" key="dynamic-config-name" value="CtsHiddenApiBlocklistApiDynamicConfig" />
         <option name="instrumentation-arg" key="hiddenapi-files" value="hiddenapi-flags.csv" />
         <option name="instrumentation-arg" key="hiddenapi-test-flags" value="blocked,max-target-o" />
         <option name="instrumentation-arg" key="hiddenapi-filter-file" value="hiddenapi-filter.csv" />
         <option name="test-api-access" value="false" />
         <option name="runtime-hint" value="30s" />
+        <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
+        <option name="isolated-storage" value="false" />
     </test>
 </configuration>
diff --git a/tests/signature/api-check/hidden-api-blocklist-28-api/src/android/signature/cts/api/api28/HiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-28-api/src/android/signature/cts/api/api28/HiddenApiTest.java
index f85dda3..091a25f 100644
--- a/tests/signature/api-check/hidden-api-blocklist-28-api/src/android/signature/cts/api/api28/HiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-28-api/src/android/signature/cts/api/api28/HiddenApiTest.java
@@ -16,5 +16,7 @@
 
 package android.signature.cts.api.api28;
 
-public class HiddenApiTest extends android.signature.cts.api.HiddenApiTest {
+import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
+
+public class HiddenApiTest extends DynamicConfigHiddenApiTest {
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-current-api/Android.bp b/tests/signature/api-check/hidden-api-blocklist-current-api/Android.bp
index 67e5742..6cf1a56 100644
--- a/tests/signature/api-check/hidden-api-blocklist-current-api/Android.bp
+++ b/tests/signature/api-check/hidden-api-blocklist-current-api/Android.bp
@@ -27,4 +27,11 @@
         "cts",
         "general-tests",
     ],
+
+    // Ensure that the default CtsHiddenApiBlocklistApiDynamicConfig provided for
+    // this test does not collide with the copy provided for other tests.
+    per_testcase_directory: true,
+    data: [
+        ":CtsHiddenApiBlocklistApiDynamicConfig",
+    ],
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-current-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-current-api/AndroidTest.xml
index 7063418..e503a76 100644
--- a/tests/signature/api-check/hidden-api-blocklist-current-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-current-api/AndroidTest.xml
@@ -16,8 +16,13 @@
 <configuration description="Config for CTS Hidden API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="device" />
+        <option name="config-filename" value="CtsHiddenApiBlocklistApiDynamicConfig" />
+        <option name="version" value="1.0" />
+    </target_preparer>
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -27,10 +32,13 @@
         <option name="package" value="android.signature.cts.api.hiddenapi_blocklist_current" />
         <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
         <option name="class" value="android.signature.cts.api.current.HiddenApiTest" />
+        <option name="instrumentation-arg" key="dynamic-config-name" value="CtsHiddenApiBlocklistApiDynamicConfig" />
         <option name="instrumentation-arg" key="hiddenapi-files" value="hiddenapi-flags.csv" />
         <option name="instrumentation-arg" key="hiddenapi-test-flags" value="blocked,max-target-o,max-target-p" />
         <option name="instrumentation-arg" key="hiddenapi-filter-file" value="hiddenapi-filter.csv" />
         <option name="test-api-access" value="false" />
         <option name="runtime-hint" value="30s" />
+        <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
+        <option name="isolated-storage" value="false" />
     </test>
 </configuration>
diff --git a/tests/signature/api-check/hidden-api-blocklist-current-api/src/android/signature/cts/api/current/HiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-current-api/src/android/signature/cts/api/current/HiddenApiTest.java
index 34f33fe..7726489 100644
--- a/tests/signature/api-check/hidden-api-blocklist-current-api/src/android/signature/cts/api/current/HiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-current-api/src/android/signature/cts/api/current/HiddenApiTest.java
@@ -16,5 +16,7 @@
 
 package android.signature.cts.api.current;
 
-public class HiddenApiTest extends android.signature.cts.api.HiddenApiTest {
+import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
+
+public class HiddenApiTest extends DynamicConfigHiddenApiTest {
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-debug-class/Android.bp b/tests/signature/api-check/hidden-api-blocklist-debug-class/Android.bp
index 38e68c9..ab4040a 100644
--- a/tests/signature/api-check/hidden-api-blocklist-debug-class/Android.bp
+++ b/tests/signature/api-check/hidden-api-blocklist-debug-class/Android.bp
@@ -27,4 +27,11 @@
         "cts",
         "general-tests",
     ],
+
+    // Ensure that the default CtsHiddenApiBlocklistApiDynamicConfig provided for
+    // this test does not collide with the copy provided for other tests.
+    per_testcase_directory: true,
+    data: [
+        ":CtsHiddenApiBlocklistApiDynamicConfig",
+    ],
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-debug-class/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-debug-class/AndroidTest.xml
index 2ab58bd..c7df2ee 100644
--- a/tests/signature/api-check/hidden-api-blocklist-debug-class/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-debug-class/AndroidTest.xml
@@ -16,8 +16,13 @@
 <configuration description="Config for CTS Hidden API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="device" />
+        <option name="config-filename" value="CtsHiddenApiBlocklistApiDynamicConfig" />
+        <option name="version" value="1.0" />
+    </target_preparer>
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -26,11 +31,14 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.signature.cts.api.hiddenapi_blocklist_debug_class" />
         <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
-        <option name="class" value="android.signature.cts.api.DebugClassHiddenApiTest" />
+        <option name="class" value="android.signature.cts.api.blocklist.debug.DebugClassHiddenApiTest" />
+        <option name="instrumentation-arg" key="dynamic-config-name" value="CtsHiddenApiBlocklistApiDynamicConfig" />
         <option name="instrumentation-arg" key="hiddenapi-files" value="hiddenapi-flags.csv" />
         <option name="instrumentation-arg" key="hiddenapi-test-flags" value="blocked,max-target-o,max-target-p" />
         <option name="instrumentation-arg" key="hiddenapi-filter-file" value="hiddenapi-filter.csv" />
         <option name="test-api-access" value="false" />
         <option name="runtime-hint" value="30s" />
+        <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
+        <option name="isolated-storage" value="false" />
     </test>
 </configuration>
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/DebugClassHiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-debug-class/src/android/signature/cts/api/blocklist/debug/DebugClassHiddenApiTest.java
similarity index 83%
rename from tests/signature/api-check/src/java/android/signature/cts/api/DebugClassHiddenApiTest.java
rename to tests/signature/api-check/hidden-api-blocklist-debug-class/src/android/signature/cts/api/blocklist/debug/DebugClassHiddenApiTest.java
index 8168f08..9266976 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/DebugClassHiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-debug-class/src/android/signature/cts/api/blocklist/debug/DebugClassHiddenApiTest.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package android.signature.cts.api;
+package android.signature.cts.api.blocklist.debug;
 
 import android.signature.cts.DexMemberChecker;
+import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
 
-public class DebugClassHiddenApiTest extends HiddenApiTest {
+public class DebugClassHiddenApiTest extends DynamicConfigHiddenApiTest {
     @Override
     protected void setUp() throws Exception {
         super.setUp();
diff --git a/tests/signature/api-check/hidden-api-blocklist-test-api/Android.bp b/tests/signature/api-check/hidden-api-blocklist-test-api/Android.bp
index 4ee2db5..ade111b 100644
--- a/tests/signature/api-check/hidden-api-blocklist-test-api/Android.bp
+++ b/tests/signature/api-check/hidden-api-blocklist-test-api/Android.bp
@@ -26,4 +26,11 @@
         "cts",
         "general-tests",
     ],
+
+    // Ensure that the default CtsHiddenApiBlocklistApiDynamicConfig provided for
+    // this test does not collide with the copy provided for other tests.
+    per_testcase_directory: true,
+    data: [
+        ":CtsHiddenApiBlocklistApiDynamicConfig",
+    ],
 }
diff --git a/tests/signature/api-check/hidden-api-blocklist-test-api/AndroidTest.xml b/tests/signature/api-check/hidden-api-blocklist-test-api/AndroidTest.xml
index b08abec..1f916fe 100644
--- a/tests/signature/api-check/hidden-api-blocklist-test-api/AndroidTest.xml
+++ b/tests/signature/api-check/hidden-api-blocklist-test-api/AndroidTest.xml
@@ -17,8 +17,13 @@
 <configuration description="Config for CTS Hidden API Signature test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="systems" />
-    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="device" />
+        <option name="config-filename" value="CtsHiddenApiBlocklistApiDynamicConfig" />
+        <option name="version" value="1.0" />
+    </target_preparer>
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
@@ -28,8 +33,11 @@
         <option name="package" value="android.signature.cts.api.hiddenapi_blocklist_test" />
         <option name="runner" value="repackaged.android.test.InstrumentationTestRunner" />
         <option name="class" value="android.signature.cts.api.test.HiddenApiTest" />
+        <option name="instrumentation-arg" key="dynamic-config-name" value="CtsHiddenApiBlocklistApiDynamicConfig" />
         <option name="instrumentation-arg" key="hiddenapi-files" value="hiddenapi-flags.csv" />
         <option name="test-api-access" value="false" />
         <option name="runtime-hint" value="30s" />
+        <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
+        <option name="isolated-storage" value="false" />
     </test>
 </configuration>
diff --git a/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java
index ffe85fc..3fe708c 100644
--- a/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java
+++ b/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java
@@ -17,9 +17,10 @@
 package android.signature.cts.api.test;
 
 import android.signature.cts.DexMember;
+import android.signature.cts.api.dynamic.DynamicConfigHiddenApiTest;
 import java.util.Set;
 
-public class HiddenApiTest extends android.signature.cts.api.HiddenApiTest {
+public class HiddenApiTest extends DynamicConfigHiddenApiTest {
 
     /**
      * Override to match only those members that specify both test-api and blocked.
diff --git a/tests/signature/api-check/hidden-api-killswitch-sdklist/Android.bp b/tests/signature/api-check/hidden-api-killswitch-sdklist/Android.bp
index 5384558..333a3aa 100644
--- a/tests/signature/api-check/hidden-api-killswitch-sdklist/Android.bp
+++ b/tests/signature/api-check/hidden-api-killswitch-sdklist/Android.bp
@@ -25,4 +25,12 @@
         "cts",
         "general-tests",
     ],
+    // Ideally the following should be uncommented but unfortunately due to
+    // limitations in the build that causes build failures due to duplicate copy
+    // rules being generated. In the meantime it is necessary to build
+    // CtsHiddenApiBlacklistCurrentApiTestCases before running this test to
+    // pick up any changes to CtsHiddenApiBlocklistApiDynamicConfig.dynamic.
+    // data: [
+    //     ":CtsHiddenApiBlocklistApiDynamicConfig",
+    // ],
 }
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
index 3b6fec9..97ae404 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java
@@ -43,7 +43,7 @@
     private Set<String> hiddenapiFilterSet;
 
     @Override
-    protected void initializeFromArgs(Bundle instrumentationArgs) {
+    protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
         hiddenapiFiles = getCommaSeparatedListRequired(instrumentationArgs, "hiddenapi-files");
         hiddenapiTestFlags = getCommaSeparatedListOptional(instrumentationArgs, "hiddenapi-test-flags");
         hiddenapiFilterFile = instrumentationArgs.getString("hiddenapi-filter-file");
diff --git a/tests/signature/api-check/with-dynamic-config/src/java/android/signature/cts/api/dynamic/DynamicConfigHiddenApiTest.java b/tests/signature/api-check/with-dynamic-config/src/java/android/signature/cts/api/dynamic/DynamicConfigHiddenApiTest.java
new file mode 100644
index 0000000..99f6b7b
--- /dev/null
+++ b/tests/signature/api-check/with-dynamic-config/src/java/android/signature/cts/api/dynamic/DynamicConfigHiddenApiTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.signature.cts.api.dynamic;
+
+import android.os.Bundle;
+import android.signature.cts.api.HiddenApiTest;
+import android.signature.cts.api.SignatureTest;
+import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.DynamicConfigDeviceSide;
+import java.util.Collection;
+
+/**
+ * A hidden API test that supports the use of dynamic config.
+ */
+public class DynamicConfigHiddenApiTest extends HiddenApiTest {
+
+    /**
+     * The name of the optional instrumentation option that contains the name of the dynamic config
+     * data set that contains the expected failures.
+     */
+    private static final String DYNAMIC_CONFIG_NAME_OPTION = "dynamic-config-name";
+
+    @Override
+    protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
+        super.initializeFromArgs(instrumentationArgs);
+
+        String dynamicConfigName = instrumentationArgs.getString(DYNAMIC_CONFIG_NAME_OPTION);
+        if (dynamicConfigName != null) {
+            // Make sure that the Instrumentation provided to this test is registered so it can be
+            // retrieved by the DynamicConfigDeviceSide below.
+            InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
+
+            // Get the DynamicConfig.xml contents and extract the expected failures list.
+            DynamicConfigDeviceSide dcds = new DynamicConfigDeviceSide(dynamicConfigName);
+            Collection<String> expectedFailures = dcds.getValues("expected_failures");
+            initExpectedFailures(expectedFailures);
+        }
+    }
+}
diff --git a/tests/tests/appop/Android.bp b/tests/tests/appop/Android.bp
index 4656ec9..70b1a57 100644
--- a/tests/tests/appop/Android.bp
+++ b/tests/tests/appop/Android.bp
@@ -94,8 +94,8 @@
         "liblog",
         "liblzma",
         "libnativehelper",
-        "libnetdbpf",
         "libnetdutils",
+        "libnetworkstats",
         "libnetworkstatsfactorytestjni",
         "libpackagelistparser",
         "libpermission",
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/AdvertisingSetParametersTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/AdvertisingSetParametersTest.java
new file mode 100644
index 0000000..a6ec271
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/AdvertisingSetParametersTest.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.cts;
+
+import static android.bluetooth.BluetoothDevice.PHY_LE_1M;
+import static android.bluetooth.BluetoothDevice.PHY_LE_2M;
+import static android.bluetooth.BluetoothDevice.PHY_LE_CODED;
+import static android.bluetooth.le.AdvertisingSetParameters.INTERVAL_LOW;
+import static android.bluetooth.le.AdvertisingSetParameters.INTERVAL_MAX;
+import static android.bluetooth.le.AdvertisingSetParameters.INTERVAL_MEDIUM;
+import static android.bluetooth.le.AdvertisingSetParameters.INTERVAL_MIN;
+import static android.bluetooth.le.AdvertisingSetParameters.TX_POWER_MAX;
+import static android.bluetooth.le.AdvertisingSetParameters.TX_POWER_MEDIUM;
+import static android.bluetooth.le.AdvertisingSetParameters.TX_POWER_MIN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class AdvertisingSetParametersTest {
+
+    @Test
+    public void testCreateFromParcel() {
+        final Parcel parcel = Parcel.obtain();
+        try {
+            AdvertisingSetParameters params = new AdvertisingSetParameters.Builder().build();
+            params.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            AdvertisingSetParameters paramsFromParcel =
+                    AdvertisingSetParameters.CREATOR.createFromParcel(parcel);
+            assertParamsEquals(params, paramsFromParcel);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Test
+    public void testDefaultParameters() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder().build();
+
+        assertFalse(params.isConnectable());
+        assertFalse(params.isScannable());
+        assertFalse(params.isLegacy());
+        assertFalse(params.isAnonymous());
+        assertFalse(params.includeTxPower());
+        assertEquals(PHY_LE_1M, params.getPrimaryPhy());
+        assertEquals(PHY_LE_1M, params.getSecondaryPhy());
+        assertEquals(INTERVAL_LOW, params.getInterval());
+        assertEquals(TX_POWER_MEDIUM, params.getTxPowerLevel());
+    }
+
+    @Test
+    public void testIsConnectable() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setConnectable(true)
+                .build();
+        assertTrue(params.isConnectable());
+    }
+
+    @Test
+    public void testIsScannable() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setScannable(true)
+                .build();
+        assertTrue(params.isScannable());
+    }
+
+    @Test
+    public void testIsLegacyMode() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setLegacyMode(true)
+                .build();
+        assertTrue(params.isLegacy());
+    }
+
+    @Test
+    public void testIncludeTxPower() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setIncludeTxPower(true)
+                .build();
+        assertTrue(params.includeTxPower());
+    }
+
+    @Test
+    public void testSetPrimaryPhyWithInvalidValue() {
+        try {
+            // Set invalid value
+            new AdvertisingSetParameters.Builder().setPrimaryPhy(PHY_LE_2M);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testSetPrimaryPhyWithLE1M() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setPrimaryPhy(PHY_LE_1M)
+                .build();
+        assertEquals(PHY_LE_1M, params.getPrimaryPhy());
+    }
+
+    @Test
+    public void testSetPrimaryPhyWithLECoded() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setPrimaryPhy(PHY_LE_CODED)
+                .build();
+        assertEquals(PHY_LE_CODED, params.getPrimaryPhy());
+    }
+
+    @Test
+    public void testSetSecondaryPhyWithInvalidValue() {
+        int INVALID_SECONDARY_PHY = -1;
+        try {
+            // Set invalid value
+            new AdvertisingSetParameters.Builder().setSecondaryPhy(INVALID_SECONDARY_PHY);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testSetSecondaryPhyWithLE1M() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setSecondaryPhy(PHY_LE_1M)
+                .build();
+        assertEquals(PHY_LE_1M, params.getSecondaryPhy());
+    }
+
+    @Test
+    public void testSetSecondaryPhyWithLE2M() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setSecondaryPhy(PHY_LE_2M)
+                .build();
+        assertEquals(PHY_LE_2M, params.getSecondaryPhy());
+    }
+
+    @Test
+    public void testSetSecondaryPhyWithLECoded() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setSecondaryPhy(PHY_LE_CODED)
+                .build();
+        assertEquals(PHY_LE_CODED, params.getSecondaryPhy());
+    }
+
+    @Test
+    public void testIntervalWithInvalidValues() {
+        int[] invalidValues = {INTERVAL_MIN - 1, INTERVAL_MAX + 1};
+        for (int i = 0; i < invalidValues.length; i++) {
+            try {
+                // Set invalid value
+                new AdvertisingSetParameters.Builder().setInterval(invalidValues[i]);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+        }
+    }
+
+    @Test
+    public void testInterval() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setInterval(INTERVAL_MEDIUM)
+                .build();
+        assertEquals(INTERVAL_MEDIUM, params.getInterval());
+    }
+
+    @Test
+    public void testTxPowerLevelWithInvalidValues() {
+        int[] invalidValues = { TX_POWER_MIN - 1, TX_POWER_MAX + 1 };
+        for (int i = 0; i < invalidValues.length; i++) {
+            try {
+                // Set invalid value
+                new AdvertisingSetParameters.Builder().setTxPowerLevel(TX_POWER_MIN - 1);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+        }
+    }
+
+    @Test
+    public void testTxPowerLevel() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder()
+                .setTxPowerLevel(TX_POWER_MEDIUM)
+                .build();
+        assertEquals(TX_POWER_MEDIUM, params.getTxPowerLevel());
+    }
+
+    @Test
+    public void testIsAnonymous() {
+        AdvertisingSetParameters params =
+                new AdvertisingSetParameters.Builder().setAnonymous(true).build();
+        assertTrue(params.isAnonymous());
+    }
+
+    @Test
+    public void testDescribeContents() {
+        AdvertisingSetParameters params = new AdvertisingSetParameters.Builder().build();
+        assertEquals(0, params.describeContents());
+    }
+
+    private void assertParamsEquals(AdvertisingSetParameters p, AdvertisingSetParameters other) {
+        if (p == null && other == null) {
+            return;
+        }
+
+        if (p == null || other == null) {
+            fail("Cannot compare null with non-null value: p=" + p + ", other=" + other);
+        }
+
+        assertEquals(p.isConnectable(), other.isConnectable());
+        assertEquals(p.isScannable(), other.isScannable());
+        assertEquals(p.isLegacy(), other.isLegacy());
+        assertEquals(p.isAnonymous(), other.isAnonymous());
+        assertEquals(p.includeTxPower(), other.includeTxPower());
+        assertEquals(p.getPrimaryPhy(), other.getPrimaryPhy());
+        assertEquals(p.getSecondaryPhy(), other.getSecondaryPhy());
+        assertEquals(p.getInterval(), other.getInterval());
+        assertEquals(p.getTxPowerLevel(), other.getTxPowerLevel());
+    }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
index 43183fe..adfe356 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
@@ -25,6 +25,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.SystemProperties;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -245,6 +247,49 @@
         assertNotSame(BluetoothStatusCodes.ERROR_UNKNOWN, adapter.isLeAudioSupported());
     }
 
+    public void test_isLeAudioBroadcastSourceSupported() throws IOException {
+        if (!mHasBluetooth) {
+            // Skip the test if bluetooth is not present.
+            return;
+        }
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        assertNotSame(BluetoothStatusCodes.ERROR_UNKNOWN, adapter.isLeAudioBroadcastSourceSupported());
+    }
+
+    public void test_isLeAudioBroadcastAssistantSupported() throws IOException {
+        if (!mHasBluetooth) {
+            // Skip the test if bluetooth is not present.
+            return;
+        }
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        assertNotSame(BluetoothStatusCodes.ERROR_UNKNOWN, adapter.isLeAudioBroadcastAssistantSupported());
+    }
+
+    public void test_getMaxConnectedAudioDevices() {
+        if (!mHasBluetooth) {
+            // Skip the test if bluetooth is not present.
+            return;
+        }
+
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
+        int maxConnectedAudioDevicesConfig = 0;
+        try {
+            Resources bluetoothRes = mContext.getPackageManager()
+                    .getResourcesForApplication("com.android.bluetooth");
+            maxConnectedAudioDevicesConfig = bluetoothRes.getInteger(
+                    bluetoothRes.getIdentifier("config_bluetooth_max_connected_audio_devices",
+                    "integer", "com.android.bluetooth"));
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+
+        maxConnectedAudioDevicesConfig =
+                SystemProperties.getInt("persist.bluetooth.maxconnectedaudiodevices",
+                        maxConnectedAudioDevicesConfig);
+        assertEquals(maxConnectedAudioDevicesConfig, adapter.getMaxConnectedAudioDevices());
+    }
+
     public void test_listenUsingRfcommWithServiceRecord() throws IOException {
         if (!mHasBluetooth) {
             // Skip the test if bluetooth is not present.
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicBluetoothGattTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicBluetoothGattTest.java
new file mode 100644
index 0000000..1e67d2a
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicBluetoothGattTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.cts;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.test.AndroidTestCase;
+
+import java.util.List;
+
+/**
+ * Tests a small part of the {@link BluetoothGatt} methods without a real Bluetooth device.
+ * Other tests that run with real bluetooth connections are located in CtsVerifier.
+ */
+public class BasicBluetoothGattTest extends AndroidTestCase {
+
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothDevice mBluetoothDevice;
+    private BluetoothGatt mBluetoothGatt;
+
+    @Override
+    public void setUp() {
+        if (!TestUtils.isBleSupported(getContext())) {
+            return;
+        }
+
+        mBluetoothAdapter = mContext.getSystemService(BluetoothManager.class).getAdapter();
+        if (!mBluetoothAdapter.isEnabled()) {
+            assertTrue(BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext));
+        }
+        mBluetoothDevice = mBluetoothAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+        mBluetoothGatt = mBluetoothDevice.connectGatt(
+                mContext, /*autoConnect=*/ true, new BluetoothGattCallback() {});
+    }
+
+    @Override
+    public void tearDown() {
+        if (!TestUtils.isBleSupported(getContext())) {
+            // mBluetoothAdapter == null.
+            return;
+        }
+        mBluetoothGatt.disconnect();
+        assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
+    }
+
+    public void testGetServices() throws Exception {
+        if (!TestUtils.isBleSupported(getContext())) {
+            return;
+        }
+
+        // getServices() returns an empty list if service discovery has not yet been performed.
+        List<BluetoothGattService> services = mBluetoothGatt.getServices();
+        assertNotNull(services);
+        assertTrue(services.isEmpty());
+    }
+
+    public void testConnect() throws Exception {
+        if (!TestUtils.isBleSupported(getContext())) {
+            return;
+        }
+
+        try {
+            mBluetoothGatt.connect();
+        } catch (Exception e) {
+            fail("Exception caught from connect(): " + e.toString());
+        }
+    }
+
+    public void testSetPreferredPhy() throws Exception {
+        if (!TestUtils.isBleSupported(getContext())) {
+            return;
+        }
+
+        try {
+            mBluetoothGatt.setPreferredPhy(BluetoothDevice.PHY_LE_1M, BluetoothDevice.PHY_LE_1M,
+                    BluetoothDevice.PHY_OPTION_NO_PREFERRED);
+        } catch (Exception e) {
+            fail("Exception caught from setPreferredPhy(): " + e.toString());
+        }
+    }
+
+    public void testGetConnectedDevices() {
+        if (!TestUtils.isBleSupported(getContext())) {
+            return;
+        }
+        try {
+            mBluetoothGatt.getConnectedDevices();
+            fail("Should throw UnsupportedOperationException!");
+        } catch (UnsupportedOperationException ex) {
+            // Expected
+        }
+    }
+
+    public void testGetConnectionState() {
+        if (!TestUtils.isBleSupported(getContext())) {
+            return;
+        }
+        try {
+            mBluetoothGatt.getConnectionState(null);
+            fail("Should throw UnsupportedOperationException!");
+        } catch (UnsupportedOperationException ex) {
+            // Expected
+        }
+    }
+
+    public void testGetDevicesMatchingConnectionStates() {
+        if (!TestUtils.isBleSupported(getContext())) {
+            return;
+        }
+        try {
+            mBluetoothGatt.getDevicesMatchingConnectionStates(null);
+            fail("Should throw UnsupportedOperationException!");
+        } catch (UnsupportedOperationException ex) {
+            // Expected
+        }
+    }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceAppSdpSettingsTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceAppSdpSettingsTest.java
new file mode 100644
index 0000000..97599e0
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceAppSdpSettingsTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.cts;
+
+import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Unit test cases for {@link BluetoothHidDeviceAppSdpSettings}.
+ */
+public class BluetoothHidDeviceAppSdpSettingsTest extends AndroidTestCase {
+    @SmallTest
+    public void testGetters() {
+        String name = "test-name";
+        String description = "test-description";
+        String provider = "test-provider";
+        byte subclass = 1;
+        byte[] descriptors = new byte[] {10};
+        BluetoothHidDeviceAppSdpSettings settings = new BluetoothHidDeviceAppSdpSettings(
+                name, description, provider, subclass, descriptors);
+        assertEquals(name, settings.getName());
+        assertEquals(description, settings.getDescription());
+        assertEquals(provider, settings.getProvider());
+        assertEquals(subclass, settings.getSubclass());
+        assertEquals(descriptors.length, settings.getDescriptors().length);
+        assertEquals(descriptors[0], settings.getDescriptors()[0]);
+    }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceCallbackTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceCallbackTest.java
new file mode 100644
index 0000000..bcbe8fb
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothHidDeviceCallbackTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.cts;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDevice;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BluetoothHidDeviceCallbackTest {
+    private BluetoothHidDevice.Callback mHidDeviceCallback = new BluetoothHidDevice.Callback() {
+        @Override
+        public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+            super.onGetReport(device, type, id, bufferSize);
+        }
+
+        @Override
+        public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+            super.onSetReport(device, type, id, data);
+        }
+
+        @Override
+        public void onSetProtocol(BluetoothDevice device, byte protocol) {
+            super.onSetProtocol(device, protocol);
+        }
+
+        @Override
+        public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
+            super.onInterruptData(device, reportId, data);
+        }
+
+        @Override
+        public void onVirtualCableUnplug(BluetoothDevice device) {
+            super.onVirtualCableUnplug(device);
+        }
+    };
+
+    @Test
+    public void testHidDeviceCallback() {
+        // TODO: Provide a way to simulate BluetoothHidHost for better testing.
+        // We may need to have a new BluetoothAdapter.getProfileProxy method with a new test profile
+        // like HID_DEVICE_TEST which also takes a mock hid host instance.
+        mHidDeviceCallback.onGetReport(null, (byte) 0, (byte) 0, 0);
+        mHidDeviceCallback.onSetReport(null, (byte) 0, (byte) 0, null);
+        mHidDeviceCallback.onSetProtocol(null, (byte) 0);
+        mHidDeviceCallback.onInterruptData(null, (byte) 0, null);
+        mHidDeviceCallback.onVirtualCableUnplug(null);
+        // FYI, onAppStatusChanged and onConnectionStateChanged are covered by CtsVerifier.
+    }
+}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecTest.java
index 3cf7e26..38599f5 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecTest.java
@@ -17,6 +17,7 @@
 package android.bluetooth.cts;
 
 import android.bluetooth.BluetoothLeAudioCodecConfig;
+import android.os.Parcel;
 import android.test.AndroidTestCase;
 
 public class BluetoothLeAudioCodecTest extends AndroidTestCase {
@@ -25,6 +26,41 @@
         BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID,
     };
 
+    private int[] mCodecPriorityArray = new int[] {
+        BluetoothLeAudioCodecConfig.CODEC_PRIORITY_DISABLED,
+        BluetoothLeAudioCodecConfig.CODEC_PRIORITY_DEFAULT,
+        BluetoothLeAudioCodecConfig.CODEC_PRIORITY_HIGHEST
+    };
+
+    private int[] mSampleRateArray = new int[] {
+        BluetoothLeAudioCodecConfig.SAMPLE_RATE_NONE,
+        BluetoothLeAudioCodecConfig.SAMPLE_RATE_8000,
+        BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000,
+        BluetoothLeAudioCodecConfig.SAMPLE_RATE_24000,
+        BluetoothLeAudioCodecConfig.SAMPLE_RATE_32000,
+        BluetoothLeAudioCodecConfig.SAMPLE_RATE_44100,
+        BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000
+    };
+
+    private int[] mBitsPerSampleArray = new int[] {
+        BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_NONE,
+        BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_16,
+        BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_24,
+        BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_32
+    };
+
+    private int[] mChannelModeArray = new int[] {
+        BluetoothLeAudioCodecConfig.CHANNEL_MODE_NONE,
+        BluetoothLeAudioCodecConfig.CHANNEL_MODE_MONO,
+        BluetoothLeAudioCodecConfig.CHANNEL_MODE_STEREO
+    };
+
+    private int[] mFrameDurationArray = new int[] {
+        BluetoothLeAudioCodecConfig.FRAME_DURATION_NONE,
+        BluetoothLeAudioCodecConfig.FRAME_DURATION_7500,
+        BluetoothLeAudioCodecConfig.FRAME_DURATION_10000
+    };
+
     public void testGetCodecNameAndType() {
         try {
             for (int codecIdx = 0; codecIdx < mCodecTypeArray.length; codecIdx++) {
@@ -58,4 +94,147 @@
             fail(e.getMessage());
         }
     }
+
+    public void testGetCodecPriority() {
+        for (int priorityIdx = 0; priorityIdx < mCodecPriorityArray.length; priorityIdx++) {
+            int codecPriority = mCodecPriorityArray[priorityIdx];
+
+            BluetoothLeAudioCodecConfig leAudioCodecConfig =
+                    new BluetoothLeAudioCodecConfig.Builder()
+                        .setCodecPriority(codecPriority)
+                        .build();
+
+            assertEquals(codecPriority, leAudioCodecConfig.getCodecPriority());
+        }
+    }
+
+    public void testGetSampleRate() {
+        for (int sampleRateIdx = 0; sampleRateIdx < mSampleRateArray.length; sampleRateIdx++) {
+            int sampleRate = mSampleRateArray[sampleRateIdx];
+
+            BluetoothLeAudioCodecConfig leAudioCodecConfig =
+                    new BluetoothLeAudioCodecConfig.Builder()
+                        .setSampleRate(sampleRate)
+                        .build();
+
+            assertEquals(sampleRate, leAudioCodecConfig.getSampleRate());
+        }
+    }
+
+    public void testGetBitsPerSample() {
+        for (int bitsPerSampleIdx = 0; bitsPerSampleIdx < mBitsPerSampleArray.length;
+                bitsPerSampleIdx++) {
+            int bitsPerSample = mBitsPerSampleArray[bitsPerSampleIdx];
+
+            BluetoothLeAudioCodecConfig leAudioCodecConfig =
+                    new BluetoothLeAudioCodecConfig.Builder()
+                        .setBitsPerSample(bitsPerSampleIdx)
+                        .build();
+
+            assertEquals(bitsPerSampleIdx, leAudioCodecConfig.getBitsPerSample());
+        }
+    }
+
+    public void testGetChannelMode() {
+        for (int channelModeIdx = 0; channelModeIdx < mChannelModeArray.length; channelModeIdx++) {
+            int channelMode = mChannelModeArray[channelModeIdx];
+
+            BluetoothLeAudioCodecConfig leAudioCodecConfig =
+                    new BluetoothLeAudioCodecConfig.Builder()
+                        .setChannelMode(channelMode)
+                        .build();
+
+            assertEquals(channelMode, leAudioCodecConfig.getChannelMode());
+        }
+    }
+
+    public void testGetFrameDuration() {
+        for (int frameDurationIdx = 0; frameDurationIdx < mFrameDurationArray.length;
+                frameDurationIdx++) {
+            int frameDuration = mFrameDurationArray[frameDurationIdx];
+
+            BluetoothLeAudioCodecConfig leAudioCodecConfig =
+                    new BluetoothLeAudioCodecConfig.Builder()
+                        .setFrameDuration(frameDurationIdx)
+                        .build();
+
+            assertEquals(frameDuration, leAudioCodecConfig.getFrameDuration());
+        }
+    }
+
+    public void testGetOctetsPerFrame() {
+        final int octetsPerFrame = 100;
+        BluetoothLeAudioCodecConfig leAudioCodecConfig =
+                new BluetoothLeAudioCodecConfig.Builder()
+                    .setOctetsPerFrame(octetsPerFrame)
+                    .build();
+
+        assertEquals(octetsPerFrame, leAudioCodecConfig.getOctetsPerFrame());
+    }
+
+    public void testDescribeContents() {
+        BluetoothLeAudioCodecConfig leAudioCodecConfig =
+            new BluetoothLeAudioCodecConfig.Builder().build();
+        assertEquals(0, leAudioCodecConfig.describeContents());
+    }
+
+    public void testReadWriteParcel() {
+        final int octetsPerFrame = 100;
+        Parcel parcel = Parcel.obtain();
+        BluetoothLeAudioCodecConfig leAudioCodecConfig = new BluetoothLeAudioCodecConfig.Builder()
+                .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
+                .setCodecPriority(BluetoothLeAudioCodecConfig.CODEC_PRIORITY_HIGHEST)
+                .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_24000)
+                .setBitsPerSample(BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_24)
+                .setChannelMode(BluetoothLeAudioCodecConfig.CHANNEL_MODE_STEREO)
+                .setFrameDuration(BluetoothLeAudioCodecConfig.FRAME_DURATION_7500)
+                .setOctetsPerFrame(octetsPerFrame)
+                .build();
+        leAudioCodecConfig.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        BluetoothLeAudioCodecConfig leAudioCodecConfigFromParcel =
+                BluetoothLeAudioCodecConfig.CREATOR.createFromParcel(parcel);
+        assertEquals(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3,
+                leAudioCodecConfigFromParcel.getCodecType());
+        assertEquals(BluetoothLeAudioCodecConfig.CODEC_PRIORITY_HIGHEST,
+                leAudioCodecConfigFromParcel.getCodecPriority());
+        assertEquals(BluetoothLeAudioCodecConfig.SAMPLE_RATE_24000,
+                leAudioCodecConfigFromParcel.getSampleRate());
+        assertEquals(BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_24,
+                leAudioCodecConfigFromParcel.getBitsPerSample());
+        assertEquals(BluetoothLeAudioCodecConfig.CHANNEL_MODE_STEREO,
+                leAudioCodecConfigFromParcel.getChannelMode());
+        assertEquals(BluetoothLeAudioCodecConfig.FRAME_DURATION_7500,
+                leAudioCodecConfigFromParcel.getFrameDuration());
+        assertEquals(octetsPerFrame, leAudioCodecConfigFromParcel.getOctetsPerFrame());
+    }
+
+    public void testBuilderWithExistingObject() {
+        final int octetsPerFrame = 100;
+        BluetoothLeAudioCodecConfig oriLeAudioCodecConfig =
+            new BluetoothLeAudioCodecConfig.Builder()
+                .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
+                .setCodecPriority(BluetoothLeAudioCodecConfig.CODEC_PRIORITY_HIGHEST)
+                .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_24000)
+                .setBitsPerSample(BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_24)
+                .setChannelMode(BluetoothLeAudioCodecConfig.CHANNEL_MODE_STEREO)
+                .setFrameDuration(BluetoothLeAudioCodecConfig.FRAME_DURATION_7500)
+                .setOctetsPerFrame(octetsPerFrame)
+                .build();
+        BluetoothLeAudioCodecConfig toBuilderCodecConfig =
+                new BluetoothLeAudioCodecConfig.Builder(oriLeAudioCodecConfig).build();
+        assertEquals(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3,
+                toBuilderCodecConfig.getCodecType());
+        assertEquals(BluetoothLeAudioCodecConfig.CODEC_PRIORITY_HIGHEST,
+                toBuilderCodecConfig.getCodecPriority());
+        assertEquals(BluetoothLeAudioCodecConfig.SAMPLE_RATE_24000,
+                toBuilderCodecConfig.getSampleRate());
+        assertEquals(BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_24,
+                toBuilderCodecConfig.getBitsPerSample());
+        assertEquals(BluetoothLeAudioCodecConfig.CHANNEL_MODE_STEREO,
+                toBuilderCodecConfig.getChannelMode());
+        assertEquals(BluetoothLeAudioCodecConfig.FRAME_DURATION_7500,
+                toBuilderCodecConfig.getFrameDuration());
+        assertEquals(octetsPerFrame, toBuilderCodecConfig.getOctetsPerFrame());
+    }
 }
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
index 24bcafb..214645f 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioTest.java
@@ -140,6 +140,21 @@
                 mBluetoothLeAudio.getConnectionState(testDevice));
     }
 
+    public void testGetAudioLocation() {
+        if (!(mHasBluetooth && mIsLeAudioSupported)) return;
+
+        assertTrue(waitForProfileConnect());
+        assertNotNull(mBluetoothLeAudio);
+
+        BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+
+        assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
+
+        // Verify returns false if bluetooth is not enabled
+        assertEquals(BluetoothLeAudio.AUDIO_LOCATION_INVALID,
+                mBluetoothLeAudio.getAudioLocation(testDevice));
+    }
+
     private boolean waitForProfileConnect() {
         mProfileConnectedlock.lock();
         try {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/PeriodicAdvertisingParametersTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/PeriodicAdvertisingParametersTest.java
new file mode 100644
index 0000000..1a8c841
--- /dev/null
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/PeriodicAdvertisingParametersTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.bluetooth.le.PeriodicAdvertisingParameters;
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PeriodicAdvertisingParametersTest {
+
+    // Values copied over from PeriodicAdvertisingParameters class.
+    private static final int INTERVAL_MIN = 80;
+    private static final int INTERVAL_MAX = 65519;
+
+    @Test
+    public void testCreateFromParcel() {
+        final Parcel parcel = Parcel.obtain();
+        try {
+            PeriodicAdvertisingParameters params =
+                    new PeriodicAdvertisingParameters.Builder().build();
+            params.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            PeriodicAdvertisingParameters paramsFromParcel =
+                    PeriodicAdvertisingParameters.CREATOR.createFromParcel(parcel);
+            assertParamsEquals(params, paramsFromParcel);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Test
+    public void testDefaultParameters() {
+        PeriodicAdvertisingParameters params = new PeriodicAdvertisingParameters.Builder().build();
+        assertFalse(params.getIncludeTxPower());
+        assertEquals(INTERVAL_MAX, params.getInterval());
+    }
+
+    @Test
+    public void testIncludeTxPower() {
+        PeriodicAdvertisingParameters params =
+                new PeriodicAdvertisingParameters.Builder().setIncludeTxPower(true).build();
+        assertTrue(params.getIncludeTxPower());
+    }
+
+    @Test
+    public void testIntervalWithInvalidValues() {
+        int[] invalidValues = { INTERVAL_MIN - 1, INTERVAL_MAX + 1 };
+        for (int i = 0; i < invalidValues.length; i++) {
+            try {
+                new PeriodicAdvertisingParameters.Builder().setInterval(invalidValues[i]).build();
+                fail();
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+        }
+    }
+
+    @Test
+    public void testInterval() {
+        PeriodicAdvertisingParameters params =
+                new PeriodicAdvertisingParameters.Builder().setInterval(INTERVAL_MIN).build();
+        assertEquals(INTERVAL_MIN, params.getInterval());
+    }
+
+    @Test
+    public void testDescribeContents() {
+        PeriodicAdvertisingParameters params = new PeriodicAdvertisingParameters.Builder().build();
+        assertEquals(0, params.describeContents());
+    }
+
+    private void assertParamsEquals(PeriodicAdvertisingParameters p,
+            PeriodicAdvertisingParameters other) {
+        if (p == null && other == null) {
+            return;
+        }
+
+        if (p == null || other == null) {
+            fail("Cannot compare null with non-null value: p=" + p + ", other=" + other);
+        }
+
+        assertEquals(p.getIncludeTxPower(), other.getIncludeTxPower());
+        assertEquals(p.getInterval(), other.getInterval());
+    }
+}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index dba96476..c2ea73b 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -21,6 +21,7 @@
 import static android.carrierapi.cts.IccUtils.hexStringToBytes;
 import static android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL;
 import static android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
+import static android.telephony.SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER;
 import static android.telephony.TelephonyManager.DATA_ENABLED_REASON_THERMAL;
 import static android.telephony.TelephonyManager.DATA_ENABLED_REASON_USER;
 
@@ -990,10 +991,12 @@
 
     /**
      * This test verifies that {@link TelephonyManager#setLine1NumberForDisplay(String, String)}
-     * correctly sets the Line 1 alpha tag and number when called.
+     * correctly sets the Line 1 alpha tag and number when called, and the phone number
+     * of {@link SubscriptionManager#PHONE_NUMBER_SOURCE_CARRIER}.
      */
     @Test
     public void testLine1NumberForDisplay() {
+        int subId = SubscriptionManager.getDefaultSubscriptionId();
         // Cache original alpha tag and number values.
         String originalAlphaTag = mTelephonyManager.getLine1AlphaTag();
         String originalNumber = mTelephonyManager.getLine1Number();
@@ -1007,15 +1010,21 @@
             assertThat(mTelephonyManager.setLine1NumberForDisplay(ALPHA_TAG_A, NUMBER_A)).isTrue();
             assertThat(mTelephonyManager.getLine1AlphaTag()).isEqualTo(ALPHA_TAG_A);
             assertThat(mTelephonyManager.getLine1Number()).isEqualTo(NUMBER_A);
+            assertThat(mSubscriptionManager.getPhoneNumber(subId, PHONE_NUMBER_SOURCE_CARRIER))
+                    .isEqualTo(NUMBER_A);
 
             assertThat(mTelephonyManager.setLine1NumberForDisplay(ALPHA_TAG_B, NUMBER_B)).isTrue();
             assertThat(mTelephonyManager.getLine1AlphaTag()).isEqualTo(ALPHA_TAG_B);
             assertThat(mTelephonyManager.getLine1Number()).isEqualTo(NUMBER_B);
+            assertThat(mSubscriptionManager.getPhoneNumber(subId, PHONE_NUMBER_SOURCE_CARRIER))
+                    .isEqualTo(NUMBER_B);
 
             // null is used to clear the Line 1 alpha tag and number values.
             assertThat(mTelephonyManager.setLine1NumberForDisplay(null, null)).isTrue();
             assertThat(mTelephonyManager.getLine1AlphaTag()).isEqualTo(defaultAlphaTag);
             assertThat(mTelephonyManager.getLine1Number()).isEqualTo(defaultNumber);
+            assertThat(mSubscriptionManager.getPhoneNumber(subId, PHONE_NUMBER_SOURCE_CARRIER))
+                    .isEqualTo("");
         } finally {
             // Reset original alpha tag and number values.
             mTelephonyManager.setLine1NumberForDisplay(originalAlphaTag, originalNumber);
diff --git a/tests/tests/content/Android.bp b/tests/tests/content/Android.bp
index 9ada51d..b78ccbe 100644
--- a/tests/tests/content/Android.bp
+++ b/tests/tests/content/Android.bp
@@ -107,7 +107,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-documentsui",
     ],
     min_sdk_version: "29",
 }
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
index ce9746b..209b912 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
@@ -82,6 +82,7 @@
     @Before
     public void setUp() throws Exception {
         mActivityRule.getActivity().clearUnhandleKeyCode();
+        mActivityRule.getActivity().setInputCallback(mInputListener);
         mDecorView = mActivityRule.getActivity().getWindow().getDecorView();
         mParser = new InputJsonParser(mInstrumentation.getTargetContext());
         mVid = mParser.readVendorId(mRegisterResourceId);
@@ -228,7 +229,6 @@
     }
 
     protected void verifyEvents(List<InputEvent> events) {
-        mActivityRule.getActivity().setInputCallback(mInputListener);
         // Make sure we received the expected input events
         if (events.size() == 0) {
             // If no event is expected we need to wait for event until timeout and fail on
diff --git a/tests/tests/icu/Android.bp b/tests/tests/icu/Android.bp
index c5a5141..26c195c 100644
--- a/tests/tests/icu/Android.bp
+++ b/tests/tests/icu/Android.bp
@@ -30,7 +30,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
     ],
     platform_apis: true,
 }
diff --git a/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java b/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java
index 4d99052..ec3504b 100644
--- a/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/DynamicAuthTest.java
@@ -287,7 +287,6 @@
         assertArrayEquals(new byte[]{42, 43, 44}, rd.getStaticAuthenticationData());
         assertArrayEquals(new int[]{1, 0, 0, 0, 0}, credential.getAuthenticationDataUsageCount());
 
-
         // Now do this one more time.... this time key4 should have been used. Check this by
         // inspecting use-counts and the static authentication data.
         credential = store.getCredentialByName(credentialName,
@@ -592,6 +591,166 @@
         store.deleteCredentialByName(credentialName);
     }
 
+    @Test
+    public void dynamicAuthMultipleGetEntries() throws Exception {
+        assumeTrue("IC HAL is not implemented", TestUtil.isHalImplemented());
+
+        Context appContext = InstrumentationRegistry.getTargetContext();
+        IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+
+        String credentialName = "test";
+
+        store.deleteCredentialByName(credentialName);
+        Collection<X509Certificate> certChain = ProvisioningTest.createCredential(store,
+                credentialName);
+
+        IdentityCredential credential = store.getCredentialByName(credentialName,
+                IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+        assertNotNull(credential);
+        assertArrayEquals(new int[0], credential.getAuthenticationDataUsageCount());
+
+        credential.setAvailableAuthenticationKeys(5, 3);
+        assertArrayEquals(new int[]{0, 0, 0, 0, 0},
+                          credential.getAuthenticationDataUsageCount());
+
+        Collection<X509Certificate> certs = credential.getAuthKeysNeedingCertification();
+        assertEquals(5, certs.size());
+
+        // Certify authKeys 0 and 1
+        //
+        X509Certificate key0Cert = new ArrayList<X509Certificate>(certs).get(0);
+        credential.storeStaticAuthenticationData(key0Cert, new byte[]{42, 43, 44});
+        X509Certificate key1Cert = new ArrayList<X509Certificate>(certs).get(1);
+        credential.storeStaticAuthenticationData(key1Cert, new byte[]{43, 44, 45});
+
+        // Get ready to present...
+        //
+        Map<String, Collection<String>> entriesToRequest = new LinkedHashMap<>();
+        entriesToRequest.put("org.iso.18013-5.2019", Arrays.asList("First name", "Last name"));
+        KeyPair ephemeralKeyPair = credential.createEphemeralKeyPair();
+        KeyPair readerEphemeralKeyPair = Util.createEphemeralKeyPair();
+        byte[] sessionTranscript = Util.buildSessionTranscript(ephemeralKeyPair);
+        credential.setReaderEphemeralPublicKey(readerEphemeralKeyPair.getPublic());
+
+        // First getEntries() call should pick key0
+        //
+        ResultData rd = credential.getEntries(
+                Util.createItemsRequest(entriesToRequest, null),
+                entriesToRequest,
+                sessionTranscript,
+                null);
+        assertArrayEquals(new int[]{1, 0, 0, 0, 0}, credential.getAuthenticationDataUsageCount());
+        assertArrayEquals(new byte[]{42, 43, 44}, rd.getStaticAuthenticationData());
+
+        // Doing another getEntries() call on the same object should use the same key. Check this
+        // by inspecting use-counts and the static authentication data.
+        //
+        rd = credential.getEntries(
+            Util.createItemsRequest(entriesToRequest, null),
+            entriesToRequest,
+            sessionTranscript,
+            null);
+        assertArrayEquals(new int[]{1, 0, 0, 0, 0}, credential.getAuthenticationDataUsageCount());
+        assertArrayEquals(new byte[]{42, 43, 44}, rd.getStaticAuthenticationData());
+    }
+
+    @Test
+    public void dynamicAuthNoUsageCountIncrement() throws Exception {
+        assumeTrue("IC HAL is not implemented", TestUtil.isHalImplemented());
+
+        Context appContext = InstrumentationRegistry.getTargetContext();
+        IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+        assumeTrue(
+            "IdentityCredential.setIncrementKeyUsageCount(boolean) not supported",
+            TestUtil.getFeatureVersion() >= 202201);
+
+        String credentialName = "test";
+
+        store.deleteCredentialByName(credentialName);
+        Collection<X509Certificate> certChain = ProvisioningTest.createCredential(store,
+                                                                                  credentialName);
+
+        IdentityCredential credential = store.getCredentialByName(
+            credentialName,
+            IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+        assertNotNull(credential);
+        assertArrayEquals(new int[0], credential.getAuthenticationDataUsageCount());
+
+        credential.setAvailableAuthenticationKeys(5, 3);
+        assertArrayEquals(new int[]{0, 0, 0, 0, 0},
+                          credential.getAuthenticationDataUsageCount());
+
+        Collection<X509Certificate> certs = credential.getAuthKeysNeedingCertification();
+        assertEquals(5, certs.size());
+
+        // Certify authKeys 0 and 1
+        //
+        X509Certificate key0Cert = new ArrayList<X509Certificate>(certs).get(0);
+        credential.storeStaticAuthenticationData(key0Cert, new byte[]{42, 43, 44});
+        X509Certificate key1Cert = new ArrayList<X509Certificate>(certs).get(1);
+        credential.storeStaticAuthenticationData(key1Cert, new byte[]{43, 44, 45});
+
+        // Get ready to present...
+        //
+        Map<String, Collection<String>> entriesToRequest = new LinkedHashMap<>();
+        entriesToRequest.put("org.iso.18013-5.2019", Arrays.asList("First name", "Last name"));
+        KeyPair ephemeralKeyPair = credential.createEphemeralKeyPair();
+        KeyPair readerEphemeralKeyPair = Util.createEphemeralKeyPair();
+        byte[] sessionTranscript = Util.buildSessionTranscript(ephemeralKeyPair);
+        credential.setReaderEphemeralPublicKey(readerEphemeralKeyPair.getPublic());
+
+        // First getEntries() call should pick key0
+        //
+        ResultData rd = credential.getEntries(
+            Util.createItemsRequest(entriesToRequest, null),
+            entriesToRequest,
+            sessionTranscript,
+            null);
+        assertArrayEquals(new int[]{1, 0, 0, 0, 0}, credential.getAuthenticationDataUsageCount());
+        assertArrayEquals(new byte[]{42, 43, 44}, rd.getStaticAuthenticationData());
+
+        // Now configure to not increase the usage count... we should be able to do as many
+        // presentations as we want and usage counts shouldn't change. We'll just do 3.
+        //
+        // Note that key1 will be picked (we can check that by inspecting static authentication
+        // data) but its usage count won't be increased.
+        //
+        credential = store.getCredentialByName(credentialName,
+                IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+        ephemeralKeyPair = credential.createEphemeralKeyPair();
+        readerEphemeralKeyPair = Util.createEphemeralKeyPair();
+        sessionTranscript = Util.buildSessionTranscript(ephemeralKeyPair);
+        credential.setReaderEphemeralPublicKey(readerEphemeralKeyPair.getPublic());
+        credential.setIncrementKeyUsageCount(false);
+        for (int n = 0; n < 3; n++) {
+            rd = credential.getEntries(
+                Util.createItemsRequest(entriesToRequest, null),
+                entriesToRequest,
+                sessionTranscript,
+                null);
+            assertArrayEquals(new int[]{1, 0, 0, 0, 0},
+                              credential.getAuthenticationDataUsageCount());
+            assertArrayEquals(new byte[]{43, 44, 45}, rd.getStaticAuthenticationData());
+        }
+
+        // With usage counts incrementing again, do another getEntries() call. It should pick key1
+        // and use it up. Check this by inspecting use-counts and the static authentication data.
+        //
+        credential = store.getCredentialByName(credentialName,
+                IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+        ephemeralKeyPair = credential.createEphemeralKeyPair();
+        readerEphemeralKeyPair = Util.createEphemeralKeyPair();
+        sessionTranscript = Util.buildSessionTranscript(ephemeralKeyPair);
+        credential.setReaderEphemeralPublicKey(readerEphemeralKeyPair.getPublic());
+        rd = credential.getEntries(
+            Util.createItemsRequest(entriesToRequest, null),
+            entriesToRequest,
+            sessionTranscript,
+            null);
+        assertArrayEquals(new int[]{1, 1, 0, 0, 0}, credential.getAuthenticationDataUsageCount());
+        assertArrayEquals(new byte[]{43, 44, 45}, rd.getStaticAuthenticationData());
+    }
+
     // TODO: test storeStaticAuthenticationData() throwing UnknownAuthenticationKeyException
     // on an unknown auth key
 }
diff --git a/tests/tests/identity/src/android/security/identity/cts/MultiDocumentPresentationTest.java b/tests/tests/identity/src/android/security/identity/cts/MultiDocumentPresentationTest.java
new file mode 100644
index 0000000..21ef402
--- /dev/null
+++ b/tests/tests/identity/src/android/security/identity/cts/MultiDocumentPresentationTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+
+import android.security.identity.AccessControlProfile;
+import android.security.identity.AccessControlProfileId;
+import android.security.identity.PersonalizationData;
+import android.security.identity.IdentityCredential;
+import android.security.identity.IdentityCredentialException;
+import android.security.identity.IdentityCredentialStore;
+import android.security.identity.InvalidReaderSignatureException;
+import android.security.identity.ResultData;
+import android.security.identity.WritableIdentityCredential;
+import android.security.identity.PresentationSession;
+import android.security.identity.CredentialDataRequest;
+import android.security.identity.CredentialDataResult;
+import com.android.security.identity.internal.Util;
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import co.nstant.in.cbor.CborException;
+
+import javax.crypto.SecretKey;
+
+public class MultiDocumentPresentationTest {
+    private static final String TAG = "MultiDocumentPresentationTest";
+
+    int[] getAuthKeyUsageCount(IdentityCredentialStore store, String credentialName)
+        throws Exception {
+        IdentityCredential credential = store.getCredentialByName(
+            credentialName,
+            IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+        return credential.getAuthenticationDataUsageCount();
+    }
+
+    static Collection<X509Certificate> createAuthKeys(IdentityCredentialStore store, String credentialName)
+            throws Exception {
+        IdentityCredential credential = store.getCredentialByName(
+            credentialName,
+            IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+        credential.setAvailableAuthenticationKeys(5, 3);
+        Collection<X509Certificate> certificates = credential.getAuthKeysNeedingCertification();
+        for (X509Certificate certificate : certificates) {
+            credential.storeStaticAuthenticationData(certificate, new byte[]{42, 43, 44});
+        }
+        return certificates;
+    }
+
+    @Test
+    public void multipleDocuments() throws Exception {
+        assumeTrue("IC HAL is not implemented", TestUtil.isHalImplemented());
+        assumeTrue("IdentityCredentialStore.createPresentationSession(int) not supported",
+                   TestUtil.getFeatureVersion() >= 202201);
+
+        Context appContext = InstrumentationRegistry.getTargetContext();
+        IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext);
+
+        store.deleteCredentialByName("credential1");
+        assertNull(store.deleteCredentialByName("credential1"));
+        ProvisioningTest.createCredential(store, "credential1");
+        Collection<X509Certificate> credential1AuthKeys = createAuthKeys(store, "credential1");
+
+        store.deleteCredentialByName("credential2");
+        assertNull(store.deleteCredentialByName("credential2"));
+        ProvisioningTest.createCredential(store, "credential2");
+        Collection<X509Certificate> credential2AuthKeys = createAuthKeys(store, "credential2");
+
+        PresentationSession session = store.createPresentationSession(
+            IdentityCredentialStore.CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+
+        KeyPair ephemeralKeyPair = session.getEphemeralKeyPair();
+        KeyPair readerEphemeralKeyPair = Util.createEphemeralKeyPair();
+        session.setReaderEphemeralPublicKey(readerEphemeralKeyPair.getPublic());
+        byte[] sessionTranscript = Util.buildSessionTranscript(ephemeralKeyPair);
+        session.setSessionTranscript(sessionTranscript);
+
+        checkPresentation(session, "credential1",
+                          credential1AuthKeys.iterator().next().getPublicKey(),
+                          readerEphemeralKeyPair.getPrivate(), sessionTranscript);
+        // We should only have used a single key here.
+        assertArrayEquals(new int[]{1, 0, 0, 0, 0}, getAuthKeyUsageCount(store, "credential1"));
+
+        checkPresentation(session, "credential2",
+                          credential2AuthKeys.iterator().next().getPublicKey(),
+                          readerEphemeralKeyPair.getPrivate(), sessionTranscript);
+        assertArrayEquals(new int[]{1, 0, 0, 0, 0}, getAuthKeyUsageCount(store, "credential2"));
+
+        // Since it's the same session, additional getCredentialData() calls shouldn't consume
+        // additional auth-keys. Check this.
+        checkPresentation(session, "credential1",
+                          null,
+                          readerEphemeralKeyPair.getPrivate(), sessionTranscript);
+        assertArrayEquals(new int[]{1, 0, 0, 0, 0}, getAuthKeyUsageCount(store, "credential1"));
+    }
+
+    static void checkPresentation(PresentationSession session,
+                                  String credentialName,
+                                  PublicKey expectedAuthKey,
+                                  PrivateKey readerEphemeralPrivateKey,
+                                  byte[] sessionTranscript) throws Exception {
+        // Now use one of the keys...
+        Map<String, Collection<String>> dsEntriesToRequest = new LinkedHashMap<>();
+        dsEntriesToRequest.put("org.iso.18013-5.2019",
+                Arrays.asList("First name",
+                        "Last name",
+                        "Home address",
+                        "Birth date",
+                        "Cryptanalyst",
+                        "Portrait image",
+                        "Height"));
+        CredentialDataResult rd = session.getCredentialData(
+            credentialName,
+            new CredentialDataRequest.Builder()
+            .setDeviceSignedEntriesToRequest(dsEntriesToRequest)
+            .setRequestMessage(Util.createItemsRequest(dsEntriesToRequest, null))
+            .build());
+        byte[] resultCbor = rd.getDeviceNameSpaces();
+        try {
+            String pretty = Util.cborPrettyPrint(Util.canonicalizeCbor(resultCbor));
+            assertEquals("{\n"
+                         + "  'org.iso.18013-5.2019' : {\n"
+                         + "    'Height' : 180,\n"
+                         + "    'Last name' : 'Turing',\n"
+                         + "    'Birth date' : '19120623',\n"
+                         + "    'First name' : 'Alan',\n"
+                         + "    'Cryptanalyst' : true,\n"
+                         + "    'Home address' : 'Maida Vale, London, England',\n"
+                         + "    'Portrait image' : [0x01, 0x02]\n"
+                         + "  }\n"
+                         + "}",
+                         pretty);
+        } catch (CborException e) {
+            e.printStackTrace();
+            assertTrue(false);
+        }
+
+        byte[] deviceAuthenticationCbor = Util.buildDeviceAuthenticationCbor(
+            "org.iso.18013-5.2019.mdl",
+            sessionTranscript,
+            resultCbor);
+
+        if (expectedAuthKey != null) {
+            // Calculate the MAC by deriving the key using ECDH and HKDF.
+            SecretKey eMacKey = Util.calcEMacKeyForReader(
+                expectedAuthKey,
+                readerEphemeralPrivateKey,
+                sessionTranscript);
+            byte[] deviceAuthenticationBytes =
+                Util.prependSemanticTagForEncodedCbor(deviceAuthenticationCbor);
+            byte[] expectedMac = Util.coseMac0(eMacKey,
+                                               new byte[0],                 // payload
+                                               deviceAuthenticationBytes);  // detached content
+
+            // Then compare it with what the TA produced.
+            assertArrayEquals(expectedMac, rd.getDeviceMac());
+        }
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java b/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
index 3339fd8..4fa15b8 100644
--- a/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
@@ -60,12 +60,6 @@
     private void assertNONEwithECDSATruncatesInputToFieldSize(KeyPair keyPair)
             throws Exception {
         int keySizeBits = TestUtils.getKeySizeBits(keyPair.getPublic());
-        if (keySizeBits == 521) {
-            /*
-             * Skip P521 test until b/184307265 is fixed.
-             */
-            return;
-        }
         byte[] message = new byte[(keySizeBits * 3) / 8];
         for (int i = 0; i < message.length; i++) {
             message[i] = (byte) (i + 1);
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 3f83027..27e2f30 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -115,7 +115,7 @@
 import org.junit.runner.RunWith;
 
 /**
- * Tests for Android KeysStore attestation.
+ * Tests for Android Keystore attestation.
  */
 @RunWith(AndroidJUnit4.class)
 public class KeyAttestationTest {
@@ -1106,9 +1106,9 @@
 
     @SuppressWarnings("unchecked")
     private void checkAttestationSecurityLevelDependentParams(Attestation attestation) {
-        assertThat("Attestation version must be 1, 2, 3, 4 or 100",
+        assertThat("Attestation version must be one of: {1, 2, 3, 4, 100, 200}",
                 attestation.getAttestationVersion(),
-                either(is(1)).or(is(2)).or(is(3)).or(is(4)).or(is(100)));
+                either(is(1)).or(is(2)).or(is(3)).or(is(4)).or(is(100)).or(is(200)));
 
         AuthorizationList teeEnforced = attestation.getTeeEnforced();
         AuthorizationList softwareEnforced = attestation.getSoftwareEnforced();
@@ -1122,7 +1122,7 @@
                         attestation.getKeymasterSecurityLevel(),
                         is(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT));
                 assertThat(attestation.getKeymasterVersion(),
-                        either(is(2)).or(is(3)).or(is(4)).or(is(41)).or(is(100)));
+                        either(is(2)).or(is(3)).or(is(4)).or(is(41)).or(is(100)).or(is(200)));
 
                 checkRootOfTrust(attestation, false /* requireLocked */);
                 assertThat(teeEnforced.getOsVersion(), is(systemOsVersion));
diff --git a/tests/tests/libcoreapievolution/Android.bp b/tests/tests/libcoreapievolution/Android.bp
index 891ebc3..eed4fc3 100644
--- a/tests/tests/libcoreapievolution/Android.bp
+++ b/tests/tests/libcoreapievolution/Android.bp
@@ -30,6 +30,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-art",
     ],
 }
diff --git a/tests/tests/libcoreapievolution/AndroidTest.xml b/tests/tests/libcoreapievolution/AndroidTest.xml
index 08f47fd..cf8ca2d 100644
--- a/tests/tests/libcoreapievolution/AndroidTest.xml
+++ b/tests/tests/libcoreapievolution/AndroidTest.xml
@@ -42,4 +42,7 @@
         <!-- ART Mainline Module (external (AOSP) version). -->
         <option name="mainline-module-package-name" value="com.android.art" />
     </object>
+
+    <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
 </configuration>
diff --git a/tests/tests/libcorefileio/Android.bp b/tests/tests/libcorefileio/Android.bp
index 58c388c..3febb32 100644
--- a/tests/tests/libcorefileio/Android.bp
+++ b/tests/tests/libcorefileio/Android.bp
@@ -30,6 +30,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-art",
     ],
 }
diff --git a/tests/tests/libcorefileio/AndroidTest.xml b/tests/tests/libcorefileio/AndroidTest.xml
index c90b702..b8cef26 100644
--- a/tests/tests/libcorefileio/AndroidTest.xml
+++ b/tests/tests/libcorefileio/AndroidTest.xml
@@ -40,4 +40,7 @@
         <!-- ART Mainline Module (external (AOSP) version). -->
         <option name="mainline-module-package-name" value="com.android.art" />
     </object>
+
+    <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
 </configuration>
diff --git a/tests/tests/libcorelegacy22/Android.bp b/tests/tests/libcorelegacy22/Android.bp
index 44a997a..98684b2 100644
--- a/tests/tests/libcorelegacy22/Android.bp
+++ b/tests/tests/libcorelegacy22/Android.bp
@@ -26,6 +26,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-art",
     ],
 }
diff --git a/tests/tests/libcorelegacy22/AndroidTest.xml b/tests/tests/libcorelegacy22/AndroidTest.xml
index 94c1134..9640013 100644
--- a/tests/tests/libcorelegacy22/AndroidTest.xml
+++ b/tests/tests/libcorelegacy22/AndroidTest.xml
@@ -42,4 +42,7 @@
         <!-- ART Mainline Module (external (AOSP) version). -->
         <option name="mainline-module-package-name" value="com.android.art" />
     </object>
+
+    <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
 </configuration>
diff --git a/tests/tests/media/Android.bp b/tests/tests/media/Android.bp
index 856f4d4..7ee7d2a 100644
--- a/tests/tests/media/Android.bp
+++ b/tests/tests/media/Android.bp
@@ -14,13 +14,16 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-CC-BY
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license",
+    ],
+}
+
+license {
+    name: "cts_tests_tests_media_license",
+    license_kinds: ["SPDX-license-identifier-CC-BY-3.0"],
+    license_text: ["LICENSE_CC_BY"],
 }
 
 java_library {
@@ -43,14 +46,53 @@
     target_sdk_version: "31",
 }
 
-// Build the res and asset library to
-// share with android.mediadrm.cts package.
-android_library {
-    name: "cts-media-res-lib",
-    asset_dirs: ["assets"],
-    resource_dirs: ["res"],
-    min_sdk_version: "29",
-    target_sdk_version: "31",
+cc_test_library {
+    name: "libctscodecutils_jni",
+    srcs: [
+        "common/jni/codec-utils-jni.cpp",
+        "common/jni/md5_utils.cpp",
+    ],
+    shared_libs: [
+        "libnativehelper_compat_libc++",
+        "liblog",
+    ],
+    header_libs: ["liblog_headers"],
+    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
+    // (revisit if/when we add features to this library that require newer sdk.
+    sdk_version: "29",
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DEGL_EGLEXT_PROTOTYPES",
+    ],
+    stl: "libc++_static",
+    gtest: false,
+}
+
+cc_test_library {
+    name: "libctsmediacommon_jni",
+    srcs: [
+        "common/jni/NdkInputSurface-jni.cpp",
+        "common/jni/NdkMediaCodec-jni.cpp",
+    ],
+    shared_libs: [
+        "libandroid",
+        "libnativehelper_compat_libc++",
+        "liblog",
+        "libmediandk",
+        "libEGL",
+    ],
+    header_libs: ["liblog_headers"],
+    stl: "libc++_static",
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DEGL_EGLEXT_PROTOTYPES",
+    ],
+    gtest: false,
+    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
+    // (revisit if/when we add features to this library that require newer sdk.
+    sdk_version: "29",
 }
 
 android_library {
@@ -91,10 +133,8 @@
         "hamcrest-library",
         "ctstestserver",
         "cts-media-common",
-        "cts-media-res-lib",
         "junit",
         "junit-params",
-        "ndkaudio",
         "testng",
         "truth-prebuilt",
         "mockito-target-minus-junit4",
@@ -104,17 +144,14 @@
     jni_libs: [
         "libctscodecutils_jni",
         "libctsimagereader_jni",
-        "libctsmediadrm_jni",
         "libctsmediacodec_jni",
+        "libctsmediacommon_jni",
         "libnativehelper_compat_libc++",
-        "libndkaudioLib",
     ],
     asset_dirs: ["assets"],
     resource_dirs: ["res"],
     aaptflags: [
         "--auto-add-overlay",
-        // Give com.android.mediadrm.cts Java files access to the R class
-        "--extra-packages com.android.mediadrm.cts",
 
         // Do not compress these files:
         "-0 .vp9",
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index f82b326..86f744f 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -39,6 +39,8 @@
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
 
     <uses-permission android:name="android.permission.VIBRATE"/>
 
@@ -59,16 +61,6 @@
              android:label="AudioManagerStub"/>
         <activity android:name="android.media.cts.AudioManagerStubHelper"
              android:label="AudioManagerStubHelper"/>
-        <activity android:name="android.media.cts.DecodeAccuracyTestActivity"
-             android:label="DecodeAccuracyTestActivity"
-             android:screenOrientation="locked"
-             android:configChanges="mcc|mnc|keyboard|keyboardHidden|orientation|screenSize|navigation"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
-            </intent-filter>
-        </activity>
         <activity android:name="android.media.cts.MediaSessionTestActivity"
              android:label="MediaSessionTestActivity"
              android:screenOrientation="nosensor"
@@ -119,13 +111,6 @@
         </activity>
         <activity android:name="android.media.cts.MockActivity"/>
         <activity android:name="android.media.cts.MediaRouter2TestActivity"/>
-        <service android:name="android.media.cts.RemoteVirtualDisplayService"
-             android:process=":remoteService"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-            </intent-filter>
-        </service>
         <service android:name="android.media.cts.StubMediaBrowserService"
              android:exported="true">
             <intent-filter>
diff --git a/tests/tests/media/AndroidTest.xml b/tests/tests/media/AndroidTest.xml
index 5b09a1a..8d20655 100644
--- a/tests/tests/media/AndroidTest.xml
+++ b/tests/tests/media/AndroidTest.xml
@@ -54,7 +54,6 @@
         <!-- test-timeout unit is ms, value = 30 min -->
         <option name="test-timeout" value="1800000" />
         <option name="runtime-hint" value="4h" />
-        <option name="exclude-annotation" value="org.junit.Ignore" />
         <option name="hidden-api-checks" value="false" />
         <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
         <option name="isolated-storage" value="false" />
diff --git a/tests/tests/media/DynamicConfig.xml b/tests/tests/media/DynamicConfig.xml
index 942ab80..62ba6b7 100644
--- a/tests/tests/media/DynamicConfig.xml
+++ b/tests/tests/media/DynamicConfig.xml
@@ -14,18 +14,6 @@
 -->
 
 <dynamicConfig>
-    <entry key="media_codec_capabilities_test_avc_baseline12">
-        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=160&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=9EDCA0B395B8A949C511FD5E59B9F805CFF797FD.702DE9BA7AF96785FD6930AD2DD693A0486C880E&amp;key=ik0</value>
-    </entry>
-    <entry key="media_codec_capabilities_test_avc_baseline30">
-        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=18&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=7DCDE3A6594D0B91A27676A3CDC3A87B149F82EA.7A83031734CB1EDCE06766B6228842F954927960&amp;key=ik0</value>
-    </entry>
-    <entry key="media_codec_capabilities_test_avc_high31">
-        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=22&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=179525311196616BD8E1381759B0E5F81A9E91B5.C4A50E44059FEBCC6BBC78E3B3A4E0E0065777&amp;key=ik0</value>
-    </entry>
-    <entry key="media_codec_capabilities_test_avc_high40">
-        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=137&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=B0976085596DD42DEA3F08307F76587241CB132B.043B719C039E8B92F45391ADC0BE3665E2332930&amp;key=ik0</value>
-    </entry>
     <entry key="streaming_media_player_test_http_h263_amr_video1">
         <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=13&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=5729247E22691EBB3E804DDD523EC42DC17DD8CE.443B81C1E8E6D64E4E1555F568BA46C206507D78&amp;key=ik0&amp;user=android-device-test</value>
     </entry>
diff --git a/tests/tests/media/LICENSE_CC_BY b/tests/tests/media/LICENSE_CC_BY
new file mode 100644
index 0000000..e3feb12
--- /dev/null
+++ b/tests/tests/media/LICENSE_CC_BY
@@ -0,0 +1,348 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta name="generator" content="HTML Tidy for Linux/x86 (vers 1 September 2005), see www.w3.org" />
+<title>Creative Commons Legal Code</title>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<link rel="stylesheet" type="text/css" href="https://creativecommons.org/includes/deed3.css" media="screen" />
+<link rel="stylesheet" type="text/css" href="https://creativecommons.org/includes/deed3-print.css" media="print" />
+<!--[if lt IE 7]><link rel="stylesheet" type="text/css" href="https://creativecommons.org/includes/deed3-ie.css" media="screen" /><![endif]-->
+<script type="text/javascript" src="https://creativecommons.org/includes/errata.js">
+</script>
+</head>
+<body>
+<p align="center" id="header"><a href="https://creativecommons.org/">Creative Commons</a></p>
+<div id="deed" class="green">
+<div id="deed-head">
+<div id="cc-logo">
+<img src="https://creativecommons.org/images/deed/cc-logo.jpg" alt="" />
+</div>
+<h1><span>Creative Commons Legal Code</span></h1>
+<div id="deed-license">
+<h2>Attribution 3.0 United States</h2>
+</div>
+</div>
+<div id="deed-main">
+<div id="deed-main-content">
+<img src="https://creativecommons.org/images/international/us.png" alt="" />
+<blockquote>
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES
+NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE
+DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE
+COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS.
+CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE
+INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES
+RESULTING FROM ITS USE.
+</blockquote>
+<h3><em>License</em></h3>
+<p>THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS
+OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR
+"LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER
+APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS
+PROHIBITED.</p>
+<p>BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU
+ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE.
+TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A
+CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE
+IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.</p>
+<p><strong>1. Definitions</strong></p>
+<ol type="a">
+<li><strong>"Collective Work"</strong> means a work, such
+as a periodical issue, anthology or encyclopedia, in
+which the Work in its entirety in unmodified form, along
+with one or more other contributions, constituting
+separate and independent works in themselves, are
+assembled into a collective whole. A work that
+constitutes a Collective Work will not be considered a
+Derivative Work (as defined below) for the purposes of
+this License.</li>
+<li><strong>"Derivative Work"</strong> means a work based
+upon the Work or upon the Work and other pre-existing
+works, such as a translation, musical arrangement,
+dramatization, fictionalization, motion picture version,
+sound recording, art reproduction, abridgment,
+condensation, or any other form in which the Work may be
+recast, transformed, or adapted, except that a work that
+constitutes a Collective Work will not be considered a
+Derivative Work for the purpose of this License. For the
+avoidance of doubt, where the Work is a musical
+composition or sound recording, the synchronization of
+the Work in timed-relation with a moving image
+("synching") will be considered a Derivative Work for the
+purpose of this License.</li>
+<li><strong>"Licensor"</strong> means the individual,
+individuals, entity or entities that offers the Work
+under the terms of this License.</li>
+<li><strong>"Original Author"</strong> means the
+individual, individuals, entity or entities who created
+the Work.</li>
+<li><strong>"Work"</strong> means the copyrightable work
+of authorship offered under the terms of this
+License.</li>
+<li><strong>"You"</strong> means an individual or entity
+exercising rights under this License who has not
+previously violated the terms of this License with
+respect to the Work, or who has received express
+permission from the Licensor to exercise rights under
+this License despite a previous violation.</li>
+</ol>
+<p><strong>2. Fair Use Rights.</strong> Nothing in this
+license is intended to reduce, limit, or restrict any
+rights arising from fair use, first sale or other
+limitations on the exclusive rights of the copyright owner
+under copyright law or other applicable laws.</p>
+<p><strong>3. License Grant.</strong> Subject to the terms
+and conditions of this License, Licensor hereby grants You
+a worldwide, royalty-free, non-exclusive, perpetual (for
+the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:</p>
+<ol type="a">
+<li>to reproduce the Work, to incorporate the Work into
+one or more Collective Works, and to reproduce the Work
+as incorporated in the Collective Works;</li>
+<li>to create and reproduce Derivative Works provided
+that any such Derivative Work, including any translation
+in any medium, takes reasonable steps to clearly label,
+demarcate or otherwise identify that changes were made to
+the original Work. For example, a translation could be
+marked "The original work was translated from English to
+Spanish," or a modification could indicate "The original
+work has been modified.";;</li>
+<li>to distribute copies or phonorecords of, display
+publicly, perform publicly, and perform publicly by means
+of a digital audio transmission the Work including as
+incorporated in Collective Works;</li>
+<li>to distribute copies or phonorecords of, display
+publicly, perform publicly, and perform publicly by means
+of a digital audio transmission Derivative Works.</li>
+<li>
+<p>For the avoidance of doubt, where the Work is a
+musical composition:</p>
+<ol type="i">
+<li><strong>Performance Royalties Under Blanket
+Licenses</strong>. Licensor waives the exclusive
+right to collect, whether individually or, in the
+event that Licensor is a member of a performance
+rights society (e.g. ASCAP, BMI, SESAC), via that
+society, royalties for the public performance or
+public digital performance (e.g. webcast) of the
+Work.</li>
+<li><strong>Mechanical Rights and Statutory
+Royalties</strong>. Licensor waives the exclusive
+right to collect, whether individually or via a music
+rights agency or designated agent (e.g. Harry Fox
+Agency), royalties for any phonorecord You create
+from the Work ("cover version") and distribute,
+subject to the compulsory license created by 17 USC
+Section 115 of the US Copyright Act (or the
+equivalent in other jurisdictions).</li>
+</ol>
+</li>
+<li><strong>Webcasting Rights and Statutory
+Royalties</strong>. For the avoidance of doubt, where the
+Work is a sound recording, Licensor waives the exclusive
+right to collect, whether individually or via a
+performance-rights society (e.g. SoundExchange),
+royalties for the public digital performance (e.g.
+webcast) of the Work, subject to the compulsory license
+created by 17 USC Section 114 of the US Copyright Act (or
+the equivalent in other jurisdictions).</li>
+</ol>
+<p>The above rights may be exercised in all media and
+formats whether now known or hereafter devised. The above
+rights include the right to make such modifications as are
+technically necessary to exercise the rights in other media
+and formats. All rights not expressly granted by Licensor
+are hereby reserved.</p>
+<p><strong>4. Restrictions.</strong> The license granted in
+Section 3 above is expressly made subject to and limited by
+the following restrictions:</p>
+<ol type="a">
+<li>You may distribute, publicly display, publicly
+perform, or publicly digitally perform the Work only
+under the terms of this License, and You must include a
+copy of, or the Uniform Resource Identifier for, this
+License with every copy or phonorecord of the Work You
+distribute, publicly display, publicly perform, or
+publicly digitally perform. You may not offer or impose
+any terms on the Work that restrict the terms of this
+License or the ability of a recipient of the Work to
+exercise the rights granted to that recipient under the
+terms of the License. You may not sublicense the Work.
+You must keep intact all notices that refer to this
+License and to the disclaimer of warranties. When You
+distribute, publicly display, publicly perform, or
+publicly digitally perform the Work, You may not impose
+any technological measures on the Work that restrict the
+ability of a recipient of the Work from You to exercise
+the rights granted to that recipient under the terms of
+the License. This Section 4(a) applies to the Work as
+incorporated in a Collective Work, but this does not
+require the Collective Work apart from the Work itself to
+be made subject to the terms of this License. If You
+create a Collective Work, upon notice from any Licensor
+You must, to the extent practicable, remove from the
+Collective Work any credit as required by Section 4(b),
+as requested. If You create a Derivative Work, upon
+notice from any Licensor You must, to the extent
+practicable, remove from the Derivative Work any credit
+as required by Section 4(b), as requested.</li>
+<li>If You distribute, publicly display, publicly
+perform, or publicly digitally perform the Work (as
+defined in Section 1 above) or any Derivative Works (as
+defined in Section 1 above) or Collective Works (as
+defined in Section 1 above), You must, unless a request
+has been made pursuant to Section 4(a), keep intact all
+copyright notices for the Work and provide, reasonable to
+the medium or means You are utilizing: (i) the name of
+the Original Author (or pseudonym, if applicable) if
+supplied, and/or (ii) if the Original Author and/or
+Licensor designate another party or parties (e.g. a
+sponsor institute, publishing entity, journal) for
+attribution ("Attribution Parties") in Licensor's
+copyright notice, terms of service or by other reasonable
+means, the name of such party or parties; the title of
+the Work if supplied; to the extent reasonably
+practicable, the Uniform Resource Identifier, if any,
+that Licensor specifies to be associated with the Work,
+unless such URI does not refer to the copyright notice or
+licensing information for the Work; and, consistent with
+Section 3(b) in the case of a Derivative Work, a credit
+identifying the use of the Work in the Derivative Work
+(e.g., "French translation of the Work by Original
+Author," or "Screenplay based on original Work by
+Original Author"). The credit required by this Section
+4(b) may be implemented in any reasonable manner;
+provided, however, that in the case of a Derivative Work
+or Collective Work, at a minimum such credit will appear,
+if a credit for all contributing authors of the
+Derivative Work or Collective Work appears, then as part
+of these credits and in a manner at least as prominent as
+the credits for the other contributing authors. For the
+avoidance of doubt, You may only use the credit required
+by this Section for the purpose of attribution in the
+manner set out above and, by exercising Your rights under
+this License, You may not implicitly or explicitly assert
+or imply any connection with, sponsorship or endorsement
+by the Original Author, Licensor and/or Attribution
+Parties, as appropriate, of You or Your use of the Work,
+without the separate, express prior written permission of
+the Original Author, Licensor and/or Attribution
+Parties.</li>
+</ol>
+<p><strong>5. Representations, Warranties and
+Disclaimer</strong></p>
+<p>UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN
+WRITING, LICENSOR OFFERS THE WORK AS-IS AND ONLY TO THE
+EXTENT OF ANY RIGHTS HELD IN THE LICENSED WORK BY THE
+LICENSOR. THE LICENSOR MAKES NO REPRESENTATIONS OR
+WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS,
+IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT
+LIMITATION, WARRANTIES OF TITLE, MARKETABILITY,
+MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE,
+NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
+ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE
+EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT
+APPLY TO YOU.</p>
+<p><strong>6. Limitation on Liability.</strong> EXCEPT TO
+THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL
+LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY
+SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY
+DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK,
+EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.</p>
+<p><strong>7. Termination</strong></p>
+<ol type="a">
+<li>This License and the rights granted hereunder will
+terminate automatically upon any breach by You of the
+terms of this License. Individuals or entities who have
+received Derivative Works (as defined in Section 1 above)
+or Collective Works (as defined in Section 1 above) from
+You under this License, however, will not have their
+licenses terminated provided such individuals or entities
+remain in full compliance with those licenses. Sections
+1, 2, 5, 6, 7, and 8 will survive any termination of this
+License.</li>
+<li>Subject to the above terms and conditions, the
+license granted here is perpetual (for the duration of
+the applicable copyright in the Work). Notwithstanding
+the above, Licensor reserves the right to release the
+Work under different license terms or to stop
+distributing the Work at any time; provided, however that
+any such election will not serve to withdraw this License
+(or any other license that has been, or is required to
+be, granted under the terms of this License), and this
+License will continue in full force and effect unless
+terminated as stated above.</li>
+</ol>
+ <p><strong>8. Miscellaneous</strong></p>
+<ol type="a">
+<li>Each time You distribute or publicly digitally
+perform the Work (as defined in Section 1 above) or a
+Collective Work (as defined in Section 1 above), the
+Licensor offers to the recipient a license to the Work on
+the same terms and conditions as the license granted to
+You under this License.</li>
+<li>Each time You distribute or publicly digitally
+perform a Derivative Work, Licensor offers to the
+recipient a license to the original Work on the same
+terms and conditions as the license granted to You under
+this License.</li>
+<li>If any provision of this License is invalid or
+unenforceable under applicable law, it shall not affect
+the validity or enforceability of the remainder of the
+terms of this License, and without further action by the
+parties to this agreement, such provision shall be
+reformed to the minimum extent necessary to make such
+provision valid and enforceable.</li>
+<li>No term or provision of this License shall be deemed
+waived and no breach consented to unless such waiver or
+consent shall be in writing and signed by the party to be
+charged with such waiver or consent.</li>
+<li>This License constitutes the entire agreement between
+the parties with respect to the Work licensed here. There
+are no understandings, agreements or representations with
+respect to the Work not specified here. Licensor shall
+not be bound by any additional provisions that may appear
+in any communication from You. This License may not be
+modified without the mutual written agreement of the
+Licensor and You.</li>
+</ol>
+
+<blockquote>
+<h3>Creative Commons Notice</h3>
+<p>Creative Commons is not a party to this License, and
+makes no warranty whatsoever in connection with the Work.
+Creative Commons will not be liable to You or any party
+on any legal theory for any damages whatsoever, including
+without limitation any general, special, incidental or
+consequential damages arising in connection to this
+license. Notwithstanding the foregoing two (2) sentences,
+if Creative Commons has expressly identified itself as
+the Licensor hereunder, it shall have all rights and
+obligations of Licensor.</p>
+<p>Except for the limited purpose of indicating to the
+public that the Work is licensed under the CCPL, Creative
+Commons does not authorize the use by either party of the
+trademark "Creative Commons" or any related trademark or
+logo of Creative Commons without the prior written
+consent of Creative Commons. Any permitted use will be in
+compliance with Creative Commons' then-current trademark
+usage guidelines, as may be published on its website or
+otherwise made available upon request from time to time.
+For the avoidance of doubt, this trademark restriction
+does not form part of the License.</p>
+<p>Creative Commons may be contacted at <a href="https://creativecommons.org/">https://creativecommons.org/</a>.</p>
+</blockquote>
+</div>
+</div>
+<div id="deed-foot">
+<p id="footer"><a href="./">« Back to Commons Deed</a></p>
+</div>
+</div>
+</body>
+</html>
diff --git a/tests/tests/media/assets/noiseandchirps.ogg b/tests/tests/media/assets/noiseandchirps.ogg
deleted file mode 100644
index 1acb643..0000000
--- a/tests/tests/media/assets/noiseandchirps.ogg
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/assets/ringer.mp3 b/tests/tests/media/assets/ringer.mp3
deleted file mode 100644
index aa052e7..0000000
--- a/tests/tests/media/assets/ringer.mp3
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/audio/Android.bp b/tests/tests/media/audio/Android.bp
index 0cbdaaf..e7f1363 100644
--- a/tests/tests/media/audio/Android.bp
+++ b/tests/tests/media/audio/Android.bp
@@ -14,13 +14,56 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-CC-BY
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license", // CC-BY
+    ],
+}
+
+cc_test_library {
+    name: "libaudiocts_jni",
+    srcs: [
+        "jni/AudioPlayer_jni.cpp",
+        "jni/AudioPlayer.cpp",
+        "jni/AudioRecorder_jni.cpp",
+        "jni/AudioRecorder.cpp",
+        "jni/AudioSource.cpp",
+        "jni/OpenSLESUtils.cpp",
+        "jni/PeriodicAudioSource.cpp",
+        "jni/SystemParams.cpp",
+        "jni/WaveTableGenerator.cpp",
+        "jni/WaveTableOscillator.cpp",
+        "jni/appendix-b-1-1-buffer-queue.cpp",
+        "jni/appendix-b-1-2-recording.cpp",
+        "jni/audio-metadata-native.cpp",
+        "jni/audio-record-native.cpp",
+        "jni/audio-track-native.cpp",
+        "jni/sl-utils.cpp",
+    ],
+    include_dirs: ["frameworks/wilhelm/include",
+                   "frameworks/wilhelm/src/android",
+                   "system/core/include"],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+        "libnativehelper_compat_libc++",
+        "libOpenSLES",
+    ],
+    header_libs: [
+        "libaudioutils_headers",
+        "liblog_headers",
+
+    ],
+    stl: "libc++_static",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-deprecated-declarations",
+    ],
+    gtest: false,
+    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
+    // (revisit if/when we add features to this library that require newer sdk.
+    sdk_version: "29",
 }
 
 android_test {
@@ -33,26 +76,20 @@
         "compatibility-device-util-axt",
         "ctstestrunner-axt",
         "hamcrest-library",
-        "ctstestserver",
         "cts-media-common",
         "junit-params",
-        "ndkaudio",
         "testng",
     ],
     jni_libs: [
-        "libaudio_jni",
         "libaudiocts_jni",
-        "libndkaudioLib",
     ],
     resource_dirs: ["res"],
     srcs: [
         "src/**/*.java",
-        "aidl/**/*.aidl",
     ],
     platform_apis: true,
     jni_uses_sdk_apis: true,
     libs: [
-        "org.apache.http.legacy",
         "android.test.base",
         "android.test.runner",
     ],
@@ -62,7 +99,6 @@
         "general-tests",
         "mts-media",
     ],
-    host_required: ["cts-dynamic-config"],
     min_sdk_version: "29",
     target_sdk_version: "31",
 }
diff --git a/tests/tests/media/audio/AndroidManifest.xml b/tests/tests/media/audio/AndroidManifest.xml
index 171d3f1..bc7fc4b 100644
--- a/tests/tests/media/audio/AndroidManifest.xml
+++ b/tests/tests/media/audio/AndroidManifest.xml
@@ -18,8 +18,6 @@
      package="android.media.audio.cts"
      android:targetSandboxVersion="2">
 
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
@@ -32,12 +30,9 @@
 
     <uses-permission android:name="android.permission.VIBRATE"/>
 
-    <application android:networkSecurityConfig="@xml/network_security_config"
-         android:requestLegacyExternalStorage="true"
+    <application android:requestLegacyExternalStorage="true"
          android:largeHeap="true">
         <uses-library android:name="android.test.runner"/>
-        <uses-library android:name="org.apache.http.legacy"
-             android:required="false"/>
 
         <activity android:name="android.media.audio.cts.RingtonePickerActivity"
              android:label="RingtonePickerActivity"
diff --git a/tests/tests/media/audio/AndroidTest.xml b/tests/tests/media/audio/AndroidTest.xml
index bc02c60..e72ab2c 100644
--- a/tests/tests/media/audio/AndroidTest.xml
+++ b/tests/tests/media/audio/AndroidTest.xml
@@ -27,25 +27,10 @@
         <option name="disable-audio" value="false"/>
         <option name="screen-saver" value="off"/>
     </target_preparer>
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
-        <option name="target" value="host" />
-        <option name="config-filename" value="CtsMediaAudioTestCases" />
-        <option name="dynamic-config-name" value="CtsMediaAudioTestCases" />
-        <option name="version" value="1.0"/>
-    </target_preparer>
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
-        <option name="push-all" value="true" />
-        <option name="media-folder-name" value="CtsMediaTestCases-1.4" />
-        <option name="dynamic-config-module" value="CtsMediaAudioTestCases" />
-    </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsMediaAudioTestCases.apk" />
     </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <!-- MediaProjectionTest needs this one to not be granted, SuiteApkInstaller grants all of them by default.-->
-        <option name="run-command" value="pm revoke android.media.audio.cts android.permission.SYSTEM_ALERT_WINDOW"/>
-    </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.media.audio.cts" />
         <!-- setup can be expensive so limit the number of shards -->
@@ -55,7 +40,5 @@
         <option name="runtime-hint" value="1h" />
         <option name="exclude-annotation" value="org.junit.Ignore" />
         <option name="hidden-api-checks" value="false" />
-        <!-- disable isolated storage so tests can access dynamic config stored in /sdcard. -->
-        <option name="isolated-storage" value="false" />
     </test>
 </configuration>
diff --git a/tests/tests/media/audio/jni/Android.bp b/tests/tests/media/audio/jni/Android.bp
deleted file mode 100644
index ec4e575..0000000
--- a/tests/tests/media/audio/jni/Android.bp
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_test_library {
-    name: "libaudiocts_jni",
-    srcs: [
-        "appendix-b-1-1-buffer-queue.cpp",
-        "appendix-b-1-2-recording.cpp",
-        "audio-metadata-native.cpp",
-    ],
-    include_dirs: ["system/core/include",
-                   "cts/tests/tests/media/libaudiojni"],
-
-    shared_libs: [
-        "libaudio_jni",
-        "libandroid",
-        "liblog",
-        "libnativehelper_compat_libc++",
-        "libOpenSLES",
-    ],
-    header_libs: [
-        "libaudioutils_headers",
-        "liblog_headers",
-    ],
-    stl: "libc++_static",
-    cflags: [
-        "-Werror",
-        "-Wall",
-        "-Wno-deprecated-declarations",
-    ],
-    gtest: false,
-    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
-    // (revisit if/when we add features to this library that require newer sdk.
-    sdk_version: "29",
-}
diff --git a/tests/tests/media/libndkaudio/AudioPlayer.cpp b/tests/tests/media/audio/jni/AudioPlayer.cpp
similarity index 100%
rename from tests/tests/media/libndkaudio/AudioPlayer.cpp
rename to tests/tests/media/audio/jni/AudioPlayer.cpp
diff --git a/tests/tests/media/libndkaudio/AudioPlayer.h b/tests/tests/media/audio/jni/AudioPlayer.h
similarity index 100%
rename from tests/tests/media/libndkaudio/AudioPlayer.h
rename to tests/tests/media/audio/jni/AudioPlayer.h
diff --git a/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioPlayer.cpp b/tests/tests/media/audio/jni/AudioPlayer_jni.cpp
similarity index 71%
rename from tests/tests/media/libndkaudio/com_android_ndkaudio_AudioPlayer.cpp
rename to tests/tests/media/audio/jni/AudioPlayer_jni.cpp
index ade0e34..9336ba2 100644
--- a/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioPlayer.cpp
+++ b/tests/tests/media/audio/jni/AudioPlayer_jni.cpp
@@ -15,14 +15,12 @@
  */
 #include <android/log.h>
 
-#include "com_android_ndkaudio_AudioPlayer.h"
-
 #include "AudioPlayer.h"
 #include "WaveTableGenerator.h"
 #include "WaveTableOscillator.h"
 #include "SystemParams.h"
 
-static const char* TAG = "_com_android_ndkaudio_AudioPlayer_";
+static const char* TAG = "AudioPlayer_jni";
 
 using namespace ndkaudio;
 
@@ -37,7 +35,7 @@
 
 extern "C" {
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_Create(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioPlayer_Create(JNIEnv*, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_Create() ...");
 
   if (nativePlayer == 0) {
@@ -50,32 +48,32 @@
   }
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_Destroy(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioPlayer_Destroy(JNIEnv*, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_Destroy() ...");
   nativePlayer->Close();
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_RealizePlayer(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioPlayer_RealizePlayer(JNIEnv*, jobject) {
     __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_RealizePlayer() ...");
     nativePlayer->RealizePlayer();
   }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_RealizeRoutingProxy(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioPlayer_RealizeRoutingProxy(JNIEnv*, jobject) {
     __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_RealizeRoutingProxy() ...");
     nativePlayer->RealizeRoutingProxy();
   }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_Start(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioPlayer_Start(JNIEnv*, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_Start() ...");
   nativePlayer->Start();
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_Stop(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioPlayer_Stop(JNIEnv*, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_Stop() ...");
   nativePlayer->Stop();
 }
 
-JNIEXPORT jobject JNICALL Java_com_android_ndkaudio_AudioPlayer_GetRoutingInterface(JNIEnv*, jobject) {
+JNIEXPORT jobject JNICALL Java_android_media_audio_cts_AudioPlayer_GetRoutingInterface(JNIEnv*, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_GetRoutingInterface() ...");
 
   SLAndroidConfigurationItf configItf = nativePlayer->getConfigItf();
@@ -86,18 +84,18 @@
   return routingObj;
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_ReleaseRoutingInterface(JNIEnv*, jobject, jobject /*proxyObj*/) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioPlayer_ReleaseRoutingInterface(JNIEnv*, jobject, jobject /*proxyObj*/) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_ReleaseRoutingInterface() ...");
 
   SLAndroidConfigurationItf configItf = nativePlayer->getConfigItf();
   lastSLResult = (*configItf)->ReleaseJavaProxy(configItf, SL_ANDROID_JAVA_PROXY_ROUTING/*, proxyObj*/);
 }
 
-JNIEXPORT jlong JNICALL Java_com_android_ndkaudio_AudioPlayer_GetLastSLResult(JNIEnv*, jobject) {
+JNIEXPORT jlong JNICALL Java_android_media_audio_cts_AudioPlayer_GetLastSLResult(JNIEnv*, jobject) {
     return lastSLResult;
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_ClearLastSLResult(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioPlayer_ClearLastSLResult(JNIEnv*, jobject) {
     lastSLResult = 0;
 }
 
diff --git a/tests/tests/media/libndkaudio/AudioRecorder.cpp b/tests/tests/media/audio/jni/AudioRecorder.cpp
similarity index 100%
rename from tests/tests/media/libndkaudio/AudioRecorder.cpp
rename to tests/tests/media/audio/jni/AudioRecorder.cpp
diff --git a/tests/tests/media/libndkaudio/AudioRecorder.h b/tests/tests/media/audio/jni/AudioRecorder.h
similarity index 100%
rename from tests/tests/media/libndkaudio/AudioRecorder.h
rename to tests/tests/media/audio/jni/AudioRecorder.h
diff --git a/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioRecorder.cpp b/tests/tests/media/audio/jni/AudioRecorder_jni.cpp
similarity index 65%
rename from tests/tests/media/libndkaudio/com_android_ndkaudio_AudioRecorder.cpp
rename to tests/tests/media/audio/jni/AudioRecorder_jni.cpp
index f4dcc20..91ed35b 100644
--- a/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioRecorder.cpp
+++ b/tests/tests/media/audio/jni/AudioRecorder_jni.cpp
@@ -15,13 +15,11 @@
  */
 #include <android/log.h>
 
-#include "com_android_ndkaudio_AudioRecorder.h"
-
 #include "AudioRecorder.h"
 
 using namespace ndkaudio;
 
-static const char* TAG = "_com_android_ndkaudio_AudioRecorder_";
+static const char* TAG = "AudioRecorder_jni";
 
 static int numChannels = 2;
 
@@ -30,7 +28,7 @@
 static SLresult lastSLResult = 0;
 extern "C" {
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_Create(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioRecorder_Create(JNIEnv*, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioRecorder_Create() ...");
   if (nativeRecorder == 0) {
       nativeRecorder = new AudioRecorder();
@@ -38,34 +36,34 @@
   nativeRecorder->Open(numChannels, 0);
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_Destroy(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioRecorder_Destroy(JNIEnv*, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioRecorder_Destroy() ...");
   nativeRecorder->Close();
   delete nativeRecorder;
   nativeRecorder = 0;
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_RealizeRecorder(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioRecorder_RealizeRecorder(JNIEnv*, jobject) {
     __android_log_print(ANDROID_LOG_INFO, TAG, "AudioRecorder_RealizePlayer() ...");
     nativeRecorder->RealizeRecorder();
   }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_RealizeRoutingProxy(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioRecorder_RealizeRoutingProxy(JNIEnv*, jobject) {
     __android_log_print(ANDROID_LOG_INFO, TAG, "RealizeRoutingProxy ...");
     nativeRecorder->RealizeRoutingProxy();
   }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_Start(JNIEnv *, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioRecorder_Start(JNIEnv *, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioRecorder_Start() ...");
   nativeRecorder->Start();
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_Stop(JNIEnv *, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioRecorder_Stop(JNIEnv *, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioRecorder_Stop() ...");
   nativeRecorder->Stop();
 }
 
-JNIEXPORT jobject JNICALL Java_com_android_ndkaudio_AudioRecorder_GetRoutingInterface(JNIEnv*, jobject) {
+JNIEXPORT jobject JNICALL Java_android_media_audio_cts_AudioRecorder_GetRoutingInterface(JNIEnv*, jobject) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_GetRoutingObj() ...");
 
   SLAndroidConfigurationItf configItf = nativeRecorder->getConfigItf();
@@ -75,29 +73,29 @@
   return routingObj;
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_ReleaseRoutingInterface(JNIEnv*, jobject, jobject /*proxyObj*/) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioRecorder_ReleaseRoutingInterface(JNIEnv*, jobject, jobject /*proxyObj*/) {
   __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayer_ReleaseRoutingInterface() ...");
 
   SLAndroidConfigurationItf configItf = nativeRecorder->getConfigItf();
   lastSLResult = (*configItf)->ReleaseJavaProxy(configItf, SL_ANDROID_JAVA_PROXY_ROUTING/*, proxyObj*/);
 }
 
-JNIEXPORT jint JNICALL Java_com_android_ndkaudio_AudioRecorder_GetNumBufferSamples(JNIEnv*, jobject) {
+JNIEXPORT jint JNICALL Java_android_media_audio_cts_AudioRecorder_GetNumBufferSamples(JNIEnv*, jobject) {
     return nativeRecorder->GetNumBufferSamples();
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_GetBufferData(JNIEnv* jEnv, jobject, jfloatArray j_data) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioRecorder_GetBufferData(JNIEnv* jEnv, jobject, jfloatArray j_data) {
     float* dataBuffer = nativeRecorder->GetRecordBuffer();
     if (dataBuffer != 0) {
         jEnv->SetFloatArrayRegion(j_data, 0, nativeRecorder->GetNumBufferSamples(), dataBuffer);
     }
 }
 
-JNIEXPORT jlong JNICALL Java_com_android_ndkaudio_AudioRecorder_GetLastSLResult(JNIEnv*, jobject) {
+JNIEXPORT jlong JNICALL Java_android_media_audio_cts_AudioRecorder_GetLastSLResult(JNIEnv*, jobject) {
     return lastSLResult;
 }
 
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_ClearLastSLResult(JNIEnv*, jobject) {
+JNIEXPORT void JNICALL Java_android_media_audio_cts_AudioRecorder_ClearLastSLResult(JNIEnv*, jobject) {
     lastSLResult = 0;
 }
 
diff --git a/tests/tests/media/libndkaudio/AudioSink.h b/tests/tests/media/audio/jni/AudioSink.h
similarity index 100%
rename from tests/tests/media/libndkaudio/AudioSink.h
rename to tests/tests/media/audio/jni/AudioSink.h
diff --git a/tests/tests/media/libndkaudio/AudioSource.cpp b/tests/tests/media/audio/jni/AudioSource.cpp
similarity index 100%
rename from tests/tests/media/libndkaudio/AudioSource.cpp
rename to tests/tests/media/audio/jni/AudioSource.cpp
diff --git a/tests/tests/media/libndkaudio/AudioSource.h b/tests/tests/media/audio/jni/AudioSource.h
similarity index 100%
rename from tests/tests/media/libndkaudio/AudioSource.h
rename to tests/tests/media/audio/jni/AudioSource.h
diff --git a/tests/tests/media/libaudiojni/Blob.h b/tests/tests/media/audio/jni/Blob.h
similarity index 100%
rename from tests/tests/media/libaudiojni/Blob.h
rename to tests/tests/media/audio/jni/Blob.h
diff --git a/tests/tests/media/libaudiojni/Gate.h b/tests/tests/media/audio/jni/Gate.h
similarity index 100%
rename from tests/tests/media/libaudiojni/Gate.h
rename to tests/tests/media/audio/jni/Gate.h
diff --git a/tests/tests/media/libndkaudio/OpenSLESUtils.cpp b/tests/tests/media/audio/jni/OpenSLESUtils.cpp
similarity index 100%
rename from tests/tests/media/libndkaudio/OpenSLESUtils.cpp
rename to tests/tests/media/audio/jni/OpenSLESUtils.cpp
diff --git a/tests/tests/media/libndkaudio/OpenSLESUtils.h b/tests/tests/media/audio/jni/OpenSLESUtils.h
similarity index 100%
rename from tests/tests/media/libndkaudio/OpenSLESUtils.h
rename to tests/tests/media/audio/jni/OpenSLESUtils.h
diff --git a/tests/tests/media/libndkaudio/PeriodicAudioSource.cpp b/tests/tests/media/audio/jni/PeriodicAudioSource.cpp
similarity index 100%
rename from tests/tests/media/libndkaudio/PeriodicAudioSource.cpp
rename to tests/tests/media/audio/jni/PeriodicAudioSource.cpp
diff --git a/tests/tests/media/libndkaudio/PeriodicAudioSource.h b/tests/tests/media/audio/jni/PeriodicAudioSource.h
similarity index 100%
rename from tests/tests/media/libndkaudio/PeriodicAudioSource.h
rename to tests/tests/media/audio/jni/PeriodicAudioSource.h
diff --git a/tests/tests/media/libndkaudio/SystemParams.cpp b/tests/tests/media/audio/jni/SystemParams.cpp
similarity index 100%
rename from tests/tests/media/libndkaudio/SystemParams.cpp
rename to tests/tests/media/audio/jni/SystemParams.cpp
diff --git a/tests/tests/media/libndkaudio/SystemParams.h b/tests/tests/media/audio/jni/SystemParams.h
similarity index 100%
rename from tests/tests/media/libndkaudio/SystemParams.h
rename to tests/tests/media/audio/jni/SystemParams.h
diff --git a/tests/tests/media/libndkaudio/WaveTableGenerator.cpp b/tests/tests/media/audio/jni/WaveTableGenerator.cpp
similarity index 100%
rename from tests/tests/media/libndkaudio/WaveTableGenerator.cpp
rename to tests/tests/media/audio/jni/WaveTableGenerator.cpp
diff --git a/tests/tests/media/libndkaudio/WaveTableGenerator.h b/tests/tests/media/audio/jni/WaveTableGenerator.h
similarity index 100%
rename from tests/tests/media/libndkaudio/WaveTableGenerator.h
rename to tests/tests/media/audio/jni/WaveTableGenerator.h
diff --git a/tests/tests/media/libndkaudio/WaveTableOscillator.cpp b/tests/tests/media/audio/jni/WaveTableOscillator.cpp
similarity index 100%
rename from tests/tests/media/libndkaudio/WaveTableOscillator.cpp
rename to tests/tests/media/audio/jni/WaveTableOscillator.cpp
diff --git a/tests/tests/media/libndkaudio/WaveTableOscillator.h b/tests/tests/media/audio/jni/WaveTableOscillator.h
similarity index 100%
rename from tests/tests/media/libndkaudio/WaveTableOscillator.h
rename to tests/tests/media/audio/jni/WaveTableOscillator.h
diff --git a/tests/tests/media/libaudiojni/audio-record-native.cpp b/tests/tests/media/audio/jni/audio-record-native.cpp
similarity index 94%
rename from tests/tests/media/libaudiojni/audio-record-native.cpp
rename to tests/tests/media/audio/jni/audio-record-native.cpp
index 07e71e0..efa2979 100644
--- a/tests/tests/media/libaudiojni/audio-record-native.cpp
+++ b/tests/tests/media/audio/jni/audio-record-native.cpp
@@ -39,7 +39,7 @@
 
 using namespace android;
 
-// Must be kept in sync with Java android.media.cts.AudioRecordNative.ReadFlags
+// Must be kept in sync with Java android.media.audio.cts.AudioRecordNative.ReadFlags
 enum {
     READ_FLAG_BLOCKING = (1 << 0),
 };
@@ -463,7 +463,7 @@
  * to be passed in.
  */
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeTest(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeTest(
         JNIEnv * /* env */, jclass /* clazz */,
         jint numChannels, jint channelMask, jint sampleRate,
         jboolean useFloat, jint msecPerBuffer, jint numBuffers) {
@@ -498,19 +498,19 @@
     return res;
 }
 
-extern "C" jlong Java_android_media_cts_AudioRecordNative_nativeCreateRecord(
+extern "C" jlong Java_android_media_audio_cts_AudioRecordNative_nativeCreateRecord(
     JNIEnv * /* env */, jclass /* clazz */)
 {
     return (jlong)(new shared_pointer<AudioRecordNative>(new AudioRecordNative()));
 }
 
-extern "C" void Java_android_media_cts_AudioRecordNative_nativeDestroyRecord(
+extern "C" void Java_android_media_audio_cts_AudioRecordNative_nativeDestroyRecord(
     JNIEnv * /* env */, jclass /* clazz */, jlong jrecord)
 {
     delete (shared_pointer<AudioRecordNative> *)jrecord;
 }
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeOpen(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeOpen(
         JNIEnv * /* env */, jclass /* clazz */, jlong jrecord,
         jint numChannels, jint channelMask, jint sampleRate, jboolean useFloat, jint numBuffers)
 {
@@ -522,7 +522,7 @@
             numBuffers);
 }
 
-extern "C" void Java_android_media_cts_AudioRecordNative_nativeClose(
+extern "C" void Java_android_media_audio_cts_AudioRecordNative_nativeClose(
     JNIEnv * /* env */, jclass /* clazz */, jlong jrecord)
 {
     auto record = *(shared_pointer<AudioRecordNative> *)jrecord;
@@ -531,7 +531,7 @@
     }
 }
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeStart(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeStart(
     JNIEnv * /* env */, jclass /* clazz */, jlong jrecord)
 {
     auto record = *(shared_pointer<AudioRecordNative> *)jrecord;
@@ -541,7 +541,7 @@
     return (jint)record->start();
 }
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeStop(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeStop(
     JNIEnv * /* env */, jclass /* clazz */, jlong jrecord)
 {
     auto record = *(shared_pointer<AudioRecordNative> *)jrecord;
@@ -551,7 +551,7 @@
     return (jint)record->stop();
 }
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativePause(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativePause(
     JNIEnv * /* env */, jclass /* clazz */, jlong jrecord)
 {
     auto record = *(shared_pointer<AudioRecordNative> *)jrecord;
@@ -561,7 +561,7 @@
     return (jint)record->pause();
 }
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeFlush(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeFlush(
     JNIEnv * /* env */, jclass /* clazz */, jlong jrecord)
 {
     auto record = *(shared_pointer<AudioRecordNative> *)jrecord;
@@ -571,7 +571,7 @@
     return (jint)record->flush();
 }
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeGetPositionInMsec(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeGetPositionInMsec(
     JNIEnv *env, jclass /* clazz */, jlong jrecord, jlongArray jPosition)
 {
     auto record = *(shared_pointer<AudioRecordNative> *)jrecord;
@@ -594,7 +594,7 @@
 }
 
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeGetBuffersPending(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeGetBuffersPending(
     JNIEnv * /* env */, jclass /* clazz */, jlong jrecord)
 {
     auto record = *(shared_pointer<AudioRecordNative> *)jrecord;
@@ -604,7 +604,7 @@
     return (jint)record->getBuffersPending();
 }
 
-extern "C" jobject Java_android_media_cts_AudioRecordNative_nativeGetRoutingInterface(
+extern "C" jobject Java_android_media_audio_cts_AudioRecordNative_nativeGetRoutingInterface(
     JNIEnv * /* env */, jclass /* clazz */, jlong jrecord)
 {
     auto record = *(shared_pointer<AudioRecordNative> *)jrecord;
@@ -648,21 +648,21 @@
     return ret;
 }
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeReadByteArray(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeReadByteArray(
     JNIEnv *env, jclass clazz, jlong jrecord,
     jbyteArray byteArray, jint offsetInSamples, jint sizeInSamples, jint readFlags)
 {
     return readArray(env, clazz, jrecord, byteArray, offsetInSamples, sizeInSamples, readFlags);
 }
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeReadShortArray(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeReadShortArray(
     JNIEnv *env, jclass clazz, jlong jrecord,
     jshortArray shortArray, jint offsetInSamples, jint sizeInSamples, jint readFlags)
 {
     return readArray(env, clazz, jrecord, shortArray, offsetInSamples, sizeInSamples, readFlags);
 }
 
-extern "C" jint Java_android_media_cts_AudioRecordNative_nativeReadFloatArray(
+extern "C" jint Java_android_media_audio_cts_AudioRecordNative_nativeReadFloatArray(
     JNIEnv *env, jclass clazz, jlong jrecord,
     jfloatArray floatArray, jint offsetInSamples, jint sizeInSamples, jint readFlags)
 {
diff --git a/tests/tests/media/libaudiojni/audio-track-native.cpp b/tests/tests/media/audio/jni/audio-track-native.cpp
similarity index 94%
rename from tests/tests/media/libaudiojni/audio-track-native.cpp
rename to tests/tests/media/audio/jni/audio-track-native.cpp
index ce8bf91..65f9e21 100644
--- a/tests/tests/media/libaudiojni/audio-track-native.cpp
+++ b/tests/tests/media/audio/jni/audio-track-native.cpp
@@ -374,7 +374,7 @@
  * to be passed in.
  */
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeTest(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeTest(
     JNIEnv * /* env */, jclass /* clazz */,
     jint numChannels, jint channelMask, jint sampleRate, jboolean useFloat,
     jint msecPerBuffer, jint numBuffers)
@@ -411,19 +411,19 @@
     return res;
 }
 
-extern "C" jlong Java_android_media_cts_AudioTrackNative_nativeCreateTrack(
+extern "C" jlong Java_android_media_audio_cts_AudioTrackNative_nativeCreateTrack(
     JNIEnv * /* env */, jclass /* clazz */)
 {
     return (jlong)(new shared_pointer<AudioTrackNative>(new AudioTrackNative()));
 }
 
-extern "C" void Java_android_media_cts_AudioTrackNative_nativeDestroyTrack(
+extern "C" void Java_android_media_audio_cts_AudioTrackNative_nativeDestroyTrack(
     JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
 {
     delete (shared_pointer<AudioTrackNative> *)jtrack;
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeOpen(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeOpen(
     JNIEnv * /* env */, jclass /* clazz */, jlong jtrack,
     jint numChannels, jint channelMask, jint sampleRate,
     jboolean useFloat, jint numBuffers)
@@ -439,7 +439,7 @@
                               numBuffers);
 }
 
-extern "C" void Java_android_media_cts_AudioTrackNative_nativeClose(
+extern "C" void Java_android_media_audio_cts_AudioTrackNative_nativeClose(
     JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
 {
     auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
@@ -448,7 +448,7 @@
     }
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeStart(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeStart(
     JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
 {
     auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
@@ -458,7 +458,7 @@
     return (jint)track->start();
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeStop(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeStop(
     JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
 {
     auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
@@ -468,7 +468,7 @@
     return (jint)track->stop();
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativePause(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativePause(
     JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
 {
     auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
@@ -478,7 +478,7 @@
     return (jint)track->pause();
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeFlush(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeFlush(
     JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
 {
     auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
@@ -488,7 +488,7 @@
     return (jint)track->flush();
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeGetPositionInMsec(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeGetPositionInMsec(
     JNIEnv *env, jclass /* clazz */, jlong jtrack, jlongArray jPosition)
 {
     auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
@@ -510,7 +510,7 @@
     return OK;
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeGetBuffersPending(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeGetBuffersPending(
     JNIEnv * /* env */, jclass /* clazz */, jlong jtrack)
 {
     auto track = *(shared_pointer<AudioTrackNative> *)jtrack;
@@ -554,7 +554,7 @@
     return ret;
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeWriteByteArray(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeWriteByteArray(
     JNIEnv *env, jclass clazz, jlong jtrack,
     jbyteArray byteArray, jint offsetInSamples, jint sizeInSamples, jint writeFlags)
 {
@@ -563,7 +563,7 @@
     return writeArray(env, clazz, jtrack, byteArray, offsetInSamples, sizeInSamples, writeFlags);
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeWriteShortArray(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeWriteShortArray(
     JNIEnv *env, jclass clazz, jlong jtrack,
     jshortArray shortArray, jint offsetInSamples, jint sizeInSamples, jint writeFlags)
 {
@@ -572,7 +572,7 @@
     return writeArray(env, clazz, jtrack, shortArray, offsetInSamples, sizeInSamples, writeFlags);
 }
 
-extern "C" jint Java_android_media_cts_AudioTrackNative_nativeWriteFloatArray(
+extern "C" jint Java_android_media_audio_cts_AudioTrackNative_nativeWriteFloatArray(
     JNIEnv *env, jclass clazz, jlong jtrack,
     jfloatArray floatArray, jint offsetInSamples, jint sizeInSamples, jint writeFlags)
 {
diff --git a/tests/tests/media/libaudiojni/sl-utils.cpp b/tests/tests/media/audio/jni/sl-utils.cpp
similarity index 100%
rename from tests/tests/media/libaudiojni/sl-utils.cpp
rename to tests/tests/media/audio/jni/sl-utils.cpp
diff --git a/tests/tests/media/libaudiojni/sl-utils.h b/tests/tests/media/audio/jni/sl-utils.h
similarity index 100%
rename from tests/tests/media/libaudiojni/sl-utils.h
rename to tests/tests/media/audio/jni/sl-utils.h
diff --git a/tests/tests/media/audio/res/raw/john_cage.ogg b/tests/tests/media/audio/res/raw/john_cage.ogg
new file mode 100644
index 0000000..62d2335
--- /dev/null
+++ b/tests/tests/media/audio/res/raw/john_cage.ogg
Binary files differ
diff --git a/tests/tests/media/audio/res/raw/sine1320hz5sec.wav b/tests/tests/media/audio/res/raw/sine1320hz5sec.wav
new file mode 100644
index 0000000..22fde48
--- /dev/null
+++ b/tests/tests/media/audio/res/raw/sine1320hz5sec.wav
Binary files differ
diff --git a/tests/tests/media/audio/res/raw/sinesweepraw.raw b/tests/tests/media/audio/res/raw/sinesweepraw.raw
new file mode 100644
index 0000000..c0d48ce
--- /dev/null
+++ b/tests/tests/media/audio/res/raw/sinesweepraw.raw
Binary files differ
diff --git a/tests/tests/media/audio/res/raw/testmp3_2.mp3 b/tests/tests/media/audio/res/raw/testmp3_2.mp3
new file mode 100644
index 0000000..6a70c69
--- /dev/null
+++ b/tests/tests/media/audio/res/raw/testmp3_2.mp3
Binary files differ
diff --git a/tests/tests/media/audio/res/raw/voice12_32k_128kbps_15s_ac3_spdif.raw b/tests/tests/media/audio/res/raw/voice12_32k_128kbps_15s_ac3_spdif.raw
new file mode 100644
index 0000000..a2610bd
--- /dev/null
+++ b/tests/tests/media/audio/res/raw/voice12_32k_128kbps_15s_ac3_spdif.raw
Binary files differ
diff --git a/tests/tests/media/audio/res/raw/voice12_44k_128kbps_15s_ac3_spdif.raw b/tests/tests/media/audio/res/raw/voice12_44k_128kbps_15s_ac3_spdif.raw
new file mode 100644
index 0000000..4023a5c
--- /dev/null
+++ b/tests/tests/media/audio/res/raw/voice12_44k_128kbps_15s_ac3_spdif.raw
Binary files differ
diff --git a/tests/tests/media/audio/res/raw/voice12_48k_128kbps_15s_ac3.raw b/tests/tests/media/audio/res/raw/voice12_48k_128kbps_15s_ac3.raw
new file mode 100644
index 0000000..e8b46af
--- /dev/null
+++ b/tests/tests/media/audio/res/raw/voice12_48k_128kbps_15s_ac3.raw
Binary files differ
diff --git a/tests/tests/media/audio/res/raw/voice12_48k_128kbps_15s_ac3_readme.txt b/tests/tests/media/audio/res/raw/voice12_48k_128kbps_15s_ac3_readme.txt
new file mode 100644
index 0000000..90ca3a0
--- /dev/null
+++ b/tests/tests/media/audio/res/raw/voice12_48k_128kbps_15s_ac3_readme.txt
@@ -0,0 +1,7 @@
+file=voice12_48k_128kbps_15s.raw
+author=Phil Burk
+copyright=2016 Google LLC
+license=Apache Open Source V2
+channels=2
+encoding=AC3
+rate=48000
diff --git a/tests/tests/media/audio/res/raw/voice12_48k_128kbps_15s_ac3_spdif.raw b/tests/tests/media/audio/res/raw/voice12_48k_128kbps_15s_ac3_spdif.raw
new file mode 100644
index 0000000..3215769
--- /dev/null
+++ b/tests/tests/media/audio/res/raw/voice12_48k_128kbps_15s_ac3_spdif.raw
Binary files differ
diff --git a/tests/tests/media/audio/res/xml/network_security_config.xml b/tests/tests/media/audio/res/xml/network_security_config.xml
deleted file mode 100644
index c15c09c..0000000
--- a/tests/tests/media/audio/res/xml/network_security_config.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<network-security-config>
-    <base-config cleartextTrafficPermitted="true"/>
-</network-security-config>
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioEffectTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioEffectTest.java
index 3e70863..392bf20 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioEffectTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioEffectTest.java
@@ -159,8 +159,11 @@
         MediaPlayer mp = new MediaPlayer();
         assertNotNull("could not create mediaplayer", mp);
         AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(R.raw.testmp3);
-        mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
-        afd.close();
+        try {
+            mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+        } finally {
+            afd.close();
+        }
         getEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, mp.getAudioSessionId());
         try {
             mEffect.setEnabled(true);
@@ -185,10 +188,13 @@
 
             mError = 0;
             AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(R.raw.testmp3);
-            mMediaPlayer.setDataSource(afd.getFileDescriptor(),
-                                       afd.getStartOffset(),
-                                       afd.getLength());
-            afd.close();
+            try {
+                mMediaPlayer.setDataSource(afd.getFileDescriptor(),
+                                           afd.getStartOffset(),
+                                           afd.getLength());
+            } finally {
+                afd.close();
+            }
             getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
             try {
                 try {
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioFocusTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioFocusTest.java
index d687df1..f84e680 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioFocusTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioFocusTest.java
@@ -18,26 +18,25 @@
 
 import android.Manifest;
 import android.annotation.Nullable;
+import android.annotation.RawRes;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
 import android.media.AudioAttributes;
 import android.media.AudioFocusRequest;
 import android.media.AudioManager;
 import android.media.AudioManager.OnAudioFocusChangeListener;
 import android.media.MediaPlayer;
+import android.media.audio.cts.R;
 import android.media.cts.NonMediaMainlineTest;
 import android.media.cts.TestUtils;
-import android.net.Uri;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
 import com.android.compatibility.common.util.CtsAndroidTestCase;
 
-import java.io.File;
-
 @NonMediaMainlineTest
 public class AudioFocusTest extends CtsAndroidTestCase {
     private static final String TAG = "AudioFocusTest";
@@ -233,7 +232,6 @@
      * Test delayed focus loss after fade out
      * @throws Exception
      */
-    @AppModeFull(reason = "Instant apps cannot access the SD card")
     public void testAudioFocusRequestMediaGainLossWithPlayer() throws Exception {
         if (hasAutomotiveFeature(getContext())) {
             Log.i(TAG, "Test testAudioFocusRequestMediaGainLossWithPlayer "
@@ -272,9 +270,7 @@
         final String simFocusClientId = "fakeClientId";
         try {
             // set up the test conditions: a focus owner is playing media on a MediaPlayer
-            mp = createPreparedMediaPlayer(
-                    Uri.fromFile(new File(WorkDir.getMediaDirString() + "sine1khzs40dblong.mp3")),
-                    mediaAttributes);
+            mp = createPreparedMediaPlayer(R.raw.sine1khzs40dblong, mediaAttributes);
             int res = am.requestAudioFocus(focusRequests[FOCUS_UNDER_TEST]);
             assertEquals("real focus request failed",
                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -315,7 +311,6 @@
      * Test there is no delayed focus loss when focus loser is playing speech
      * @throws Exception
      */
-    @AppModeFull(reason = "Instant apps cannot access the SD card")
     public void testAudioFocusRequestMediaGainLossWithSpeechPlayer() throws Exception {
         if (hasAutomotiveFeature(getContext())) {
             Log.i(TAG, "Test testAudioFocusRequestMediaGainLossWithSpeechPlayer "
@@ -333,7 +328,6 @@
      * AudioAttributes with speech content type
      * @throws Exception
      */
-    @AppModeFull(reason = "Instant apps cannot access the SD card")
     public void testAudioFocusRequestMediaGainLossWithSpeechFocusRequest() throws Exception {
         if (hasAutomotiveFeature(getContext())) {
             Log.i(TAG, "Test testAudioFocusRequestMediaGainLossWithSpeechPlayer "
@@ -351,7 +345,6 @@
      * it pauses on duck
      * @throws Exception
      */
-    @AppModeFull(reason = "Instant apps cannot access the SD card")
     public void testAudioFocusRequestMediaGainLossWithPauseOnDuckFocusRequest() throws Exception {
         if (hasAutomotiveFeature(getContext())) {
             Log.i(TAG, "Test testAudioFocusRequestMediaGainLossWithSpeechPlayer "
@@ -408,9 +401,7 @@
         final String simFocusClientId = "fakeClientId";
         try {
             // set up the test conditions: a focus owner is playing media on a MediaPlayer
-            mp = createPreparedMediaPlayer(
-                    Uri.fromFile(new File(WorkDir.getMediaDirString() + "sine1khzs40dblong.mp3")),
-                    playerAttributes);
+            mp = createPreparedMediaPlayer(R.raw.sine1khzs40dblong, playerAttributes);
             int res = am.requestAudioFocus(focusRequests[FOCUS_UNDER_TEST]);
             assertEquals("real focus request failed",
                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
@@ -555,12 +546,17 @@
     }
 
     private @Nullable MediaPlayer createPreparedMediaPlayer(
-            Uri uri, AudioAttributes aa) throws Exception {
+            @RawRes int resID, AudioAttributes aa) throws Exception {
         final TestUtils.Monitor onPreparedCalled = new TestUtils.Monitor();
 
         MediaPlayer mp = new MediaPlayer();
         mp.setAudioAttributes(aa);
-        mp.setDataSource(getContext(), uri);
+        AssetFileDescriptor afd = getContext().getResources().openRawResourceFd(resID);
+        try {
+            mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+        } finally {
+            afd.close();
+        }
         mp.setOnPreparedListener(mp1 -> onPreparedCalled.signal());
         mp.prepare();
         onPreparedCalled.waitForSignal(MEDIAPLAYER_PREPARE_TIMEOUT_MS);
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioManagerTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioManagerTest.java
index 21d7991..fa7308f 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioManagerTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioManagerTest.java
@@ -63,9 +63,11 @@
 import android.media.MediaPlayer;
 import android.media.MediaRecorder;
 import android.media.MicrophoneInfo;
+import android.media.audio.cts.R;
 import android.media.audiopolicy.AudioProductStrategy;
 import android.media.cts.NonMediaMainlineTest;
 import android.media.cts.Utils;
+
 import android.os.Build;
 import android.os.SystemClock;
 import android.os.Vibrator;
@@ -1016,6 +1018,11 @@
     }
 
     private void testStreamMuting(int stream) {
+        getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(Manifest.permission.QUERY_AUDIO_STATE);
+
+        final int streamVolume = mAudioManager.getLastAudibleStreamVolume(stream);
+
         // Voice call requires MODIFY_PHONE_STATE, so we should not be able to mute
         if (stream == STREAM_VOICE_CALL) {
             mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
@@ -1026,22 +1033,32 @@
             assertTrue("Muting stream " + stream + " failed.",
                     mAudioManager.isStreamMute(stream));
 
+            assertEquals(streamVolume, mAudioManager.getLastAudibleStreamVolume(stream));
+
             mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_UNMUTE, 0);
             assertFalse("Unmuting stream " + stream + " failed.",
                     mAudioManager.isStreamMute(stream));
 
+            assertEquals(streamVolume, mAudioManager.getLastAudibleStreamVolume(stream));
+
             mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE, 0);
             assertTrue("Toggling mute on stream " + stream + " failed.",
                     mAudioManager.isStreamMute(stream));
 
+            assertEquals(streamVolume, mAudioManager.getLastAudibleStreamVolume(stream));
+
             mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE, 0);
             assertFalse("Toggling mute on stream " + stream + " failed.",
                     mAudioManager.isStreamMute(stream));
 
+            assertEquals(streamVolume, mAudioManager.getLastAudibleStreamVolume(stream));
+
             mAudioManager.setStreamMute(stream, true);
             assertTrue("Muting stream " + stream + " using setStreamMute failed",
                     mAudioManager.isStreamMute(stream));
 
+            assertEquals(streamVolume, mAudioManager.getLastAudibleStreamVolume(stream));
+
             // mute it three more times to verify the ref counting is gone.
             mAudioManager.setStreamMute(stream, true);
             mAudioManager.setStreamMute(stream, true);
@@ -1051,6 +1068,9 @@
             assertFalse("Unmuting stream " + stream + " using setStreamMute failed.",
                     mAudioManager.isStreamMute(stream));
         }
+        assertEquals(streamVolume, mAudioManager.getLastAudibleStreamVolume(stream));
+
+        getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
     }
 
     public void testSetInvalidRingerMode() {
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioNativeTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioNativeTest.java
index 5004e8e..1fbabbf 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioNativeTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioNativeTest.java
@@ -24,14 +24,14 @@
 import android.media.AudioManager;
 import android.media.AudioRouting;
 import android.media.cts.AudioHelper;
-import android.media.cts.AudioRecordNative;
-import android.media.cts.AudioTrackNative;
 import android.media.cts.NonMediaMainlineTest;
 import android.os.Build;
 
 import com.android.compatibility.common.util.ApiLevelUtil;
 import com.android.compatibility.common.util.CtsAndroidTestCase;
 
+import org.junit.Assert;
+
 @NonMediaMainlineTest
 public class AudioNativeTest extends CtsAndroidTestCase {
     public static final int MAX_CHANNEL_COUNT = 2;
@@ -213,7 +213,7 @@
         if (!hasMicrophone()) {
             return;
         }
-        AudioRecordNative record = new AudioHelper.AudioRecordAuditNative();
+        AudioRecordNative record = new AudioRecordAuditNative();
         doRecordTest(record, 4 /* numChannels */, 44100 /* sampleRate */, false /* useFloat */,
                 1000 /* segmentDurationMs */, 10 /* numSegments */);
     }
@@ -386,4 +386,78 @@
 
     private static native void nativeAppendixBBufferQueue();
     private static native void nativeAppendixBRecording();
+
+    /* AudioRecordAudit extends AudioRecord to allow concurrent playback
+     * of read content to an AudioTrack.  This is for testing only.
+     * For general applications, it is NOT recommended to extend AudioRecord.
+     * This affects AudioRecord timing.
+     */
+    public static class AudioRecordAuditNative extends AudioRecordNative {
+        public AudioRecordAuditNative() {
+            super();
+            // Caution: delayMs too large results in buffer sizes that cannot be created.
+            mTrack = new AudioTrackNative();
+        }
+
+        public boolean open(int numChannels, int sampleRate, boolean useFloat, int numBuffers) {
+            if (super.open(numChannels, sampleRate, useFloat, numBuffers)) {
+                if (!mTrack.open(numChannels, sampleRate, useFloat, 2 /* numBuffers */)) {
+                    mTrack = null; // remove track
+                }
+                return true;
+            }
+            return false;
+        }
+
+        public void close() {
+            super.close();
+            if (mTrack != null) {
+                mTrack.close();
+            }
+        }
+
+        public boolean start() {
+            if (super.start()) {
+                if (mTrack != null) {
+                    mTrack.start();
+                }
+                return true;
+            }
+            return false;
+        }
+
+        public boolean stop() {
+            if (super.stop()) {
+                if (mTrack != null) {
+                    mTrack.stop(); // doesn't allow remaining data to play out
+                }
+                return true;
+            }
+            return false;
+        }
+
+        public int read(short[] audioData, int offsetInShorts, int sizeInShorts, int readFlags) {
+            int samples = super.read(audioData, offsetInShorts, sizeInShorts, readFlags);
+            if (mTrack != null) {
+                Assert.assertEquals(samples, mTrack.write(audioData, offsetInShorts, samples,
+                        AudioTrackNative.WRITE_FLAG_BLOCKING));
+                mPosition += samples / mTrack.getChannelCount();
+            }
+            return samples;
+        }
+
+        public int read(float[] audioData, int offsetInFloats, int sizeInFloats, int readFlags) {
+            int samples = super.read(audioData, offsetInFloats, sizeInFloats, readFlags);
+            if (mTrack != null) {
+                Assert.assertEquals(samples, mTrack.write(audioData, offsetInFloats, samples,
+                        AudioTrackNative.WRITE_FLAG_BLOCKING));
+                mPosition += samples / mTrack.getChannelCount();
+            }
+            return samples;
+        }
+
+        public AudioTrackNative mTrack;
+        private final static String TAG = "AudioRecordAuditNative";
+        private int mPosition;
+    }
 }
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioPlayRoutingNative.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlayRoutingNative.java
index 2edb324..7bd4ada 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioPlayRoutingNative.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlayRoutingNative.java
@@ -26,7 +26,6 @@
 import android.util.Log;
 
 import com.android.compatibility.common.util.CtsAndroidTestCase;
-import com.android.ndkaudio.AudioPlayer;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -37,7 +36,7 @@
     private AudioManager mAudioManager;
 
     static {
-        System.loadLibrary("ndkaudioLib");
+        System.loadLibrary("audiocts_jni");
     }
 
     @Override
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackCaptureTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackCaptureTest.java
index cce07a5..4ab67a5 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackCaptureTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackCaptureTest.java
@@ -40,6 +40,7 @@
 import android.media.AudioRecord;
 import android.media.cts.MediaProjectionActivity;
 import android.media.MediaPlayer;
+import android.media.audio.cts.R;
 import android.media.projection.MediaProjection;
 import android.media.cts.NonMediaMainlineTest;
 import android.os.Handler;
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackConfigurationTest.java
index 810b973..d7a8e86 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlaybackConfigurationTest.java
@@ -21,38 +21,35 @@
 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM;
 
 import android.annotation.Nullable;
+import android.annotation.RawRes;
 import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
 import android.media.AudioAttributes;
 import android.media.AudioAttributes.CapturePolicy;
 import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
 import android.media.MediaPlayer;
 import android.media.SoundPool;
+import android.media.audio.cts.R;
 import android.media.cts.NonMediaMainlineTest;
-import android.media.cts.Preconditions;
 import android.media.cts.TestUtils;
-import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Parcel;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
 import com.android.compatibility.common.util.CtsAndroidTestCase;
 import com.android.internal.annotations.GuardedBy;
 
-import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
-@AppModeFull(reason = "Instant apps cannot access the SD card")
 @NonMediaMainlineTest
 public class AudioPlaybackConfigurationTest extends CtsAndroidTestCase {
     private final static String TAG = "AudioPlaybackConfigurationTest";
 
-    static final String mInpPrefix = WorkDir.getMediaDirString();
     private final static int TEST_TIMING_TOLERANCE_MS = 150;
     /** acceptable timeout for the time it takes for a prepared MediaPlayer to have an audio device
      * selected and reported when starting to play */
@@ -99,10 +96,7 @@
                 .setContentType(TEST_CONTENT)
                 .setAllowedCapturePolicy(ALLOW_CAPTURE_BY_NONE)
                 .build();
-        Preconditions.assertTestFileExists(mInpPrefix + "sine1khzs40dblong.mp3");
-        mMp = createPreparedMediaPlayer(
-                Uri.fromFile(new File(mInpPrefix + "sine1khzs40dblong.mp3")), aa,
-                am.generateAudioSessionId());
+        mMp = createPreparedMediaPlayer(R.raw.sine1khzs40dblong, aa, am.generateAudioSessionId());
         mMp.start();
         Thread.sleep(TEST_TIMING_TOLERANCE_MS);// waiting for playback to start
         List<AudioPlaybackConfiguration> configs = am.getActivePlaybackConfigurations();
@@ -149,10 +143,7 @@
         List<AudioPlaybackConfiguration> configs = am.getActivePlaybackConfigurations();
         final int nbActivePlayersBeforeStart = configs.size();
 
-        Preconditions.assertTestFileExists(mInpPrefix + "sine1khzs40dblong.mp3");
-        mMp = createPreparedMediaPlayer(
-                Uri.fromFile(new File(mInpPrefix + "sine1khzs40dblong.mp3")), aa,
-                am.generateAudioSessionId());
+        mMp = createPreparedMediaPlayer(R.raw.sine1khzs40dblong, aa, am.generateAudioSessionId());
         configs = am.getActivePlaybackConfigurations();
         assertEquals("inactive MediaPlayer, number of configs shouldn't have changed",
                 nbActivePlayersBeforeStart /*expected*/, configs.size());
@@ -218,10 +209,8 @@
                 .setContentType(TEST_CONTENT)
                 .build();
 
-        Preconditions.assertTestFileExists(mInpPrefix + "sine1khzs40dblong.mp3");
         try {
-            mMp =  createPreparedMediaPlayer(
-                    Uri.fromFile(new File(mInpPrefix + "sine1khzs40dblong.mp3")), aa,
+            mMp =  createPreparedMediaPlayer(R.raw.sine1khzs40dblong, aa,
                     am.generateAudioSessionId());
 
             am.registerAudioPlaybackCallback(callback, h /*handler*/);
@@ -284,10 +273,8 @@
                 .setContentType(TEST_CONTENT)
                 .build();
 
-        Preconditions.assertTestFileExists(mInpPrefix + "sine1khzs40dblong.mp3");
         try {
-            mMp = createPreparedMediaPlayer(
-                    Uri.fromFile(new File(mInpPrefix + "sine1khzs40dblong.mp3")), aa,
+            mMp = createPreparedMediaPlayer(R.raw.sine1khzs40dblong, aa,
                     am.generateAudioSessionId());
 
             am.registerAudioPlaybackCallback(callback, h /*handler*/);
@@ -359,8 +346,7 @@
                 }
             }
         });
-        Preconditions.assertTestFileExists(mInpPrefix +  "sine1320hz5sec.wav");
-        final int loadId = mSp.load(mInpPrefix + "sine1320hz5sec.wav", 1/*priority*/);
+        final int loadId = mSp.load(getContext(), R.raw.sine1320hz5sec, 1/*priority*/);
         synchronized (loadLock) {
             loadLock.wait(TEST_TIMEOUT_SOUNDPOOL_LOAD_MS);
         }
@@ -402,10 +388,8 @@
                 .setContentType(TEST_CONTENT)
                 .build();
 
-        Preconditions.assertTestFileExists(mInpPrefix +  "sine1khzs40dblong.mp3");
         try {
-            mMp = createPreparedMediaPlayer(
-                    Uri.fromFile(new File(mInpPrefix + "sine1khzs40dblong.mp3")), aa,
+            mMp = createPreparedMediaPlayer(R.raw.sine1khzs40dblong, aa,
                     am.generateAudioSessionId());
 
             am.registerAudioPlaybackCallback(callback, h /*handler*/);
@@ -430,9 +414,9 @@
     }
 
     private @Nullable MediaPlayer createPreparedMediaPlayer(
-            Uri uri, AudioAttributes aa, int session) throws Exception {
+            @RawRes int resID, AudioAttributes aa, int session) throws Exception {
         final TestUtils.Monitor onPreparedCalled = new TestUtils.Monitor();
-        final MediaPlayer mp = createPlayer(uri, aa, session);
+        final MediaPlayer mp = createPlayer(resID, aa, session);
         mp.setOnPreparedListener(mp1 -> onPreparedCalled.signal());
         mp.prepare();
         onPreparedCalled.waitForSignal(MEDIAPLAYER_PREPARE_TIMEOUT_MS);
@@ -443,11 +427,16 @@
     }
 
     private MediaPlayer createPlayer(
-            Uri uri, AudioAttributes aa, int session) throws IOException {
+            @RawRes int resID, AudioAttributes aa, int session) throws IOException {
         MediaPlayer mp = new MediaPlayer();
         mp.setAudioAttributes(aa);
         mp.setAudioSessionId(session);
-        mp.setDataSource(getContext(), uri);
+        AssetFileDescriptor afd = getContext().getResources().openRawResourceFd(resID);
+        try {
+            mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+        } finally {
+            afd.close();
+        }
         return mp;
     }
 
diff --git a/tests/tests/media/libndkaudio/src/com/android/ndkaudio/AudioPlayer.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlayer.java
similarity index 96%
rename from tests/tests/media/libndkaudio/src/com/android/ndkaudio/AudioPlayer.java
rename to tests/tests/media/audio/src/android/media/audio/cts/AudioPlayer.java
index f9cd109..dbea2ca 100644
--- a/tests/tests/media/libndkaudio/src/com/android/ndkaudio/AudioPlayer.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioPlayer.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.ndkaudio;
+package android.media.audio.cts;
 
 import android.media.AudioRouting;
 
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioPresentationTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioPresentationTest.java
index 10ad15d..af834f0 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioPresentationTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioPresentationTest.java
@@ -32,7 +32,7 @@
 @NonMediaMainlineTest
 public class AudioPresentationTest extends CtsAndroidTestCase {
     private String TAG = "AudioPresentationTest";
-    private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+    private static final String REPORT_LOG_NAME = "CtsMediaAudioTestCases";
 
     public void testGetters() throws Exception {
         final int PRESENTATION_ID = 42;
diff --git a/tests/tests/media/common/src/android/media/cts/AudioRecordNative.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordNative.java
similarity index 98%
rename from tests/tests/media/common/src/android/media/cts/AudioRecordNative.java
rename to tests/tests/media/audio/src/android/media/audio/cts/AudioRecordNative.java
index f6eb501..f1b6ed1 100644
--- a/tests/tests/media/common/src/android/media/cts/AudioRecordNative.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordNative.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.audio.cts;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -131,7 +131,7 @@
     }
 
     static {
-        System.loadLibrary("audio_jni");
+        System.loadLibrary("audiocts_jni");
     }
 
     private static final String TAG = "AudioRecordNative";
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordRoutingNative.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordRoutingNative.java
index f8b48de..f6fe1cd 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordRoutingNative.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordRoutingNative.java
@@ -27,8 +27,6 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-import com.android.ndkaudio.AudioRecorder;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -38,7 +36,7 @@
     private AudioManager mAudioManager;
 
     static {
-        System.loadLibrary("ndkaudioLib");
+        System.loadLibrary("audiocts_jni");
     }
 
     @Override
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordTest.java
index b16c5d7..ee5f0da 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioRecordTest.java
@@ -79,7 +79,7 @@
 @RunWith(AndroidJUnit4.class)
 public class AudioRecordTest {
     private final static String TAG = "AudioRecordTest";
-    private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+    private static final String REPORT_LOG_NAME = "CtsMediaAudioTestCases";
     private AudioRecord mAudioRecord;
     private static final int SAMPLING_RATE_HZ = 44100;
     private boolean mIsOnMarkerReachedCalled;
diff --git a/tests/tests/media/libndkaudio/src/com/android/ndkaudio/AudioRecorder.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioRecorder.java
similarity index 97%
rename from tests/tests/media/libndkaudio/src/com/android/ndkaudio/AudioRecorder.java
rename to tests/tests/media/audio/src/android/media/audio/cts/AudioRecorder.java
index 52d20c4..0899a18 100644
--- a/tests/tests/media/libndkaudio/src/com/android/ndkaudio/AudioRecorder.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioRecorder.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.ndkaudio;
+package android.media.audio.cts;
 
 import android.media.AudioRouting;
 
diff --git a/tests/tests/media/common/src/android/media/cts/AudioTrackNative.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackNative.java
similarity index 98%
rename from tests/tests/media/common/src/android/media/cts/AudioTrackNative.java
rename to tests/tests/media/audio/src/android/media/audio/cts/AudioTrackNative.java
index 065dd3a..3b6f3ea 100644
--- a/tests/tests/media/common/src/android/media/cts/AudioTrackNative.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackNative.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.audio.cts;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -133,7 +133,7 @@
     }
 
     static {
-        System.loadLibrary("audio_jni");
+        System.loadLibrary("audiocts_jni");
     }
 
     private static final String TAG = "AudioTrackNative";
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackOffloadTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackOffloadTest.java
index f7ef5c5..acc6724 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackOffloadTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackOffloadTest.java
@@ -24,6 +24,7 @@
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioTrack;
+import android.media.audio.cts.R;
 import android.media.cts.NonMediaMainlineTest;
 import android.os.SystemClock;
 import android.util.Log;
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackSurroundTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackSurroundTest.java
index fc9fe4e..c84b8e0 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackSurroundTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackSurroundTest.java
@@ -28,16 +28,14 @@
 import android.media.AudioManager;
 import android.media.AudioTimestamp;
 import android.media.AudioTrack;
+import android.media.audio.cts.R;
 import android.media.cts.NonMediaMainlineTest;
-import android.media.cts.Preconditions;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
 import com.android.compatibility.common.util.CtsAndroidTestCase;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -51,7 +49,6 @@
 // sample rate based on the AudioTimestamps.
 
 @NonMediaMainlineTest
-@AppModeFull(reason = "Instant apps cannot access the SD card")
 public class AudioTrackSurroundTest extends CtsAndroidTestCase {
     private static final String TAG = "AudioTrackSurroundTest";
 
@@ -75,10 +72,10 @@
     private final static int MILLIS_PER_SECOND = 1000;
     private final static long NANOS_PER_SECOND = NANOS_PER_MILLISECOND * MILLIS_PER_SECOND;
 
-    private final static String RES_AC3_SPDIF_VOICE_32000 = "voice12_32k_128kbps_15s_ac3_spdif.raw";
-    private final static String RES_AC3_SPDIF_VOICE_44100 = "voice12_44k_128kbps_15s_ac3_spdif.raw";
-    private final static String RES_AC3_SPDIF_VOICE_48000 = "voice12_48k_128kbps_15s_ac3_spdif.raw";
-    private final static String RES_AC3_VOICE_48000 = "voice12_48k_128kbps_15s_ac3.raw";
+    private final static int RES_AC3_SPDIF_VOICE_32000 = R.raw.voice12_32k_128kbps_15s_ac3_spdif;
+    private final static int RES_AC3_SPDIF_VOICE_44100 = R.raw.voice12_44k_128kbps_15s_ac3_spdif;
+    private final static int RES_AC3_SPDIF_VOICE_48000 = R.raw.voice12_48k_128kbps_15s_ac3_spdif;
+    private final static int RES_AC3_VOICE_48000 = R.raw.voice12_48k_128kbps_15s_ac3;
 
     private static int mLastPlayedEncoding = AudioFormat.ENCODING_INVALID;
 
@@ -173,10 +170,8 @@
     }
 
     // Load a resource into a byte[]
-    private byte[] loadRawResourceBytes(@RawRes final String res) throws Exception {
-        final String mInpPrefix = WorkDir.getMediaDirString();
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        InputStream is = new FileInputStream(mInpPrefix + res);
+    private byte[] loadRawResourceBytes(@RawRes int id) throws Exception {
+        InputStream is = getContext().getResources().openRawResource(id);
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         try (BufferedInputStream bis = new BufferedInputStream(is)) {
             for (int b = bis.read(); b != -1; b = bis.read()) {
@@ -187,8 +182,8 @@
     }
 
     // Load a resource into a short[]
-    private short[] loadRawResourceShorts(@RawRes final String res) throws Exception {
-        byte[] byteBuffer = loadRawResourceBytes(res);
+    private short[] loadRawResourceShorts(@RawRes int id) throws Exception {
+        byte[] byteBuffer = loadRawResourceBytes(id);
         ShortBuffer shortBuffer =
                 ByteBuffer.wrap(byteBuffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
         // Unfortunately, ShortBuffer.array() works with allocated buffers only.
@@ -201,9 +196,9 @@
 
     public void testLoadSineSweep() throws Exception {
         final String TEST_NAME = "testLoadSineSweep";
-        short[] shortData = loadRawResourceShorts("sinesweepraw.raw");
+        short[] shortData = loadRawResourceShorts(R.raw.sinesweepraw);
         assertTrue(TEST_NAME + ": load sinesweepraw as shorts", shortData.length > 100);
-        byte[] byteData = loadRawResourceBytes("sinesweepraw.raw");
+        byte[] byteData = loadRawResourceBytes(R.raw.sinesweepraw);
         assertTrue(TEST_NAME + ": load sinesweepraw as bytes", byteData.length > shortData.length);
     }
 
@@ -479,10 +474,10 @@
             }
         }
 
-        SamplePlayerShorts(int sampleRate, int encoding, int channelConfig,
-                @RawRes final String res) throws Exception {
+        SamplePlayerShorts(int sampleRate, int encoding, int channelConfig, @RawRes int resourceId)
+                throws Exception {
             super(sampleRate, encoding, channelConfig);
-            mData = loadRawResourceShorts(res);
+            mData = loadRawResourceShorts(resourceId);
             assertTrue("SamplePlayerShorts: load resource file as shorts", mData.length > 0);
         }
 
@@ -513,10 +508,10 @@
             mData = new byte[128 * 1024];
         }
 
-        SamplePlayerBytes(int sampleRate, int encoding, int channelConfig, @RawRes final String res)
+        SamplePlayerBytes(int sampleRate, int encoding, int channelConfig, @RawRes int resourceId)
                 throws Exception {
             super(sampleRate, encoding, channelConfig);
-            mData = loadRawResourceBytes(res);
+            mData = loadRawResourceBytes(resourceId);
             assertTrue("SamplePlayerBytes: load resource file as bytes", mData.length > 0);
         }
 
@@ -600,7 +595,7 @@
         if (isPcmTestingEnabled()) {
             SamplePlayerShorts player = new SamplePlayerShorts(
                     44100, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_OUT_STEREO,
-                    "sinesweepraw.raw");
+                    R.raw.sinesweepraw);
             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
         }
     }
@@ -609,7 +604,7 @@
         if (isPcmTestingEnabled()) {
             SamplePlayerBytes player = new SamplePlayerBytes(
                     44100, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_OUT_STEREO,
-                    "sinesweepraw.raw");
+                    R.raw.sinesweepraw);
             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
         }
     }
@@ -618,7 +613,7 @@
         if (isPcmTestingEnabled()) {
             SamplePlayerBytes player = new SamplePlayerBytes(
                     48000, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_OUT_STEREO,
-                    "sinesweepraw.raw");
+                    R.raw.sinesweepraw);
             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
         }
     }
@@ -627,7 +622,7 @@
         if (isPcmTestingEnabled()) {
             SamplePlayerShorts player = new SamplePlayerShorts(44100, AudioFormat.ENCODING_PCM_16BIT,
                     AudioFormat.CHANNEL_OUT_MONO,
-                    "sinesweepraw.raw");
+                    R.raw.sinesweepraw);
             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
         }
     }
@@ -636,7 +631,7 @@
             throws Exception {
         if (isPcmTestingEnabled()) {
             SamplePlayerBytes player = new SamplePlayerBytes(44100, AudioFormat.ENCODING_PCM_16BIT,
-                    AudioFormat.CHANNEL_OUT_MONO, "sinesweepraw.raw");
+                    AudioFormat.CHANNEL_OUT_MONO, R.raw.sinesweepraw);
             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
         }
     }
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioTrackTest.java
old mode 100755
new mode 100644
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/AudioTrack_ListenerTest.java b/tests/tests/media/audio/src/android/media/audio/cts/AudioTrack_ListenerTest.java
index 1c28c95..3606de1 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/AudioTrack_ListenerTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/AudioTrack_ListenerTest.java
@@ -36,7 +36,7 @@
 @NonMediaMainlineTest
 public class AudioTrack_ListenerTest extends CtsAndroidTestCase {
     private final static String TAG = "AudioTrack_ListenerTest";
-    private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+    private static final String REPORT_LOG_NAME = "CtsMediaAudioTestCases";
     private final static int TEST_SR = 11025;
     private final static int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
     private final static int TEST_FORMAT = AudioFormat.ENCODING_PCM_8BIT;
diff --git a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java b/tests/tests/media/audio/src/android/media/audio/cts/EnumDevicesTest.java
similarity index 97%
rename from tests/tests/media/src/android/media/cts/EnumDevicesTest.java
rename to tests/tests/media/audio/src/android/media/audio/cts/EnumDevicesTest.java
index 13a9cf8..fabb7dc 100644
--- a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/EnumDevicesTest.java
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.audio.cts;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
 
+import android.media.cts.AudioHelper;
+import android.media.cts.DeviceUtils;
+import android.media.cts.NonMediaMainlineTest;
 import android.media.AudioDeviceCallback;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
diff --git a/tests/tests/media/src/android/media/cts/MediaSyncEventTest.java b/tests/tests/media/audio/src/android/media/audio/cts/MediaSyncEventTest.java
similarity index 98%
rename from tests/tests/media/src/android/media/cts/MediaSyncEventTest.java
rename to tests/tests/media/audio/src/android/media/audio/cts/MediaSyncEventTest.java
index c54cca3..5ef6518 100644
--- a/tests/tests/media/src/android/media/cts/MediaSyncEventTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/MediaSyncEventTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.audio.cts;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -22,6 +22,8 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.cts.AudioHelper;
+import android.media.cts.NonMediaMainlineTest;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioRecord;
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/PresetReverbTest.java b/tests/tests/media/audio/src/android/media/audio/cts/PresetReverbTest.java
index 28baaa0..d4d2416 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/PresetReverbTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/PresetReverbTest.java
@@ -22,7 +22,6 @@
 import android.media.audiofx.PresetReverb;
 import android.media.cts.NonMediaMainlineTest;
 import android.media.cts.PostProcTestBase;
-import android.media.cts.Preconditions;
 import android.os.Looper;
 import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/RingtoneManagerTest.java b/tests/tests/media/audio/src/android/media/audio/cts/RingtoneManagerTest.java
index 478f42a..a2235ad 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/RingtoneManagerTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/RingtoneManagerTest.java
@@ -18,6 +18,7 @@
 import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.res.AssetFileDescriptor;
+import android.media.audio.cts.R;
 
 import android.app.Activity;
 import android.app.Instrumentation;
@@ -27,7 +28,6 @@
 import android.media.AudioManager;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
-import android.media.cts.Preconditions;
 import android.media.cts.Utils;
 import android.net.Uri;
 import android.platform.test.annotations.AppModeFull;
@@ -44,7 +44,6 @@
 
     private static final String PKG = "android.media.audio.cts";
     private static final String TAG = "RingtoneManagerTest";
-    static final String mInpPrefix = WorkDir.getMediaDirString();
 
     private RingtonePickerActivity mActivity;
     private Instrumentation mInstrumentation;
@@ -182,10 +181,9 @@
         Cursor c = mRingtoneManager.getCursor();
         assertTrue("Must have at least one ring tone available", c.getCount() > 0);
 
-        Preconditions.assertTestFileExists(mInpPrefix + "john_cage.ogg");
         mRingtoneManager.setStopPreviousRingtone(true);
         assertTrue(mRingtoneManager.getStopPreviousRingtone());
-        Uri uri = Uri.parse(mInpPrefix + "john_cage.ogg");
+        Uri uri = Uri.parse("android.resource://" + PKG + "/" + R.raw.john_cage);
         Ringtone ringtone = RingtoneManager.getRingtone(mContext, uri);
         ringtone.play();
         assertTrue(ringtone.isPlaying());
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java b/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java
index 5d1bac4..848d74c 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java
@@ -30,11 +30,10 @@
 import android.media.MediaPlayer;
 import android.media.MediaFormat;
 import android.media.MediaRecorder;
+import android.media.audio.cts.R;
 import android.media.cts.DeviceUtils;
-import android.media.cts.Preconditions;
 import android.media.cts.TestUtils.Monitor;
 
-import android.net.Uri;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
@@ -79,7 +78,6 @@
     private static final long WAIT_PLAYBACK_START_TIME_MS = 1000;
     private static final Set<Integer> AVAILABLE_INPUT_DEVICES_TYPE = new HashSet<>(
         Arrays.asList(AudioDeviceInfo.TYPE_BUILTIN_MIC));
-    static final String mInpPrefix = WorkDir.getMediaDirString();
 
     private AudioManager mAudioManager;
     private File mOutFile;
@@ -618,10 +616,8 @@
     }
 
     private MediaPlayer allocMediaPlayer(AudioDeviceInfo device, boolean start) {
-        final String res = "testmp3_2.mp3";
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        MediaPlayer mediaPlayer = MediaPlayer.create(mContext, Uri
-                .fromFile(new File(mInpPrefix + res)));
+        final int resid = R.raw.testmp3_2;
+        MediaPlayer mediaPlayer = MediaPlayer.create(mContext, resid);
         mediaPlayer.setAudioAttributes(
                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
         if (device != null) {
diff --git a/tests/tests/media/src/android/media/cts/UtilsTest.java b/tests/tests/media/audio/src/android/media/audio/cts/UtilsTest.java
old mode 100755
new mode 100644
similarity index 96%
rename from tests/tests/media/src/android/media/cts/UtilsTest.java
rename to tests/tests/media/audio/src/android/media/audio/cts/UtilsTest.java
index 7dc89aa..7d07111
--- a/tests/tests/media/src/android/media/cts/UtilsTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/UtilsTest.java
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.audio.cts;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.media.cts.NonMediaMainlineTest;
 import android.media.Utils;
 import android.os.Handler;
 import android.os.HandlerThread;
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/VisualizerTest.java b/tests/tests/media/audio/src/android/media/audio/cts/VisualizerTest.java
index 32ed6a4..943ff4a 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/VisualizerTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/VisualizerTest.java
@@ -21,16 +21,15 @@
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
+import android.media.audio.cts.R;
 import android.media.audiofx.Visualizer;
 import android.media.audiofx.Visualizer.MeasurementPeakRms;
 import android.media.cts.PostProcTestBase;
 import android.media.cts.Preconditions;
-import android.net.Uri;
 import android.os.Looper;
 import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 
-import java.io.File;
 import java.util.UUID;
 import android.util.Log;
 
@@ -42,7 +41,6 @@
     private final static int MIN_CAPTURE_SIZE_MAX = 1024;
     private final static int MAX_CAPTURE_SIZE_MIN = 512;
     private final static int MAX_LOOPER_WAIT_COUNT = 10;
-    static final String mInpPrefix = WorkDir.getMediaDirString();
 
     private Visualizer mVisualizer = null;
     private byte[] mWaveform = null;
@@ -293,11 +291,9 @@
             return;
         }
         AudioEffect vc = null;
-        Preconditions.assertTestFileExists(mInpPrefix + "sine1khzm40db.wav");
         try {
             // this test will play a 1kHz sine wave with peaks at -40dB
-            MediaPlayer mp = MediaPlayer
-                    .create(getContext(), Uri.fromFile(new File(mInpPrefix + "sine1khzm40db.wav")));
+            MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db);
             final int EXPECTED_PEAK_MB = -4015;
             final int EXPECTED_RMS_MB =  -4300;
             final int MAX_MEASUREMENT_ERROR_MB = 2000;
@@ -368,11 +364,9 @@
             return;
         }
         AudioEffect vc = null;
-        Preconditions.assertTestFileExists(mInpPrefix + "sine1khzs40dblong.mp3");
         try {
             // this test will play a 1kHz sine wave with peaks at -40dB
-            MediaPlayer mp = MediaPlayer.create(getContext(),
-                    Uri.fromFile(new File(mInpPrefix + "sine1khzs40dblong.mp3")));
+            MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong);
             final int EXPECTED_PEAK_MB = -4015;
             final int EXPECTED_RMS_MB =  -4300;
             final int MAX_MEASUREMENT_ERROR_MB = 2000;
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/VolumeShaperTest.java b/tests/tests/media/audio/src/android/media/audio/cts/VolumeShaperTest.java
index 3c373de..1408275 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/VolumeShaperTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/VolumeShaperTest.java
@@ -16,6 +16,9 @@
 
 package android.media.audio.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.testng.Assert.assertThrows;
 
 import android.app.ActivityManager;
@@ -34,15 +37,19 @@
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.LargeTest;
 import androidx.test.filters.SmallTest;
 
-import com.android.compatibility.common.util.CtsAndroidTestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-import java.lang.AutoCloseable;
 import java.util.Arrays;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 /**
  * VolumeShaperTest is automated using VolumeShaper.getVolume() to verify that a ramp
  * or a duck is at the expected volume level. Listening to some tests is also possible,
@@ -53,7 +60,8 @@
  * adb logcat | grep VolumeShaperTest
  */
 @AppModeFull(reason = "TODO: evaluate and port to instant")
-public class VolumeShaperTest extends CtsAndroidTestCase {
+@RunWith(JUnitParamsRunner.class)
+public class VolumeShaperTest {
     private static final String TAG = "VolumeShaperTest";
 
     // ramp or duck time (duration) used in tests.
@@ -150,6 +158,13 @@
                 .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_CUBIC)
                 .build();
 
+    private static final VolumeShaper.Configuration[] MONOTONIC_RAMPS = {
+            MONOTONIC_TEST,
+            CUBIC_RAMP,
+            SCURVE_RAMP,
+            SINE_RAMP,
+    };
+
     private static final VolumeShaper.Operation[] ALL_STANDARD_OPERATIONS = {
         VolumeShaper.Operation.PLAY,
         VolumeShaper.Operation.REVERSE,
@@ -343,6 +358,7 @@
     }
 
     @SmallTest
+    @Test
     public void testVolumeShaperConfigurationBuilder() throws Exception {
         final String TEST_NAME = "testVolumeShaperConfigurationBuilder";
 
@@ -500,6 +516,7 @@
     } // testVolumeShaperConfigurationBuilder
 
     @SmallTest
+    @Test
     public void testVolumeShaperConfigurationParcelable() throws Exception {
         final String TEST_NAME = "testVolumeShaperConfigurationParcelable";
 
@@ -525,6 +542,7 @@
     } // testVolumeShaperConfigurationParcelable
 
     @SmallTest
+    @Test
     public void testVolumeShaperOperationParcelable() throws Exception {
         final String TEST_NAME = "testVolumeShaperOperationParcelable";
 
@@ -553,6 +571,7 @@
     // to crash due to memory or performance issues.  Typically around 16 app based
     // shapers are allowed by the audio server.
     @SmallTest
+    @Test
     public void testMaximumShapers() {
         final String TEST_NAME = "testMaximumShapers";
         if (!hasAudioOutput()) {
@@ -582,60 +601,122 @@
         }
     } // testMaximumShapers
 
-    @LargeTest
-    public void testPlayerDuck() throws Exception {
-        final String TEST_NAME = "testPlayerDuck";
+    @Test
+    public void testDuckAudioTrack() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_AUDIO_TRACK)) {
+            runTestDuckPlayer("testDuckAudioTrack", player);
+        }
+    }
+
+    @Test
+    public void testDuckMediaPlayerNonOffloaded() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_NON_OFFLOADED)) {
+            runTestDuckPlayer("testDuckMediaPlayerNonOffloaded", player);
+        }
+    }
+
+    @Test
+    public void testDuckMediaPlayerOffloaded() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_OFFLOADED)) {
+            runTestDuckPlayer("testDuckMediaPlayerOffloaded", player);
+        }
+    }
+
+    private void runTestDuckPlayer(String testName, Player player) throws Exception {
         if (!hasAudioOutput()) {
             Log.w(TAG, "AUDIO_OUTPUT feature not found. This system might not have a valid "
                     + "audio output HAL");
             return;
         }
 
-        for (int p = 0; p < PLAYER_TYPES; ++p) {
-            try (   Player player = createPlayer(p);
-                    VolumeShaper volumeShaper = player.createVolumeShaper(SILENCE);
-                    ) {
-                final String testName = TEST_NAME + " " + player.name();
+        try (VolumeShaper volumeShaper = player.createVolumeShaper(SILENCE)) {
+            Log.d(TAG, testName + " starting");
+            player.start();
+            Thread.sleep(WARMUP_TIME_MS);
 
-                Log.d(TAG, testName + " starting");
-                player.start();
-                Thread.sleep(WARMUP_TIME_MS);
-
-                runDuckTest(testName, volumeShaper);
-                runCloseTest(testName, volumeShaper);
-            }
+            runDuckTest(testName, volumeShaper);
+            runCloseTest(testName, volumeShaper);
         }
-    } // testPlayerDuck
+    }
 
-    @LargeTest
-    public void testPlayerRamp() throws Exception {
-        final String TEST_NAME = "testPlayerRamp";
+    private VolumeShaper.Configuration[] getAllStandardRamps() {
+        return ALL_STANDARD_RAMPS;
+    }
+
+    // Parameters are indices to ALL_STANDARD_RAMPS configuration array.
+    @Test
+    @Parameters(method = "getAllStandardRamps")
+    public void testRampAudioTrack(
+            VolumeShaper.Configuration configuration) throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_AUDIO_TRACK)) {
+            runTestRampPlayer("testRampAudioTrack", player, configuration);
+        }
+    }
+
+    // Parameters are indices to ALL_STANDARD_RAMPS configuration array.
+    @Test
+    @Parameters(method = "getAllStandardRamps")
+    public void testRampMediaPlayerNonOffloaded(
+            VolumeShaper.Configuration configuration) throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_NON_OFFLOADED)) {
+            runTestRampPlayer("testRampMediaPlayerNonOffloaded", player, configuration);
+        }
+    }
+
+    // Parameters are indices to ALL_STANDARD_RAMPS configuration array.
+    @Test
+    @Parameters(method = "getAllStandardRamps")
+    public void testRampMediaPlayerOffloaded(
+            VolumeShaper.Configuration configuration) throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_OFFLOADED)) {
+            runTestRampPlayer("testRampMediaPlayerOffloaded", player, configuration);
+        }
+    }
+
+    private void runTestRampPlayer(
+            String testName, Player player, VolumeShaper.Configuration configuration)
+            throws Exception {
         if (!hasAudioOutput()) {
             Log.w(TAG, "AUDIO_OUTPUT feature not found. This system might not have a valid "
                     + "audio output HAL");
             return;
         }
 
-        for (int p = 0; p < PLAYER_TYPES; ++p) {
-            try (   Player player = createPlayer(p);
-                    VolumeShaper volumeShaper = player.createVolumeShaper(SILENCE);
-                    ) {
-                final String testName = TEST_NAME + " " + player.name();
+        try (VolumeShaper volumeShaper = player.createVolumeShaper(SILENCE)) {
+            Log.d(TAG, testName + " starting");
+            player.start();
+            Thread.sleep(WARMUP_TIME_MS);
 
-                Log.d(TAG, testName + " starting");
-                player.start();
-                Thread.sleep(WARMUP_TIME_MS);
-
-                runRampTest(testName, volumeShaper);
-                runCloseTest(testName, volumeShaper);
-            }
+            runRampTest(testName, volumeShaper, configuration);
+            runCloseTest(testName, volumeShaper);
         }
-    } // testPlayerRamp
+    }
 
     @FlakyTest
-    @LargeTest
-    public void testPlayerCornerCase() throws Exception {
-        final String TEST_NAME = "testPlayerCornerCase";
+    @Test
+    public void testCornerCaseAudioTrack() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_AUDIO_TRACK)) {
+            runTestCornerCasePlayer("testCornerCaseAudioTrack", player);
+        }
+    }
+
+    @FlakyTest
+    @Test
+    public void testCornerCaseMediaPlayerNonOffloaded() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_NON_OFFLOADED)) {
+            runTestCornerCasePlayer("testCornerCaseMediaPlayerNonOffloaded", player);
+        }
+    }
+
+    @FlakyTest
+    @Test
+    public void testCornerCaseMediaPlayerOffloaded() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_OFFLOADED)) {
+            runTestCornerCasePlayer("testCornerCaseMediaPlayerOffloaded", player);
+        }
+    }
+
+    private void runTestCornerCasePlayer(String testName, Player player) throws Exception {
         if (!hasAudioOutput()) {
             Log.w(TAG, "AUDIO_OUTPUT feature not found. This system might not have a valid "
                     + "audio output HAL");
@@ -643,48 +724,40 @@
         }
 
         final VolumeShaper.Configuration config = LINEAR_RAMP;
+        VolumeShaper volumeShaper = null;
+        try {
+            volumeShaper = player.createVolumeShaper(config);
 
-        for (int p = 0; p < PLAYER_TYPES; ++p) {
-            Player player = null;
-            VolumeShaper volumeShaper = null;
-            try {
-                player = createPlayer(p);
-                volumeShaper = player.createVolumeShaper(config);
-                final String testName = TEST_NAME + " " + player.name();
+            runStartIdleTest(testName, volumeShaper, player);
+            runRampCornerCaseTest(testName, volumeShaper, config);
+            runCloseTest(testName, volumeShaper);
 
-                runStartIdleTest(testName, volumeShaper, player);
-                runRampCornerCaseTest(testName, volumeShaper, config);
-                runCloseTest(testName, volumeShaper);
+            Log.d(TAG, testName + " recreating VolumeShaper and repeating with pause");
+            volumeShaper = player.createVolumeShaper(config);
+            player.pause();
+            Thread.sleep(100 /* millis */);
+            runStartIdleTest(testName, volumeShaper, player);
 
-                Log.d(TAG, testName + " recreating VolumeShaper and repeating with pause");
-                volumeShaper = player.createVolumeShaper(config);
-                player.pause();
-                Thread.sleep(100 /* millis */);
-                runStartIdleTest(testName, volumeShaper, player);
+            // volumeShaper not explicitly closed, will close upon finalize or player close.
+            Log.d(TAG, testName + " recreating VolumeShaper and repeating with stop");
+            volumeShaper = player.createVolumeShaper(config);
+            player.stop();
+            Thread.sleep(100 /* millis */);
+            runStartIdleTest(testName, volumeShaper, player);
 
-                // volumeShaper not explicitly closed, will close upon finalize or player close.
-                Log.d(TAG, testName + " recreating VolumeShaper and repeating with stop");
-                volumeShaper = player.createVolumeShaper(config);
-                player.stop();
-                Thread.sleep(100 /* millis */);
-                runStartIdleTest(testName, volumeShaper, player);
-
-                Log.d(TAG, testName + " closing Player before VolumeShaper");
-                player.close();
-                runCloseTest2(testName, volumeShaper);
-            } finally {
-                if (volumeShaper != null) {
-                    volumeShaper.close();
-                }
-                if (player != null) {
-                    player.close();
-                }
+            Log.d(TAG, testName + " closing Player before VolumeShaper");
+            player.close();
+            runCloseTest2(testName, volumeShaper);
+        } finally {
+            if (volumeShaper != null) {
+                volumeShaper.close();
             }
         }
-    } // testPlayerCornerCase
+    } // runTestCornerCasePlayer
 
     @FlakyTest
     @LargeTest
+    @Test
     public void testPlayerCornerCase2() throws Exception {
         final String TEST_NAME = "testPlayerCornerCase2";
         if (!hasAudioOutput()) {
@@ -728,6 +801,7 @@
 
     @FlakyTest
     @LargeTest
+    @Test
     public void testPlayerJoin() throws Exception {
         final String TEST_NAME = "testPlayerJoin";
         if (!hasAudioOutput()) {
@@ -747,7 +821,7 @@
 
                 Log.d(TAG, " we join several LINEAR_RAMPS together "
                         + " this effectively is one LINEAR_RAMP (volume increasing).");
-                final long durationMs = 10000;
+                final long durationMs = 5000;
                 final long incrementMs = 1000;
                 for (long i = 0; i < durationMs; i += incrementMs) {
                     Log.d(TAG, testName + " Play - join " + i);
@@ -764,133 +838,174 @@
         }
     } // testPlayerJoin
 
-    @LargeTest
-    public void testPlayerCubicMonotonic() throws Exception {
-        final String TEST_NAME = "testPlayerCubicMonotonic";
+    private VolumeShaper.Configuration[] getMonotonicRamps() {
+        return MONOTONIC_RAMPS;
+    }
+
+    // Parameters are indices to MONOTONIC_RAMPS configuration array.
+    @Test
+    @Parameters(method = "getMonotonicRamps")
+    public void testCubicMonotonicAudioTrack(
+            VolumeShaper.Configuration configuration) throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_AUDIO_TRACK)) {
+            runTestCubicMonotonicPlayer(
+                    "testCubicMonotonicAudioTrack", player, configuration);
+        }
+    }
+
+    // Parameters are indices to MONOTONIC_RAMPS configuration array.
+    @Test
+    @Parameters(method = "getMonotonicRamps")
+    public void testCubicMonotonicMediaPlayerNonOffloaded(
+            VolumeShaper.Configuration configuration) throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_NON_OFFLOADED)) {
+            runTestCubicMonotonicPlayer(
+                    "testCubicMonotonicMediaPlayerNonOffloaded", player, configuration);
+        }
+    }
+
+    // Parameters are indices to MONOTONIC_RAMPS configuration array.
+    @Test
+    @Parameters(method = "getMonotonicRamps")
+    public void testCubicMonotonicMediaPlayerOffloaded(
+            VolumeShaper.Configuration configuration) throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_OFFLOADED)) {
+            runTestCubicMonotonicPlayer(
+                    "testCubicMonotonicMediaPlayerOffloaded", player, configuration);
+        }
+    }
+
+    private void runTestCubicMonotonicPlayer(
+            String testName, Player player, VolumeShaper.Configuration configuration)
+            throws Exception {
         if (!hasAudioOutput()) {
             Log.w(TAG, "AUDIO_OUTPUT feature not found. This system might not have a valid "
                     + "audio output HAL");
             return;
         }
 
-        final VolumeShaper.Configuration configurations[] =
-                new VolumeShaper.Configuration[] {
-                MONOTONIC_TEST,
-                CUBIC_RAMP,
-                SCURVE_RAMP,
-                SINE_RAMP,
-        };
+        try (VolumeShaper volumeShaper = player.createVolumeShaper(SILENCE)) {
+            volumeShaper.apply(VolumeShaper.Operation.PLAY);
+            player.start();
+            Thread.sleep(WARMUP_TIME_MS);
 
-        for (int p = 0; p < PLAYER_TYPES; ++p) {
-            try (   Player player = createPlayer(p);
-                    VolumeShaper volumeShaper = player.createVolumeShaper(SILENCE);
-                    ) {
-                final String testName = TEST_NAME + " " + player.name();
-                volumeShaper.apply(VolumeShaper.Operation.PLAY);
-                player.start();
-                Thread.sleep(WARMUP_TIME_MS);
+            // test configurations known monotonic
+            Log.d(TAG, testName + " starting test");
 
-                for (VolumeShaper.Configuration configuration : configurations) {
-                    // test configurations known monotonic
-                    Log.d(TAG, testName + " starting test");
+            float lastVolume = 0;
+            final long incrementMs = 100;
 
-                    float lastVolume = 0;
-                    final long incrementMs = 100;
-
-                    volumeShaper.replace(configuration,
-                            VolumeShaper.Operation.PLAY, true /* join */);
-                    // monotonicity test
-                    for (long i = 0; i < RAMP_TIME_MS; i += incrementMs) {
-                        final float volume = volumeShaper.getVolume();
-                        assertTrue(testName + " montonic volume should increase "
-                                + volume + " >= " + lastVolume,
-                                (volume >= lastVolume));
-                        lastVolume = volume;
-                        Thread.sleep(incrementMs);
-                    }
-                    Thread.sleep(WARMUP_TIME_MS);
-                    lastVolume = volumeShaper.getVolume();
-                    assertEquals(testName
-                            + " final monotonic value should be 1.f, but is " + lastVolume,
-                            1.f, lastVolume, VOLUME_TOLERANCE);
-
-                    Log.d(TAG, "invert");
-                    // invert
-                    VolumeShaper.Configuration newConfiguration =
-                            new VolumeShaper.Configuration.Builder(configuration)
-                    .invertVolumes()
-                    .build();
-                    volumeShaper.replace(newConfiguration,
-                            VolumeShaper.Operation.PLAY, true /* join */);
-                    // monotonicity test
-                    for (long i = 0; i < RAMP_TIME_MS; i += incrementMs) {
-                        final float volume = volumeShaper.getVolume();
-                        assertTrue(testName + " montonic volume should decrease "
-                                + volume + " <= " + lastVolume,
-                                (volume <= lastVolume));
-                        lastVolume = volume;
-                        Thread.sleep(incrementMs);
-                    }
-                    Thread.sleep(WARMUP_TIME_MS);
-                    lastVolume = volumeShaper.getVolume();
-                    assertEquals(testName
-                            + " final monotonic value should be 0.f, but is " + lastVolume,
-                            0.f, lastVolume, VOLUME_TOLERANCE);
-
-                    // invert + reflect
-                    Log.d(TAG, "invert and reflect");
-                    newConfiguration =
-                            new VolumeShaper.Configuration.Builder(configuration)
-                    .invertVolumes()
-                    .reflectTimes()
-                    .build();
-                    volumeShaper.replace(newConfiguration,
-                            VolumeShaper.Operation.PLAY, true /* join */);
-                    // monotonicity test
-                    for (long i = 0; i < RAMP_TIME_MS; i += incrementMs) {
-                        final float volume = volumeShaper.getVolume();
-                        assertTrue(testName + " montonic volume should increase "
-                                + volume + " >= " + lastVolume,
-                                (volume >= lastVolume - VOLUME_TOLERANCE));
-                        lastVolume = volume;
-                        Thread.sleep(incrementMs);
-                    }
-                    Thread.sleep(WARMUP_TIME_MS);
-                    lastVolume = volumeShaper.getVolume();
-                    assertEquals(testName
-                            + " final monotonic value should be 1.f, but is " + lastVolume,
-                            1.f, lastVolume, VOLUME_TOLERANCE);
-
-                    // reflect
-                    Log.d(TAG, "reflect");
-                    newConfiguration =
-                            new VolumeShaper.Configuration.Builder(configuration)
-                    .reflectTimes()
-                    .build();
-                    volumeShaper.replace(newConfiguration,
-                            VolumeShaper.Operation.PLAY, true /* join */);
-                    // monotonicity test
-                    for (long i = 0; i < RAMP_TIME_MS; i += incrementMs) {
-                        final float volume = volumeShaper.getVolume();
-                        assertTrue(testName + " montonic volume should decrease "
-                                + volume + " <= " + lastVolume,
-                                (volume <= lastVolume));
-                        lastVolume = volume;
-                        Thread.sleep(incrementMs);
-                    }
-                    Thread.sleep(WARMUP_TIME_MS);
-                    lastVolume = volumeShaper.getVolume();
-                    assertEquals(testName
-                            + " final monotonic value should be 0.f, but is " + lastVolume,
-                            0.f, lastVolume, VOLUME_TOLERANCE);
-                }
+            volumeShaper.replace(configuration,
+                    VolumeShaper.Operation.PLAY, true /* join */);
+            // monotonicity test
+            for (long i = 0; i < RAMP_TIME_MS; i += incrementMs) {
+                final float volume = volumeShaper.getVolume();
+                assertTrue(testName + " montonic volume should increase "
+                        + volume + " >= " + lastVolume,
+                        (volume >= lastVolume));
+                lastVolume = volume;
+                Thread.sleep(incrementMs);
             }
-        }
-    } // testPlayerCubicMonotonic
+            Thread.sleep(WARMUP_TIME_MS);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " final monotonic value should be 1.f, but is " + lastVolume,
+                    1.f, lastVolume, VOLUME_TOLERANCE);
 
-    @LargeTest
-    public void testPlayerStepRamp() throws Exception {
-        final String TEST_NAME = "testPlayerStepRamp";
+            Log.d(TAG, "invert");
+            // invert
+            VolumeShaper.Configuration newConfiguration =
+                    new VolumeShaper.Configuration.Builder(configuration)
+                    .invertVolumes()
+                    .build();
+            volumeShaper.replace(newConfiguration,
+                    VolumeShaper.Operation.PLAY, true /* join */);
+            // monotonicity test
+            for (long i = 0; i < RAMP_TIME_MS; i += incrementMs) {
+                final float volume = volumeShaper.getVolume();
+                assertTrue(testName + " montonic volume should decrease "
+                        + volume + " <= " + lastVolume,
+                        (volume <= lastVolume));
+                lastVolume = volume;
+                Thread.sleep(incrementMs);
+            }
+            Thread.sleep(WARMUP_TIME_MS);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " final monotonic value should be 0.f, but is " + lastVolume,
+                    0.f, lastVolume, VOLUME_TOLERANCE);
+
+            // invert + reflect
+            Log.d(TAG, "invert and reflect");
+            newConfiguration =
+                    new VolumeShaper.Configuration.Builder(configuration)
+            .invertVolumes()
+            .reflectTimes()
+            .build();
+            volumeShaper.replace(newConfiguration,
+                    VolumeShaper.Operation.PLAY, true /* join */);
+            // monotonicity test
+            for (long i = 0; i < RAMP_TIME_MS; i += incrementMs) {
+                final float volume = volumeShaper.getVolume();
+                assertTrue(testName + " montonic volume should increase "
+                        + volume + " >= " + lastVolume,
+                        (volume >= lastVolume - VOLUME_TOLERANCE));
+                lastVolume = volume;
+                Thread.sleep(incrementMs);
+            }
+            Thread.sleep(WARMUP_TIME_MS);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " final monotonic value should be 1.f, but is " + lastVolume,
+                    1.f, lastVolume, VOLUME_TOLERANCE);
+
+            // reflect
+            Log.d(TAG, "reflect");
+            newConfiguration =
+                    new VolumeShaper.Configuration.Builder(configuration)
+            .reflectTimes()
+            .build();
+            volumeShaper.replace(newConfiguration,
+                    VolumeShaper.Operation.PLAY, true /* join */);
+            // monotonicity test
+            for (long i = 0; i < RAMP_TIME_MS; i += incrementMs) {
+                final float volume = volumeShaper.getVolume();
+                assertTrue(testName + " montonic volume should decrease "
+                        + volume + " <= " + lastVolume,
+                        (volume <= lastVolume));
+                lastVolume = volume;
+                Thread.sleep(incrementMs);
+            }
+            Thread.sleep(WARMUP_TIME_MS);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " final monotonic value should be 0.f, but is " + lastVolume,
+                    0.f, lastVolume, VOLUME_TOLERANCE);
+        }
+    } // runTestCubicMonotonicPlayer
+
+    @Test
+    public void testStepRampAudioTrack() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_AUDIO_TRACK)) {
+            runTestStepRampPlayer("testStepRampAudioTrack", player);
+        }
+    }
+
+    @Test
+    public void testStepRampMediaPlayerNonOffloaded() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_NON_OFFLOADED)) {
+            runTestStepRampPlayer("testStepRampMediaPlayerNonOffloaded", player);
+        }
+    }
+
+    @Test
+    public void testStepRampMediaPlayerOffloaded() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_OFFLOADED)) {
+            runTestStepRampPlayer("testStepRampMediaPlayerOffloaded", player);
+        }
+    }
+
+    private void runTestStepRampPlayer(String testName, Player player) throws Exception {
         if (!hasAudioOutput()) {
             Log.w(TAG, "AUDIO_OUTPUT feature not found. This system might not have a valid "
                     + "audio output HAL");
@@ -902,121 +1017,137 @@
         // It should suddenly jump to full volume at 1.f (full duration).
         // Note: invertVolumes() and reflectTimes() are not symmetric for STEP interpolation;
         // however, VolumeShaper.Operation.REVERSE will behave symmetrically.
-        for (int p = 0; p < PLAYER_TYPES; ++p) {
-            try (   Player player = createPlayer(p);
-                    VolumeShaper volumeShaper = player.createVolumeShaper(SILENCE);
-                    ) {
-                final String testName = TEST_NAME + " " + player.name();
-                volumeShaper.apply(VolumeShaper.Operation.PLAY);
-                player.start();
-                Thread.sleep(WARMUP_TIME_MS);
 
-                final VolumeShaper.Configuration configuration = STEP_RAMP;
-                Log.d(TAG, testName + " starting test (sudden jump to full after "
-                        + RAMP_TIME_MS + " milliseconds)");
+        try (VolumeShaper volumeShaper = player.createVolumeShaper(SILENCE)) {
+            volumeShaper.apply(VolumeShaper.Operation.PLAY);
+            player.start();
+            Thread.sleep(WARMUP_TIME_MS);
 
-                volumeShaper.replace(configuration,
-                        VolumeShaper.Operation.PLAY, true /* join */);
+            final VolumeShaper.Configuration configuration = STEP_RAMP;
+            Log.d(TAG, testName + " starting test (sudden jump to full after "
+                    + RAMP_TIME_MS + " milliseconds)");
 
-                Thread.sleep(RAMP_TIME_MS / 2);
-                float lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " middle value should be 0.f, but is " + lastVolume,
-                        0.f, lastVolume, VOLUME_TOLERANCE);
+            volumeShaper.replace(configuration,
+                    VolumeShaper.Operation.PLAY, true /* join */);
 
-                Thread.sleep(RAMP_TIME_MS / 2 + 1000);
-                lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " final value should be 1.f, but is " + lastVolume,
-                        1.f, lastVolume, VOLUME_TOLERANCE);
+            Thread.sleep(RAMP_TIME_MS / 2);
+            float lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " middle value should be 0.f, but is " + lastVolume,
+                    0.f, lastVolume, VOLUME_TOLERANCE);
 
-                Log.d(TAG, "invert (sudden jump to silence after "
-                        + RAMP_TIME_MS + " milliseconds)");
-                // invert
-                VolumeShaper.Configuration newConfiguration =
-                        new VolumeShaper.Configuration.Builder(configuration)
-                            .invertVolumes()
-                            .build();
-                volumeShaper.replace(newConfiguration,
-                        VolumeShaper.Operation.PLAY, true /* join */);
+            Thread.sleep(RAMP_TIME_MS / 2 + 1000);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " final value should be 1.f, but is " + lastVolume,
+                    1.f, lastVolume, VOLUME_TOLERANCE);
 
-                Thread.sleep(RAMP_TIME_MS / 2);
-                lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " middle value should be 1.f, but is " + lastVolume,
-                        1.f, lastVolume, VOLUME_TOLERANCE);
+            Log.d(TAG, "invert (sudden jump to silence after "
+                    + RAMP_TIME_MS + " milliseconds)");
+            // invert
+            VolumeShaper.Configuration newConfiguration =
+                    new VolumeShaper.Configuration.Builder(configuration)
+                        .invertVolumes()
+                        .build();
+            volumeShaper.replace(newConfiguration,
+                    VolumeShaper.Operation.PLAY, true /* join */);
 
-                Thread.sleep(RAMP_TIME_MS / 2 + 1000);
-                lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " final value should be 0.f, but is " + lastVolume,
-                        0.f, lastVolume, VOLUME_TOLERANCE);
+            Thread.sleep(RAMP_TIME_MS / 2);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " middle value should be 1.f, but is " + lastVolume,
+                    1.f, lastVolume, VOLUME_TOLERANCE);
 
-                // invert + reflect
-                Log.d(TAG, "invert and reflect (sudden jump to full after "
-                        + RAMP_TIME_MS + " milliseconds)");
-                newConfiguration =
-                        new VolumeShaper.Configuration.Builder(configuration)
-                            .invertVolumes()
-                            .reflectTimes()
-                            .build();
-                volumeShaper.replace(newConfiguration,
-                        VolumeShaper.Operation.PLAY, true /* join */);
+            Thread.sleep(RAMP_TIME_MS / 2 + 1000);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " final value should be 0.f, but is " + lastVolume,
+                    0.f, lastVolume, VOLUME_TOLERANCE);
 
-                Thread.sleep(RAMP_TIME_MS / 2);
-                lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " middle value should be 0.f, but is " + lastVolume,
-                        0.f, lastVolume, VOLUME_TOLERANCE);
+            // invert + reflect
+            Log.d(TAG, "invert and reflect (sudden jump to full after "
+                    + RAMP_TIME_MS + " milliseconds)");
+            newConfiguration =
+                    new VolumeShaper.Configuration.Builder(configuration)
+                        .invertVolumes()
+                        .reflectTimes()
+                        .build();
+            volumeShaper.replace(newConfiguration,
+                    VolumeShaper.Operation.PLAY, true /* join */);
 
-                Thread.sleep(RAMP_TIME_MS / 2 + 1000);
-                lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " final value should be 1.f, but is " + lastVolume,
-                        1.f, lastVolume, VOLUME_TOLERANCE);
+            Thread.sleep(RAMP_TIME_MS / 2);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " middle value should be 0.f, but is " + lastVolume,
+                    0.f, lastVolume, VOLUME_TOLERANCE);
 
-                // reflect
-                Log.d(TAG, "reflect (sudden jump to silence after "
-                        + RAMP_TIME_MS + " milliseconds)");
-                newConfiguration =
-                        new VolumeShaper.Configuration.Builder(configuration)
-                            .reflectTimes()
-                            .build();
-                volumeShaper.replace(newConfiguration,
-                        VolumeShaper.Operation.PLAY, true /* join */);
+            Thread.sleep(RAMP_TIME_MS / 2 + 1000);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " final value should be 1.f, but is " + lastVolume,
+                    1.f, lastVolume, VOLUME_TOLERANCE);
 
-                Thread.sleep(RAMP_TIME_MS / 2);
-                lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " middle value should be 1.f, but is " + lastVolume,
-                        1.f, lastVolume, VOLUME_TOLERANCE);
+            // reflect
+            Log.d(TAG, "reflect (sudden jump to silence after "
+                    + RAMP_TIME_MS + " milliseconds)");
+            newConfiguration =
+                    new VolumeShaper.Configuration.Builder(configuration)
+                        .reflectTimes()
+                        .build();
+            volumeShaper.replace(newConfiguration,
+                    VolumeShaper.Operation.PLAY, true /* join */);
 
-                Thread.sleep(RAMP_TIME_MS / 2 + 1000);
-                lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " final value should be 0.f, but is " + lastVolume,
-                        0.f, lastVolume, VOLUME_TOLERANCE);
+            Thread.sleep(RAMP_TIME_MS / 2);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " middle value should be 1.f, but is " + lastVolume,
+                    1.f, lastVolume, VOLUME_TOLERANCE);
 
-                Log.d(TAG, "reverse (immediate jump to full)");
-                volumeShaper.apply(VolumeShaper.Operation.REVERSE);
-                Thread.sleep(RAMP_TIME_MS / 2);
-                lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " middle value should be 1.f, but is " + lastVolume,
-                        1.f, lastVolume, VOLUME_TOLERANCE);
+            Thread.sleep(RAMP_TIME_MS / 2 + 1000);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " final value should be 0.f, but is " + lastVolume,
+                    0.f, lastVolume, VOLUME_TOLERANCE);
 
-                Thread.sleep(RAMP_TIME_MS / 2 + 1000);
-                lastVolume = volumeShaper.getVolume();
-                assertEquals(testName
-                        + " final value should be 1.f, but is " + lastVolume,
-                        1.f, lastVolume, VOLUME_TOLERANCE);
-            }
+            Log.d(TAG, "reverse (immediate jump to full)");
+            volumeShaper.apply(VolumeShaper.Operation.REVERSE);
+            Thread.sleep(RAMP_TIME_MS / 2);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " middle value should be 1.f, but is " + lastVolume,
+                    1.f, lastVolume, VOLUME_TOLERANCE);
+
+            Thread.sleep(RAMP_TIME_MS / 2 + 1000);
+            lastVolume = volumeShaper.getVolume();
+            assertEquals(testName
+                    + " final value should be 1.f, but is " + lastVolume,
+                    1.f, lastVolume, VOLUME_TOLERANCE);
         }
-    } // testPlayerStepRamp
+    } // runTestStepRampPlayer
 
-    @LargeTest
-    public void testPlayerTwoShapers() throws Exception {
-        final String TEST_NAME = "testPlayerTwoShapers";
+    @Test
+    public void testTwoShapersAudioTrack() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_AUDIO_TRACK)) {
+            runTestTwoShapersPlayer("testTwoShapersAudioTrack", player);
+        }
+    }
+
+    @Test
+    public void testTwoShapersMediaPlayerNonOffloaded() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_NON_OFFLOADED)) {
+            runTestTwoShapersPlayer("testTwoShapersMediaPlayerNonOffloaded", player);
+        }
+    }
+
+    @Test
+    public void testTwoShapersMediaPlayerOffloaded() throws Exception {
+        try (Player player = createPlayer(PLAYER_TYPE_MEDIA_PLAYER_OFFLOADED)) {
+            runTestTwoShapersPlayer("testTwoShapersMediaPlayerOffloaded", player);
+        }
+    }
+
+    private void runTestTwoShapersPlayer(String testName, Player player) throws Exception {
+
         if (!hasAudioOutput()) {
             Log.w(TAG, "AUDIO_OUTPUT feature not found. This system might not have a valid "
                     + "audio output HAL");
@@ -1036,72 +1167,69 @@
                     .reflectTimes()
                     .build();
 
-        for (int p = 0; p < PLAYER_TYPES; ++p) {
-            try (   Player player = createPlayer(p);
-                    VolumeShaper volumeShaperRamp = player.createVolumeShaper(LONG_RAMP);
-                    VolumeShaper volumeShaperDuck = player.createVolumeShaper(LONG_DUCK);
-                    ) {
-                final String testName = TEST_NAME + " " + player.name();
+        try (
+                VolumeShaper volumeShaperRamp = player.createVolumeShaper(LONG_RAMP);
+                VolumeShaper volumeShaperDuck = player.createVolumeShaper(LONG_DUCK);
+                ) {
+            final float firstVolumeRamp = volumeShaperRamp.getVolume();
+            final float firstVolumeDuck = volumeShaperDuck.getVolume();
+            assertEquals(testName
+                    + " first ramp value should be 0.f, but is " + firstVolumeRamp,
+                    0.f, firstVolumeRamp, VOLUME_TOLERANCE);
+            assertEquals(testName
+                    + " first duck value should be 1.f, but is " + firstVolumeDuck,
+                    1.f, firstVolumeDuck, VOLUME_TOLERANCE);
+            player.start();
 
-                final float firstVolumeRamp = volumeShaperRamp.getVolume();
-                final float firstVolumeDuck = volumeShaperDuck.getVolume();
-                assertEquals(testName
-                        + " first ramp value should be 0.f, but is " + firstVolumeRamp,
-                        0.f, firstVolumeRamp, VOLUME_TOLERANCE);
-                assertEquals(testName
-                        + " first duck value should be 1.f, but is " + firstVolumeDuck,
-                        1.f, firstVolumeDuck, VOLUME_TOLERANCE);
-                player.start();
+            Thread.sleep(1000);
 
-                Thread.sleep(1000);
+            final float lastVolumeRamp = volumeShaperRamp.getVolume();
+            final float lastVolumeDuck = volumeShaperDuck.getVolume();
+            assertEquals(testName
+                    + " no-play ramp value should be 0.f, but is " + lastVolumeRamp,
+                    0.f, lastVolumeRamp, VOLUME_TOLERANCE);
+            assertEquals(testName
+                    + " no-play duck value should be 1.f, but is " + lastVolumeDuck,
+                    1.f, lastVolumeDuck, VOLUME_TOLERANCE);
 
-                final float lastVolumeRamp = volumeShaperRamp.getVolume();
-                final float lastVolumeDuck = volumeShaperDuck.getVolume();
-                assertEquals(testName
-                        + " no-play ramp value should be 0.f, but is " + lastVolumeRamp,
-                        0.f, lastVolumeRamp, VOLUME_TOLERANCE);
-                assertEquals(testName
-                        + " no-play duck value should be 1.f, but is " + lastVolumeDuck,
-                        1.f, lastVolumeDuck, VOLUME_TOLERANCE);
+            Log.d(TAG, testName + " volume should be silent and start increasing now");
 
-                Log.d(TAG, testName + " volume should be silent and start increasing now");
+            // we actually start now!
+            volumeShaperRamp.apply(VolumeShaper.Operation.PLAY);
+            volumeShaperDuck.apply(VolumeShaper.Operation.PLAY);
+            Thread.sleep(durationMs / 2);
 
-                // we actually start now!
-                volumeShaperRamp.apply(VolumeShaper.Operation.PLAY);
-                volumeShaperDuck.apply(VolumeShaper.Operation.PLAY);
-                Thread.sleep(durationMs / 2);
+            Log.d(TAG, testName + " volume should be > 0 and about maximum here");
+            final float lastVolumeRamp2 = volumeShaperRamp.getVolume();
+            final float lastVolumeDuck2 = volumeShaperDuck.getVolume();
+            assertTrue(testName
+                    + " last ramp value should be > 0.f " + lastVolumeRamp2,
+                    lastVolumeRamp2 > 0.f);
+            assertTrue(testName
+                    + " last duck value should be < 1.f " + lastVolumeDuck2,
+                    lastVolumeDuck2 < 1.f);
 
-                Log.d(TAG, testName + " volume should be > 0 and about maximum here");
-                final float lastVolumeRamp2 = volumeShaperRamp.getVolume();
-                final float lastVolumeDuck2 = volumeShaperDuck.getVolume();
-                assertTrue(testName
-                        + " last ramp value should be > 0.f " + lastVolumeRamp2,
-                        lastVolumeRamp2 > 0.f);
-                assertTrue(testName
-                        + " last duck value should be < 1.f " + lastVolumeDuck2,
-                        lastVolumeDuck2 < 1.f);
+            Log.d(TAG, testName + " volume should start decreasing shortly");
+            Thread.sleep(durationMs / 2 + 1000);
 
-                Log.d(TAG, testName + " volume should start decreasing shortly");
-                Thread.sleep(durationMs / 2 + 1000);
+            Log.d(TAG, testName + " volume should be silent now");
+            final float lastVolumeRamp3 = volumeShaperRamp.getVolume();
+            final float lastVolumeDuck3 = volumeShaperDuck.getVolume();
+            assertEquals(testName
+                    + " last ramp value should be 1.f, but is " + lastVolumeRamp3,
+                    1.f, lastVolumeRamp3, VOLUME_TOLERANCE);
+            assertEquals(testName
+                    + " last duck value should be 0.f, but is " + lastVolumeDuck3,
+                    0.f, lastVolumeDuck3, VOLUME_TOLERANCE);
 
-                Log.d(TAG, testName + " volume should be silent now");
-                final float lastVolumeRamp3 = volumeShaperRamp.getVolume();
-                final float lastVolumeDuck3 = volumeShaperDuck.getVolume();
-                assertEquals(testName
-                        + " last ramp value should be 1.f, but is " + lastVolumeRamp3,
-                        1.f, lastVolumeRamp3, VOLUME_TOLERANCE);
-                assertEquals(testName
-                        + " last duck value should be 0.f, but is " + lastVolumeDuck3,
-                        0.f, lastVolumeDuck3, VOLUME_TOLERANCE);
-
-                runCloseTest(testName, volumeShaperRamp);
-                runCloseTest(testName, volumeShaperDuck);
-            }
+            runCloseTest(testName, volumeShaperRamp);
+            runCloseTest(testName, volumeShaperDuck);
         }
-    } // testPlayerTwoShapers
+    } // runTestTwoShapersPlayer
 
     // tests that shaper advances in the presence of pause and stop (time based after start).
     @LargeTest
+    @Test
     public void testPlayerRunDuringPauseStop() throws Exception {
         final String TEST_NAME = "testPlayerRunDuringPauseStop";
         if (!hasAudioOutput()) {
@@ -1156,55 +1284,55 @@
     } // testPlayerRunDuringPauseStop
 
     // Player should be started before calling (as it is not an argument to method).
-    private void runRampTest(String testName, VolumeShaper volumeShaper) throws Exception {
-        for (VolumeShaper.Configuration config : ALL_STANDARD_RAMPS) {
-            // This replaces with play.
-            Log.d(TAG, testName + " Replace + Play (volume should increase)");
-            volumeShaper.replace(config, VolumeShaper.Operation.PLAY, false /* join */);
-            Thread.sleep(RAMP_TIME_MS / 2);
+    private void runRampTest(
+            String testName, VolumeShaper volumeShaper, VolumeShaper.Configuration config)
+            throws Exception {
+        // This replaces with play.
+        Log.d(TAG, testName + " Replace + Play (volume should increase)");
+        volumeShaper.replace(config, VolumeShaper.Operation.PLAY, false /* join */);
+        Thread.sleep(RAMP_TIME_MS / 2);
 
-            // Reverse the direction of the volume shaper curve
-            Log.d(TAG, testName + " Reverse (volume should decrease)");
-            volumeShaper.apply(VolumeShaper.Operation.REVERSE);
-            Thread.sleep(RAMP_TIME_MS / 2 + 1000);
+        // Reverse the direction of the volume shaper curve
+        Log.d(TAG, testName + " Reverse (volume should decrease)");
+        volumeShaper.apply(VolumeShaper.Operation.REVERSE);
+        Thread.sleep(RAMP_TIME_MS / 2 + 1000);
 
-            Log.d(TAG, testName + " Check Volume (silent)");
-            assertEquals(testName + " volume should be 0.f",
-                    0.f, volumeShaper.getVolume(), VOLUME_TOLERANCE);
+        Log.d(TAG, testName + " Check Volume (silent)");
+        assertEquals(testName + " volume should be 0.f",
+                0.f, volumeShaper.getVolume(), VOLUME_TOLERANCE);
 
-            // Forwards
-            Log.d(TAG, testName + " Play (volume should increase)");
-            volumeShaper.apply(VolumeShaper.Operation.PLAY);
-            Thread.sleep(RAMP_TIME_MS + 1000);
+        // Forwards
+        Log.d(TAG, testName + " Play (volume should increase)");
+        volumeShaper.apply(VolumeShaper.Operation.PLAY);
+        Thread.sleep(RAMP_TIME_MS + 1000);
 
-            Log.d(TAG, testName + " Check Volume (volume at max)");
-            assertEquals(testName + " volume should be 1.f",
-                    1.f, volumeShaper.getVolume(), VOLUME_TOLERANCE);
+        Log.d(TAG, testName + " Check Volume (volume at max)");
+        assertEquals(testName + " volume should be 1.f",
+                1.f, volumeShaper.getVolume(), VOLUME_TOLERANCE);
 
-            // Reverse
-            Log.d(TAG, testName + " Reverse (volume should decrease)");
-            volumeShaper.apply(VolumeShaper.Operation.REVERSE);
-            Thread.sleep(RAMP_TIME_MS + 1000);
+        // Reverse
+        Log.d(TAG, testName + " Reverse (volume should decrease)");
+        volumeShaper.apply(VolumeShaper.Operation.REVERSE);
+        Thread.sleep(RAMP_TIME_MS + 1000);
 
-            Log.d(TAG, testName + " Check Volume (volume should be silent)");
-            assertEquals(testName + " volume should be 0.f",
-                    0.f, volumeShaper.getVolume(), VOLUME_TOLERANCE);
+        Log.d(TAG, testName + " Check Volume (volume should be silent)");
+        assertEquals(testName + " volume should be 0.f",
+                0.f, volumeShaper.getVolume(), VOLUME_TOLERANCE);
 
-            // Forwards
-            Log.d(TAG, testName + " Play (volume should increase)");
-            volumeShaper.apply(VolumeShaper.Operation.PLAY);
-            Thread.sleep(RAMP_TIME_MS + 1000);
+        // Forwards
+        Log.d(TAG, testName + " Play (volume should increase)");
+        volumeShaper.apply(VolumeShaper.Operation.PLAY);
+        Thread.sleep(RAMP_TIME_MS + 1000);
 
-            // Comment out for headset plug/unplug test
-            // Log.d(TAG, testName + " headset check"); Thread.sleep(10000 /* millis */);
-            //
+        // Comment out for headset plug/unplug test
+        // Log.d(TAG, testName + " headset check"); Thread.sleep(10000 /* millis */);
+        //
 
-            Log.d(TAG, testName + " Check Volume (volume at max)");
-            assertEquals(testName + " volume should be 1.f",
-                    1.f, volumeShaper.getVolume(), VOLUME_TOLERANCE);
+        Log.d(TAG, testName + " Check Volume (volume at max)");
+        assertEquals(testName + " volume should be 1.f",
+                1.f, volumeShaper.getVolume(), VOLUME_TOLERANCE);
 
-            Log.d(TAG, testName + " done");
-        }
+        Log.d(TAG, testName + " done");
     } // runRampTest
 
     // Player should be started before calling (as it is not an argument to method).
@@ -1392,4 +1520,8 @@
         assertTrue(testName + " volume should be greater than 0.f",
                 volumeShaper.getVolume() > 0.f);
     } // runStartSyncTest
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getTargetContext();
+    }
 }
diff --git a/tests/tests/media/codec/Android.bp b/tests/tests/media/codec/Android.bp
new file mode 100644
index 0000000..ab6edba
--- /dev/null
+++ b/tests/tests/media/codec/Android.bp
@@ -0,0 +1,64 @@
+// 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license", // CC-BY
+    ],
+}
+
+android_test {
+    name: "CtsMediaCodecTestCases",
+    defaults: ["cts_defaults"],
+    // include both the 32 and 64 bit versions
+    compile_multilib: "both",
+    static_libs: [
+        "ctstestrunner-axt",
+        "cts-media-common",
+        "testng",
+    ],
+    jni_libs: [
+        "libctsmediacommon_jni",
+    ],
+    aaptflags: [
+        // Do not compress these files:
+        "-0 .vp9",
+        "-0 .ts",
+        "-0 .heic",
+        "-0 .trp",
+        "-0 .ota",
+        "-0 .mxmf",
+    ],
+    srcs: [
+        "src/**/*.java",
+        "aidl/**/*.aidl",
+    ],
+    // This test uses private APIs
+    platform_apis: true,
+    jni_uses_sdk_apis: true,
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+    ],
+    test_suites: [
+        "cts",
+        "general-tests",
+        "mts-media",
+    ],
+    host_required: ["cts-dynamic-config"],
+    min_sdk_version: "29",
+    target_sdk_version: "31",
+}
diff --git a/tests/tests/media/codec/AndroidManifest.xml b/tests/tests/media/codec/AndroidManifest.xml
new file mode 100644
index 0000000..4ff8f2d
--- /dev/null
+++ b/tests/tests/media/codec/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="android.media.codec.cts"
+     android:targetSandboxVersion="2">
+
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="31"/>
+
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+
+    <application android:requestLegacyExternalStorage="true"
+         android:largeHeap="true">
+        <uses-library android:name="android.test.runner"/>
+        <service android:name="android.media.codec.cts.RemoteVirtualDisplayService"
+            android:process=":remoteService"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+            </intent-filter>
+        </service>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="android.media.codec.cts"
+         android:label="CTS tests of android.media">
+        <meta-data android:name="listener"
+             android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/mediadrm/AndroidTest.xml b/tests/tests/media/codec/AndroidTest.xml
similarity index 69%
copy from tests/tests/mediadrm/AndroidTest.xml
copy to tests/tests/media/codec/AndroidTest.xml
index f45ddbf..ce2b7ed 100644
--- a/tests/tests/mediadrm/AndroidTest.xml
+++ b/tests/tests/media/codec/AndroidTest.xml
@@ -13,38 +13,42 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Config for CTS Media Drm test cases">
+<configuration description="Config for CTS Media test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+        <option name="set-test-harness" value="false" />
+        <option name="screen-always-on" value="on" />
+        <option name="screen-adaptive-brightness" value="off" />
+        <option name="disable-audio" value="false"/>
+        <option name="screen-saver" value="off"/>
+    </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="host" />
-        <option name="config-filename" value="CtsMediaDrmTestCases" />
-        <option name="dynamic-config-name" value="CtsMediaDrmTestCases" />
-        <option name="version" value="9.0_r1"/>
+        <option name="config-filename" value="CtsMediaCodecTestCases" />
+        <option name="dynamic-config-name" value="CtsMediaCodecTestCases" />
+        <option name="version" value="1.0"/>
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="push-all" value="true" />
-        <option name="media-folder-name" value="CtsMediaDrmTestCases-1.4" />
-        <option name="dynamic-config-module" value="CtsMediaDrmTestCases" />
+        <option name="media-folder-name" value="CtsMediaCodecTestCases-1.0" />
+        <option name="dynamic-config-module" value="CtsMediaCodecTestCases" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsMediaDrmTestCases.apk" />
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <!-- MediaProjectionTest needs this one to not be granted, SuiteApkInstaller grants all of them by default.-->
-        <option name="run-command" value="pm revoke android.mediadrm.cts android.permission.SYSTEM_ALERT_WINDOW"/>
+        <option name="test-file-name" value="CtsMediaCodecTestCases.apk" />
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="device" />
-        <option name="config-filename" value="CtsMediaDrmTestCases" />
+        <option name="config-filename" value="CtsMediaCodecTestCases" />
         <option name="version" value="7.0"/>
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.mediadrm.cts" />
+        <option name="package" value="android.media.codec.cts" />
         <!-- setup can be expensive so limit the number of shards -->
         <option name="ajur-max-shard" value="5" />
         <!-- test-timeout unit is ms, value = 30 min -->
diff --git a/tests/tests/media/codec/DynamicConfig.xml b/tests/tests/media/codec/DynamicConfig.xml
new file mode 100644
index 0000000..53dbb01
--- /dev/null
+++ b/tests/tests/media/codec/DynamicConfig.xml
@@ -0,0 +1,32 @@
+<!-- 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.
+-->
+
+<dynamicConfig>
+    <entry key="media_codec_capabilities_test_avc_baseline12">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=160&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=9EDCA0B395B8A949C511FD5E59B9F805CFF797FD.702DE9BA7AF96785FD6930AD2DD693A0486C880E&amp;key=ik0</value>
+    </entry>
+    <entry key="media_codec_capabilities_test_avc_baseline30">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=18&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=7DCDE3A6594D0B91A27676A3CDC3A87B149F82EA.7A83031734CB1EDCE06766B6228842F954927960&amp;key=ik0</value>
+    </entry>
+    <entry key="media_codec_capabilities_test_avc_high31">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=22&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=179525311196616BD8E1381759B0E5F81A9E91B5.C4A50E44059FEBCC6BBC78E3B3A4E0E0065777&amp;key=ik0</value>
+    </entry>
+    <entry key="media_codec_capabilities_test_avc_high40">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=137&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=B0976085596DD42DEA3F08307F76587241CB132B.043B719C039E8B92F45391ADC0BE3665E2332930&amp;key=ik0</value>
+    </entry>
+    <entry key="media_files_url">
+    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/codec/CtsMediaCodecTestCases-1.0.zip</value>
+    </entry>
+</dynamicConfig>
diff --git a/tests/tests/media/codec/OWNERS b/tests/tests/media/codec/OWNERS
new file mode 100644
index 0000000..f3d95c44
--- /dev/null
+++ b/tests/tests/media/codec/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+include ../../../media/OWNERS
diff --git a/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
index 4bada72..5820089 100644
--- a/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/DecodeEditEncodeTest.java
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
+import android.media.cts.InputSurface;
+import android.media.cts.OutputSurface;
 import android.opengl.GLES20;
 import android.test.AndroidTestCase;
 import android.util.Log;
diff --git a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
old mode 100755
new mode 100644
similarity index 99%
rename from tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
index b82019a..00b2e82
--- a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeDecodeTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.graphics.ImageFormat;
 import android.media.Image;
@@ -22,6 +22,12 @@
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
+import android.media.cts.InputSurface;
+import android.media.cts.InputSurfaceInterface;
+import android.media.cts.MediaCodecWrapper;
+import android.media.cts.NdkMediaCodec;
+import android.media.cts.OutputSurface;
+import android.media.cts.SdkMediaCodec;
 import android.opengl.GLES20;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresDevice;
diff --git a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
similarity index 98%
rename from tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
index 96ae72a..9250141 100755
--- a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.app.Presentation;
 import android.content.Context;
@@ -26,6 +26,10 @@
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
 import android.media.MediaMuxer;
+import android.media.cts.CompositionTextureView;
+import android.media.cts.InputSurface;
+import android.media.cts.NonMediaMainlineTest;
+import android.media.cts.OutputSurface;
 import android.opengl.GLES20;
 import android.os.Build;
 import android.os.Bundle;
diff --git a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayWithCompositionTest.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTest.java
similarity index 98%
rename from tests/tests/media/src/android/media/cts/EncodeVirtualDisplayWithCompositionTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTest.java
index de54f12..0625743 100644
--- a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayWithCompositionTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTest.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.media.MediaFormat;
+import android.media.cts.NonMediaMainlineTest;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresDevice;
 import android.test.AndroidTestCase;
diff --git a/tests/tests/media/common/src/android/media/cts/EncodeVirtualDisplayWithCompositionTestImpl.java b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTestImpl.java
similarity index 99%
rename from tests/tests/media/common/src/android/media/cts/EncodeVirtualDisplayWithCompositionTestImpl.java
rename to tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTestImpl.java
index 4ad8d27..b9c1856 100644
--- a/tests/tests/media/common/src/android/media/cts/EncodeVirtualDisplayWithCompositionTestImpl.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/EncodeVirtualDisplayWithCompositionTestImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.app.Presentation;
 import android.content.ComponentName;
@@ -30,7 +30,10 @@
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
+import android.media.cts.CompositionTextureView;
 import android.media.cts.R;
+import android.media.cts.InputSurface;
+import android.media.cts.OutputSurface;
 import android.opengl.GLES11Ext;
 import android.opengl.GLES20;
 import android.opengl.Matrix;
@@ -1403,8 +1406,8 @@
 
         void connect() throws Exception {
             Intent intent = new Intent();
-            intent.setClassName("android.media.cts",
-                    "android.media.cts.RemoteVirtualDisplayService");
+            intent.setClassName("android.media.codec.cts",
+                    "android.media.codec.cts.RemoteVirtualDisplayService");
             mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
             if (!mConnectionWait.tryAcquire(DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
                 fail("cannot bind to service");
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/codec/src/android/media/codec/cts/ExtractDecodeEditEncodeMuxTest.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/ExtractDecodeEditEncodeMuxTest.java
index a1a8150..eec4568 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.annotation.TargetApi;
 import android.content.res.AssetFileDescriptor;
@@ -27,6 +27,10 @@
 import android.media.MediaFormat;
 import android.media.MediaMuxer;
 import android.media.MediaPlayer;
+import android.media.cts.InputSurface;
+import android.media.cts.MediaStubActivity;
+import android.media.cts.OutputSurface;
+import android.media.cts.Preconditions;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
diff --git a/tests/tests/media/src/android/media/cts/IvfReader.java b/tests/tests/media/codec/src/android/media/codec/cts/IvfReader.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/IvfReader.java
rename to tests/tests/media/codec/src/android/media/codec/cts/IvfReader.java
index 2f679ae..35d3220 100644
--- a/tests/tests/media/src/android/media/cts/IvfReader.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/IvfReader.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import java.io.IOException;
 import java.io.RandomAccessFile;
diff --git a/tests/tests/media/src/android/media/cts/IvfWriter.java b/tests/tests/media/codec/src/android/media/codec/cts/IvfWriter.java
similarity index 85%
rename from tests/tests/media/src/android/media/cts/IvfWriter.java
rename to tests/tests/media/codec/src/android/media/codec/cts/IvfWriter.java
index 36fb679..a41143b 100644
--- a/tests/tests/media/src/android/media/cts/IvfWriter.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/IvfWriter.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.media.MediaFormat;
+import android.util.Log;
 
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -29,6 +30,7 @@
  */
 
 public class IvfWriter {
+    private static final String TAG = "IvfWriter";
     private static final byte HEADER_END = 32;
     private RandomAccessFile mOutputFile;
     private int mWidth;
@@ -102,6 +104,24 @@
         mFrameCount++;
     }
 
+    private static byte[] getCodecFourcc(String mimeType) {
+        switch(mimeType) {
+        case MediaFormat.MIMETYPE_VIDEO_AVC:
+            return new byte[] {'H', '2', '6', '4'};
+        case MediaFormat.MIMETYPE_VIDEO_HEVC:
+            return new byte[] {'H', 'E', 'V', 'C'};
+        case MediaFormat.MIMETYPE_VIDEO_VP8:
+            return new byte[] {'V', 'P', '8', '0'};
+        case MediaFormat.MIMETYPE_VIDEO_VP9:
+            return new byte[] {'V', 'P', '9', '0'};
+        case MediaFormat.MIMETYPE_VIDEO_AV1:
+            return new byte[] {'A', 'V', '0', '1'};
+        default:
+            Log.w(TAG, "Unexpected mimeType in getCodecFourcc: " + mimeType);
+            return new byte[] {'0', '0', '0', '0'};
+      }
+    }
+
     /**
      * Makes a 32 byte file header for IVF format.
      *
@@ -122,10 +142,11 @@
         ivfHeader[3] = 'F';
         lay16Bits(ivfHeader, 4, 0);  // version
         lay16Bits(ivfHeader, 6, 32);  // header size
-        ivfHeader[8] = 'V';  // fourcc
-        ivfHeader[9] = 'P';
-        ivfHeader[10] = (byte) (MediaFormat.MIMETYPE_VIDEO_VP8.equals(mimeType) ? '8' : '9');
-        ivfHeader[11] = '0';
+        byte[] codecFourcc = getCodecFourcc(mimeType);
+        ivfHeader[8] = codecFourcc[0];
+        ivfHeader[9] = codecFourcc[1];
+        ivfHeader[10] = codecFourcc[2];
+        ivfHeader[11] = codecFourcc[3];
         lay16Bits(ivfHeader, 12, width);
         lay16Bits(ivfHeader, 14, height);
         lay32Bits(ivfHeader, 16, rate);  // scale/rate
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecBlockModelTest.java
similarity index 98%
rename from tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/MediaCodecBlockModelTest.java
index b4f41cd..475bd74 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecBlockModelTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.content.res.AssetFileDescriptor;
 import android.hardware.HardwareBuffer;
@@ -26,6 +26,8 @@
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.cts.MediaCodecBlockModelHelper;
+import android.media.cts.NonMediaMainlineTest;
+import android.media.cts.Preconditions;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecCapabilitiesTest.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/MediaCodecCapabilitiesTest.java
index 4511867..91dcf3a 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecCapabilitiesTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.app.ActivityManager;
 import android.content.Context;
@@ -37,6 +37,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.media.cts.MediaPlayerTestBase;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Range;
@@ -82,7 +83,7 @@
             "media_codec_capabilities_test_avc_baseline30";
     private static final String AVC_HIGH_31_KEY = "media_codec_capabilities_test_avc_high31";
     private static final String AVC_HIGH_40_KEY = "media_codec_capabilities_test_avc_high40";
-    private static final String MODULE_NAME = "CtsMediaTestCases";
+    private static final String MODULE_NAME = "CtsMediaCodecTestCases";
     private DynamicConfigDeviceSide dynamicConfig;
 
     @Before
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/MediaCodecTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java
index b20e259..6ea7ef9 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import static org.testng.Assert.assertThrows;
 
@@ -38,6 +38,9 @@
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.cts.AudioHelper;
+import android.media.cts.InputSurface;
+import android.media.cts.OutputSurface;
+import android.media.cts.Preconditions;
 import android.media.cts.StreamUtils;
 import android.opengl.GLES20;
 import android.os.Build;
diff --git a/tests/tests/media/common/src/android/media/cts/RemoteVirtualDisplayService.java b/tests/tests/media/codec/src/android/media/codec/cts/RemoteVirtualDisplayService.java
similarity index 99%
rename from tests/tests/media/common/src/android/media/cts/RemoteVirtualDisplayService.java
rename to tests/tests/media/codec/src/android/media/codec/cts/RemoteVirtualDisplayService.java
index 38cd279..fbcc904 100644
--- a/tests/tests/media/common/src/android/media/cts/RemoteVirtualDisplayService.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/RemoteVirtualDisplayService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.app.Presentation;
 import android.app.Service;
diff --git a/tests/tests/media/src/android/media/cts/VideoCodecTest.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/VideoCodecTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java
index 697d3ff..2ee1137 100644
--- a/tests/tests/media/src/android/media/cts/VideoCodecTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTest.java
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
+import android.media.cts.MediaCodecWrapper;
+import android.media.cts.MediaHeavyPresubmitTest;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
diff --git a/tests/tests/media/src/android/media/cts/VideoCodecTestBase.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTestBase.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/VideoCodecTestBase.java
rename to tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTestBase.java
index 31785e3..02754ab 100644
--- a/tests/tests/media/src/android/media/cts/VideoCodecTestBase.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoCodecTestBase.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -24,6 +24,10 @@
 import android.media.MediaCodecList;
 import android.media.MediaCodecInfo;
 import android.media.MediaFormat;
+import android.media.cts.MediaCodecWrapper;
+import android.media.cts.NdkMediaCodec;
+import android.media.cts.Preconditions;
+import android.media.cts.SdkMediaCodec;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Looper;
diff --git a/tests/tests/media/src/android/media/cts/VideoDecoderRotationTest.java b/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java
similarity index 97%
rename from tests/tests/media/src/android/media/cts/VideoDecoderRotationTest.java
rename to tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java
index 08c22b7..2cd4632 100644
--- a/tests/tests/media/src/android/media/cts/VideoDecoderRotationTest.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/VideoDecoderRotationTest.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.codec.cts;
 
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
+import android.media.cts.NonMediaMainlineTest;
 import android.platform.test.annotations.RequiresDevice;
 import android.util.Log;
 import android.util.Size;
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java b/tests/tests/media/codec/src/android/media/codec/cts/WorkDir.java
similarity index 82%
copy from tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
copy to tests/tests/media/codec/src/android/media/codec/cts/WorkDir.java
index e3fb1b5..10921c8 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
+++ b/tests/tests/media/codec/src/android/media/codec/cts/WorkDir.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media.audio.cts;
+package android.media.codec.cts;
 
 import android.media.cts.WorkDirBase;
 
 class WorkDir extends WorkDirBase {
     public static final String getMediaDirString() {
-        return getMediaDirString("CtsMediaTestCases-1.4");
+        return getMediaDirString("CtsMediaCodecTestCases-1.0");
     }
 }
diff --git a/tests/tests/media/common/jni/NdkInputSurface-jni.cpp b/tests/tests/media/common/jni/NdkInputSurface-jni.cpp
new file mode 100644
index 0000000..bb4009b
--- /dev/null
+++ b/tests/tests/media/common/jni/NdkInputSurface-jni.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NdkInputSurfaceJni"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <android/native_window_jni.h>
+#include <assert.h>
+#include <jni.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+extern "C" jlong Java_android_media_cts_NdkInputSurface_eglGetDisplay(JNIEnv * /*env*/, jclass /*clazz*/) {
+
+    EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (eglDisplay == EGL_NO_DISPLAY) {
+        return 0;
+    }
+
+    EGLint major, minor;
+    if (!eglInitialize(eglDisplay, &major, &minor)) {
+        return 0;
+    }
+
+    return reinterpret_cast<jlong>(eglDisplay);
+
+}
+
+extern "C" jlong Java_android_media_cts_NdkInputSurface_eglChooseConfig(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay) {
+
+    // Configure EGL for recordable and OpenGL ES 2.0.  We want enough RGB bits
+    // to minimize artifacts from possible YUV conversion.
+    EGLint attribList[] = {
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RECORDABLE_ANDROID, 1,
+            EGL_NONE
+    };
+
+    EGLConfig configs[1];
+    EGLint numConfigs[1];
+    if (!eglChooseConfig(reinterpret_cast<EGLDisplay>(eglDisplay), attribList, configs, 1, numConfigs)) {
+        return 0;
+    }
+    return reinterpret_cast<jlong>(configs[0]);
+
+}
+
+extern "C" jlong Java_android_media_cts_NdkInputSurface_eglCreateContext(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglConfig) {
+
+    // Configure context for OpenGL ES 2.0.
+    int attrib_list[] = {
+            EGL_CONTEXT_CLIENT_VERSION, 2,
+            EGL_NONE
+    };
+
+    EGLConfig eglContext = eglCreateContext(
+            reinterpret_cast<EGLDisplay>(eglDisplay),
+            reinterpret_cast<EGLConfig>(eglConfig),
+            EGL_NO_CONTEXT,
+            attrib_list);
+
+    if (eglGetError() != EGL_SUCCESS) {
+        return 0;
+    }
+
+    return reinterpret_cast<jlong>(eglContext);
+
+}
+
+extern "C" jlong Java_android_media_cts_NdkInputSurface_createEGLSurface(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglConfig, jlong nativeWindow) {
+
+    int surfaceAttribs[] = {EGL_NONE};
+    EGLSurface eglSurface = eglCreateWindowSurface(
+            reinterpret_cast<EGLDisplay>(eglDisplay),
+            reinterpret_cast<EGLConfig>(eglConfig),
+            reinterpret_cast<EGLNativeWindowType>(nativeWindow),
+            surfaceAttribs);
+
+    if (eglGetError() != EGL_SUCCESS) {
+        return 0;
+    }
+
+    return reinterpret_cast<jlong>(eglSurface);
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkInputSurface_eglMakeCurrent(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface, jlong eglContext) {
+
+    return eglMakeCurrent(
+            reinterpret_cast<EGLDisplay>(eglDisplay),
+            reinterpret_cast<EGLSurface>(eglSurface),
+            reinterpret_cast<EGLSurface>(eglSurface),
+            reinterpret_cast<EGLContext>(eglContext));
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkInputSurface_eglSwapBuffers(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface) {
+
+    return eglSwapBuffers(
+            reinterpret_cast<EGLDisplay>(eglDisplay),
+            reinterpret_cast<EGLSurface>(eglSurface));
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkInputSurface_eglPresentationTimeANDROID(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface, jlong nsecs) {
+
+    return eglPresentationTimeANDROID(
+            reinterpret_cast<EGLDisplay>(eglDisplay),
+            reinterpret_cast<EGLSurface>(eglSurface),
+            reinterpret_cast<EGLnsecsANDROID>(nsecs));
+
+}
+
+extern "C" jint Java_android_media_cts_NdkInputSurface_eglGetWidth(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface) {
+
+    EGLint width;
+    eglQuerySurface(
+            reinterpret_cast<EGLDisplay>(eglDisplay),
+            reinterpret_cast<EGLSurface>(eglSurface),
+            EGL_WIDTH,
+            &width);
+
+    return width;
+
+}
+
+extern "C" jint Java_android_media_cts_NdkInputSurface_eglGetHeight(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface) {
+
+    EGLint height;
+    eglQuerySurface(
+            reinterpret_cast<EGLDisplay>(eglDisplay),
+            reinterpret_cast<EGLSurface>(eglSurface),
+            EGL_HEIGHT,
+            &height);
+
+    return height;
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkInputSurface_eglDestroySurface(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface) {
+
+    return eglDestroySurface(
+            reinterpret_cast<EGLDisplay>(eglDisplay),
+            reinterpret_cast<EGLSurface>(eglSurface));
+
+}
+
+extern "C" void Java_android_media_cts_NdkInputSurface_nativeRelease(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface, jlong eglContext, jlong nativeWindow) {
+
+    if (eglDisplay != 0) {
+
+        EGLDisplay _eglDisplay = reinterpret_cast<EGLDisplay>(eglDisplay);
+        EGLSurface _eglSurface = reinterpret_cast<EGLSurface>(eglSurface);
+        EGLContext _eglContext = reinterpret_cast<EGLContext>(eglContext);
+
+        eglDestroySurface(_eglDisplay, _eglSurface);
+        eglDestroyContext(_eglDisplay, _eglContext);
+        eglReleaseThread();
+        eglTerminate(_eglDisplay);
+
+    }
+
+    ANativeWindow_release(reinterpret_cast<ANativeWindow *>(nativeWindow));
+
+}
\ No newline at end of file
diff --git a/tests/tests/media/common/jni/NdkMediaCodec-jni.cpp b/tests/tests/media/common/jni/NdkMediaCodec-jni.cpp
new file mode 100644
index 0000000..441e0f3
--- /dev/null
+++ b/tests/tests/media/common/jni/NdkMediaCodec-jni.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Original code copied from NDK Native-media sample code */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NdkMediaCodec-jni"
+#include <log/log.h>
+
+#include <android/native_window_jni.h>
+#include <assert.h>
+#include <jni.h>
+#include <string.h>
+#include <unistd.h>
+
+
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaCodec.h"
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDataSource.h"
+#include "media/NdkMediaFormat.h"
+#include "media/NdkMediaMuxer.h"
+
+extern "C" jlong Java_android_media_cts_NdkMediaCodec_AMediaCodecCreateCodecByName(
+        JNIEnv *env, jclass /*clazz*/, jstring name) {
+
+    if (name == NULL) {
+        return 0;
+    }
+
+    const char *tmp = env->GetStringUTFChars(name, NULL);
+    if (tmp == NULL) {
+        return 0;
+    }
+
+    AMediaCodec *codec = AMediaCodec_createCodecByName(tmp);
+    if (codec == NULL) {
+        env->ReleaseStringUTFChars(name, tmp);
+        return 0;
+    }
+
+    env->ReleaseStringUTFChars(name, tmp);
+    return reinterpret_cast<jlong>(codec);
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecDelete(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong codec) {
+    media_status_t err = AMediaCodec_delete(reinterpret_cast<AMediaCodec *>(codec));
+    return err == AMEDIA_OK;
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecStart(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong codec) {
+    media_status_t err = AMediaCodec_start(reinterpret_cast<AMediaCodec *>(codec));
+    return err == AMEDIA_OK;
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecStop(
+        JNIEnv * /*env*/, jclass /*clazz*/, jlong codec) {
+    media_status_t err = AMediaCodec_stop(reinterpret_cast<AMediaCodec *>(codec));
+    return err == AMEDIA_OK;
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecConfigure(
+        JNIEnv *env,
+        jclass /*clazz*/,
+        jlong codec,
+        jstring mime,
+        jint width,
+        jint height,
+        jint colorFormat,
+        jint bitRate,
+        jint frameRate,
+        jint iFrameInterval,
+        jobject csd0,
+        jobject csd1,
+        jint flags,
+        jint lowLatency,
+        jobject surface,
+        jint range,
+        jint standard,
+        jint transfer) {
+
+    AMediaFormat* format = AMediaFormat_new();
+    if (format == NULL) {
+        return false;
+    }
+
+    const char *tmp = env->GetStringUTFChars(mime, NULL);
+    if (tmp == NULL) {
+        AMediaFormat_delete(format);
+        return false;
+    }
+
+    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, tmp);
+    env->ReleaseStringUTFChars(mime, tmp);
+
+    const char *keys[] = {
+            AMEDIAFORMAT_KEY_WIDTH,
+            AMEDIAFORMAT_KEY_HEIGHT,
+            AMEDIAFORMAT_KEY_COLOR_FORMAT,
+            AMEDIAFORMAT_KEY_BIT_RATE,
+            AMEDIAFORMAT_KEY_FRAME_RATE,
+            AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
+            // need to specify the actual string, since this test needs
+            // to run on API 29, where the symbol doesn't exist
+            "low-latency", // AMEDIAFORMAT_KEY_LOW_LATENCY
+            AMEDIAFORMAT_KEY_COLOR_RANGE,
+            AMEDIAFORMAT_KEY_COLOR_STANDARD,
+            AMEDIAFORMAT_KEY_COLOR_TRANSFER,
+    };
+
+    jint values[] = {width, height, colorFormat, bitRate, frameRate, iFrameInterval, lowLatency,
+                     range, standard, transfer};
+    for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) {
+        if (values[i] >= 0) {
+            AMediaFormat_setInt32(format, keys[i], values[i]);
+        }
+    }
+
+    if (csd0 != NULL) {
+        void *csd0Ptr = env->GetDirectBufferAddress(csd0);
+        jlong csd0Size = env->GetDirectBufferCapacity(csd0);
+        AMediaFormat_setBuffer(format, "csd-0", csd0Ptr, csd0Size);
+    }
+
+    if (csd1 != NULL) {
+        void *csd1Ptr = env->GetDirectBufferAddress(csd1);
+        jlong csd1Size = env->GetDirectBufferCapacity(csd1);
+        AMediaFormat_setBuffer(format, "csd-1", csd1Ptr, csd1Size);
+    }
+
+    media_status_t err = AMediaCodec_configure(
+            reinterpret_cast<AMediaCodec *>(codec),
+            format,
+            surface == NULL ? NULL : ANativeWindow_fromSurface(env, surface),
+            NULL,
+            flags);
+
+    AMediaFormat_delete(format);
+    return err == AMEDIA_OK;
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSetInputSurface(
+        JNIEnv* env, jclass /*clazz*/, jlong codec, jobject surface) {
+
+    media_status_t err = AMediaCodec_setInputSurface(
+            reinterpret_cast<AMediaCodec *>(codec),
+            ANativeWindow_fromSurface(env, surface));
+
+    return err == AMEDIA_OK;
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSetNativeInputSurface(
+        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec, jlong nativeWindow) {
+
+    media_status_t err = AMediaCodec_setInputSurface(
+            reinterpret_cast<AMediaCodec *>(codec),
+            reinterpret_cast<ANativeWindow *>(nativeWindow));
+
+    return err == AMEDIA_OK;
+
+}
+
+extern "C" jlong Java_android_media_cts_NdkMediaCodec_AMediaCodecCreateInputSurface(
+        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec) {
+
+    ANativeWindow *nativeWindow;
+    media_status_t err = AMediaCodec_createInputSurface(
+            reinterpret_cast<AMediaCodec *>(codec),
+            &nativeWindow);
+
+     if (err == AMEDIA_OK) {
+         return reinterpret_cast<jlong>(nativeWindow);
+     }
+
+     return 0;
+
+}
+
+extern "C" jlong Java_android_media_cts_NdkMediaCodec_AMediaCodecCreatePersistentInputSurface(
+        JNIEnv* /*env*/, jclass /*clazz*/) {
+
+    ANativeWindow *nativeWindow;
+    media_status_t err = AMediaCodec_createPersistentInputSurface(&nativeWindow);
+
+     if (err == AMEDIA_OK) {
+         return reinterpret_cast<jlong>(nativeWindow);
+     }
+
+     return 0;
+
+}
+
+extern "C" jstring Java_android_media_cts_NdkMediaCodec_AMediaCodecGetOutputFormatString(
+        JNIEnv* env, jclass /*clazz*/, jlong codec) {
+
+    AMediaFormat *format = AMediaCodec_getOutputFormat(reinterpret_cast<AMediaCodec *>(codec));
+    const char *str = AMediaFormat_toString(format);
+    jstring jstr = env->NewStringUTF(str);
+    AMediaFormat_delete(format);
+    return jstr;
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSignalEndOfInputStream(
+        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec) {
+
+    media_status_t err = AMediaCodec_signalEndOfInputStream(reinterpret_cast<AMediaCodec *>(codec));
+    return err == AMEDIA_OK;
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecReleaseOutputBuffer(
+        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec, jint index, jboolean render) {
+
+    media_status_t err = AMediaCodec_releaseOutputBuffer(
+            reinterpret_cast<AMediaCodec *>(codec),
+            index,
+            render);
+
+    return err == AMEDIA_OK;
+
+}
+
+static jobject AMediaCodecGetBuffer(
+        JNIEnv* env,
+        jlong codec,
+        jint index,
+        uint8_t *(*getBuffer)(AMediaCodec*, size_t, size_t*)) {
+
+    size_t bufsize;
+    uint8_t *buf = getBuffer(
+            reinterpret_cast<AMediaCodec *>(codec),
+            index,
+            &bufsize);
+
+    return env->NewDirectByteBuffer(buf, bufsize);
+
+}
+
+extern "C" jobject Java_android_media_cts_NdkMediaCodec_AMediaCodecGetOutputBuffer(
+        JNIEnv* env, jclass /*clazz*/, jlong codec, jint index) {
+
+    return AMediaCodecGetBuffer(env, codec, index, AMediaCodec_getOutputBuffer);
+
+}
+
+extern "C" jlongArray Java_android_media_cts_NdkMediaCodec_AMediaCodecDequeueOutputBuffer(
+        JNIEnv* env, jclass /*clazz*/, jlong codec, jlong timeoutUs) {
+
+    AMediaCodecBufferInfo info;
+    memset(&info, 0, sizeof(info));
+    int status = AMediaCodec_dequeueOutputBuffer(
+        reinterpret_cast<AMediaCodec *>(codec),
+        &info,
+        timeoutUs);
+
+    jlong ret[5] = {0};
+    ret[0] = status;
+    ret[1] = 0; // NdkMediaCodec calls ABuffer::data, which already adds offset
+    ret[2] = info.size;
+    ret[3] = info.presentationTimeUs;
+    ret[4] = info.flags;
+
+    jlongArray jret = env->NewLongArray(5);
+    env->SetLongArrayRegion(jret, 0, 5, ret);
+    return jret;
+
+}
+
+extern "C" jobject Java_android_media_cts_NdkMediaCodec_AMediaCodecGetInputBuffer(
+        JNIEnv* env, jclass /*clazz*/, jlong codec, jint index) {
+
+    return AMediaCodecGetBuffer(env, codec, index, AMediaCodec_getInputBuffer);
+
+}
+
+extern "C" jint Java_android_media_cts_NdkMediaCodec_AMediaCodecDequeueInputBuffer(
+        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec, jlong timeoutUs) {
+
+    return AMediaCodec_dequeueInputBuffer(
+            reinterpret_cast<AMediaCodec *>(codec),
+            timeoutUs);
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecQueueInputBuffer(
+        JNIEnv* /*env*/,
+        jclass /*clazz*/,
+        jlong codec,
+        jint index,
+        jint offset,
+        jint size,
+        jlong presentationTimeUs,
+        jint flags) {
+
+    media_status_t err = AMediaCodec_queueInputBuffer(
+            reinterpret_cast<AMediaCodec *>(codec),
+            index,
+            offset,
+            size,
+            presentationTimeUs,
+            flags);
+
+    return err == AMEDIA_OK;
+
+}
+
+extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSetParameter(
+        JNIEnv* env, jclass /*clazz*/, jlong codec, jstring jkey, jint value) {
+
+    AMediaFormat* params = AMediaFormat_new();
+    if (params == NULL) {
+        return false;
+    }
+
+    const char *key = env->GetStringUTFChars(jkey, NULL);
+    if (key == NULL) {
+        AMediaFormat_delete(params);
+        return false;
+    }
+
+    AMediaFormat_setInt32(params, key, value);
+    media_status_t err = AMediaCodec_setParameters(
+            reinterpret_cast<AMediaCodec *>(codec),
+            params);
+    env->ReleaseStringUTFChars(jkey, key);
+    AMediaFormat_delete(params);
+    return err == AMEDIA_OK;
+
+}
\ No newline at end of file
diff --git a/tests/tests/media/libmediandkjni/codec-utils-jni.cpp b/tests/tests/media/common/jni/codec-utils-jni.cpp
similarity index 93%
rename from tests/tests/media/libmediandkjni/codec-utils-jni.cpp
rename to tests/tests/media/common/jni/codec-utils-jni.cpp
index 9af0cfe..e14dd8e 100644
--- a/tests/tests/media/libmediandkjni/codec-utils-jni.cpp
+++ b/tests/tests/media/common/jni/codec-utils-jni.cpp
@@ -38,6 +38,9 @@
 
 #include "md5_utils.h"
 
+// ImageFormat.YCBCR_P010 was defined in API31.
+#define __YCBCR_P010_MIN_API__ 31
+
 typedef ssize_t offs_t;
 
 struct NativeImage {
@@ -99,6 +102,7 @@
 static struct ImageFieldsAndMethods {
     // android.graphics.ImageFormat
     int YUV_420_888;
+    int YCBCR_P010;
     // android.media.Image
     jmethodID methodWidth;
     jmethodID methodHeight;
@@ -126,6 +130,14 @@
         jclass imageFormatClazz = env->FindClass("android/graphics/ImageFormat");
         const jfieldID fieldYUV420888 = env->GetStaticFieldID(imageFormatClazz, "YUV_420_888", "I");
         gFields.YUV_420_888 = env->GetStaticIntField(imageFormatClazz, fieldYUV420888);
+        if (__builtin_available(android __YCBCR_P010_MIN_API__, *)) {
+            const jfieldID fieldYCBCRP010 = env->GetStaticFieldID(imageFormatClazz,
+                    "YCBCR_P010", "I");
+            gFields.YCBCR_P010 = env->GetStaticIntField(imageFormatClazz, fieldYCBCRP010);
+        } else {
+            // Set this to -1 to ensure it doesn't get equated to any valid color format
+            gFields.YCBCR_P010 = -1;
+        }
         env->DeleteLocalRef(imageFormatClazz);
         imageFormatClazz = NULL;
     }
@@ -199,10 +211,10 @@
         cropRect = NULL;
     }
 
-    if (img->format != gFields.YUV_420_888) {
+    if (img->format != gFields.YUV_420_888 && img->format != gFields.YCBCR_P010) {
         jniThrowException(
                 env, "java/lang/UnsupportedOperationException",
-                "only support YUV_420_888 images");
+                "only support YUV_420_888 and YCBCR_P010 images");
         delete img;
         img = NULL;
         return NULL;
@@ -406,9 +418,16 @@
         uint8_t *vcol = (uint8_t *)vrow;
 
         for (size_t x = img->plane[0].cropWidth; x; --x) {
-            uint64_t Y = *ycol;
-            uint64_t U = *ucol;
-            uint64_t V = *vcol;
+            uint64_t Y = 0, U = 0, V = 0;
+            if (img->format == gFields.YCBCR_P010) {
+                Y = ((uint16_t)(*(ycol + 1)) << 2) | (*ycol >> 6);
+                U = ((uint16_t)(*(ucol + 1)) << 2) | (*ucol >> 6);
+                V = ((uint16_t)(*(vcol + 1)) << 2) | (*vcol >> 6);
+            } else {
+                Y = *ycol;
+                U = *ucol;
+                V = *vcol;
+            }
 
             sum_x[0] += Y;
             sum_x[1] += U;
diff --git a/tests/tests/media/libmediandkjni/md5_utils.cpp b/tests/tests/media/common/jni/md5_utils.cpp
similarity index 100%
rename from tests/tests/media/libmediandkjni/md5_utils.cpp
rename to tests/tests/media/common/jni/md5_utils.cpp
diff --git a/tests/tests/media/libmediandkjni/md5_utils.h b/tests/tests/media/common/jni/md5_utils.h
similarity index 100%
rename from tests/tests/media/libmediandkjni/md5_utils.h
rename to tests/tests/media/common/jni/md5_utils.h
diff --git a/tests/tests/media/common/src/android/media/cts/AudioHelper.java b/tests/tests/media/common/src/android/media/cts/AudioHelper.java
index ad09295..3130c9d 100644
--- a/tests/tests/media/common/src/android/media/cts/AudioHelper.java
+++ b/tests/tests/media/common/src/android/media/cts/AudioHelper.java
@@ -611,78 +611,4 @@
         private int mPosition;
         private long mFinishAtMs;
     }
-
-    /* AudioRecordAudit extends AudioRecord to allow concurrent playback
-     * of read content to an AudioTrack.  This is for testing only.
-     * For general applications, it is NOT recommended to extend AudioRecord.
-     * This affects AudioRecord timing.
-     */
-    public static class AudioRecordAuditNative extends AudioRecordNative {
-        public AudioRecordAuditNative() {
-            super();
-            // Caution: delayMs too large results in buffer sizes that cannot be created.
-            mTrack = new AudioTrackNative();
-        }
-
-        public boolean open(int numChannels, int sampleRate, boolean useFloat, int numBuffers) {
-            if (super.open(numChannels, sampleRate, useFloat, numBuffers)) {
-                if (!mTrack.open(numChannels, sampleRate, useFloat, 2 /* numBuffers */)) {
-                    mTrack = null; // remove track
-                }
-                return true;
-            }
-            return false;
-        }
-
-        public void close() {
-            super.close();
-            if (mTrack != null) {
-                mTrack.close();
-            }
-        }
-
-        public boolean start() {
-            if (super.start()) {
-                if (mTrack != null) {
-                    mTrack.start();
-                }
-                return true;
-            }
-            return false;
-        }
-
-        public boolean stop() {
-            if (super.stop()) {
-                if (mTrack != null) {
-                    mTrack.stop(); // doesn't allow remaining data to play out
-                }
-                return true;
-            }
-            return false;
-        }
-
-        public int read(short[] audioData, int offsetInShorts, int sizeInShorts, int readFlags) {
-            int samples = super.read(audioData, offsetInShorts, sizeInShorts, readFlags);
-            if (mTrack != null) {
-                Assert.assertEquals(samples, mTrack.write(audioData, offsetInShorts, samples,
-                        AudioTrackNative.WRITE_FLAG_BLOCKING));
-                mPosition += samples / mTrack.getChannelCount();
-            }
-            return samples;
-        }
-
-        public int read(float[] audioData, int offsetInFloats, int sizeInFloats, int readFlags) {
-            int samples = super.read(audioData, offsetInFloats, sizeInFloats, readFlags);
-            if (mTrack != null) {
-                Assert.assertEquals(samples, mTrack.write(audioData, offsetInFloats, samples,
-                        AudioTrackNative.WRITE_FLAG_BLOCKING));
-                mPosition += samples / mTrack.getChannelCount();
-            }
-            return samples;
-        }
-
-        public AudioTrackNative mTrack;
-        private final static String TAG = "AudioRecordAuditNative";
-        private int mPosition;
-    }
 }
diff --git a/tests/tests/media/common/src/android/media/cts/CodecState.java b/tests/tests/media/common/src/android/media/cts/CodecState.java
index 1e430b9..e117c3b 100644
--- a/tests/tests/media/common/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/common/src/android/media/cts/CodecState.java
@@ -27,10 +27,12 @@
 import android.util.Log;
 import android.view.Surface;
 
+import androidx.test.filters.SdkSuppress;
+
 import com.android.compatibility.common.util.ApiLevelUtil;
 import com.android.compatibility.common.util.MediaUtils;
 
-import androidx.test.filters.SdkSuppress;
+import com.google.common.collect.ImmutableList;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -43,10 +45,12 @@
 public class CodecState {
     private static final String TAG = CodecState.class.getSimpleName();
 
+    public static final int UNINITIALIZED_TIMESTAMP = Integer.MIN_VALUE;
+
     private boolean mSawInputEOS;
     private volatile boolean mSawOutputEOS;
     private boolean mLimitQueueDepth;
-    private boolean mTunneled;
+    private boolean mIsTunneled;
     private boolean mIsAudio;
     private int mAudioSessionId;
     private ByteBuffer[] mCodecInputBuffers;
@@ -55,10 +59,18 @@
     private int mAvailableInputBufferIndex;
     private LinkedList<Integer> mAvailableOutputBufferIndices;
     private LinkedList<MediaCodec.BufferInfo> mAvailableOutputBufferInfos;
-    private volatile long mPresentationTimeUs;
+    /**
+     * The media timestamp of the latest frame decoded by this codec.
+     *
+     * Note: in tunnel mode, this coincides with the latest rendered frame.
+     */
+    private volatile long mDecodedFramePresentationTimeUs;
+    private volatile long mRenderedVideoFramePresentationTimeUs;
+    private volatile long mRenderedVideoFrameSystemTimeNano;
     private long mFirstSampleTimeUs;
     private long mPlaybackStartTimeUs;
     private long mLastPresentTimeUs;
+    private long mOffsetTimestampUs = 0;
     private MediaCodec mCodec;
     private MediaTimeProvider mMediaTimeProvider;
     private MediaExtractor mExtractor;
@@ -68,8 +80,17 @@
     private volatile OnFrameRenderedListener mOnFrameRenderedListener;
     /** A list of reported rendered video frames' timestamps. */
     private ArrayList<Long> mRenderedVideoFrameTimestampList;
-    private boolean mFirstTunnelFrameReady;
+    private ArrayList<Long> mRenderedVideoFrameSystemTimeList;
+    private boolean mIsFirstTunnelFrameReady;
     private volatile OnFirstTunnelFrameReadyListener mOnFirstTunnelFrameReadyListener;
+    /** If true, starves the underlying {@link MediaCodec} to simulate an underrun. */
+    private boolean mShouldSimulateUnderrun;
+    /**
+     * An offset (in nanoseconds) to add to presentation timestamps fed to the {@link AudioTrack}.
+     *
+     * This is used to simulate desynchronization between tracks.
+     */
+    private long mAudioOffsetNs;
 
     private static boolean mIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
 
@@ -94,7 +115,7 @@
         mFormat = format;
         mSawInputEOS = mSawOutputEOS = false;
         mLimitQueueDepth = limitQueueDepth;
-        mTunneled = tunneled;
+        mIsTunneled = tunneled;
         mAudioSessionId = audioSessionId;
         mFirstSampleTimeUs = -1;
         mPlaybackStartTimeUs = 0;
@@ -106,18 +127,22 @@
         mAvailableOutputBufferIndices = new LinkedList<Integer>();
         mAvailableOutputBufferInfos = new LinkedList<MediaCodec.BufferInfo>();
         mRenderedVideoFrameTimestampList = new ArrayList<Long>();
+        mRenderedVideoFrameSystemTimeList = new ArrayList<Long>();
 
-        mPresentationTimeUs = 0;
+        mDecodedFramePresentationTimeUs = UNINITIALIZED_TIMESTAMP;
+        mRenderedVideoFramePresentationTimeUs = UNINITIALIZED_TIMESTAMP;
+        mRenderedVideoFrameSystemTimeNano = UNINITIALIZED_TIMESTAMP;
 
-        mFirstTunnelFrameReady = false;
+        mIsFirstTunnelFrameReady = false;
+        mShouldSimulateUnderrun = false;
+
+        mAudioOffsetNs = 0;
 
         String mime = mFormat.getString(MediaFormat.KEY_MIME);
         Log.d(TAG, "CodecState::CodecState " + mime);
         mIsAudio = mime.startsWith("audio/");
 
-        if (mTunneled && !mIsAudio) {
-            setFrameListeners(mCodec);
-        }
+        setFrameListeners(mCodec);
     }
 
     public void release() {
@@ -147,7 +172,7 @@
     public void start() {
         mCodec.start();
         mCodecInputBuffers = mCodec.getInputBuffers();
-        if (!mTunneled || mIsAudio) {
+        if (!mIsTunneled || mIsAudio) {
             mCodecOutputBuffers = mCodec.getOutputBuffers();
         }
 
@@ -162,12 +187,28 @@
         }
     }
 
+    /**
+     * Returns the media timestamp of the latest decoded sample/frame.
+     *
+     * TODO(b/202710709): Disambiguate getCurrentPosition's meaning
+     */
     public long getCurrentPositionUs() {
-        return mPresentationTimeUs;
+        // Use decoded frame time when available, otherwise default to render time (typically, in
+        // tunnel mode).
+        if (mDecodedFramePresentationTimeUs != UNINITIALIZED_TIMESTAMP) {
+            return mDecodedFramePresentationTimeUs;
+        } else {
+            return mRenderedVideoFramePresentationTimeUs;
+        }
+    }
+
+    /** Returns the system time of the latest rendered video frame. */
+    public long getRenderedVideoSystemTimeNano() {
+        return mRenderedVideoFrameSystemTimeNano;
     }
 
     public void flush() {
-        if (!mTunneled || mIsAudio) {
+        if (!mIsTunneled || mIsAudio) {
             mAvailableOutputBufferIndices.clear();
             mAvailableOutputBufferInfos.clear();
         }
@@ -182,9 +223,12 @@
         }
 
         mCodec.flush();
-        mPresentationTimeUs = 0;
+        mDecodedFramePresentationTimeUs = UNINITIALIZED_TIMESTAMP;
+        mRenderedVideoFramePresentationTimeUs = UNINITIALIZED_TIMESTAMP;
+        mRenderedVideoFrameSystemTimeNano = UNINITIALIZED_TIMESTAMP;
         mRenderedVideoFrameTimestampList = new ArrayList<Long>();
-        mFirstTunnelFrameReady = false;
+        mRenderedVideoFrameSystemTimeList = new ArrayList<Long>();
+        mIsFirstTunnelFrameReady = false;
     }
 
     public boolean isEnded() {
@@ -223,7 +267,7 @@
         }
 
         // Queue output data, if relevant
-        if (mIsAudio || !mTunneled) {
+        if (mIsAudio || !mIsTunneled) {
             MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
             int indexOutput = mCodec.dequeueOutputBuffer(info, 0 /* timeoutUs */);
 
@@ -250,12 +294,18 @@
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     private void setFrameListeners(MediaCodec codec) {
-        mOnFrameRenderedListener = new OnFrameRenderedListener();
-        codec.setOnFrameRenderedListener(mOnFrameRenderedListener,
-                new Handler(Looper.getMainLooper()));
-        mOnFirstTunnelFrameReadyListener = new OnFirstTunnelFrameReadyListener();
-        codec.setOnFirstTunnelFrameReadyListener(new Handler(Looper.getMainLooper()),
-                mOnFirstTunnelFrameReadyListener);
+        if (!mIsAudio) {
+            // Setup frame rendered callback for video codecs
+            mOnFrameRenderedListener = new OnFrameRenderedListener();
+            codec.setOnFrameRenderedListener(mOnFrameRenderedListener,
+                    new Handler(Looper.getMainLooper()));
+
+            if (mIsTunneled) {
+                mOnFirstTunnelFrameReadyListener = new OnFirstTunnelFrameReadyListener();
+                codec.setOnFirstTunnelFrameReadyListener(new Handler(Looper.getMainLooper()),
+                        mOnFirstTunnelFrameReadyListener);
+            }
+        }
     }
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@@ -308,14 +358,14 @@
                 return null;
             }
 
-            if (mTunneled && !mIsAudio) {
+            if (mIsTunneled && !mIsAudio) {
                 if (mFirstSampleTimeUs == -1) {
                     mFirstSampleTimeUs = sampleTime;
                 }
                 sampleTime -= mFirstSampleTimeUs;
             }
 
-            mLastPresentTimeUs = mPlaybackStartTimeUs + sampleTime;
+            mLastPresentTimeUs = mPlaybackStartTimeUs + sampleTime + mOffsetTimestampUs;
 
             if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
                 MediaCodec.CryptoInfo info = new MediaCodec.CryptoInfo();
@@ -374,7 +424,7 @@
                 return;
             }
             mAudioTrack = new NonBlockingAudioTrack(sampleRate, channelCount,
-                                    mTunneled, mAudioSessionId);
+                                    mIsTunneled, mAudioSessionId);
             mAudioTrack.play();
         }
 
@@ -388,7 +438,7 @@
 
     /** Returns true if more output data could be drained. */
     private boolean drainOutputBuffer() {
-        if (mSawOutputEOS || mAvailableOutputBufferIndices.isEmpty()) {
+        if (mSawOutputEOS || mAvailableOutputBufferIndices.isEmpty() || mShouldSimulateUnderrun) {
             return false;
         }
 
@@ -407,13 +457,6 @@
             return false;
         }
 
-        long realTimeUs =
-            mMediaTimeProvider.getRealTimeUsForMediaTime(info.presentationTimeUs);
-
-        long nowUs = mMediaTimeProvider.getNowUs();
-
-        long lateUs = nowUs - realTimeUs;
-
         if (mAudioTrack != null) {
             ByteBuffer buffer = mCodecOutputBuffers[index];
             byte[] audioArray = new byte[info.size];
@@ -421,11 +464,11 @@
             buffer.clear();
 
             mAudioTrack.write(ByteBuffer.wrap(audioArray), info.size,
-                    info.presentationTimeUs*1000);
+                    info.presentationTimeUs * 1000 + mAudioOffsetNs);
 
             mCodec.releaseOutputBuffer(index, false /* render */);
 
-            mPresentationTimeUs = info.presentationTimeUs;
+            mDecodedFramePresentationTimeUs = info.presentationTimeUs;
 
             mAvailableOutputBufferIndices.removeFirst();
             mAvailableOutputBufferInfos.removeFirst();
@@ -433,6 +476,12 @@
         } else {
             // video
             boolean render;
+            long realTimeUs =
+                    mMediaTimeProvider.getRealTimeUsForMediaTime(info.presentationTimeUs);
+
+            long nowUs = mMediaTimeProvider.getNowUs();
+
+            long lateUs = nowUs - realTimeUs;
 
             if (lateUs < -45000) {
                 // too early;
@@ -442,7 +491,7 @@
                 render = false;
             } else {
                 render = true;
-                mPresentationTimeUs = info.presentationTimeUs;
+                mDecodedFramePresentationTimeUs = info.presentationTimeUs;
             }
 
             mCodec.releaseOutputBuffer(index, render);
@@ -453,7 +502,10 @@
         }
     }
 
-    /** Callback called by the renderer in tunneling mode. */
+    /**
+     * Callback called by {@link MediaCodec} when it is notified that a decoded video frame has been
+     * rendered on the attached {@link Surface}.
+    */
     private class OnFrameRenderedListener implements MediaCodec.OnFrameRenderedListener {
         private static final long TUNNELING_EOS_PRESENTATION_TIME_US = Long.MAX_VALUE;
 
@@ -465,9 +517,11 @@
             if (presentationTimeUs == TUNNELING_EOS_PRESENTATION_TIME_US) {
                  mSawOutputEOS = true;
             } else {
-                 mPresentationTimeUs = presentationTimeUs;
+                mRenderedVideoFramePresentationTimeUs = presentationTimeUs;
             }
+            mRenderedVideoFrameSystemTimeNano = nanoTime;
             mRenderedVideoFrameTimestampList.add(presentationTimeUs);
+            mRenderedVideoFrameSystemTimeList.add(mRenderedVideoFrameSystemTimeNano);
         }
     }
 
@@ -479,6 +533,11 @@
         return mAudioTrack.getAudioTimeUs();
     }
 
+    /** Returns the presentation timestamp of the last rendered video frame. */
+    public long getVideoTimeUs() {
+        return mRenderedVideoFramePresentationTimeUs;
+    }
+
     /** Callback called in tunnel mode when video peek is ready */
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     private class OnFirstTunnelFrameReadyListener
@@ -489,18 +548,27 @@
             if (this != mOnFirstTunnelFrameReadyListener) {
                 return; // stale event
             }
-            mFirstTunnelFrameReady = true;
+            mIsFirstTunnelFrameReady = true;
         }
     }
 
     /**
-     * If a video codec, returns the list of rendered frames' timestamps.
-     * Otherwise, returns an empty list.
+     * If a video codec, returns the list of rendered frames' timestamps. Otherwise, returns an
+     * empty list.
      */
-    public ArrayList<Long> getRenderedVideoFrameTimestampList() {
-        return new ArrayList<Long>(mRenderedVideoFrameTimestampList);
+    public ImmutableList<Long> getRenderedVideoFrameTimestampList() {
+        return ImmutableList.<Long>copyOf(mRenderedVideoFrameTimestampList);
     }
 
+    /**
+     * If a video codec, returns the list system times when frames were rendered. Otherwise, returns
+     * an empty list.
+     */
+    public ImmutableList<Long> getRenderedVideoFrameSystemTimeList() {
+        return ImmutableList.<Long>copyOf(mRenderedVideoFrameSystemTimeList);
+    }
+
+
     /** Process the attached {@link AudioTrack}, if any. */
     public void processAudioTrack() {
         if (mAudioTrack != null) {
@@ -508,6 +576,13 @@
         }
     }
 
+    public int getFramesWritten() {
+        if (mAudioTrack != null) {
+            return mAudioTrack.getFramesWritten();
+        }
+        return 0;
+    }
+
     public AudioTimestamp getTimestamp() {
         if (mAudioTrack == null) {
             return null;
@@ -516,7 +591,6 @@
         return mAudioTrack.getTimestamp();
     }
 
-
     /** Stop the attached {@link AudioTrack}, if any. */
     public void stopAudioTrack() {
         if (mAudioTrack != null) {
@@ -549,6 +623,58 @@
 
     /** In tunnel mode, queries whether the first video frame is ready for video peek. */
     public boolean isFirstTunnelFrameReady() {
-        return mFirstTunnelFrameReady;
+        return mIsFirstTunnelFrameReady;
+    }
+
+    /**
+     * Option to simulate underrun by pausing the release of the underlying codec's output buffers
+     * (in non-tunnel mode).
+     * Note: This might starve the underlying buffer queue.
+     */
+    public void simulateUnderrun(boolean enable) {
+        mShouldSimulateUnderrun = enable;
+    }
+
+    /**
+     * Option to introduce an offset (positive or negative, in ms) to content queued to the
+     * {@link AudioTrack}.
+     */
+    public void setAudioOffsetMs(int audioOffsetMs) {
+        mAudioOffsetNs = audioOffsetMs * 1000000;
+    }
+
+    public void stopWritingToAudioTrack(boolean stopWriting) {
+        if (mAudioTrack != null) {
+            mAudioTrack.stopWriting(stopWriting);
+        }
+    }
+
+    /** Returns the underlying {@code AudioTrack}, if any. */
+    public AudioTrack getAudioTrack() {
+        if (mAudioTrack != null) {
+            return mAudioTrack.getAudioTrack();
+        }
+        return null;
+    }
+
+    /**
+     * Seek media extractor to the beginning of the configured track.
+     *
+     * @param shouldContinuePts  a boolean that controls whether timestamps keep increasing
+     */
+    public void seekToBeginning(boolean shouldContinuePts) {
+        mExtractor.seekTo(UNINITIALIZED_TIMESTAMP, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
+        if (shouldContinuePts) {
+            if (mDecodedFramePresentationTimeUs != UNINITIALIZED_TIMESTAMP) {
+                mOffsetTimestampUs = mDecodedFramePresentationTimeUs;
+                return;
+            }
+            if (mRenderedVideoFramePresentationTimeUs != UNINITIALIZED_TIMESTAMP) {
+                mOffsetTimestampUs = mRenderedVideoFramePresentationTimeUs;
+                return;
+            }
+        } else {
+            mOffsetTimestampUs = 0;
+        }
     }
 }
diff --git a/tests/tests/media/common/src/android/media/cts/MediaCodecBlockModelHelper.java b/tests/tests/media/common/src/android/media/cts/MediaCodecBlockModelHelper.java
index c5f9209..8db3a81 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaCodecBlockModelHelper.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaCodecBlockModelHelper.java
@@ -318,12 +318,12 @@
     }
 
     public static class SlotEvent {
-        SlotEvent(boolean input, int index) {
+        public SlotEvent(boolean input, int index) {
             this.input = input;
             this.index = index;
         }
-        final boolean input;
-        final int index;
+        public final boolean input;
+        public final int index;
     }
 
     private static final UUID CLEARKEY_SCHEME_UUID =
diff --git a/tests/tests/media/common/src/android/media/cts/MediaCodecPlayerTestBase.java b/tests/tests/media/common/src/android/media/cts/MediaCodecPlayerTestBase.java
index 56197fc..f200586 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaCodecPlayerTestBase.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaCodecPlayerTestBase.java
@@ -27,6 +27,7 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 import android.view.Surface;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -36,7 +37,10 @@
     private static final String TAG = MediaCodecPlayerTestBase.class.getSimpleName();
     private static final int CONNECTION_RETRIES = 10;
     private static final int SLEEP_TIME_MS = 1000;
-    private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(25, TimeUnit.SECONDS);
+    // The first ten seconds in PLAY_TIME_MS plays the clear lead,
+    // the next ten seconds verifies encrypted playback.
+    // This applies to both streaming and offline tests.
+    private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(20, TimeUnit.SECONDS);
 
     protected Context mContext;
     protected MediaCodecClearKeyPlayer mMediaCodecPlayer;
diff --git a/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java b/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
index f8a3161..a602a13 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.media.AudioTimestamp;
+import android.media.AudioTrack;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
@@ -26,9 +27,9 @@
 import android.util.Log;
 import android.view.SurfaceHolder;
 
+import com.google.common.collect.ImmutableList;
+
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -40,9 +41,13 @@
 public class MediaCodecTunneledPlayer implements MediaTimeProvider {
     private static final String TAG = MediaCodecTunneledPlayer.class.getSimpleName();
 
+    /** State the player starts in, before configuration. */
     private static final int STATE_IDLE = 1;
+    /** State of the player during initial configuration. */
     private static final int STATE_PREPARING = 2;
+    /** State of the player during playback. */
     private static final int STATE_PLAYING = 3;
+    /** State of the player when configured but not playing. */
     private static final int STATE_PAUSED = 4;
 
     private Boolean mThreadStarted = false;
@@ -63,7 +68,7 @@
     private Thread mThread;
     private Uri mAudioUri;
     private Uri mVideoUri;
-    private boolean mTunneled;
+    private boolean mIsTunneled;
     private int mAudioSessionId;
     private Context mContext;
 
@@ -73,7 +78,7 @@
     public MediaCodecTunneledPlayer(Context context, SurfaceHolder holder, boolean tunneled, int AudioSessionId) {
         mContext = context;
         mSurfaceHolder = holder;
-        mTunneled = tunneled;
+        mIsTunneled = tunneled;
         mAudioTrackState = null;
         mState = STATE_IDLE;
         mAudioSessionId = AudioSessionId;
@@ -207,7 +212,9 @@
         }
 
         mAudioExtractor.setDataSource(mContext, mAudioUri, mAudioHeaders);
-        mVideoExtractor.setDataSource(mContext, mVideoUri, mVideoHeaders);
+        if (mVideoUri != null) {
+            mVideoExtractor.setDataSource(mContext, mVideoUri, mVideoHeaders);
+        }
 
         if (null == mVideoCodecStates) {
             mVideoCodecStates = new HashMap<Integer, CodecState>();
@@ -243,7 +250,7 @@
         MediaCodec codec;
 
         // setup tunneled video codec if needed
-        if (isVideo && mTunneled) {
+        if (isVideo && mIsTunneled) {
             format.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback,
                         true);
             MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
@@ -280,11 +287,11 @@
         CodecState state;
         if (isVideo) {
             state = new CodecState((MediaTimeProvider)this, mVideoExtractor,
-                            trackIndex, format, codec, true, mTunneled, mAudioSessionId);
+                            trackIndex, format, codec, true, mIsTunneled, mAudioSessionId);
             mVideoCodecStates.put(Integer.valueOf(trackIndex), state);
         } else {
             state = new CodecState((MediaTimeProvider)this, mAudioExtractor,
-                            trackIndex, format, codec, true, mTunneled, mAudioSessionId);
+                            trackIndex, format, codec, true, mIsTunneled, mAudioSessionId);
             mAudioCodecStates.put(Integer.valueOf(trackIndex), state);
         }
 
@@ -395,12 +402,14 @@
 
     /**
      * Flushes all the video codecs when the player is in stand-by.
+     *
+     * @throws IllegalStateException  if the player is not paused
      */
     public void videoFlush() {
         Log.d(TAG, "videoFlush");
         synchronized (mState) {
-            if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
-                return;
+            if (mState != STATE_PAUSED) {
+                throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
             }
 
             for (CodecState state : mVideoCodecStates.values()) {
@@ -409,6 +418,24 @@
         }
     }
 
+    /** Seek all video tracks to their very beginning.
+     *
+     * @param  shouldContinuePts      a boolean that controls whether timestamps keep increasing
+     * @throws IllegalStateException  if the player is not paused
+     */
+    public void videoSeekToBeginning(boolean shouldContinuePts) {
+        Log.d(TAG, "videoSeekToBeginning");
+        synchronized (mState) {
+            if (mState != STATE_PAUSED) {
+                throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
+            }
+
+            for (CodecState state : mVideoCodecStates.values()) {
+                state.seekToBeginning(shouldContinuePts);
+            }
+        }
+    }
+
     /**
      * Enables or disables looping. Should be called after {@link #prepare()}.
      */
@@ -526,21 +553,50 @@
         return (int)((mDurationUs + 500) / 1000);
     }
 
+    /**
+     * Retrieve the presentation timestamp of the latest queued output sample.
+     * In tunnel mode, retrieves the presentation timestamp of the latest rendered video frame.
+     * @return presentation timestamp in microseconds, or {@code CodecState.UNINITIALIZED_TIMESTAMP}
+     * if playback has not started.
+    */
     public int getCurrentPosition() {
         if (mVideoCodecStates == null) {
-                return 0;
+            return CodecState.UNINITIALIZED_TIMESTAMP;
         }
 
-        long positionUs = 0;
+        long positionUs = CodecState.UNINITIALIZED_TIMESTAMP;
 
         for (CodecState state : mVideoCodecStates.values()) {
             long trackPositionUs = state.getCurrentPositionUs();
-
             if (trackPositionUs > positionUs) {
                 positionUs = trackPositionUs;
             }
         }
-        return (int)((positionUs + 500) / 1000);
+
+        if (positionUs == CodecState.UNINITIALIZED_TIMESTAMP) {
+            return CodecState.UNINITIALIZED_TIMESTAMP;
+        }
+        return (int) (positionUs + 500) / 1000;
+    }
+
+    /**
+     * Returns the system time of the latest rendered frame in any of the video codecs.
+     */
+    public long getCurrentRenderedSystemTimeNano() {
+        if (mVideoCodecStates == null) {
+            return 0;
+        }
+
+        long position = 0;
+
+        for (CodecState state : mVideoCodecStates.values()) {
+            long trackPosition = state.getRenderedVideoSystemTimeNano();
+
+            if (trackPosition > position) {
+                position = trackPosition;
+            }
+        }
+        return position;
     }
 
     /**
@@ -554,22 +610,39 @@
     }
 
     /**
+     * Returns the presentation timestamp of the last rendered video frame.
+     *
+     * Note: This assumes there is exactly one video codec running in the player.
+     */
+    public long getVideoTimeUs() {
+        return mVideoCodecStates.get(0).getVideoTimeUs();
+    }
+
+    /**
      * Returns the ordered list of video frame timestamps rendered in tunnel mode.
      *
+     * Note: This assumes there is exactly one video codec running in the player.
+     */
+    public ImmutableList<Long> getRenderedVideoFrameTimestampList() {
+        return mVideoCodecStates.get(0).getRenderedVideoFrameTimestampList();
+    }
+
+    /**
+     * Returns the ordered list of system times of rendered video frames in tunnel-mode.
+     *
      * Note: This assumes there is at most one tunneled mode video codec running in the player.
      */
-    public ArrayList<Long> getRenderedVideoFrameTimestampList() {
+    public ImmutableList<Long> getRenderedVideoFrameSystemTimeList() {
         if (mVideoCodecStates == null) {
-            return new ArrayList<Long>();
+            return ImmutableList.<Long>of();
         }
 
-        ArrayList<Long> timestamps = null;
         for (CodecState state : mVideoCodecStates.values()) {
-            timestamps = state.getRenderedVideoFrameTimestampList();
+            ImmutableList<Long> timestamps = state.getRenderedVideoFrameSystemTimeList();
             if (!timestamps.isEmpty())
-                break;
-            }
-        return timestamps;
+                return timestamps;
+        }
+        return ImmutableList.<Long>of();
     }
 
     /**
@@ -645,11 +718,47 @@
             return false;
         }
 
-        for (CodecState state: mVideoCodecStates.values()) {
+        for (CodecState state : mVideoCodecStates.values()) {
             if (state.isFirstTunnelFrameReady()) {
                 return true;
             }
         }
         return false;
     }
+
+    /** Returns the number of frames that have been sent down to the HAL. */
+    public int getAudioFramesWritten() {
+        if (mAudioCodecStates == null) {
+            return -1;
+        }
+        return mAudioCodecStates.entrySet().iterator().next().getValue().getFramesWritten();
+    }
+
+    public void stopWritingToAudioTrack(boolean stopWriting) {
+        for (CodecState state : mAudioCodecStates.values()) {
+            state.stopWritingToAudioTrack(stopWriting);
+        }
+    }
+
+    /** Configure underrun simulation on audio codecs. */
+    public void simulateAudioUnderrun(boolean enabled) {
+        for (CodecState state: mAudioCodecStates.values()) {
+            state.simulateUnderrun(enabled);
+        }
+    }
+
+    /** Configure an offset (in ms) to audio content to simulate track desynchronization. */
+    public void setAudioTrackOffsetMs(int audioOffsetMs) {
+        if (mAudioTrackState != null) {
+            mAudioTrackState.setAudioOffsetMs(audioOffsetMs);
+        }
+    }
+
+    /** Returns the underlying {@code AudioTrack}, if any. */
+    public AudioTrack getAudioTrack() {
+        if (mAudioTrackState != null) {
+            return mAudioTrackState.getAudioTrack();
+        }
+        return null;
+    }
 }
diff --git a/tests/tests/media/common/src/android/media/cts/MediaPlayerTestBase.java b/tests/tests/media/common/src/android/media/cts/MediaPlayerTestBase.java
index 9433ba2..0d247fa 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaPlayerTestBase.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaPlayerTestBase.java
@@ -48,19 +48,15 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.junit.After;
+import org.junit.Before;
+
 /**
  * Base class for tests which use MediaPlayer to play audio or video.
  */
-public class MediaPlayerTestBase {
+public class MediaPlayerTestBase extends MediaTestBase {
     private static final Logger LOG = Logger.getLogger(MediaPlayerTestBase.class.getName());
 
-    static final String mInpPrefix = WorkDir.getMediaDirString();
-
-    protected static final int SLEEP_TIME = 1000;
-    protected static final int LONG_SLEEP_TIME = 6000;
-    protected static final int STREAM_RETRIES = 20;
-    protected static boolean sUseScaleToFitMode = false;
-
     protected Monitor mOnVideoSizeChangedCalled = new Monitor();
     protected Monitor mOnVideoRenderingStartCalled = new Monitor();
     protected Monitor mOnBufferingUpdateCalled = new Monitor();
@@ -70,34 +66,21 @@
     protected Monitor mOnInfoCalled = new Monitor();
     protected Monitor mOnErrorCalled = new Monitor();
 
-    protected Context mContext;
-
     protected MediaPlayer mMediaPlayer = null;
     protected MediaPlayer mMediaPlayer2 = null;
-    protected ActivityScenario<MediaStubActivity> mActivityScenario;
-    protected MediaStubActivity mActivity;
 
-    @CallSuper
-    protected void setUp() throws Throwable {
-        mActivityScenario = ActivityScenario.launch(MediaStubActivity.class);
-        ConditionVariable activityReferenceObtained = new ConditionVariable();
-        mActivityScenario.onActivity(activity -> {
-            mActivity = activity;
-            activityReferenceObtained.open();
-        });
-        activityReferenceObtained.block(/* timeoutMs= */ 10000);
-        assertNotNull("Failed to acquire activity reference.", mActivity);
-
-        mContext = getInstrumentation().getTargetContext();
-        getInstrumentation().waitForIdleSync();
+    @Before
+    @Override
+    public void setUp() throws Throwable {
+        super.setUp();
         runOnUiThread(() -> {
             mMediaPlayer = new MediaPlayer();
             mMediaPlayer2 = new MediaPlayer();
         });
     }
-
-    @CallSuper
-    protected void tearDown() {
+    @After
+    @Override
+    public void tearDown() {
         if (mMediaPlayer != null) {
             mMediaPlayer.release();
             mMediaPlayer = null;
@@ -106,15 +89,7 @@
             mMediaPlayer2.release();
             mMediaPlayer2 = null;
         }
-        mActivity = null;
-    }
-
-    protected Instrumentation getInstrumentation() {
-        return InstrumentationRegistry.getInstrumentation();
-    }
-
-    protected MediaStubActivity getActivity() {
-        return mActivity;
+        super.tearDown();
     }
 
     protected void runOnUiThread(Runnable runnable) throws Throwable {
@@ -131,49 +106,6 @@
         }
     }
 
-    protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
-            throws FileNotFoundException {
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        File inpFile = new File(mInpPrefix + res);
-        ParcelFileDescriptor parcelFD =
-                ParcelFileDescriptor.open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY);
-        return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
-    }
-
-    // returns true on success
-    protected boolean loadResource(final String res) throws Exception {
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        if (!MediaUtils.hasCodecsForResource(mInpPrefix + res)) {
-            return false;
-        }
-
-        try (AssetFileDescriptor afd = getAssetFileDescriptorFor(res)) {
-            mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
-                    afd.getLength());
-
-            // Although it is only meant for video playback, it should not
-            // cause issues for audio-only playback.
-            int videoScalingMode = sUseScaleToFitMode ?
-                    MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT
-                    : MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
-
-            mMediaPlayer.setVideoScalingMode(videoScalingMode);
-        }
-        sUseScaleToFitMode = !sUseScaleToFitMode;  // Alternate the scaling mode
-        return true;
-    }
-
-    protected boolean checkLoadResource(String res) throws Exception {
-        return MediaUtils.check(loadResource(res), "no decoder found");
-    }
-
-    protected void loadSubtitleSource(String res) throws Exception {
-        try (AssetFileDescriptor afd = getAssetFileDescriptorFor(res)) {
-            mMediaPlayer.addTimedTextSource(afd.getFileDescriptor(), afd.getStartOffset(),
-                    afd.getLength(), MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP);
-        }
-    }
-
     protected void playLiveVideoTest(String path, int playTime) throws Exception {
         playVideoWithRetries(path, null, null, playTime);
     }
@@ -204,14 +136,6 @@
         assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
     }
 
-    protected void playLoadedVideoTest(final String res, int width, int height) throws Exception {
-        if (!checkLoadResource(res)) {
-            return; // skip
-        }
-
-        playLoadedVideo(width, height, 0);
-    }
-
     protected void playLiveVideoTest(
             Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
             int playTime) throws Exception {
@@ -360,16 +284,6 @@
 
     private static class PrepareFailedException extends Exception {}
 
-    public boolean isTv() {
-        PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
-        return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
-                && pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
-    }
-
-    public boolean checkTv() {
-        return MediaUtils.check(isTv(), "not a TV");
-    }
-
     protected void setOnErrorListener() {
         mMediaPlayer.setOnErrorListener((mp, what, extra) -> {
             mOnErrorCalled.signal();
diff --git a/tests/tests/media/common/src/android/media/cts/MediaTestBase.java b/tests/tests/media/common/src/android/media/cts/MediaTestBase.java
new file mode 100644
index 0000000..9f98508
--- /dev/null
+++ b/tests/tests/media/common/src/android/media/cts/MediaTestBase.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
+import android.media.MediaPlayer;
+import android.media.cts.TestUtils.Monitor;
+import android.net.Uri;
+import android.os.ConditionVariable;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+
+import androidx.annotation.CallSuper;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.MediaUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.HttpCookie;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Base class for tests which use MediaPlayer to play audio or video.
+ */
+public class MediaTestBase {
+    private static final Logger LOG = Logger.getLogger(MediaTestBase.class.getName());
+
+    protected static final int SLEEP_TIME = 1000;
+    protected static final int LONG_SLEEP_TIME = 6000;
+    protected static final int STREAM_RETRIES = 20;
+    protected static boolean sUseScaleToFitMode = false;
+
+    protected Context mContext;
+
+    protected ActivityScenario<MediaStubActivity> mActivityScenario;
+    protected MediaStubActivity mActivity;
+
+    @CallSuper
+    protected void setUp() throws Throwable {
+        mActivityScenario = ActivityScenario.launch(MediaStubActivity.class);
+        ConditionVariable activityReferenceObtained = new ConditionVariable();
+        mActivityScenario.onActivity(activity -> {
+            mActivity = activity;
+            activityReferenceObtained.open();
+        });
+        activityReferenceObtained.block(/* timeoutMs= */ 10000);
+        assertNotNull("Failed to acquire activity reference.", mActivity);
+
+        mContext = getInstrumentation().getTargetContext();
+        getInstrumentation().waitForIdleSync();
+    }
+
+    @CallSuper
+    protected void tearDown() {
+        mActivity = null;
+    }
+
+    protected Instrumentation getInstrumentation() {
+        return InstrumentationRegistry.getInstrumentation();
+    }
+
+    protected MediaStubActivity getActivity() {
+        return mActivity;
+    }
+
+    public boolean isTv() {
+        PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+                && pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
+    public boolean checkTv() {
+        return MediaUtils.check(isTv(), "not a TV");
+    }
+
+}
diff --git a/tests/tests/media/common/src/android/media/cts/NdkMediaCodec.java b/tests/tests/media/common/src/android/media/cts/NdkMediaCodec.java
index 9b8136a..b110c59 100644
--- a/tests/tests/media/common/src/android/media/cts/NdkMediaCodec.java
+++ b/tests/tests/media/common/src/android/media/cts/NdkMediaCodec.java
@@ -39,7 +39,7 @@
 
     static {
         Log.i("@@@", "before loadlibrary");
-        System.loadLibrary("ctsmediacodec_jni");
+        System.loadLibrary("ctsmediacommon_jni");
         Log.i("@@@", "after loadlibrary");
     }
 
diff --git a/tests/tests/media/common/src/android/media/cts/NonBlockingAudioTrack.java b/tests/tests/media/common/src/android/media/cts/NonBlockingAudioTrack.java
index f4f1fac..409ba86 100644
--- a/tests/tests/media/common/src/android/media/cts/NonBlockingAudioTrack.java
+++ b/tests/tests/media/common/src/android/media/cts/NonBlockingAudioTrack.java
@@ -15,15 +15,15 @@
  */
 package android.media.cts;
 
+import android.media.AudioAttributes;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioTimestamp;
 import android.media.AudioTrack;
-import android.media.AudioAttributes;
-import android.util.Log;
 
 import java.nio.ByteBuffer;
 import java.util.LinkedList;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Class for playing audio by using audio track.
@@ -43,8 +43,11 @@
     private AudioTrack mAudioTrack;
     private int mSampleRate;
     private int mNumBytesQueued = 0;
+    private AtomicInteger mTotalBytesWritten = new AtomicInteger(0);
     private LinkedList<QueueElement> mQueue = new LinkedList<QueueElement>();
     private boolean mStopped;
+    private boolean mShouldStopWriting = false;
+    private int mBufferSizeInBytes;
 
     public NonBlockingAudioTrack(int sampleRate, int channelCount, boolean hwAvSync,
                     int audioSessionId) {
@@ -69,7 +72,7 @@
                     channelConfig,
                     AudioFormat.ENCODING_PCM_16BIT);
 
-        int bufferSize = 2 * minBufferSize;
+        mBufferSizeInBytes = 2 * minBufferSize;
 
         if (!hwAvSync) {
             mAudioTrack = new AudioTrack(
@@ -77,7 +80,7 @@
                     sampleRate,
                     channelConfig,
                     AudioFormat.ENCODING_PCM_16BIT,
-                    bufferSize,
+                    mBufferSizeInBytes,
                     AudioTrack.MODE_STREAM);
         }
         else {
@@ -91,7 +94,7 @@
                             .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                             .setSampleRate(sampleRate)
                             .build();
-             mAudioTrack = new AudioTrack(audioAttributes, audioFormat, bufferSize,
+            mAudioTrack = new AudioTrack(audioAttributes, audioFormat, mBufferSizeInBytes,
                                     AudioTrack.MODE_STREAM, audioSessionId);
         }
 
@@ -151,7 +154,7 @@
     }
 
     public void process() {
-        while (!mQueue.isEmpty()) {
+        while (!mQueue.isEmpty() && !mShouldStopWriting) {
             QueueElement element = mQueue.peekFirst();
             int written = mAudioTrack.write(element.data, element.size,
                                             AudioTrack.WRITE_NON_BLOCKING, element.pts);
@@ -159,6 +162,7 @@
                 throw new RuntimeException("Audiotrack.write() failed.");
             }
 
+            mTotalBytesWritten.addAndGet(written);
             mNumBytesQueued -= written;
             element.size -= written;
             if (element.size != 0) {
@@ -173,6 +177,17 @@
         }
     }
 
+    public int getFramesWritten() {
+        if (mAudioTrack == null) {
+            return -1;
+        }
+        return mTotalBytesWritten.get() / mAudioTrack.getFormat().getFrameSizeInBytes();
+    }
+
+    public void stopWriting(boolean shouldStopWriting) {
+        mShouldStopWriting = shouldStopWriting;
+    }
+
     public int getPlayState() {
         return mAudioTrack.getPlayState();
     }
@@ -187,5 +202,9 @@
         mNumBytesQueued += size;
         mQueue.add(element);
     }
-}
 
+    /** Returns the underlying {@code AudioTrack}. */
+    public AudioTrack getAudioTrack() {
+        return mAudioTrack;
+    }
+}
diff --git a/tests/tests/media/common/src/android/media/cts/Utils.java b/tests/tests/media/common/src/android/media/cts/Utils.java
index bf942a7..1524e26 100644
--- a/tests/tests/media/common/src/android/media/cts/Utils.java
+++ b/tests/tests/media/common/src/android/media/cts/Utils.java
@@ -29,7 +29,6 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
-import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import androidx.test.platform.app.InstrumentationRegistry;
 import java.io.File;
@@ -149,24 +148,19 @@
      * is created once. The playback will be stopped immediately after that.
      * <p>For a media session to receive media button events, an actual playback is needed.
      */
-    @AppModeFull(reason = "Instant apps cannot access the SD card")
     static void assertMediaPlaybackStarted(Context context) {
         final AudioManager am = new AudioManager(context);
         final HandlerThread handlerThread = new HandlerThread(TAG);
         handlerThread.start();
         final TestAudioPlaybackCallback callback = new TestAudioPlaybackCallback();
         MediaPlayer mediaPlayer = null;
-        final String mInpPrefix = WorkDir.getMediaDirString();
 
         try {
             final int activeConfigSizeBeforeStart = am.getActivePlaybackConfigurations().size();
             final Handler handler = new Handler(handlerThread.getLooper());
 
             am.registerAudioPlaybackCallback(callback, handler);
-            File testAudioFile = new File(mInpPrefix + "sine1khzm40db.wav");
-            Assert.assertTrue("Test audio file does not exist! path="
-                            + testAudioFile.getAbsolutePath(), testAudioFile.exists());
-            mediaPlayer = MediaPlayer.create(context, Uri.fromFile(testAudioFile));
+            mediaPlayer = MediaPlayer.create(context, R.raw.sine1khzs40dblong);
             mediaPlayer.start();
             if (!callback.mCountDownLatch.await(TEST_TIMING_TOLERANCE_MS, TimeUnit.MILLISECONDS)
                     || callback.mActiveConfigSize != activeConfigSizeBeforeStart + 1) {
diff --git a/tests/tests/mediadrm/Android.bp b/tests/tests/media/decoder/Android.bp
similarity index 62%
copy from tests/tests/mediadrm/Android.bp
copy to tests/tests/media/decoder/Android.bp
index 27afc76..410fb5b 100644
--- a/tests/tests/mediadrm/Android.bp
+++ b/tests/tests/media/decoder/Android.bp
@@ -14,42 +14,55 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-CC-BY
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license", // CC-BY
+    ],
+}
+
+cc_test_library {
+    name: "libctsmediadecodertest_jni",
+    srcs: [
+        "jni/native-media-jni.cpp",
+    ],
+    shared_libs: [
+        "libandroid",
+        "libnativehelper_compat_libc++",
+        "liblog",
+        "libmediandk",
+        "libEGL",
+    ],
+    header_libs: ["liblog_headers"],
+    stl: "libc++_static",
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DEGL_EGLEXT_PROTOTYPES",
+    ],
+    gtest: false,
+    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
+    // (revisit if/when we add features to this library that require newer sdk.
+    sdk_version: "29",
 }
 
 android_test {
-    name: "CtsMediaDrmTestCases",
+    name: "CtsMediaDecoderTestCases",
     defaults: ["cts_defaults"],
     // include both the 32 and 64 bit versions
     compile_multilib: "both",
     static_libs: [
-        "androidx.test.core",
-        "androidx.test.ext.junit",
-        "compatibility-device-util-axt",
-        "ctsdeviceutillegacy-axt",
         "ctstestrunner-axt",
-        "cts-media-common",
-        "cts-media-res-lib",
-        "hamcrest-library",
         "ctstestserver",
-        "junit",
-        "junit-params",
-        "testng",
-        "truth-prebuilt",
-        "mockito-target-minus-junit4",
-        "androidx.heifwriter_heifwriter",
+        "cts-media-common",
     ],
+    jni_libs: [
+        "libctscodecutils_jni",
+        "libctsmediacommon_jni",
+        "libctsmediadecodertest_jni",
+        "libnativehelper_compat_libc++",
+    ],
+    resource_dirs: ["res"],
     aaptflags: [
-        "--auto-add-overlay",
-        // Give com.android.mediadrm.cts Java files access to the R class
-        "--extra-packages com.android.mediadrm.cts",
-
         // Do not compress these files:
         "-0 .vp9",
         "-0 .ts",
@@ -58,10 +71,6 @@
         "-0 .ota",
         "-0 .mxmf",
     ],
-    jni_libs: [
-        "libmediadrm_jni",
-        "libnativehelper_compat_libc++",
-    ],
     srcs: [
         "src/**/*.java",
         "aidl/**/*.aidl",
@@ -74,7 +83,6 @@
         "android.test.base",
         "android.test.runner",
     ],
-    // Tag this module as a cts test artifact
     test_suites: [
         "cts",
         "general-tests",
diff --git a/tests/tests/media/decoder/AndroidManifest.xml b/tests/tests/media/decoder/AndroidManifest.xml
new file mode 100644
index 0000000..d25d904
--- /dev/null
+++ b/tests/tests/media/decoder/AndroidManifest.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="android.media.decoder.cts"
+     android:targetSandboxVersion="2">
+
+    <uses-sdk android:minSdkVersion="29"
+         android:targetSdkVersion="31"/>
+
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+
+    <application android:requestLegacyExternalStorage="true"
+         android:largeHeap="true">
+        <uses-library android:name="android.test.runner"/>
+        <uses-library android:name="org.apache.http.legacy"
+             android:required="false"/>
+
+        <activity android:name="android.media.decoder.cts.DecodeAccuracyTestActivity"
+             android:label="DecodeAccuracyTestActivity"
+             android:screenOrientation="locked"
+             android:configChanges="mcc|mnc|keyboard|keyboardHidden|orientation|screenSize|navigation"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="android.media.decoder.cts"
+         android:label="CTS tests of android.media">
+        <meta-data android:name="listener"
+             android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/mediadrm/AndroidTest.xml b/tests/tests/media/decoder/AndroidTest.xml
similarity index 68%
copy from tests/tests/mediadrm/AndroidTest.xml
copy to tests/tests/media/decoder/AndroidTest.xml
index f45ddbf..c7d4550 100644
--- a/tests/tests/mediadrm/AndroidTest.xml
+++ b/tests/tests/media/decoder/AndroidTest.xml
@@ -13,38 +13,42 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Config for CTS Media Drm test cases">
+<configuration description="Config for CTS Media test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+        <option name="set-test-harness" value="false" />
+        <option name="screen-always-on" value="on" />
+        <option name="screen-adaptive-brightness" value="off" />
+        <option name="disable-audio" value="false"/>
+        <option name="screen-saver" value="off"/>
+    </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="host" />
-        <option name="config-filename" value="CtsMediaDrmTestCases" />
-        <option name="dynamic-config-name" value="CtsMediaDrmTestCases" />
-        <option name="version" value="9.0_r1"/>
+        <option name="config-filename" value="CtsMediaDecoderTestCases" />
+        <option name="dynamic-config-name" value="CtsMediaDecoderTestCases" />
+        <option name="version" value="1.0"/>
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="push-all" value="true" />
-        <option name="media-folder-name" value="CtsMediaDrmTestCases-1.4" />
-        <option name="dynamic-config-module" value="CtsMediaDrmTestCases" />
+        <option name="media-folder-name" value="CtsMediaDecoderTestCases-1.1" />
+        <option name="dynamic-config-module" value="CtsMediaDecoderTestCases" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsMediaDrmTestCases.apk" />
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <!-- MediaProjectionTest needs this one to not be granted, SuiteApkInstaller grants all of them by default.-->
-        <option name="run-command" value="pm revoke android.mediadrm.cts android.permission.SYSTEM_ALERT_WINDOW"/>
+        <option name="test-file-name" value="CtsMediaDecoderTestCases.apk" />
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="device" />
-        <option name="config-filename" value="CtsMediaDrmTestCases" />
-        <option name="version" value="7.0"/>
+        <option name="config-filename" value="CtsMediaDecoderTestCases" />
+        <option name="version" value="1.0"/>
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.mediadrm.cts" />
+        <option name="package" value="android.media.decoder.cts" />
         <!-- setup can be expensive so limit the number of shards -->
         <option name="ajur-max-shard" value="5" />
         <!-- test-timeout unit is ms, value = 30 min -->
diff --git a/tests/tests/media/audio/DynamicConfig.xml b/tests/tests/media/decoder/DynamicConfig.xml
similarity index 92%
copy from tests/tests/media/audio/DynamicConfig.xml
copy to tests/tests/media/decoder/DynamicConfig.xml
index 53528de..f7df099 100644
--- a/tests/tests/media/audio/DynamicConfig.xml
+++ b/tests/tests/media/decoder/DynamicConfig.xml
@@ -15,6 +15,6 @@
 
 <dynamicConfig>
     <entry key="media_files_url">
-    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/CtsMediaTestCases-1.4.zip</value>
+    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/decoder/CtsMediaDecoderTestCases-1.1.zip</value>
     </entry>
 </dynamicConfig>
diff --git a/tests/tests/media/decoder/OWNERS b/tests/tests/media/decoder/OWNERS
new file mode 100644
index 0000000..f3d95c44
--- /dev/null
+++ b/tests/tests/media/decoder/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+include ../../../media/OWNERS
diff --git a/tests/tests/media/decoder/jni/native-media-jni.cpp b/tests/tests/media/decoder/jni/native-media-jni.cpp
new file mode 100755
index 0000000..8bc6ae6
--- /dev/null
+++ b/tests/tests/media/decoder/jni/native-media-jni.cpp
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Original code copied from NDK Native-media sample code */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMedia"
+#include <log/log.h>
+
+#include <assert.h>
+#include <jni.h>
+#include <mutex>
+#include <queue>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android/native_window_jni.h>
+
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaCodec.h"
+#include "media/NdkMediaDataSource.h"
+#include "media/NdkMediaFormat.h"
+template <class T>
+class simplevector {
+    T *storage;
+    int capacity;
+    int numfilled;
+public:
+    simplevector() {
+        capacity = 16;
+        numfilled = 0;
+        storage = new T[capacity];
+    }
+    ~simplevector() {
+        delete[] storage;
+    }
+
+    void add(T item) {
+        if (numfilled == capacity) {
+            T *old = storage;
+            capacity *= 2;
+            storage = new T[capacity];
+            for (int i = 0; i < numfilled; i++) {
+                storage[i] = old[i];
+            }
+            delete[] old;
+        }
+        storage[numfilled] = item;
+        numfilled++;
+    }
+
+    int size() {
+        return numfilled;
+    }
+
+    T* data() {
+        return storage;
+    }
+};
+
+struct FdDataSource {
+
+    FdDataSource(int fd, jlong offset, jlong size)
+        : mFd(dup(fd)),
+          mOffset(offset),
+          mSize(size) {
+    }
+
+    ssize_t readAt(off64_t offset, void *data, size_t size) {
+        ssize_t ssize = size;
+        if (!data || offset < 0 || offset + ssize < offset) {
+            return -1;
+        }
+        if (offset >= mSize) {
+            return 0; // EOS
+        }
+        if (offset + ssize > mSize) {
+            ssize = mSize - offset;
+        }
+        if (lseek(mFd, mOffset + offset, SEEK_SET) < 0) {
+            return -1;
+        }
+        return read(mFd, data, ssize);
+    }
+
+    ssize_t getSize() {
+        return mSize;
+    }
+
+    void close() {
+        ::close(mFd);
+    }
+
+private:
+
+    int mFd;
+    off64_t mOffset;
+    int64_t mSize;
+
+};
+
+static ssize_t FdSourceReadAt(void *userdata, off64_t offset, void *data, size_t size) {
+    FdDataSource *src = (FdDataSource*) userdata;
+    return src->readAt(offset, data, size);
+}
+
+static ssize_t FdSourceGetSize(void *userdata) {
+    FdDataSource *src = (FdDataSource*) userdata;
+    return src->getSize();
+}
+
+static void FdSourceClose(void *userdata) {
+    FdDataSource *src = (FdDataSource*) userdata;
+    src->close();
+}
+
+class CallbackData {
+    std::mutex mMutex;
+    std::queue<int32_t> mInputBufferIds;
+    std::queue<int32_t> mOutputBufferIds;
+    std::queue<AMediaCodecBufferInfo> mOutputBufferInfos;
+    std::queue<AMediaFormat*> mFormats;
+
+public:
+    CallbackData() { }
+
+    ~CallbackData() {
+        mMutex.lock();
+        while (!mFormats.empty()) {
+            AMediaFormat* format = mFormats.front();
+            mFormats.pop();
+            AMediaFormat_delete(format);
+        }
+        mMutex.unlock();
+    }
+
+    void addInputBufferId(int32_t index) {
+        mMutex.lock();
+        mInputBufferIds.push(index);
+        mMutex.unlock();
+    }
+
+    int32_t getInputBufferId() {
+        int32_t id = -1;
+        mMutex.lock();
+        if (!mInputBufferIds.empty()) {
+            id = mInputBufferIds.front();
+            mInputBufferIds.pop();
+        }
+        mMutex.unlock();
+        return id;
+    }
+
+    void addOutputBuffer(int32_t index, AMediaCodecBufferInfo *bufferInfo) {
+        mMutex.lock();
+        mOutputBufferIds.push(index);
+        mOutputBufferInfos.push(*bufferInfo);
+        mMutex.unlock();
+    }
+
+    void addOutputFormat(AMediaFormat *format) {
+        mMutex.lock();
+        mOutputBufferIds.push(AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED);
+        mFormats.push(format);
+        mMutex.unlock();
+    }
+
+    int32_t getOutput(AMediaCodecBufferInfo *bufferInfo, AMediaFormat **format) {
+        int32_t id = AMEDIACODEC_INFO_TRY_AGAIN_LATER;
+        mMutex.lock();
+        if (!mOutputBufferIds.empty()) {
+            id = mOutputBufferIds.front();
+            mOutputBufferIds.pop();
+
+            if (id >= 0) {
+                *bufferInfo = mOutputBufferInfos.front();
+                mOutputBufferInfos.pop();
+            } else {  // AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED
+                *format = mFormats.front();
+                mFormats.pop();
+            }
+        }
+        mMutex.unlock();
+        return id;
+    }
+};
+
+static void OnInputAvailableCB(
+        AMediaCodec * /* aMediaCodec */,
+        void *userdata,
+        int32_t index) {
+    ALOGV("OnInputAvailableCB: index(%d)", index);
+    CallbackData *callbackData = (CallbackData *)userdata;
+    callbackData->addInputBufferId(index);
+}
+
+static void OnOutputAvailableCB(
+        AMediaCodec * /* aMediaCodec */,
+        void *userdata,
+        int32_t index,
+        AMediaCodecBufferInfo *bufferInfo) {
+    ALOGV("OnOutputAvailableCB: index(%d), (%d, %d, %lld, 0x%x)",
+          index, bufferInfo->offset, bufferInfo->size,
+          (long long)bufferInfo->presentationTimeUs, bufferInfo->flags);
+    CallbackData *callbackData = (CallbackData *)userdata;
+    callbackData->addOutputBuffer(index, bufferInfo);
+}
+
+static void OnFormatChangedCB(
+        AMediaCodec * /* aMediaCodec */,
+        void *userdata,
+        AMediaFormat *format) {
+    ALOGV("OnFormatChangedCB: format(%s)", AMediaFormat_toString(format));
+    CallbackData *callbackData = (CallbackData *)userdata;
+    callbackData->addOutputFormat(format);
+}
+
+static void OnErrorCB(
+        AMediaCodec * /* aMediaCodec */,
+        void * /* userdata */,
+        media_status_t err,
+        int32_t actionCode,
+        const char *detail) {
+    ALOGV("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail);
+}
+
+static int adler32(const uint8_t *input, int len) {
+
+    int a = 1;
+    int b = 0;
+    for (int i = 0; i < len; i++) {
+        a += input[i];
+        b += a;
+        a = a % 65521;
+        b = b % 65521;
+    }
+    int ret = b * 65536 + a;
+    ALOGV("adler %d/%d", len, ret);
+    return ret;
+}
+
+static int checksum(const uint8_t *in, int len, AMediaFormat *format) {
+    int width, stride, height;
+    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width)) {
+        width = len;
+    }
+    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_STRIDE, &stride)) {
+        stride = width;
+    }
+    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height)) {
+        height = 1;
+    }
+    uint8_t *bb = new uint8_t[width * height];
+    for (int i = 0; i < height; i++) {
+        memcpy(bb + i * width, in + i * stride, width);
+    }
+    // bb is filled with data
+    int sum = adler32(bb, width * height);
+    delete[] bb;
+    return sum;
+}
+
+extern "C" jobject Java_android_media_decoder_cts_NativeDecoderTest_getDecodedDataNative(
+        JNIEnv *env, jclass /*clazz*/, int fd, jlong offset, jlong size, jboolean wrapFd,
+        jboolean useCallback) {
+    ALOGV("getDecodedDataNative");
+
+    FdDataSource fdSrc(fd, offset, size);
+    AMediaExtractor *ex = AMediaExtractor_new();
+    AMediaDataSource *ndkSrc = AMediaDataSource_new();
+
+    int err;
+    if (wrapFd) {
+        AMediaDataSource_setUserdata(ndkSrc, &fdSrc);
+        AMediaDataSource_setReadAt(ndkSrc, FdSourceReadAt);
+        AMediaDataSource_setGetSize(ndkSrc, FdSourceGetSize);
+        AMediaDataSource_setClose(ndkSrc, FdSourceClose);
+        err = AMediaExtractor_setDataSourceCustom(ex, ndkSrc);
+    } else {
+        err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
+    }
+    if (err != 0) {
+        ALOGE("setDataSource error: %d", err);
+        return NULL;
+    }
+
+    int numtracks = AMediaExtractor_getTrackCount(ex);
+
+    AMediaCodec **codec = new AMediaCodec*[numtracks];
+    AMediaFormat **format = new AMediaFormat*[numtracks];
+    memset(format, 0, sizeof(AMediaFormat*) * numtracks);
+    bool *sawInputEOS = new bool[numtracks];
+    bool *sawOutputEOS = new bool[numtracks];
+    simplevector<int> *sizes = new simplevector<int>[numtracks];
+    CallbackData *callbackData = new CallbackData[numtracks];
+
+    ALOGV("input has %d tracks", numtracks);
+    for (int i = 0; i < numtracks; i++) {
+        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+        const char *s = AMediaFormat_toString(format);
+        ALOGI("track %d format: %s", i, s);
+        const char *mime;
+        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+            ALOGE("no mime type");
+            return NULL;
+        } else if (!strncmp(mime, "audio/", 6) || !strncmp(mime, "video/", 6)) {
+            codec[i] = AMediaCodec_createDecoderByType(mime);
+            AMediaCodec_configure(codec[i], format, NULL /* surface */, NULL /* crypto */, 0);
+            if (useCallback) {
+                AMediaCodecOnAsyncNotifyCallback aCB = {
+                    OnInputAvailableCB,
+                    OnOutputAvailableCB,
+                    OnFormatChangedCB,
+                    OnErrorCB
+                };
+                AMediaCodec_setAsyncNotifyCallback(codec[i], aCB, &callbackData[i]);
+            }
+            AMediaCodec_start(codec[i]);
+            sawInputEOS[i] = false;
+            sawOutputEOS[i] = false;
+        } else {
+            ALOGE("expected audio or video mime type, got %s", mime);
+            return NULL;
+        }
+        AMediaFormat_delete(format);
+        AMediaExtractor_selectTrack(ex, i);
+    }
+    int eosCount = 0;
+    while(eosCount < numtracks) {
+        int t = AMediaExtractor_getSampleTrackIndex(ex);
+        if (t >=0) {
+            ssize_t bufidx;
+            if (useCallback) {
+                bufidx = callbackData[t].getInputBufferId();
+            } else {
+                bufidx = AMediaCodec_dequeueInputBuffer(codec[t], 5000);
+            }
+            ALOGV("track %d, input buffer %zd", t, bufidx);
+            if (bufidx >= 0) {
+                size_t bufsize;
+                uint8_t *buf = AMediaCodec_getInputBuffer(codec[t], bufidx, &bufsize);
+                int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
+                ALOGV("read %d", sampleSize);
+                if (sampleSize < 0) {
+                    sampleSize = 0;
+                    sawInputEOS[t] = true;
+                    ALOGV("EOS");
+                    //break;
+                }
+                int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
+
+                AMediaCodec_queueInputBuffer(codec[t], bufidx, 0, sampleSize, presentationTimeUs,
+                        sawInputEOS[t] ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+                AMediaExtractor_advance(ex);
+            }
+        } else {
+            ALOGV("@@@@ no more input samples");
+            for (int tt = 0; tt < numtracks; tt++) {
+                if (!sawInputEOS[tt]) {
+                    // we ran out of samples without ever signaling EOS to the codec,
+                    // so do that now
+                    int bufidx;
+                    if (useCallback) {
+                        bufidx = callbackData[tt].getInputBufferId();
+                    } else {
+                        bufidx = AMediaCodec_dequeueInputBuffer(codec[tt], 5000);
+                    }
+                    if (bufidx >= 0) {
+                        AMediaCodec_queueInputBuffer(codec[tt], bufidx, 0, 0, 0,
+                                AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
+                        sawInputEOS[tt] = true;
+                    }
+                }
+            }
+        }
+
+        // check all codecs for available data
+        AMediaCodecBufferInfo info;
+        AMediaFormat *outputFormat;
+        for (int tt = 0; tt < numtracks; tt++) {
+            if (!sawOutputEOS[tt]) {
+                int status;
+                if (useCallback) {
+                    status = callbackData[tt].getOutput(&info, &outputFormat);
+                } else {
+                    status = AMediaCodec_dequeueOutputBuffer(codec[tt], &info, 1);
+                }
+                ALOGV("dequeueoutput on track %d: %d", tt, status);
+                if (status >= 0) {
+                    if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+                        ALOGV("EOS on track %d", tt);
+                        sawOutputEOS[tt] = true;
+                        eosCount++;
+                    }
+                    ALOGV("got decoded buffer for track %d, size %d", tt, info.size);
+                    if (info.size > 0) {
+                        size_t bufsize;
+                        uint8_t *buf = AMediaCodec_getOutputBuffer(codec[tt], status, &bufsize);
+                        int adler = checksum(buf, info.size, format[tt]);
+                        sizes[tt].add(adler);
+                    }
+                    AMediaCodec_releaseOutputBuffer(codec[tt], status, false);
+                } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+                    ALOGV("output buffers changed for track %d", tt);
+                } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+                    if (format[tt] != NULL) {
+                        AMediaFormat_delete(format[tt]);
+                    }
+                    if (useCallback) {
+                        format[tt] = outputFormat;
+                    } else {
+                        format[tt] = AMediaCodec_getOutputFormat(codec[tt]);
+                    }
+                    ALOGV("format changed for track %d: %s", tt, AMediaFormat_toString(format[tt]));
+                } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+                    ALOGV("no output buffer right now for track %d", tt);
+                } else {
+                    ALOGV("unexpected info code for track %d : %d", tt, status);
+                }
+            } else {
+                ALOGV("already at EOS on track %d", tt);
+            }
+        }
+    }
+    ALOGV("decoding loop done");
+
+    // allocate java int array for result and return it
+    int numsamples = 0;
+    for (int i = 0; i < numtracks; i++) {
+        numsamples += sizes[i].size();
+    }
+    ALOGV("checksums: %d", numsamples);
+    jintArray ret = env->NewIntArray(numsamples);
+    jboolean isCopy;
+    jint *org = env->GetIntArrayElements(ret, &isCopy);
+    jint *dst = org;
+    for (int i = 0; i < numtracks; i++) {
+        int *data = sizes[i].data();
+        int len = sizes[i].size();
+        ALOGV("copying %d", len);
+        for (int j = 0; j < len; j++) {
+            *dst++ = data[j];
+        }
+    }
+    env->ReleaseIntArrayElements(ret, org, 0);
+
+    delete[] callbackData;
+    delete[] sizes;
+    delete[] sawOutputEOS;
+    delete[] sawInputEOS;
+    for (int i = 0; i < numtracks; i++) {
+        AMediaFormat_delete(format[i]);
+        AMediaCodec_stop(codec[i]);
+        AMediaCodec_delete(codec[i]);
+    }
+    delete[] format;
+    delete[] codec;
+    AMediaExtractor_delete(ex);
+    AMediaDataSource_delete(ndkSrc);
+    return ret;
+}
+
+extern "C" jboolean Java_android_media_decoder_cts_NativeDecoderTest_testPlaybackNative(JNIEnv *env,
+        jclass /*clazz*/, jobject surface, int fd, jlong offset, jlong size) {
+
+    ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
+    ALOGI("@@@@ native window: %p", window);
+
+    AMediaExtractor *ex = AMediaExtractor_new();
+    int err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
+    if (err != 0) {
+        ALOGE("setDataSource error: %d", err);
+        return false;
+    }
+
+    int numtracks = AMediaExtractor_getTrackCount(ex);
+
+    AMediaCodec *codec = NULL;
+    AMediaFormat *format = NULL;
+    bool sawInputEOS = false;
+    bool sawOutputEOS = false;
+
+    ALOGV("input has %d tracks", numtracks);
+    for (int i = 0; i < numtracks; i++) {
+        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+        const char *s = AMediaFormat_toString(format);
+        ALOGI("track %d format: %s", i, s);
+        const char *mime;
+        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+            ALOGE("no mime type");
+            return false;
+        } else if (!strncmp(mime, "video/", 6)) {
+            codec = AMediaCodec_createDecoderByType(mime);
+            AMediaCodec_configure(codec, format, window, NULL, 0);
+            AMediaCodec_start(codec);
+            AMediaExtractor_selectTrack(ex, i);
+        }
+        AMediaFormat_delete(format);
+    }
+
+    while (!sawOutputEOS) {
+        ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec, 5000);
+        ALOGV("input buffer %zd", bufidx);
+        if (bufidx >= 0) {
+            size_t bufsize;
+            uint8_t *buf = AMediaCodec_getInputBuffer(codec, bufidx, &bufsize);
+            int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
+            ALOGV("read %d", sampleSize);
+            if (sampleSize < 0) {
+                sampleSize = 0;
+                sawInputEOS = true;
+                ALOGV("EOS");
+            }
+            int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
+
+            AMediaCodec_queueInputBuffer(codec, bufidx, 0, sampleSize, presentationTimeUs,
+                    sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+            AMediaExtractor_advance(ex);
+        }
+
+        AMediaCodecBufferInfo info;
+        int status = AMediaCodec_dequeueOutputBuffer(codec, &info, 1);
+        ALOGV("dequeueoutput returned: %d", status);
+        if (status >= 0) {
+            if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+                ALOGV("output EOS");
+                sawOutputEOS = true;
+            }
+            ALOGV("got decoded buffer size %d", info.size);
+            AMediaCodec_releaseOutputBuffer(codec, status, true);
+            usleep(20000);
+        } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+            ALOGV("output buffers changed");
+        } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+            format = AMediaCodec_getOutputFormat(codec);
+            ALOGV("format changed to: %s", AMediaFormat_toString(format));
+        } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+            ALOGV("no output buffer right now");
+        } else {
+            ALOGV("unexpected info code: %d", status);
+        }
+    }
+
+    AMediaCodec_stop(codec);
+    AMediaCodec_delete(codec);
+    AMediaExtractor_delete(ex);
+    return true;
+}
diff --git a/tests/tests/media/res/raw/noise_1ch_16khz_aot39_ds_sbr_fl512_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_16khz_aot39_ds_sbr_fl512_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_16khz_aot39_ds_sbr_fl512_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_16khz_aot39_ds_sbr_fl512_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_24khz_aot39_ds_sbr_fl512_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_24khz_aot39_ds_sbr_fl512_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_24khz_aot39_ds_sbr_fl512_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_24khz_aot39_ds_sbr_fl512_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_24khz_aot5_dr_sbr_sig1_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_24khz_aot5_dr_sbr_sig1_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_24khz_aot5_dr_sbr_sig1_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_24khz_aot5_dr_sbr_sig1_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_24khz_aot5_ds_sbr_sig1_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_24khz_aot5_ds_sbr_sig1_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_24khz_aot5_ds_sbr_sig1_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_24khz_aot5_ds_sbr_sig1_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_29_4khz_aot42_19_lufs_drc_config_change_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_32khz_aot39_dr_sbr_fl480_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_32khz_aot39_dr_sbr_fl480_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_32khz_aot39_dr_sbr_fl480_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_32khz_aot39_dr_sbr_fl480_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_32khz_aot5_dr_sbr_sig2_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_32khz_aot5_dr_sbr_sig2_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_32khz_aot5_dr_sbr_sig2_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_32khz_aot5_dr_sbr_sig2_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_38_4khz_aot42_19_lufs_config_change_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_38_4khz_aot42_19_lufs_config_change_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_38_4khz_aot42_19_lufs_config_change_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_38_4khz_aot42_19_lufs_config_change_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_44khz_aot39_ds_sbr_fl512_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_44khz_aot39_ds_sbr_fl512_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_44khz_aot39_ds_sbr_fl512_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_44khz_aot39_ds_sbr_fl512_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_44khz_aot5_dr_sbr_sig0_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_44khz_aot5_dr_sbr_sig0_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_44khz_aot5_dr_sbr_sig0_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_44khz_aot5_dr_sbr_sig0_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_44khz_aot5_ds_sbr_sig2_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_44khz_aot5_ds_sbr_sig2_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_44khz_aot5_ds_sbr_sig2_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_44khz_aot5_ds_sbr_sig2_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_1ch_48khz_aot39_dr_sbr_fl480_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_1ch_48khz_aot39_dr_sbr_fl480_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_1ch_48khz_aot39_dr_sbr_fl480_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_1ch_48khz_aot39_dr_sbr_fl480_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_08khz_aot42_19_lufs_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_08khz_aot42_19_lufs_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_08khz_aot42_19_lufs_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_08khz_aot42_19_lufs_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_12khz_aot42_19_lufs_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_12khz_aot42_19_lufs_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_12khz_aot42_19_lufs_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_12khz_aot42_19_lufs_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_19_2khz_aot42_no_ludt_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_19_2khz_aot42_no_ludt_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_19_2khz_aot42_no_ludt_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_19_2khz_aot42_no_ludt_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_22_05khz_aot42_19_lufs_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_22_05khz_aot42_19_lufs_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_22_05khz_aot42_19_lufs_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_22_05khz_aot42_19_lufs_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_22khz_aot39_ds_sbr_fl512_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_22khz_aot39_ds_sbr_fl512_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_22khz_aot39_ds_sbr_fl512_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_22khz_aot39_ds_sbr_fl512_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_24khz_aot29_dr_sbr_sig0_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_24khz_aot29_dr_sbr_sig0_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_24khz_aot29_dr_sbr_sig0_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_24khz_aot29_dr_sbr_sig0_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_24khz_aot5_dr_sbr_sig2_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_24khz_aot5_dr_sbr_sig2_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_24khz_aot5_dr_sbr_sig2_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_24khz_aot5_dr_sbr_sig2_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_32khz_aot39_ds_sbr_fl512_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_32khz_aot39_ds_sbr_fl512_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_32khz_aot39_ds_sbr_fl512_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_32khz_aot39_ds_sbr_fl512_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_32khz_aot42_19_lufs_drc_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_32khz_aot42_19_lufs_drc_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_32khz_aot42_19_lufs_drc_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_32khz_aot42_19_lufs_drc_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_32khz_aot5_ds_sbr_sig2_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_32khz_aot5_ds_sbr_sig2_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_32khz_aot5_ds_sbr_sig2_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_32khz_aot5_ds_sbr_sig2_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_35_28khz_aot42_19_lufs_drc_config_change_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_44_1khz_aot42_19_lufs_config_change_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_44_1khz_aot42_19_lufs_config_change_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_44_1khz_aot42_19_lufs_config_change_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_44_1khz_aot42_19_lufs_config_change_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_44khz_aot29_dr_sbr_sig1_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_44khz_aot29_dr_sbr_sig1_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_44khz_aot29_dr_sbr_sig1_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_44khz_aot29_dr_sbr_sig1_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_44khz_aot39_dr_sbr_fl480_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_44khz_aot39_dr_sbr_fl480_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_44khz_aot39_dr_sbr_fl480_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_44khz_aot39_dr_sbr_fl480_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_48khz_aot29_dr_sbr_sig2_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot29_dr_sbr_sig2_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_48khz_aot29_dr_sbr_sig2_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot29_dr_sbr_sig2_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_48khz_aot39_ds_sbr_fl512_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot39_ds_sbr_fl512_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_48khz_aot39_ds_sbr_fl512_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot39_ds_sbr_fl512_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_48khz_aot42_19_lufs_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot42_19_lufs_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_48khz_aot42_19_lufs_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot42_19_lufs_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_48khz_aot5_dr_sbr_sig1_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot5_dr_sbr_sig1_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_48khz_aot5_dr_sbr_sig1_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot5_dr_sbr_sig1_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_48khz_aot5_ds_sbr_sig1_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot5_ds_sbr_sig1_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_48khz_aot5_ds_sbr_sig1_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_48khz_aot5_ds_sbr_sig1_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_48khz_tlou_19lufs_alou_21lufs_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_48khz_tlou_19lufs_alou_21lufs_mp4.m4a
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_48khz_tlou_19lufs_alou_21lufs_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_48khz_tlou_19lufs_alou_21lufs_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_48khz_tlou_19lufs_anchor_17lufs_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_48khz_tlou_19lufs_anchor_17lufs_mp4.m4a
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_48khz_tlou_19lufs_anchor_17lufs_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_48khz_tlou_19lufs_anchor_17lufs_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_48khz_tlou_19lufs_expert_23lufs_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_48khz_tlou_19lufs_expert_23lufs_mp4.m4a
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_48khz_tlou_19lufs_expert_23lufs_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_48khz_tlou_19lufs_expert_23lufs_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_64khz_aot42_19_lufs_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_64khz_aot42_19_lufs_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_64khz_aot42_19_lufs_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_64khz_aot42_19_lufs_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/noise_2ch_88_2khz_aot42_19_lufs_mp4.m4a b/tests/tests/media/decoder/res/raw/noise_2ch_88_2khz_aot42_19_lufs_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/noise_2ch_88_2khz_aot42_19_lufs_mp4.m4a
rename to tests/tests/media/decoder/res/raw/noise_2ch_88_2khz_aot42_19_lufs_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/sine_2ch_48khz_aot2_drchalf_mp4.m4a b/tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot2_drchalf_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/sine_2ch_48khz_aot2_drchalf_mp4.m4a
rename to tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot2_drchalf_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/sine_2ch_48khz_aot2_drcheavy_mp4.m4a b/tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot2_drcheavy_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/sine_2ch_48khz_aot2_drcheavy_mp4.m4a
rename to tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot2_drcheavy_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/sine_2ch_48khz_aot2_internalclip_mp4.m4a b/tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot2_internalclip_mp4.m4a
similarity index 100%
rename from tests/tests/media/res/raw/sine_2ch_48khz_aot2_internalclip_mp4.m4a
rename to tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot2_internalclip_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/sine_2ch_48khz_aot42_seek_mp4.m4a b/tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot42_seek_mp4.m4a
similarity index 100%
rename from tests/tests/media/res/raw/sine_2ch_48khz_aot42_seek_mp4.m4a
rename to tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot42_seek_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/sine_2ch_48khz_aot5_drcclip_mp4.m4a b/tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot5_drcclip_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/sine_2ch_48khz_aot5_drcclip_mp4.m4a
rename to tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot5_drcclip_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/sine_2ch_48khz_aot5_drcfull_mp4.m4a b/tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot5_drcfull_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/sine_2ch_48khz_aot5_drcfull_mp4.m4a
rename to tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot5_drcfull_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/sine_2ch_48khz_aot5_drclevel_mp4.m4a b/tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot5_drclevel_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/sine_2ch_48khz_aot5_drclevel_mp4.m4a
rename to tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot5_drclevel_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/sine_2ch_48khz_aot5_drcoff_mp4.m4a b/tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot5_drcoff_mp4.m4a
old mode 100755
new mode 100644
similarity index 100%
rename from tests/tests/media/res/raw/sine_2ch_48khz_aot5_drcoff_mp4.m4a
rename to tests/tests/media/decoder/res/raw/sine_2ch_48khz_aot5_drcoff_mp4.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_1216x2160_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1216x2160_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_1216x2160_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1216x2160_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_1280x544_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1280x544_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_1280x544_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1280x544_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_1280x720_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1280x720_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_1280x720_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1280x720_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_136x240_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_136x240_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_136x240_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_136x240_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_1440x1080_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1440x1080_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_1440x1080_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1440x1080_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_1920x1080_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1920x1080_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_1920x1080_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1920x1080_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_1920x1440_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1920x1440_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_1920x1440_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1920x1440_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_1920x818_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1920x818_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_1920x818_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_1920x818_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_192x144_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_192x144_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_192x144_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_192x144_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_202x360_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_202x360_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_202x360_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_202x360_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_2560x1090_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_2560x1090_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_2560x1090_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_2560x1090_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_2560x1440_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_2560x1440_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_2560x1440_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_2560x1440_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_256x108_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_256x108_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_256x108_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_256x108_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_256x144_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_256x144_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_256x144_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_256x144_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_270x480_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_270x480_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_270x480_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_270x480_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_2880x2160_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_2880x2160_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_2880x2160_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_2880x2160_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_320x240_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_320x240_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_320x240_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_320x240_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_3840x1634_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_3840x1634_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_3840x1634_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_3840x1634_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_3840x2160_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_3840x2160_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_3840x2160_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_3840x2160_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_406x720_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_406x720_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_406x720_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_406x720_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_426x182_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_426x182_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_426x182_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_426x182_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_426x240_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_426x240_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_426x240_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_426x240_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_480x360_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_480x360_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_480x360_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_480x360_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_608x1080_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_608x1080_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_608x1080_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_608x1080_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_640x272_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_640x272_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_640x272_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_640x272_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_640x360_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_640x360_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_640x360_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_640x360_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_640x480_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_640x480_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_640x480_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_640x480_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_810x1440_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_810x1440_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_810x1440_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_810x1440_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_82x144_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_82x144_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_82x144_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_82x144_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_854x362_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_854x362_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_854x362_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_854x362_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_854x480_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_854x480_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_854x480_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_854x480_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_accuracy_and_capability_960x720_golden.png b/tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_960x720_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_accuracy_and_capability_960x720_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_accuracy_and_capability_960x720_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_with_cropping_520x360_golden.png b/tests/tests/media/decoder/res/raw/video_decode_with_cropping_520x360_golden.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_with_cropping_520x360_golden.png
rename to tests/tests/media/decoder/res/raw/video_decode_with_cropping_520x360_golden.png
Binary files differ
diff --git a/tests/tests/media/res/raw/video_decode_with_cropping_520x360_original.png b/tests/tests/media/decoder/res/raw/video_decode_with_cropping_520x360_original.png
similarity index 100%
rename from tests/tests/media/res/raw/video_decode_with_cropping_520x360_original.png
rename to tests/tests/media/decoder/res/raw/video_decode_with_cropping_520x360_original.png
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java
index 605ed11..01e29b9 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/AdaptivePlaybackTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.decoder.cts;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -26,6 +26,10 @@
 import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
+import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.MediaTestBase;
+import android.media.cts.OutputSurface;
+import android.media.cts.Preconditions;
 import android.os.Build;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
@@ -56,7 +60,7 @@
 @MediaHeavyPresubmitTest
 @AppModeFull
 @RunWith(AndroidJUnit4.class)
-public class AdaptivePlaybackTest extends MediaPlayerTestBase {
+public class AdaptivePlaybackTest extends MediaTestBase {
 
     private static final boolean sIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
 
diff --git a/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
similarity index 98%
rename from tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
index 80aefa7..4c729ea 100644
--- a/tests/tests/media/src/android/media/cts/DecodeAccuracyTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
@@ -13,18 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.media.cts;
+package android.media.decoder.cts;
 
 import static junit.framework.TestCase.assertTrue;
 
 import static org.junit.Assert.fail;
 
-import android.media.cts.R;
+import android.media.decoder.cts.R;
 
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.media.MediaFormat;
+import android.media.cts.MediaCodecTunneledPlayer;
+import android.media.cts.MediaHeavyPresubmitTest;
 import android.os.Environment;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
diff --git a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestActivity.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestActivity.java
similarity index 94%
rename from tests/tests/media/src/android/media/cts/DecodeAccuracyTestActivity.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestActivity.java
index 8994c78..e837cfa 100644
--- a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestActivity.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestActivity.java
@@ -13,9 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.media.cts;
+package android.media.decoder.cts;
 
-import android.media.cts.R;
+import android.media.decoder.cts.R;
 
 import android.app.Activity;
 import android.os.Bundle;
diff --git a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java
index 3a4db29..b97c903 100644
--- a/tests/tests/media/src/android/media/cts/DecodeAccuracyTestBase.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.media.cts;
+package android.media.decoder.cts;
 
 import static org.junit.Assert.assertNotNull;
 
@@ -31,13 +31,13 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.SurfaceTexture;
+import android.media.decoder.cts.R;
 import android.media.MediaCodec;
 import android.media.MediaCodec.BufferInfo;
 import android.media.MediaCodecInfo.VideoCapabilities;
 import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
-import android.media.cts.R;
 import android.opengl.EGL14;
 import android.opengl.GLES11Ext;
 import android.opengl.GLES20;
diff --git a/tests/tests/media/src/android/media/cts/DecoderConformanceTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java
similarity index 96%
rename from tests/tests/media/src/android/media/cts/DecoderConformanceTest.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java
index 04ac7f8..893c437c 100644
--- a/tests/tests/media/src/android/media/cts/DecoderConformanceTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderConformanceTest.java
@@ -14,15 +14,18 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.decoder.cts;
 
 import static org.junit.Assert.fail;
 
 import android.content.res.AssetFileDescriptor;
+import android.media.decoder.cts.R;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
+import android.media.cts.MediaTestBase;
+import android.media.cts.Preconditions;
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
@@ -60,14 +63,14 @@
  */
 @AppModeFull(reason = "There should be no instant apps specific behavior related to conformance")
 @RunWith(AndroidJUnit4.class)
-public class DecoderConformanceTest extends MediaPlayerTestBase {
+public class DecoderConformanceTest extends MediaTestBase {
     private enum Status {
         FAIL,
         PASS,
         SKIP;
     }
 
-    private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+    private static final String REPORT_LOG_NAME = "CtsMediaDecoderTestCases";
     private static final String TAG = "DecoderConformanceTest";
     private static final String CONFORMANCE_SUBDIR = "conformance_vectors/";
     private DeviceReportLog mReportLog;
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
similarity index 89%
rename from tests/tests/media/src/android/media/cts/DecoderTest.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
index cae11bd..6cf8802 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
@@ -14,34 +14,63 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.decoder.cts;
+
+import static android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel31;
+import static android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel32;
+import static android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel4;
+import static android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel42;
+import static android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileHigh;
+import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel31;
+import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel41;
+import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCProfileMain;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.AssetFileDescriptor;
 import android.graphics.ImageFormat;
 import android.hardware.display.DisplayManager;
-import android.media.AudioTimestamp;
-import android.media.Image;
 import android.media.AudioFormat;
 import android.media.AudioManager;
+import android.media.AudioTimestamp;
+import android.media.AudioTrack;
+import android.media.Image;
 import android.media.MediaCodec;
 import android.media.MediaCodec.BufferInfo;
-import android.media.MediaCodecList;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
-import android.os.ParcelFileDescriptor;
+import android.media.cts.CodecState;
+import android.media.cts.MediaCodecTunneledPlayer;
+import android.media.cts.MediaCodecWrapper;
+import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.MediaTestBase;
+import android.media.cts.NdkMediaCodec;
+import android.media.cts.NonMediaMainlineTest;
+import android.media.cts.Preconditions;
+import android.media.cts.SdkMediaCodec;
+import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.view.Display;
 import android.view.Surface;
-import android.net.Uri;
-import android.os.Bundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
 
 import com.android.compatibility.common.util.ApiLevelUtil;
 import com.android.compatibility.common.util.CddTest;
@@ -51,8 +80,12 @@
 import com.android.compatibility.common.util.ResultType;
 import com.android.compatibility.common.util.ResultUnit;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SdkSuppress;
+import com.google.common.collect.ImmutableList;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.BufferedInputStream;
 import java.io.File;
@@ -63,33 +96,22 @@
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
-import java.util.function.Supplier;
-import java.util.zip.CRC32;
+import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-
-import static android.media.MediaCodecInfo.CodecProfileLevel.*;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import java.util.zip.CRC32;
 
 @MediaHeavyPresubmitTest
 @AppModeFull(reason = "There should be no instant apps specific behavior related to decoders")
 @RunWith(AndroidJUnit4.class)
-public class DecoderTest extends MediaPlayerTestBase {
+public class DecoderTest extends MediaTestBase {
     private static final String TAG = "DecoderTest";
-    private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+    private static final String REPORT_LOG_NAME = "CtsMediaDecoderTestCases";
     private static boolean mIsAtLeastR = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
 
     private static final int RESET_MODE_NONE = 0;
@@ -102,6 +124,11 @@
     private static final int CONFIG_MODE_NONE = 0;
     private static final int CONFIG_MODE_QUEUE = 1;
 
+    private static final int CODEC_ALL = 0; // All codecs must support
+    private static final int CODEC_ANY = 1; // At least one codec must support
+    private static final int CODEC_DEFAULT = 2; // Default codec must support
+    private static final int CODEC_OPTIONAL = 3; // Codec support is optional
+
     short[] mMasterBuffer;
     static final String mInpPrefix = WorkDir.getMediaDirString();
 
@@ -109,9 +136,10 @@
     private static final int SLEEP_TIME_MS = 1000;
     private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
 
-    private static final String MODULE_NAME = "CtsMediaTestCases";
+    private static final String MODULE_NAME = "CtsMediaDecoderTestCases";
     private DynamicConfigDeviceSide dynamicConfig;
     private DisplayManager mDisplayManager;
+    static final Map<String, String> sDefaultDecoders = new HashMap<>();
 
     private static boolean mIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
 
@@ -162,6 +190,18 @@
         super.tearDown();
     }
 
+    static boolean isDefaultCodec(String codecName, String mime) throws IOException {
+        if (sDefaultDecoders.containsKey(mime)) {
+            return sDefaultDecoders.get(mime).equalsIgnoreCase(codecName);
+        }
+        MediaCodec codec = MediaCodec.createDecoderByType(mime);
+        boolean isDefault = codec.getName().equalsIgnoreCase(codecName);
+        sDefaultDecoders.put(mime, codec.getName());
+        codec.release();
+
+        return isDefault;
+    }
+
     // TODO: add similar tests for other audio and video formats
     @Test
     public void testBug11696552() throws Exception {
@@ -737,7 +777,7 @@
                 " (Expect R: " + expectRange + " S: " + expectStandard + " T: " + expectTransfer + ")";
         Log.d(TAG, reportName);
 
-        DeviceReportLog log = new DeviceReportLog("CtsMediaTestCases", "color_aspects_test");
+        DeviceReportLog log = new DeviceReportLog("CtsMediaDecoderTestCases", "color_aspects_test");
         log.addValue("decoder_name", decoderName, ResultType.NEUTRAL, ResultUnit.NONE);
         log.addValue("test_id", testId, ResultType.NEUTRAL, ResultUnit.NONE);
         log.addValues(
@@ -1636,7 +1676,7 @@
         };
 
         for (Object [] sample: samples) {
-            for (String codecName : codecsFor((String)sample[0])) {
+            for (String codecName : codecsFor((String)sample[0], CODEC_DEFAULT)) {
                 AudioParameter decParams = new AudioParameter();
                 short[] decSamples = decodeToMemory(codecName, decParams,
                         (String)sample[0] /* resource */, RESET_MODE_NONE, CONFIG_MODE_NONE,
@@ -1658,7 +1698,7 @@
                 {"noise_6ch_44khz_aot5_dr_sbr_sig2_mp4.m4a", 6},
         };
         for (Object [] sample: samples) {
-            for (String codecName : codecsFor((String)sample[0] /* resource */)) {
+            for (String codecName : codecsFor((String)sample[0] /* resource */, CODEC_DEFAULT)) {
                 AudioParameter decParams = new AudioParameter();
                 short[] decSamples = decodeToMemory(codecName, decParams,
                         (String)sample[0] /* resource */, RESET_MODE_NONE, CONFIG_MODE_NONE,
@@ -1680,7 +1720,7 @@
                 "noise_2ch_48khz_aot29_dr_sbr_sig2_mp4.m4a"
         };
         for (String sample: samples) {
-            for (String codecName : codecsFor(sample)) {
+            for (String codecName : codecsFor(sample, CODEC_DEFAULT)) {
                 AudioParameter decParams = new AudioParameter();
                 short[] decSamples = decodeToMemory(codecName, decParams, sample,
                         RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
@@ -1695,20 +1735,20 @@
     @Test
     public void testDecodeAacEldM4a() throws Exception {
         // mono
-        decodeNtest("sinesweep1_1ch_16khz_aot39_fl480_mp4.m4a", 40.f);
-        decodeNtest("sinesweep1_1ch_22khz_aot39_fl512_mp4.m4a", 40.f);
-        decodeNtest("sinesweep1_1ch_24khz_aot39_fl480_mp4.m4a", 40.f);
-        decodeNtest("sinesweep1_1ch_32khz_aot39_fl512_mp4.m4a", 40.f);
-        decodeNtest("sinesweep1_1ch_44khz_aot39_fl480_mp4.m4a", 40.f);
-        decodeNtest("sinesweep1_1ch_48khz_aot39_fl512_mp4.m4a", 40.f);
+        decodeNtest("sinesweep1_1ch_16khz_aot39_fl480_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep1_1ch_22khz_aot39_fl512_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep1_1ch_24khz_aot39_fl480_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep1_1ch_32khz_aot39_fl512_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep1_1ch_44khz_aot39_fl480_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep1_1ch_48khz_aot39_fl512_mp4.m4a", 40.f, CODEC_DEFAULT);
 
         // stereo
-        decodeNtest("sinesweep_2ch_16khz_aot39_fl512_mp4.m4a", 40.f);
-        decodeNtest("sinesweep_2ch_22khz_aot39_fl480_mp4.m4a", 40.f);
-        decodeNtest("sinesweep_2ch_24khz_aot39_fl512_mp4.m4a", 40.f);
-        decodeNtest("sinesweep_2ch_32khz_aot39_fl480_mp4.m4a", 40.f);
-        decodeNtest("sinesweep_2ch_44khz_aot39_fl512_mp4.m4a", 40.f);
-        decodeNtest("sinesweep_2ch_48khz_aot39_fl480_mp4.m4a", 40.f);
+        decodeNtest("sinesweep_2ch_16khz_aot39_fl512_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep_2ch_22khz_aot39_fl480_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep_2ch_24khz_aot39_fl512_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep_2ch_32khz_aot39_fl480_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep_2ch_44khz_aot39_fl512_mp4.m4a", 40.f, CODEC_DEFAULT);
+        decodeNtest("sinesweep_2ch_48khz_aot39_fl480_mp4.m4a", 40.f, CODEC_DEFAULT);
 
         AudioParameter decParams = new AudioParameter();
 
@@ -1726,7 +1766,7 @@
                 {"noise_2ch_48khz_aot39_ds_sbr_fl512_mp4.m4a", 2},
         };
         for (Object [] sample: samples) {
-            for (String codecName : codecsFor((String)sample[0])) {
+            for (String codecName : codecsFor((String)sample[0], CODEC_DEFAULT)) {
                 short[] decSamples = decodeToMemory(codecName, decParams,
                         (String)sample[0] /* resource */, RESET_MODE_NONE, CONFIG_MODE_NONE,
                         -1, null);
@@ -1968,9 +2008,14 @@
      * @throws Exception
      */
     private void decodeNtest(final String testinput, float maxerror) throws Exception {
+        decodeNtest(testinput, maxerror, CODEC_ALL);
+    }
+
+    private void decodeNtest(final String testinput, float maxerror, int codecSupportMode)
+            throws Exception {
         String localTag = TAG + "#decodeNtest";
 
-        for (String codecName: codecsFor(testinput)) {
+        for (String codecName: codecsFor(testinput, codecSupportMode)) {
             AudioParameter decParams = new AudioParameter();
             short[] decoded = decodeToMemory(codecName, decParams, testinput,
                     RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
@@ -2017,6 +2062,11 @@
     }
 
     protected static List<String> codecsFor(String resource) throws IOException {
+        return codecsFor(resource, CODEC_ALL);
+    }
+
+    protected static List<String> codecsFor(String resource, int codecSupportMode)
+            throws IOException {
         MediaExtractor ex = new MediaExtractor();
         AssetFileDescriptor fd = getAssetFileDescriptorFor(resource);
         try {
@@ -2036,7 +2086,18 @@
             try {
                 MediaCodecInfo.CodecCapabilities caps = info.getCapabilitiesForType(mime);
                 if (caps != null) {
-                    matchingCodecs.add(info.getName());
+                    if (codecSupportMode == CODEC_ALL) {
+                        matchingCodecs.add(info.getName());
+                    } else if (codecSupportMode == CODEC_DEFAULT) {
+                        if (caps.isFormatSupported(format)) {
+                            matchingCodecs.add(info.getName());
+                        } else if (isDefaultCodec(info.getName(), mime)) {
+                            fail(info.getName() + " which is a default decoder for mime " + mime
+                                   + ", does not declare support for " + format.toString());
+                        }
+                    } else {
+                        fail("Unhandled codec support mode " + codecSupportMode);
+                    }
                 }
             } catch (IllegalArgumentException e) {
                 // type is not supported
@@ -3948,8 +4009,9 @@
                         waitTimeMs),
                 mMediaCodecPlayer.isFirstTunnelFrameReady());
         // Assert that video peek is enabled and working
-        assertTrue(String.format("First frame not rendered within %d milliseconds", waitTimeMs),
-                mMediaCodecPlayer.getCurrentPosition() != 0);
+        assertNotEquals(String.format("First frame not rendered within %d milliseconds",
+                        waitTimeMs), CodecState.UNINITIALIZED_TIMESTAMP,
+                mMediaCodecPlayer.getCurrentPosition());
 
         // mMediaCodecPlayer.reset() handled in TearDown();
     }
@@ -4022,16 +4084,16 @@
                         waitTimeMsStep1),
                 mMediaCodecPlayer.isFirstTunnelFrameReady());
         // Assert that video peek is disabled
-        assertEquals("First frame rendered while peek disabled",
-                mMediaCodecPlayer.getCurrentPosition(), 0);
+        assertEquals("First frame rendered while peek disabled", CodecState.UNINITIALIZED_TIMESTAMP,
+                mMediaCodecPlayer.getCurrentPosition());
         mMediaCodecPlayer.setVideoPeek(true); // Reenable video peek
         final int waitTimeMsStep2 = 150;
         Thread.sleep(waitTimeMsStep2);
         // Assert that video peek is enabled
-        assertTrue(String.format(
+        assertNotEquals(String.format(
                         "First frame not rendered within %d milliseconds while peek enabled",
-                        waitTimeMsStep2),
-                mMediaCodecPlayer.getCurrentPosition() != 0);
+                        waitTimeMsStep2), CodecState.UNINITIALIZED_TIMESTAMP,
+                mMediaCodecPlayer.getCurrentPosition());
 
         // mMediaCodecPlayer.reset() handled in TearDown();
     }
@@ -4067,6 +4129,186 @@
     }
 
     /**
+     * Test tunneled audio PTS gaps with HEVC if supported.
+     * If there exist PTS Gaps in AudioTrack playback, the framePosition returned by
+     * AudioTrack#getTimestamp must not advance for any silent frames rendered to fill the
+     * gap.
+     */
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+    @Test
+    public void testTunneledAudioPtsGapsHevc() throws Exception {
+        testTunneledAudioPtsGaps(MediaFormat.MIMETYPE_VIDEO_HEVC,
+                "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
+    }
+
+    /**
+     * Test tunneled audio PTS gaps with AVC if supported
+     * If there exist PTS Gaps in AudioTrack playback, the framePosition returned by
+     * AudioTrack#getTimestamp must not advance for any silent frames rendered to fill the
+     * gap.
+     */
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+    @Test
+    public void testTunneledAudioPtsGapsAvc() throws Exception {
+        testTunneledAudioPtsGaps(MediaFormat.MIMETYPE_VIDEO_AVC,
+                "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
+    }
+
+    /**
+     * Test tunneled audio PTS gaps with VP9 if supported
+     * If there exist PTS Gaps in AudioTrack playback, the framePosition returned by
+     * AudioTrack#getTimestamp must not advance for any silent frames rendered to fill the
+     * gap.
+     */
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+    @Test
+    public void testTunneledAudioPtsGapsVp9() throws Exception {
+        testTunneledAudioPtsGaps(MediaFormat.MIMETYPE_VIDEO_VP9,
+                "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
+    }
+
+    private void testTunneledAudioPtsGaps(String mimeType, String fileName) throws Exception {
+        if (!MediaUtils.check(isVideoFeatureSupported(mimeType,
+                CodecCapabilities.FEATURE_TunneledPlayback),
+                "No tunneled video playback codec found for MIME " + mimeType)) {
+            return;
+        }
+
+        AudioManager am = mContext.getSystemService(AudioManager.class);
+
+        mMediaCodecPlayer = new MediaCodecTunneledPlayer(mContext,
+                getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
+
+        final Uri mediaUri = Uri.fromFile(new File(mInpPrefix, fileName));
+        mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
+
+        assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
+        assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+
+        mMediaCodecPlayer.startThread();
+        sleepUntil(() -> mMediaCodecPlayer.getTimestamp() != null
+                && mMediaCodecPlayer.getTimestamp().framePosition > 0,  Duration.ofSeconds(1));
+        // After 30 ms, Changing the presentation offset for audio track
+        Thread.sleep(30);
+
+        // Requirement: If the audio presentation timestamp header sent by the app is greater than
+        // the current audio clock by less than 100ms, the framePosition returned by
+        // AudioTrack#getTimestamp (per get_presentation_position) must not advance for any silent
+        // frames rendered to fill the gap.
+        // TODO: add link to documentation when available
+        mMediaCodecPlayer.setAudioTrackOffsetMs(100);
+        // Wait for 20 ms so that whatever was buffered before offset is played
+        Thread.sleep(20);
+        long initialFramePosition = mMediaCodecPlayer.getTimestamp().framePosition;
+
+        // Verify that the framePosition did not advance after 30 ms. This ensures framePosition
+        // returned by AudioTrack#getTimestamp did not advance for any silent frames rendered to
+        // fill PTS gaps.
+        Thread.sleep(30);
+        assertEquals(
+                "Initial frame position != Final frame position after introducing PTS gaps",
+                initialFramePosition, mMediaCodecPlayer.getTimestamp().framePosition);
+
+        Thread.sleep(500);
+        mMediaCodecPlayer.stopWritingToAudioTrack(true);
+
+        // Sleep till framePosition stabilizes, i.e. playback is complete or till max 3 seconds.
+        long framePosCurrent = 0;
+        int totalSleepMs = 0;
+        while (totalSleepMs < 3000
+                && framePosCurrent != mMediaCodecPlayer.getTimestamp().framePosition) {
+            framePosCurrent = mMediaCodecPlayer.getTimestamp().framePosition;
+            Thread.sleep(500);
+            totalSleepMs += 500;
+        }
+
+        // Verify if number of frames written and played are same even if PTS Gaps were present
+        // in the playback.
+        assertEquals("Number of frames written != Number of frames played",
+                mMediaCodecPlayer.getAudioFramesWritten(),
+                mMediaCodecPlayer.getTimestamp().framePosition);
+    }
+
+    /**
+     * Test tunneled audioTimestamp progress with underrun, with HEVC if supported
+     */
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+    @Test
+    public void testTunneledAudioTimestampProgressWithUnderrunHevc() throws Exception {
+        testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC,
+                "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
+    }
+
+    /**
+     * Test tunneled audioTimestamp progress with underrun, with AVC if supported.
+     */
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+    @Test
+    public void testTunneledAudioTimestampProgressWithUnderrunAvc() throws Exception {
+        testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC,
+                "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
+    }
+
+    /**
+     *  Test tunneled audioTimestamp progress with underrun, with VP9 if supported.
+     */
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+    @Test
+    public void testTunneledAudioTimestampProgressWithUnderrunVp9() throws Exception {
+        testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9,
+                "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
+    }
+
+    private void testTunneledAudioTimestampProgressWithUnderrun(
+            String mimeType, String fileName) throws Exception {
+        if (!MediaUtils.check(isVideoFeatureSupported(mimeType,
+                CodecCapabilities.FEATURE_TunneledPlayback),
+                "No tunneled video playback codec found for MIME " + mimeType)) {
+            return;
+        }
+
+        AudioManager am = mContext.getSystemService(AudioManager.class);
+
+        mMediaCodecPlayer = new MediaCodecTunneledPlayer(mContext,
+                getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
+
+        final Uri mediaUri = Uri.fromFile(new File(mInpPrefix, fileName));
+        mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
+
+        assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
+        assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+
+        mMediaCodecPlayer.startThread();
+
+        // Stop writing to the AudioTrack after 200 ms.
+        Thread.sleep(200);
+        mMediaCodecPlayer.stopWritingToAudioTrack(true);
+
+        // Resume writing to the audioTrack after 1 sec. Write only for 200 ms.
+        Thread.sleep(1000);
+        mMediaCodecPlayer.stopWritingToAudioTrack(false);
+        Thread.sleep(200);
+        mMediaCodecPlayer.stopWritingToAudioTrack(true);
+
+        // Sleep till framePosition stabilizes, i.e. playback is complete or till max 3 seconds.
+        long framePosCurrent = 0;
+        int totalSleepMs = 0;
+        while (totalSleepMs < 3000
+                && framePosCurrent != mMediaCodecPlayer.getTimestamp().framePosition) {
+            framePosCurrent = mMediaCodecPlayer.getTimestamp().framePosition;
+            Thread.sleep(500);
+            totalSleepMs += 500;
+        }
+
+        // Verify if number of frames written and played are same. This ensures the
+        // framePosition returned by AudioTrack#getTimestamp progresses correctly in case of
+        // underrun
+        assertEquals("Number of frames written != Number of frames played",
+                mMediaCodecPlayer.getAudioFramesWritten(),
+                mMediaCodecPlayer.getTimestamp().framePosition);
+    }
+
+    /**
      * Test accurate video rendering after a video MediaCodec flush.
      *
      * On some devices, queuing content when the player is paused, then triggering a flush, then
@@ -4101,22 +4343,29 @@
         // start video playback
         mMediaCodecPlayer.startThread();
         Thread.sleep(100);
-        assertTrue("Video playback stalled", mMediaCodecPlayer.getCurrentPosition() != 0);
+        assertNotEquals("Video playback stalled", CodecState.UNINITIALIZED_TIMESTAMP,
+                mMediaCodecPlayer.getCurrentPosition());
         mMediaCodecPlayer.pause();
         Thread.sleep(50);
-        assertTrue("Video is ahead of audio", mMediaCodecPlayer.getCurrentPosition() <=
-                mMediaCodecPlayer.getAudioTrackPositionUs());
+        final long audioPositionUs = mMediaCodecPlayer.getAudioTrackPositionUs();
+        final long videoPositionUs = mMediaCodecPlayer.getCurrentPosition();
+        assertTrue(String.format("Video pts (%d) is ahead of audio pts (%d)",
+                        videoPositionUs, audioPositionUs),
+                videoPositionUs <= audioPositionUs);
         mMediaCodecPlayer.videoFlush();
+        mMediaCodecPlayer.videoSeekToBeginning(true /* shouldContinuePts */);
         Thread.sleep(50);
-        assertEquals("Video frame rendered after flush", mMediaCodecPlayer.getCurrentPosition(), 0);
+        assertEquals("Video frame rendered after flush", CodecState.UNINITIALIZED_TIMESTAMP,
+                mMediaCodecPlayer.getCurrentPosition());
         // We queue one frame, but expect it not to be rendered
         Long queuedVideoTimestamp = mMediaCodecPlayer.queueOneVideoFrame();
         assertNotNull("Failed to queue a video frame", queuedVideoTimestamp);
         Thread.sleep(50); // longer wait to account for buffer manipulation
-        assertEquals("Video frame rendered during pause", mMediaCodecPlayer.getCurrentPosition(), 0);
+        assertEquals("Video frame rendered during pause", CodecState.UNINITIALIZED_TIMESTAMP,
+                mMediaCodecPlayer.getCurrentPosition());
         mMediaCodecPlayer.resume();
         Thread.sleep(100);
-        ArrayList<Long> renderedVideoTimestamps =
+        ImmutableList<Long> renderedVideoTimestamps =
                 mMediaCodecPlayer.getRenderedVideoFrameTimestampList();
         assertFalse("No new video timestamps", renderedVideoTimestamps.isEmpty());
         assertEquals("First rendered video frame does not match first queued video frame",
@@ -4209,11 +4458,12 @@
         // starts video playback
         mMediaCodecPlayer.startThread();
 
-        sleepUntil(() -> mMediaCodecPlayer.getCurrentPosition() > 0, Duration.ofSeconds(1));
+        sleepUntil(() ->
+                mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP,
+                Duration.ofSeconds(1));
         final int firstPosition = mMediaCodecPlayer.getCurrentPosition();
-        assertTrue(
-                "On frame rendered not called after playback start!",
-                firstPosition > 0);
+        assertNotEquals("On frame rendered not called after playback start!",
+                CodecState.UNINITIALIZED_TIMESTAMP, firstPosition);
         AudioTimestamp firstTimestamp = mMediaCodecPlayer.getTimestamp();
         assertTrue("Timestamp is null!", firstTimestamp != null);
 
@@ -4235,6 +4485,146 @@
         assertEquals(timeStampAfterPause.nanoTime, mMediaCodecPlayer.getTimestamp().nanoTime);
     }
 
+    /**
+     * Test tunneled audio underrun, if supported.
+     *
+     * Underrun test with lower pts after underrun.
+     *
+     * TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content
+     */
+    private void tunneledAudioUnderrun(String mimeType, String videoName, int frameRate)
+            throws Exception {
+        if (!isVideoFeatureSupported(mimeType,
+                        CodecCapabilities.FEATURE_TunneledPlayback)) {
+            MediaUtils.skipTest(
+                    TAG,
+                    "No tunneled video playback codec found for MIME " + mimeType);
+            return;
+        }
+
+        AudioManager am = mContext.getSystemService(AudioManager.class);
+        mMediaCodecPlayer = new MediaCodecTunneledPlayer(
+                mContext, getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
+
+        Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName));
+        mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
+        mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
+        assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
+        assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+
+        // Start media playback
+        mMediaCodecPlayer.startThread();
+        final int waitStartMs = 50;
+        Thread.sleep(waitStartMs);
+        assertTrue(String.format("Playback has not started after %d milliseconds", waitStartMs),
+                mMediaCodecPlayer.getVideoTimeUs() != 0);
+        // Keep buffering video content but stop buffering audio content -> audio underrun
+        mMediaCodecPlayer.simulateAudioUnderrun(true);
+        // Loop to wait for audio underrun
+        // TODO(b/200280965): Find a more appropriate delay based on partner feedback
+        final int audioUnderrunTimeoutMs = 1000; // Arbitrary upper time limit on loop time duration
+        long startTimeMs = System.currentTimeMillis();
+        AudioTimestamp previousTimestamp = mMediaCodecPlayer.getTimestamp();
+        AudioTimestamp underrunAudioTimestamp;
+        while ((underrunAudioTimestamp = mMediaCodecPlayer.getTimestamp()) != previousTimestamp) {
+            assertTrue(String.format("No audio underrun after %d milliseconds",
+                            audioUnderrunTimeoutMs),
+                    System.currentTimeMillis() - startTimeMs < audioUnderrunTimeoutMs);
+            previousTimestamp = underrunAudioTimestamp;
+            Thread.sleep(50);
+        }
+        // Loop to wait until video playback stalls
+        long previousVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
+        long underrunVideoTimeUs;
+        startTimeMs = System.currentTimeMillis();
+        // TODO(b/200280965): Find a more appropriate delay based on partner feedback
+        final int videoUnderrunTimeoutMs = 1000;
+        while ((underrunVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs()) != previousVideoTimeUs) {
+            assertTrue(String.format("No video underrun after %d milliseconds",
+                            videoUnderrunTimeoutMs),
+                    System.currentTimeMillis() - startTimeMs < videoUnderrunTimeoutMs);
+            previousVideoTimeUs = underrunVideoTimeUs;
+            Thread.sleep(50);
+        }
+
+        final int underrunVideoRenderedTimestampIndex =
+                mMediaCodecPlayer.getRenderedVideoFrameTimestampList().size() - 1;
+        // Resume audio buffering with a negative offset, in order to simulate a desynchronisation.
+        // TODO(b/202710709): Use timestamp relative to last played video frame before pause
+        mMediaCodecPlayer.setAudioTrackOffsetMs(-100);
+        mMediaCodecPlayer.simulateAudioUnderrun(false);
+
+        // Loop to wait until audio playback resumes
+        startTimeMs = System.currentTimeMillis();
+        AudioTimestamp postResumeTimestamp;
+        while ((postResumeTimestamp = mMediaCodecPlayer.getTimestamp()) == underrunAudioTimestamp) {
+            assertTrue(String.format("Audio has not resumed after %d milliseconds",
+                            audioUnderrunTimeoutMs),
+                    System.currentTimeMillis() - startTimeMs < audioUnderrunTimeoutMs);
+            Thread.sleep(50);
+        }
+
+        long resumeAudioSystemTime = interpolateSystemTimeAt(
+                underrunAudioTimestamp.framePosition + 1, postResumeTimestamp,
+                mMediaCodecPlayer.getAudioTrack());
+
+        // Now that audio playback has resumed, loop to wait until video playback resumes
+        // We care about the timestamp of the first output frame, rather than the exact time the
+        // video resumed, which is why we only start polling after we are sure audio playback has
+        // resumed.
+        long resumeVideoTimeUs = 0;
+        startTimeMs = System.currentTimeMillis();
+        while ((resumeVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs()) == underrunVideoTimeUs) {
+            assertTrue(String.format("Video has not resumed after %d milliseconds",
+                            videoUnderrunTimeoutMs),
+                    System.currentTimeMillis() - startTimeMs < videoUnderrunTimeoutMs);
+            Thread.sleep(50);
+        }
+
+        final ImmutableList<Long> renderedSystemTimeList =
+                mMediaCodecPlayer.getRenderedVideoFrameSystemTimeList();
+        final long resumeVideoFrameSystemTime = mMediaCodecPlayer
+                .getRenderedVideoFrameSystemTimeList().get(underrunVideoRenderedTimestampIndex + 1);
+        final long vsync = (long) (1000 / frameRate);
+        final long avSyncOffset = resumeAudioSystemTime + 100 - resumeVideoFrameSystemTime;
+        assertTrue(String.format("Audio and video tracks are more than %d milliseconds out of sync",
+                        vsync),
+                Math.abs(avSyncOffset) <= vsync);
+    }
+
+    /**
+     * Test tunneled audio underrun with HEVC if supported
+     */
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+    @Test
+    public void testTunneledAudioUnderrunHevc() throws Exception {
+        tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC,
+                "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv",
+                25);
+    }
+
+    /**
+     * Test tunneled audio underrun with AVC if supported
+     */
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+    @Test
+    public void testTunneledAudioUnderrunAvc() throws Exception {
+        tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC,
+                "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4",
+                25);
+    }
+
+    /**
+     * Test tunneled audio underrun with VP9 if supported
+     */
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
+    @Test
+    public void testTunneledAudioUnderrunVp9() throws Exception {
+        tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9,
+                "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm",
+                30);
+    }
+
     private void sleepUntil(Supplier<Boolean> supplier, Duration maxWait) throws Exception {
         final long deadLineMs = System.currentTimeMillis() + maxWait.toMillis();
         do {
@@ -4243,6 +4633,18 @@
     }
 
     /**
+     * Returns the system time of the frame {@code framePosition} from {@code timestamp}, for a
+     * specific {@code AudioTrack}.
+     */
+    private static long interpolateSystemTimeAt(long framePosition, AudioTimestamp timestamp,
+            AudioTrack audioTrack) {
+        final long playbackRateFps = audioTrack.getPlaybackRate();  // Frames per second
+        final long playedFrames = timestamp.framePosition - framePosition;
+        final double elapsedTimeNs = playedFrames * (1000000000.0 / playbackRateFps);
+        return timestamp.nanoTime - (long) elapsedTimeNs;
+    }
+
+    /**
      * Returns list of CodecCapabilities advertising support for the given MIME type.
      */
     private static List<CodecCapabilities> getCodecCapabilitiesForMimeType(String mimeType) {
diff --git a/tests/tests/media/src/android/media/cts/DecoderTestAacDrc.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacDrc.java
old mode 100755
new mode 100644
similarity index 99%
rename from tests/tests/media/src/android/media/cts/DecoderTestAacDrc.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacDrc.java
index e33505a..e915977
--- a/tests/tests/media/src/android/media/cts/DecoderTestAacDrc.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacDrc.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.decoder.cts;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -24,11 +24,11 @@
 import android.app.Instrumentation;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
+import android.media.decoder.cts.DecoderTest.AudioParameter;
+import android.media.decoder.cts.R;
 import android.media.MediaCodec;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
-import android.media.cts.DecoderTest.AudioParameter;
-import android.media.cts.R;
 import android.os.Build;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
diff --git a/tests/tests/media/src/android/media/cts/DecoderTestAacFormat.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java
old mode 100755
new mode 100644
similarity index 98%
rename from tests/tests/media/src/android/media/cts/DecoderTestAacFormat.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java
index 1c01468..7bb5b47
--- a/tests/tests/media/src/android/media/cts/DecoderTestAacFormat.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.decoder.cts;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -23,10 +23,11 @@
 
 import android.app.Instrumentation;
 import android.content.res.AssetFileDescriptor;
+import android.media.decoder.cts.DecoderTest.AudioParameter;
 import android.media.MediaCodec;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
-import android.media.cts.DecoderTest.AudioParameter;
+import android.media.cts.Preconditions;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
diff --git a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestXheAac.java
old mode 100755
new mode 100644
similarity index 99%
rename from tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestXheAac.java
index d0052da..81d1e71
--- a/tests/tests/media/src/android/media/cts/DecoderTestXheAac.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestXheAac.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.decoder.cts;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -24,13 +24,14 @@
 import android.app.Instrumentation;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
+import android.media.decoder.cts.DecoderTest.AudioParameter;
+import android.media.decoder.cts.DecoderTestAacDrc.DrcParams;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
-import android.media.cts.DecoderTest.AudioParameter;
-import android.media.cts.DecoderTestAacDrc.DrcParams;
+import android.media.cts.TestUtils;
 import android.os.Build;
 import android.os.Bundle;
 import android.platform.test.annotations.AppModeFull;
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
similarity index 68%
rename from tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
index 713e24e..ddff307 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
@@ -14,13 +14,11 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.decoder.cts;
 
 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010;
 
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.Resources.NotFoundException;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.media.Image;
@@ -30,37 +28,46 @@
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaCodecInfo.VideoCapabilities;
-import android.media.MediaCodecList;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.cts.CodecUtils;
+import android.media.cts.Preconditions;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresDevice;
-import android.test.AndroidTestCase;
+
 import android.util.Log;
 import android.view.Surface;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.compatibility.common.util.MediaUtils;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
 /**
  * Basic test for ImageReader APIs.
  * <p>
@@ -75,7 +82,8 @@
 @SmallTest
 @RequiresDevice
 @AppModeFull(reason = "Instant apps cannot access the SD card")
-public class ImageReaderDecoderTest extends AndroidTestCase {
+@RunWith(Parameterized.class)
+public class ImageReaderDecoderTest {
     private static final String TAG = "ImageReaderDecoderTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -98,31 +106,87 @@
     private Handler mHandler;
     private ImageListener mImageListener;
 
-    @Override
-    public void setContext(Context context) {
-        super.setContext(context);
+    public String mMime;
+    public String mCodecName;
+    public MediaAsset mMediaAsset;
+    public int mMode;
+    MediaCodec mDecoder = null;
+    MediaExtractor mExtractor = null;
+
+    public ImageReaderDecoderTest(String mime, String codecName, MediaAsset asset, int mode,
+                                  String testId) {
+        mMime = mime;
+        mCodecName = codecName;
+        mMediaAsset = asset;
+        mMode = mode;
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Parameterized.Parameters(name = "{index}({0}_{1}_{4})")
+    public static Collection<Object[]> input() {
+        final List<Object[]> argsList = new ArrayList<>();
+        for (MediaAssets assets : ASSETS) {
+            String mime = assets.getMime();
+            String[] decoders = MediaUtils.getDecoderNamesForMime(mime);
+            for (String decoder: decoders) {
+                for (MediaAsset asset : assets.getAssets()) {
+                    String id = asset.getWidth() + "x" + asset.getHeight();
+                    id += "_" + asset.getBitDepth() + "bit";
+                    if (asset.getIsSwirl()) {
+                        id += "_swirl";
+                        argsList.add(new Object[]{mime, decoder, asset, MODE_IMAGE, id + "_image"});
+                    }
+                    argsList.add(new Object[]{mime, decoder, asset, MODE_IMAGEREADER,
+                            id + "_imagereader"});
+                }
+            }
+        }
+        return argsList;
+    }
+
+    @Before
+    public void setUp() throws Exception {
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
         mImageListener = new ImageListener();
+
+        mDecoder = MediaCodec.createByCodecName(mCodecName);
+        mExtractor = new MediaExtractor();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
+        closeImageReader();
         mHandlerThread.quitSafely();
         mHandler = null;
+        if (mExtractor != null) {
+            mExtractor.release();
+        }
+        if (mDecoder != null) {
+            mDecoder.release();
+        }
     }
 
     static class MediaAsset {
-        public MediaAsset(String resource, int width, int height) {
+        public MediaAsset(String resource, int width, int height, boolean isSwirl,
+                          int bitDepth) {
             mResource = resource;
             mWidth = width;
             mHeight = height;
+            mIsSwirl = isSwirl;
+            mBitDepth = bitDepth;
+        }
+
+        public MediaAsset(String resource, int width, int height) {
+            this(resource, width, height, true, 8);
+        }
+
+        public MediaAsset(String resource, int width, int height, boolean isSwirl) {
+            this(resource, width, height, isSwirl, 8);
+        }
+
+        public MediaAsset(String resource, int width, int height, int bitDepth) {
+            this(resource, width, height, true, bitDepth);
         }
 
         public int getWidth() {
@@ -133,6 +197,14 @@
             return mHeight;
         }
 
+        public boolean getIsSwirl() {
+            return mIsSwirl;
+        }
+
+        public int getBitDepth() {
+            return mBitDepth;
+        }
+
         public String getResource() {
             return mResource;
         }
@@ -140,6 +212,8 @@
         private final String mResource;
         private final int mWidth;
         private final int mHeight;
+        private final boolean mIsSwirl;
+        private final int mBitDepth;
     }
 
     static class MediaAssets {
@@ -161,14 +235,6 @@
     }
 
     static final String mInpPrefix = WorkDir.getMediaDirString();
-    protected AssetFileDescriptor getAssetFileDescriptorFor(final String res)
-            throws FileNotFoundException {
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        File inpFile = new File(mInpPrefix + res);
-        ParcelFileDescriptor parcelFD =
-                ParcelFileDescriptor.open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY);
-        return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
-    }
 
     private static MediaAssets H263_ASSETS = new MediaAssets(
             MediaFormat.MIMETYPE_VIDEO_H263,
@@ -190,7 +256,9 @@
             new MediaAsset("swirl_144x136_h264.mp4", 144, 136),
             new MediaAsset("swirl_136x144_h264.mp4", 136, 144),
             new MediaAsset("swirl_132x130_h264.mp4", 132, 130),
-            new MediaAsset("swirl_130x132_h264.mp4", 130, 132));
+            new MediaAsset("swirl_130x132_h264.mp4", 130, 132),
+            new MediaAsset("video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4",
+                    480, 360, false));
 
     private static MediaAssets H265_ASSETS = new MediaAssets(
             MediaFormat.MIMETYPE_VIDEO_HEVC,
@@ -216,241 +284,77 @@
             new MediaAsset("swirl_132x130_vp9.webm", 132, 130),
             new MediaAsset("swirl_130x132_vp9.webm", 130, 132));
 
+    private static MediaAssets AV1_ASSETS = new MediaAssets(
+            MediaFormat.MIMETYPE_VIDEO_AV1,
+            new MediaAsset("swirl_128x128_av1.webm", 128, 128),
+            new MediaAsset("swirl_144x136_av1.webm", 144, 136),
+            new MediaAsset("swirl_136x144_av1.webm", 136, 144),
+            new MediaAsset("swirl_132x130_av1.webm", 132, 130),
+            new MediaAsset("swirl_130x132_av1.webm", 130, 132));
+
     static final float SWIRL_FPS = 12.f;
 
-    class Decoder {
-        final private String mName;
-        final private String mMime;
-        final private VideoCapabilities mCaps;
-        final private ArrayList<MediaAsset> mAssets;
+    private static MediaAssets[] ASSETS = {H263_ASSETS, MPEG4_ASSETS, H264_ASSETS, H265_ASSETS,
+            VP8_ASSETS, VP9_ASSETS, AV1_ASSETS};
 
-        boolean isFlexibleFormatSupported(CodecCapabilities caps) {
-            for (int c : caps.colorFormats) {
-                if (c == COLOR_FormatYUV420Flexible) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        Decoder(String name, MediaAssets assets, CodecCapabilities caps) {
-            mName = name;
-            mMime = assets.getMime();
-            mCaps = caps.getVideoCapabilities();
-            mAssets = new ArrayList<MediaAsset>();
-
-            for (MediaAsset asset : assets.getAssets()) {
-                if (mCaps.areSizeAndRateSupported(asset.getWidth(), asset.getHeight(), SWIRL_FPS)
-                        && isFlexibleFormatSupported(caps)) {
-                    mAssets.add(asset);
-                }
+   boolean isColorFormatSupported(CodecCapabilities caps, int colorFormat) {
+        for (int c : caps.colorFormats) {
+            if (c == colorFormat) {
+                return true;
             }
         }
+        return false;
+    }
 
-        public boolean videoDecode(int mode, boolean checkSwirl) {
-            boolean skipped = true;
-            for (MediaAsset asset: mAssets) {
-                // TODO: loop over all supported image formats
-                int imageFormat = ImageFormat.YUV_420_888;
-                int colorFormat = COLOR_FormatYUV420Flexible;
-                videoDecode(asset, imageFormat, colorFormat, mode, checkSwirl);
-                skipped = false;
-            }
-            return skipped;
+    @Test
+    public void decodeTest() throws Exception {
+        int imageFormat = ImageFormat.YUV_420_888;
+        int colorFormat = COLOR_FormatYUV420Flexible;
+        String video = mMediaAsset.getResource();
+        int width = mMediaAsset.getWidth();
+        int height = mMediaAsset.getHeight();
+
+        if (8 == mMediaAsset.getBitDepth()) {
+            imageFormat = ImageFormat.YUV_420_888;
+            colorFormat = COLOR_FormatYUV420Flexible;
+        } else {
+            imageFormat = ImageFormat.YCBCR_P010;
+            colorFormat = COLOR_FormatYUVP010;
         }
 
-        private void videoDecode(
-                MediaAsset asset, int imageFormat, int colorFormat, int mode, boolean checkSwirl) {
-            String video = asset.getResource();
-            int width = asset.getWidth();
-            int height = asset.getHeight();
-
-            if (DEBUG) Log.d(TAG, "videoDecode " + mName + " " + width + "x" + height);
-
-            MediaCodec decoder = null;
-            AssetFileDescriptor vidFD = null;
-
-            MediaExtractor extractor = null;
-            File tmpFile = null;
-            InputStream is = null;
-            FileOutputStream os = null;
-            MediaFormat mediaFormat = null;
-            try {
-                extractor = new MediaExtractor();
-
-                try {
-                    vidFD = getAssetFileDescriptorFor(video);
-                    extractor.setDataSource(
-                            vidFD.getFileDescriptor(), vidFD.getStartOffset(), vidFD.getLength());
-                } catch (NotFoundException e) {
-                    // resource is compressed, uncompress locally
-                    String tmpName = "tempStream";
-                    tmpFile = File.createTempFile(tmpName, null, mContext.getCacheDir());
-                    is = new FileInputStream(mInpPrefix + video);
-                    os = new FileOutputStream(tmpFile);
-                    byte[] buf = new byte[1024];
-                    int len;
-                    while ((len = is.read(buf, 0, buf.length)) > 0) {
-                        os.write(buf, 0, len);
-                    }
-                    os.close();
-                    is.close();
-
-                    extractor.setDataSource(tmpFile.getAbsolutePath());
-                }
-
-                mediaFormat = extractor.getTrackFormat(0);
-                mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
-
-                // Create decoder
-                decoder = MediaCodec.createByCodecName(mName);
-                assertNotNull("couldn't create decoder" + mName, decoder);
-
-                decodeFramesToImage(
-                        decoder, extractor, mediaFormat,
-                        width, height, imageFormat, mode, checkSwirl);
-
-                decoder.stop();
-                if (vidFD != null) {
-                    vidFD.close();
-                }
-            } catch (Throwable e) {
-                throw new RuntimeException(
-                        "while " + mName + " decoding " + video + ": " + mediaFormat, e);
-            } finally {
-                if (decoder != null) {
-                    decoder.release();
-                }
-                if (extractor != null) {
-                    extractor.release();
-                }
-                if (tmpFile != null) {
-                    tmpFile.delete();
-                }
-            }
+        if (DEBUG) {
+            Log.d(TAG, "videoDecode " + mCodecName + " " + width + "x" + height + " bit depth " +
+                    mMediaAsset.getBitDepth());
         }
-    }
 
-    private Decoder[] decoders(MediaAssets assets, boolean goog) {
-        String mime = assets.getMime();
-        MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-        ArrayList<Decoder> result = new ArrayList<Decoder>();
+        MediaFormat mediaFormat = null;
 
-        for (MediaCodecInfo info : mcl.getCodecInfos()) {
-            if (info.isEncoder() || info.isAlias() || !info.isVendor() != goog) {
-                continue;
-            }
-            CodecCapabilities caps = null;
-            try {
-                caps = info.getCapabilitiesForType(mime);
-            } catch (IllegalArgumentException e) { // mime is not supported
-                continue;
-            }
-            assertNotNull(info.getName() + " capabilties for " + mime + " returned null", caps);
-            result.add(new Decoder(info.getName(), assets, caps));
-        }
-        return result.toArray(new Decoder[result.size()]);
-    }
+        Preconditions.assertTestFileExists(mInpPrefix + video);
+        mExtractor.setDataSource(mInpPrefix + video);
 
-    private Decoder[] goog(MediaAssets assets) {
-        return decoders(assets, true /* goog */);
-    }
+        mediaFormat = mExtractor.getTrackFormat(0);
+        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
 
-    private Decoder[] other(MediaAssets assets) {
-        return decoders(assets, false /* goog */);
-    }
 
-    private Decoder[] googH265()  { return goog(H265_ASSETS); }
-    private Decoder[] googH264()  { return goog(H264_ASSETS); }
-    private Decoder[] googH263()  { return goog(H263_ASSETS); }
-    private Decoder[] googMpeg4() { return goog(MPEG4_ASSETS); }
-    private Decoder[] googVP8()   { return goog(VP8_ASSETS); }
-    private Decoder[] googVP9()   { return goog(VP9_ASSETS); }
+        MediaCodecInfo info = mDecoder.getCodecInfo();
+        CodecCapabilities caps = info.getCapabilitiesForType(mMime);
+        VideoCapabilities videoCaps = caps.getVideoCapabilities();
 
-    private Decoder[] otherH265()  { return other(H265_ASSETS); }
-    private Decoder[] otherH264()  { return other(H264_ASSETS); }
-    private Decoder[] otherH263()  { return other(H263_ASSETS); }
-    private Decoder[] otherMpeg4() { return other(MPEG4_ASSETS); }
-    private Decoder[] otherVP8()   { return other(VP8_ASSETS); }
-    private Decoder[] otherVP9()   { return other(VP9_ASSETS); }
+        assumeTrue("Media format " + mediaFormat + " is not supported by " + mCodecName,
+                caps.isFormatSupported(mediaFormat));
+        assumeTrue(mMediaAsset.getWidth() + "x" + mMediaAsset.getHeight() + " @ " +
+                SWIRL_FPS + " fps is not supported by " + mCodecName,
+                videoCaps.areSizeAndRateSupported(mMediaAsset.getWidth(),
+                mMediaAsset.getHeight(), SWIRL_FPS));
+        assumeTrue("Color format " + colorFormat + " is not supported by " + mCodecName,
+                isColorFormatSupported(caps, colorFormat));
 
-    public void testGoogH265Image()   { swirlTest(googH265(),   MODE_IMAGE); }
-    public void testGoogH264Image()   { swirlTest(googH264(),   MODE_IMAGE); }
-    public void testGoogH263Image()   { swirlTest(googH263(),   MODE_IMAGE); }
-    public void testGoogMpeg4Image()  { swirlTest(googMpeg4(),  MODE_IMAGE); }
-    public void testGoogVP8Image()    { swirlTest(googVP8(),    MODE_IMAGE); }
-    public void testGoogVP9Image()    { swirlTest(googVP9(),    MODE_IMAGE); }
+        decodeFramesToImage(
+                mDecoder, mExtractor, mediaFormat,
+                width, height, imageFormat, mMode, mMediaAsset.getIsSwirl());
 
-    public void testOtherH265Image()  { swirlTest(otherH265(),  MODE_IMAGE); }
-    public void testOtherH264Image()  { swirlTest(otherH264(),  MODE_IMAGE); }
-    public void testOtherH263Image()  { swirlTest(otherH263(),  MODE_IMAGE); }
-    public void testOtherMpeg4Image() { swirlTest(otherMpeg4(), MODE_IMAGE); }
-    public void testOtherVP8Image()   { swirlTest(otherVP8(),   MODE_IMAGE); }
-    public void testOtherVP9Image()   { swirlTest(otherVP9(),   MODE_IMAGE); }
+        mDecoder.stop();
 
-    public void testGoogH265ImageReader()   { swirlTest(googH265(),   MODE_IMAGEREADER); }
-    public void testGoogH264ImageReader()   { swirlTest(googH264(),   MODE_IMAGEREADER); }
-    public void testGoogH263ImageReader()   { swirlTest(googH263(),   MODE_IMAGEREADER); }
-    public void testGoogMpeg4ImageReader()  { swirlTest(googMpeg4(),  MODE_IMAGEREADER); }
-    public void testGoogVP8ImageReader()    { swirlTest(googVP8(),    MODE_IMAGEREADER); }
-    public void testGoogVP9ImageReader()    { swirlTest(googVP9(),    MODE_IMAGEREADER); }
-
-    // TODO: b/186001256
-    @FlakyTest
-    public void testOtherH265ImageReader()  { swirlTest(otherH265(),  MODE_IMAGEREADER); }
-    @FlakyTest
-    public void testOtherH264ImageReader()  { swirlTest(otherH264(),  MODE_IMAGEREADER); }
-    public void testOtherH263ImageReader()  { swirlTest(otherH263(),  MODE_IMAGEREADER); }
-    public void testOtherMpeg4ImageReader() { swirlTest(otherMpeg4(), MODE_IMAGEREADER); }
-    @FlakyTest
-    public void testOtherVP8ImageReader()   { swirlTest(otherVP8(),   MODE_IMAGEREADER); }
-    @FlakyTest
-    public void testOtherVP9ImageReader()   { swirlTest(otherVP9(),   MODE_IMAGEREADER); }
-
-    /**
-     * Test ImageReader with 480x360 non-google AVC decoding for flexible yuv format
-     */
-    public void testHwAVCDecode360pForFlexibleYuv() throws Exception {
-        Decoder[] decoders = other(new MediaAssets(
-                MediaFormat.MIMETYPE_VIDEO_AVC,
-                new MediaAsset(
-                        "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4",
-                        480 /* width */, 360 /* height */)));
-
-        decodeTest(decoders, MODE_IMAGEREADER, false /* checkSwirl */);
-    }
-
-    /**
-     * Test ImageReader with 480x360 google (SW) AVC decoding for flexible yuv format
-     */
-    public void testSwAVCDecode360pForFlexibleYuv() throws Exception {
-        Decoder[] decoders = goog(new MediaAssets(
-                MediaFormat.MIMETYPE_VIDEO_AVC,
-                new MediaAsset(
-                        "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4",
-                        480 /* width */, 360 /* height */)));
-
-        decodeTest(decoders, MODE_IMAGEREADER, false /* checkSwirl */);
-    }
-
-    private void swirlTest(Decoder[] decoders, int mode) {
-        decodeTest(decoders, mode, true /* checkSwirl */);
-    }
-
-    private void decodeTest(Decoder[] decoders, int mode, boolean checkSwirl) {
-        try {
-            boolean skipped = true;
-            for (Decoder codec : decoders) {
-                if (codec.videoDecode(mode, checkSwirl)) {
-                    skipped = false;
-                }
-            }
-            if (skipped) {
-                MediaUtils.skipTest("decoder does not any of the input files");
-            }
-        } finally {
-            closeImageReader();
-        }
     }
 
     private static class ImageListener implements ImageReader.OnImageAvailableListener {
@@ -653,10 +557,20 @@
         final int NUM_SIDES = 4;
         final int step = 8;      // the width of the layers
         long[][] rawStats = new long[NUM_SIDES][10];
+        // expected colors for YUV 4:2:0 bit-depth 8
         int[][] colors = new int[][] {
             { 111, 96, 204 }, { 178, 27, 174 }, { 100, 192, 92 }, { 106, 117, 62 }
         };
 
+        // For P010 multiply expected colors by 4 to account for bit-depth 10
+        if (image.getFormat() == ImageFormat.YCBCR_P010) {
+            for (int i = 0; i < colors.length; i++) {
+                for (int j = 0; j < colors[0].length; j++) {
+                    colors[i][j] = colors[i][j] << 2;
+                }
+            }
+        }
+
         // successively accumulate statistics for each layer of the swirl
         // by using overlapping rectangles, and the observation that
         // layer_i = rectangle_i - rectangle_(i+1)
@@ -720,10 +634,11 @@
     private static void validateYuvData(byte[] yuvData, int width, int height, int format,
             long ts) {
 
-        assertTrue("YUV format must be one of the YUV_420_888, NV21, or YV12",
+        assertTrue("YUV format must be one of the YUV_420_888, NV21, YV12 or YCBCR_P010",
                 format == ImageFormat.YUV_420_888 ||
                 format == ImageFormat.NV21 ||
-                format == ImageFormat.YV12);
+                format == ImageFormat.YV12 ||
+                format == ImageFormat.YCBCR_P010);
 
         if (VERBOSE) Log.v(TAG, "Validating YUV data");
         int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
@@ -733,14 +648,15 @@
     private static void checkYuvFormat(int format) {
         if ((format != ImageFormat.YUV_420_888) &&
                 (format != ImageFormat.NV21) &&
-                (format != ImageFormat.YV12)) {
+                (format != ImageFormat.YV12) &&
+                (format != ImageFormat.YCBCR_P010)) {
             fail("Wrong formats: " + format);
         }
     }
     /**
      * <p>Check android image format validity for an image, only support below formats:</p>
      *
-     * <p>Valid formats are YUV_420_888/NV21/YV12 for video decoder</p>
+     * <p>Valid formats are YUV_420_888/NV21/YV12/P010 for video decoder</p>
      */
     private static void checkAndroidImageFormat(Image image) {
         int format = image.getFormat();
@@ -749,6 +665,7 @@
             case ImageFormat.YUV_420_888:
             case ImageFormat.NV21:
             case ImageFormat.YV12:
+            case ImageFormat.YCBCR_P010:
                 assertEquals("YUV420 format Images should have 3 planes", 3, planes.length);
                 break;
             default:
@@ -761,7 +678,7 @@
      * <p>
      * Read data from all planes of an Image into a contiguous unpadded,
      * unpacked 1-D linear byte array, such that it can be write into disk, or
-     * accessed by software conveniently. It supports YUV_420_888/NV21/YV12
+     * accessed by software conveniently. It supports YUV_420_888/NV21/YV12/P010
      * input Image format.
      * </p>
      * <p>
@@ -811,7 +728,9 @@
             buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift));
             assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w);
             for (int row = 0; row < h; row++) {
-                int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
+                // ImageFormat.getBitsPerPixel() returns total bits per pixel, which is 12 for
+                // YUV 4:2:0 8-bit, whereas bytesPerPixel is for Y plane only
+                int bytesPerPixel = (ImageFormat.getBitsPerPixel(format) * 2) / (8 * 3);
                 int length;
                 if (pixelStride == bytesPerPixel) {
                     // Special case: optimized read of the entire row
@@ -825,7 +744,9 @@
                     length = (w - 1) * pixelStride + bytesPerPixel;
                     buffer.get(rowData, 0, length);
                     for (int col = 0; col < w; col++) {
-                        data[offset++] = rowData[col * pixelStride];
+                        for (int bytePos = 0; bytePos < bytesPerPixel; ++bytePos) {
+                            data[offset++] = rowData[col * pixelStride + bytePos];
+                        }
                     }
                 }
                 // Advance buffer the remainder of the row stride
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java
new file mode 100644
index 0000000..ccfefc5
--- /dev/null
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.decoder.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.cts.MediaTestBase;
+import android.media.cts.NonMediaMainlineTest;
+import android.media.cts.Preconditions;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.RequiresDevice;
+import android.util.Log;
+import android.view.Surface;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.compatibility.common.util.MediaUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.zip.Adler32;
+
+@SmallTest
+@RequiresDevice
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+@RunWith(AndroidJUnit4.class)
+public class NativeDecoderTest extends MediaTestBase {
+    private static final String TAG = "NativeDecoderTest";
+
+    private static final int RESET_MODE_NONE = 0;
+    private static final int RESET_MODE_RECONFIGURE = 1;
+    private static final int RESET_MODE_FLUSH = 2;
+    private static final int RESET_MODE_EOS_FLUSH = 3;
+
+    private static final String[] CSD_KEYS = new String[] { "csd-0", "csd-1" };
+
+    private static final int CONFIG_MODE_NONE = 0;
+    private static final int CONFIG_MODE_QUEUE = 1;
+
+    static final String mInpPrefix = WorkDir.getMediaDirString();
+    short[] mMasterBuffer;
+
+    static {
+        // Load jni on initialization.
+        Log.i("@@@", "before loadlibrary");
+        System.loadLibrary("ctsmediadecodertest_jni");
+        Log.i("@@@", "after loadlibrary");
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Throwable {
+        super.setUp();
+    }
+
+    @After
+    @Override
+    public void tearDown() {
+        super.tearDown();
+    }
+
+    // check that native extractor behavior matches java extractor
+
+    private void compareArrays(String message, int[] a1, int[] a2) {
+        if (a1 == a2) {
+            return;
+        }
+
+        assertNotNull(message + ": array 1 is null", a1);
+        assertNotNull(message + ": array 2 is null", a2);
+
+        assertEquals(message + ": arraylengths differ", a1.length, a2.length);
+        int length = a1.length;
+
+        for (int i = 0; i < length; i++)
+            if (a1[i] != a2[i]) {
+                Log.i("@@@@", Arrays.toString(a1));
+                Log.i("@@@@", Arrays.toString(a2));
+                fail(message + ": at index " + i);
+            }
+    }
+
+    protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
+            throws FileNotFoundException {
+        Preconditions.assertTestFileExists(mInpPrefix + res);
+        File inpFile = new File(mInpPrefix + res);
+        ParcelFileDescriptor parcelFD =
+                ParcelFileDescriptor.open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY);
+        return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
+    }
+
+    private static int[] getSampleSizes(String path, String[] keys, String[] values)
+            throws IOException {
+        MediaExtractor ex = new MediaExtractor();
+        if (keys == null || values == null) {
+            ex.setDataSource(path);
+        } else {
+            Map<String, String> headers = null;
+            int numheaders = Math.min(keys.length, values.length);
+            for (int i = 0; i < numheaders; i++) {
+                if (headers == null) {
+                    headers = new HashMap<>();
+                }
+                String key = keys[i];
+                String value = values[i];
+                headers.put(key, value);
+            }
+            ex.setDataSource(path, headers);
+        }
+
+        return getSampleSizes(ex);
+    }
+
+    private static int[] getSampleSizes(FileDescriptor fd, long offset, long size)
+            throws IOException {
+        MediaExtractor ex = new MediaExtractor();
+        ex.setDataSource(fd, offset, size);
+        return getSampleSizes(ex);
+    }
+
+    private static int[] getSampleSizes(MediaExtractor ex) {
+        ArrayList<Integer> foo = new ArrayList<Integer>();
+        ByteBuffer buf = ByteBuffer.allocate(1024*1024);
+        int numtracks = ex.getTrackCount();
+        assertTrue("no tracks", numtracks > 0);
+        foo.add(numtracks);
+        for (int i = 0; i < numtracks; i++) {
+            MediaFormat format = ex.getTrackFormat(i);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            if (mime.startsWith("audio/")) {
+                foo.add(0);
+                foo.add(format.getInteger(MediaFormat.KEY_SAMPLE_RATE));
+                foo.add(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
+                foo.add((int)format.getLong(MediaFormat.KEY_DURATION));
+            } else if (mime.startsWith("video/")) {
+                foo.add(1);
+                foo.add(format.getInteger(MediaFormat.KEY_WIDTH));
+                foo.add(format.getInteger(MediaFormat.KEY_HEIGHT));
+                foo.add((int)format.getLong(MediaFormat.KEY_DURATION));
+            } else {
+                fail("unexpected mime type: " + mime);
+            }
+            ex.selectTrack(i);
+        }
+        while(true) {
+            int n = ex.readSampleData(buf, 0);
+            if (n < 0) {
+                break;
+            }
+            foo.add(n);
+            foo.add(ex.getSampleTrackIndex());
+            foo.add(ex.getSampleFlags());
+            foo.add((int)ex.getSampleTime()); // just the low bits should be OK
+            byte[] foobar = new byte[n];
+            buf.get(foobar, 0, n);
+            foo.add(adler32(foobar));
+            ex.advance();
+        }
+
+        int [] ret = new int[foo.size()];
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = foo.get(i);
+        }
+        return ret;
+    }
+
+    @Ignore
+    @Test
+    public void SKIP_testDecoder() throws Exception {
+        // duplicate of CtsMediaV2TestCases:CodecDecoderTest#testSimpleDecode where checksum  is
+        // computed over decoded output in both SDK and NDK side and checked for equality.
+        int testsRun =
+                testDecoder("sinesweepogg.ogg") +
+                testDecoder("sinesweepoggmkv.mkv") +
+                testDecoder("sinesweepoggmp4.mp4") +
+                testDecoder("sinesweepmp3lame.mp3") +
+                testDecoder("sinesweepmp3smpb.mp3") +
+                testDecoder("sinesweepopus.mkv") +
+                testDecoder("sinesweepopusmp4.mp4") +
+                testDecoder("sinesweepm4a.m4a") +
+                testDecoder("sinesweepflacmkv.mkv") +
+                testDecoder("sinesweepflac.flac") +
+                testDecoder("sinesweepflacmp4.mp4") +
+                testDecoder("sinesweepwav.wav") +
+                testDecoder("video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4") +
+                testDecoder("bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz.webm") +
+                testDecoder("bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm") +
+                testDecoder("video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz.3gp") +
+                testDecoder("video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz.mp4") +
+                testDecoder("video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
+        if (testsRun == 0) {
+            MediaUtils.skipTest("no decoders found");
+        }
+    }
+
+    @Test
+    public void testDataSource() throws Exception {
+        int testsRun = testDecoder(
+                "video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz.3gp", /* wrapFd */
+                true, /* useCallback */ false);
+        if (testsRun == 0) {
+            MediaUtils.skipTest("no decoders found");
+        }
+    }
+
+    @Test
+    public void testDataSourceAudioOnly() throws Exception {
+        int testsRun = testDecoder(
+                "loudsoftmp3.mp3",
+                /* wrapFd */ true, /* useCallback */ false) +
+                testDecoder(
+                        "loudsoftaac.aac",
+                        /* wrapFd */ false, /* useCallback */ false);
+        if (testsRun == 0) {
+            MediaUtils.skipTest("no decoders found");
+        }
+    }
+
+    @Test
+    public void testDataSourceWithCallback() throws Exception {
+        int testsRun = testDecoder(
+                "video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz.3gp",/* wrapFd */
+                true, /* useCallback */ true);
+        if (testsRun == 0) {
+            MediaUtils.skipTest("no decoders found");
+        }
+    }
+
+    private int testDecoder(final String res) throws Exception {
+        return testDecoder(res, /* wrapFd */ false, /* useCallback */ false);
+    }
+
+    private int testDecoder(final String res, boolean wrapFd, boolean useCallback)
+            throws Exception {
+        Preconditions.assertTestFileExists(mInpPrefix + res);
+        if (!MediaUtils.hasCodecsForResource(mInpPrefix  + res)) {
+            return 0; // skip
+        }
+
+        AssetFileDescriptor fd = getAssetFileDescriptorFor(res);
+
+        int[] jdata1 = getDecodedData(
+                fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+        int[] jdata2 = getDecodedData(
+                fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+        int[] ndata1 = getDecodedDataNative(
+                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength(), wrapFd,
+                useCallback);
+        int[] ndata2 = getDecodedDataNative(
+                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength(), wrapFd,
+                useCallback);
+
+        fd.close();
+        compareArrays("inconsistent java decoder", jdata1, jdata2);
+        compareArrays("inconsistent native decoder", ndata1, ndata2);
+        compareArrays("different decoded data", jdata1, ndata1);
+        return 1;
+    }
+
+    private static int[] getDecodedData(FileDescriptor fd, long offset, long size)
+            throws IOException {
+        MediaExtractor ex = new MediaExtractor();
+        ex.setDataSource(fd, offset, size);
+        return getDecodedData(ex);
+    }
+    private static int[] getDecodedData(MediaExtractor ex) throws IOException {
+        int numtracks = ex.getTrackCount();
+        assertTrue("no tracks", numtracks > 0);
+        ArrayList<Integer>[] trackdata = new ArrayList[numtracks];
+        MediaCodec[] codec = new MediaCodec[numtracks];
+        MediaFormat[] format = new MediaFormat[numtracks];
+        ByteBuffer[][] inbuffers = new ByteBuffer[numtracks][];
+        ByteBuffer[][] outbuffers = new ByteBuffer[numtracks][];
+        for (int i = 0; i < numtracks; i++) {
+            format[i] = ex.getTrackFormat(i);
+            String mime = format[i].getString(MediaFormat.KEY_MIME);
+            if (mime.startsWith("audio/") || mime.startsWith("video/")) {
+                codec[i] = MediaCodec.createDecoderByType(mime);
+                codec[i].configure(format[i], null, null, 0);
+                codec[i].start();
+                inbuffers[i] = codec[i].getInputBuffers();
+                outbuffers[i] = codec[i].getOutputBuffers();
+                trackdata[i] = new ArrayList<>();
+            } else {
+                fail("unexpected mime type: " + mime);
+            }
+            ex.selectTrack(i);
+        }
+
+        boolean[] sawInputEOS = new boolean[numtracks];
+        boolean[] sawOutputEOS = new boolean[numtracks];
+        int eosCount = 0;
+        BufferInfo info = new BufferInfo();
+        while(eosCount < numtracks) {
+            int t = ex.getSampleTrackIndex();
+            if (t >= 0) {
+                assertFalse("saw input EOS twice", sawInputEOS[t]);
+                int bufidx = codec[t].dequeueInputBuffer(5000);
+                if (bufidx >= 0) {
+                    Log.i("@@@@", "track " + t + " buffer " + bufidx);
+                    ByteBuffer buf = inbuffers[t][bufidx];
+                    int sampleSize = ex.readSampleData(buf, 0);
+                    Log.i("@@@@", "read " + sampleSize + " @ " + ex.getSampleTime());
+                    if (sampleSize < 0) {
+                        sampleSize = 0;
+                        sawInputEOS[t] = true;
+                        Log.i("@@@@", "EOS");
+                        //break;
+                    }
+                    long presentationTimeUs = ex.getSampleTime();
+
+                    codec[t].queueInputBuffer(bufidx, 0, sampleSize, presentationTimeUs,
+                            sawInputEOS[t] ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+                    ex.advance();
+                }
+            } else {
+                Log.i("@@@@", "no more input samples");
+                for (int tt = 0; tt < codec.length; tt++) {
+                    if (!sawInputEOS[tt]) {
+                        // we ran out of samples without ever signaling EOS to the codec,
+                        // so do that now
+                        int bufidx = codec[tt].dequeueInputBuffer(5000);
+                        if (bufidx >= 0) {
+                            codec[tt].queueInputBuffer(bufidx, 0, 0, 0,
+                                    MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+                            sawInputEOS[tt] = true;
+                        }
+                    }
+                }
+            }
+
+            // see if any of the codecs have data available
+            for (int tt = 0; tt < codec.length; tt++) {
+                if (!sawOutputEOS[tt]) {
+                    int status = codec[tt].dequeueOutputBuffer(info, 1);
+                    if (status >= 0) {
+                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                            Log.i("@@@@", "EOS on track " + tt);
+                            sawOutputEOS[tt] = true;
+                            eosCount++;
+                        }
+                        Log.i("@@@@", "got decoded buffer for track " + tt + ", size " + info.size);
+                        if (info.size > 0) {
+                            addSampleData(trackdata[tt], outbuffers[tt][status], info.size,
+                                    format[tt]);
+                        }
+                        codec[tt].releaseOutputBuffer(status, false);
+                    } else if (status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                        Log.i("@@@@", "output buffers changed for track " + tt);
+                        outbuffers[tt] = codec[tt].getOutputBuffers();
+                    } else if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                        format[tt] = codec[tt].getOutputFormat();
+                        Log.i("@@@@", "format changed for track " + t + ": " +
+                                format[tt].toString());
+                    } else if (status == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                        Log.i("@@@@", "no buffer right now for track " + tt);
+                    } else {
+                        Log.i("@@@@", "unexpected info code for track " + tt + ": " + status);
+                    }
+                } else {
+                    Log.i("@@@@", "already at EOS on track " + tt);
+                }
+            }
+        }
+
+        int totalsize = 0;
+        for (int i = 0; i < numtracks; i++) {
+            totalsize += trackdata[i].size();
+        }
+        int[] trackbytes = new int[totalsize];
+        int idx = 0;
+        for (int i = 0; i < numtracks; i++) {
+            ArrayList<Integer> src = trackdata[i];
+            for (Integer integer : src) {
+                trackbytes[idx++] = integer;
+            }
+        }
+
+        for (MediaCodec mediaCodec : codec) {
+            mediaCodec.release();
+        }
+
+        return trackbytes;
+    }
+
+    static void addSampleData(ArrayList<Integer> dst,
+                              ByteBuffer buf, int size, MediaFormat format) throws IOException{
+
+        Log.i("@@@", "addsample " + dst.size() + "/" + size);
+        int width = format.getInteger(MediaFormat.KEY_WIDTH, size);
+        int stride = format.getInteger(MediaFormat.KEY_STRIDE, width);
+        int height = format.getInteger(MediaFormat.KEY_HEIGHT, 1);
+        byte[] bb = new byte[width * height];
+        int offset = buf.position();
+        for (int i = 0; i < height; i++) {
+            buf.position(i * stride + offset);
+            buf.get(bb, i * width, width);
+        }
+        // bb is filled with data
+        long sum = adler32(bb);
+        dst.add( (int) (sum & 0xffffffff));
+    }
+
+    private final static Adler32 checksummer = new Adler32();
+    // simple checksum computed over every decoded buffer
+    static int adler32(byte[] input) {
+        checksummer.reset();
+        checksummer.update(input);
+        int ret = (int) checksummer.getValue();
+        Log.i("@@@", "adler " + input.length + "/" + ret);
+        return ret;
+    }
+
+    private static native int[] getDecodedDataNative(int fd, long offset, long size, boolean wrapFd,
+                                                     boolean useCallback)
+            throws IOException;
+
+    @Ignore
+    @Test
+    public void SKIP_testVideoPlayback() throws Exception {
+        // duplicate of
+        // CtsMediaV2TestCases:CodecDecoderSurfaceTest#testSimpleDecodeToSurfaceNative[*]
+        int testsRun =
+                testVideoPlayback(
+                        "video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4") +
+                testVideoPlayback(
+                        "bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz.webm") +
+                testVideoPlayback(
+                        "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm") +
+                testVideoPlayback(
+                        "video_640x360_webm_av1_470kbps_30fps_vorbis_stereo_128kbps_48000hz.webm") +
+                testVideoPlayback(
+                        "video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz.3gp") +
+                testVideoPlayback(
+                        "video_176x144_mp4_mpeg2_105kbps_25fps_aac_stereo_128kbps_44100hz.mp4") +
+                testVideoPlayback(
+                        "video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
+        if (testsRun == 0) {
+            MediaUtils.skipTest("no decoders found");
+        }
+    }
+
+    private int testVideoPlayback(final String res) throws Exception {
+        Preconditions.assertTestFileExists(mInpPrefix + res);
+        if (!MediaUtils.checkCodecsForResource(mInpPrefix + res)) {
+            return 0; // skip
+        }
+
+        AssetFileDescriptor fd = getAssetFileDescriptorFor(res);
+
+        boolean ret = testPlaybackNative(mActivity.getSurfaceHolder().getSurface(),
+                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
+        assertTrue("native playback error", ret);
+        return 1;
+    }
+
+    private static native boolean testPlaybackNative(Surface surface,
+                                                     int fd, long startOffset, long length);
+}
+
diff --git a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
similarity index 98%
rename from tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
rename to tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
index 4c797721..c182ab0 100644
--- a/tests/tests/media/src/android/media/cts/VideoDecoderPerfTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.decoder.cts;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -25,6 +25,9 @@
 import android.media.MediaCodecInfo.VideoCapabilities;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
+import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.MediaTestBase;
+import android.media.cts.Preconditions;
 import android.os.Bundle;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
@@ -53,9 +56,9 @@
 @MediaHeavyPresubmitTest
 @AppModeFull(reason = "TODO: evaluate and port to instant")
 @RunWith(AndroidJUnit4.class)
-public class VideoDecoderPerfTest extends MediaPlayerTestBase {
+public class VideoDecoderPerfTest extends MediaTestBase {
     private static final String TAG = "VideoDecoderPerfTest";
-    private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+    private static final String REPORT_LOG_NAME = "CtsMediaDecoderTestCases";
     private static final int TOTAL_FRAMES = 30000;
     private static final int MIN_FRAMES = 3000;
     private static final int MAX_TIME_MS = 120000;  // 2 minutes
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java b/tests/tests/media/decoder/src/android/media/decoder/cts/WorkDir.java
similarity index 87%
copy from tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
copy to tests/tests/media/decoder/src/android/media/decoder/cts/WorkDir.java
index e3fb1b5..3f0a330 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/WorkDir.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media.audio.cts;
+package android.media.decoder.cts;
 
 import android.media.cts.WorkDirBase;
 
 class WorkDir extends WorkDirBase {
     public static final String getMediaDirString() {
-        return getMediaDirString("CtsMediaTestCases-1.4");
+        return getMediaDirString("CtsMediaDecoderTestCases-1.1");
     }
 }
diff --git a/tests/tests/mediadrm/Android.bp b/tests/tests/media/drm/Android.bp
similarity index 65%
rename from tests/tests/mediadrm/Android.bp
rename to tests/tests/media/drm/Android.bp
index 27afc76..63facc3a 100644
--- a/tests/tests/mediadrm/Android.bp
+++ b/tests/tests/media/drm/Android.bp
@@ -14,13 +14,10 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-CC-BY
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license",
+    ],
 }
 
 android_test {
@@ -29,26 +26,15 @@
     // include both the 32 and 64 bit versions
     compile_multilib: "both",
     static_libs: [
-        "androidx.test.core",
-        "androidx.test.ext.junit",
-        "compatibility-device-util-axt",
-        "ctsdeviceutillegacy-axt",
         "ctstestrunner-axt",
         "cts-media-common",
-        "cts-media-res-lib",
         "hamcrest-library",
-        "ctstestserver",
-        "junit",
-        "junit-params",
         "testng",
-        "truth-prebuilt",
-        "mockito-target-minus-junit4",
-        "androidx.heifwriter_heifwriter",
     ],
     aaptflags: [
         "--auto-add-overlay",
-        // Give com.android.mediadrm.cts Java files access to the R class
-        "--extra-packages com.android.mediadrm.cts",
+        // Give com.android.media.drm.cts Java files access to the R class
+        "--extra-packages com.android.media.drm.cts",
 
         // Do not compress these files:
         "-0 .vp9",
@@ -70,11 +56,9 @@
     platform_apis: true,
     jni_uses_sdk_apis: true,
     libs: [
-        "org.apache.http.legacy",
         "android.test.base",
         "android.test.runner",
     ],
-    // Tag this module as a cts test artifact
     test_suites: [
         "cts",
         "general-tests",
diff --git a/tests/tests/media/drm/AndroidManifest.xml b/tests/tests/media/drm/AndroidManifest.xml
new file mode 100644
index 0000000..71c2d9d
--- /dev/null
+++ b/tests/tests/media/drm/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="android.media.drm.cts"
+     android:targetSandboxVersion="2">
+
+    <uses-sdk android:minSdkVersion="29"
+         android:targetSdkVersion="31"/>
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+
+    <uses-permission android:name="android.permission.VIBRATE"/>
+
+    <application android:requestLegacyExternalStorage="true"
+         android:largeHeap="true">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="android.media.drm.cts"
+         android:label="CTS tests of android MediaDrm">
+        <meta-data android:name="listener"
+             android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/mediadrm/AndroidTest.xml b/tests/tests/media/drm/AndroidTest.xml
similarity index 96%
rename from tests/tests/mediadrm/AndroidTest.xml
rename to tests/tests/media/drm/AndroidTest.xml
index f45ddbf..5715237 100644
--- a/tests/tests/mediadrm/AndroidTest.xml
+++ b/tests/tests/media/drm/AndroidTest.xml
@@ -36,7 +36,7 @@
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <!-- MediaProjectionTest needs this one to not be granted, SuiteApkInstaller grants all of them by default.-->
-        <option name="run-command" value="pm revoke android.mediadrm.cts android.permission.SYSTEM_ALERT_WINDOW"/>
+        <option name="run-command" value="pm revoke android.media.drm.cts android.permission.SYSTEM_ALERT_WINDOW"/>
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="device" />
@@ -44,7 +44,7 @@
         <option name="version" value="7.0"/>
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.mediadrm.cts" />
+        <option name="package" value="android.media.drm.cts" />
         <!-- setup can be expensive so limit the number of shards -->
         <option name="ajur-max-shard" value="5" />
         <!-- test-timeout unit is ms, value = 30 min -->
diff --git a/tests/tests/mediadrm/DynamicConfig.xml b/tests/tests/media/drm/DynamicConfig.xml
similarity index 100%
rename from tests/tests/mediadrm/DynamicConfig.xml
rename to tests/tests/media/drm/DynamicConfig.xml
diff --git a/tests/tests/mediadrm/OWNERS b/tests/tests/media/drm/OWNERS
similarity index 100%
rename from tests/tests/mediadrm/OWNERS
rename to tests/tests/media/drm/OWNERS
diff --git a/tests/tests/media/player/jni/AMediaObjects.h b/tests/tests/media/drm/jni/AMediaObjects.h
similarity index 100%
rename from tests/tests/media/player/jni/AMediaObjects.h
rename to tests/tests/media/drm/jni/AMediaObjects.h
diff --git a/tests/tests/mediadrm/libmediadrmndkjni/Android.bp b/tests/tests/media/drm/jni/Android.bp
similarity index 82%
rename from tests/tests/mediadrm/libmediadrmndkjni/Android.bp
rename to tests/tests/media/drm/jni/Android.bp
index edbe659..0dc55fa 100644
--- a/tests/tests/mediadrm/libmediadrmndkjni/Android.bp
+++ b/tests/tests/media/drm/jni/Android.bp
@@ -16,12 +16,7 @@
 //------------------------------------------------------------------------------
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 //------------------------------------------------------------------------------
diff --git a/tests/tests/mediadrm/libmediadrmndkjni/CtsMediaDrmJniOnLoad.cpp b/tests/tests/media/drm/jni/CtsMediaDrmJniOnLoad.cpp
similarity index 100%
rename from tests/tests/mediadrm/libmediadrmndkjni/CtsMediaDrmJniOnLoad.cpp
rename to tests/tests/media/drm/jni/CtsMediaDrmJniOnLoad.cpp
diff --git a/tests/tests/mediadrm/libmediadrmndkjni/native-mediadrm-jni.cpp b/tests/tests/media/drm/jni/native-mediadrm-jni.cpp
similarity index 98%
rename from tests/tests/mediadrm/libmediadrmndkjni/native-mediadrm-jni.cpp
rename to tests/tests/media/drm/jni/native-mediadrm-jni.cpp
index 6c700fc..e0433ba 100644
--- a/tests/tests/mediadrm/libmediadrmndkjni/native-mediadrm-jni.cpp
+++ b/tests/tests/media/drm/jni/native-mediadrm-jni.cpp
@@ -975,7 +975,7 @@
             (void *)Java_android_mediadrm_cts_NativeMediaDrmClearkeyTest_isCryptoSchemeSupportedNative },
 
     { "testClearKeyPlaybackNative",
-            "([BLandroid/media/cts/NativeMediaDrmClearkeyTest$PlaybackParams;)Z",
+            "([BLandroid/media/drm/cts/NativeMediaDrmClearkeyTest$PlaybackParams;)Z",
             (void *)Java_android_mediadrm_cts_NativeMediaDrmClearkeyTest_testClearKeyPlaybackNative },
 
     { "testGetPropertyStringNative",
@@ -999,10 +999,10 @@
 int register_android_mediadrm_cts_NativeMediaDrmClearkeyTest(JNIEnv* env) {
     jint result = JNI_ERR;
     jclass testClass =
-        env->FindClass("android/mediadrm/cts/NativeMediaDrmClearkeyTest");
+        env->FindClass("android/media/drm/cts/NativeMediaDrmClearkeyTest");
     if (testClass) {
         jclass playbackParamsClass = env->FindClass(
-            "android/mediadrm/cts/NativeMediaDrmClearkeyTest$PlaybackParams");
+            "android/media/drm/cts/NativeMediaDrmClearkeyTest$PlaybackParams");
         if (playbackParamsClass) {
             jclass surfaceClass =
                 env->FindClass("android/view/Surface");
diff --git a/tests/tests/media/res/raw/segment000001.ts b/tests/tests/media/drm/res/raw/segment000001.ts
similarity index 100%
rename from tests/tests/media/res/raw/segment000001.ts
rename to tests/tests/media/drm/res/raw/segment000001.ts
Binary files differ
diff --git a/tests/tests/media/res/raw/segment000001_scrambled.ts b/tests/tests/media/drm/res/raw/segment000001_scrambled.ts
similarity index 100%
rename from tests/tests/media/res/raw/segment000001_scrambled.ts
rename to tests/tests/media/drm/res/raw/segment000001_scrambled.ts
Binary files differ
diff --git a/tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt.webm b/tests/tests/media/drm/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt.webm
similarity index 100%
rename from tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt.webm
rename to tests/tests/media/drm/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt.webm
Binary files differ
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/DrmInitDataTest.java b/tests/tests/media/drm/src/android/media/drm/cts/DrmInitDataTest.java
similarity index 97%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/DrmInitDataTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/DrmInitDataTest.java
index ac1dacb..db6ec49 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/DrmInitDataTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/DrmInitDataTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import android.media.DrmInitData;
 import android.media.cts.NonMediaMainlineTest;
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmClearkeyTest.java b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmClearkeyTest.java
similarity index 99%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmClearkeyTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/MediaDrmClearkeyTest.java
index a1b034d..c34f9e8 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmClearkeyTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmClearkeyTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaDrm;
@@ -39,6 +39,7 @@
 import android.media.cts.NdkInputSurface;
 import android.media.cts.NdkMediaCodec;
 import android.media.cts.TestUtils.Monitor;
+import android.media.cts.Utils;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Looper;
@@ -106,11 +107,11 @@
     private static final String CENC_AUDIO_PATH = "/clear/h264/llama/llama_aac_audio.mp4";
     private static final String CENC_VIDEO_PATH = "/clearkey/llama_h264_main_720p_8000.mp4";
     private static final Uri WEBM_URL = Uri.parse(
-            "android.resource://android.mediadrm.cts/" + R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt);
+            "android.resource://android.media.drm.cts/" + R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt);
     private static final Uri MPEG2TS_SCRAMBLED_URL = Uri.parse(
-            "android.resource://android.mediadrm.cts/" + R.raw.segment000001_scrambled);
+            "android.resource://android.media.drm.cts/" + R.raw.segment000001_scrambled);
     private static final Uri MPEG2TS_CLEAR_URL = Uri.parse(
-            "android.resource://android.mediadrm.cts/" + R.raw.segment000001);
+            "android.resource://android.media.drm.cts/" + R.raw.segment000001);
 
     private static final UUID COMMON_PSSH_SCHEME_UUID =
             new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmCodecBlockModelTest.java b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmCodecBlockModelTest.java
similarity index 98%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmCodecBlockModelTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/MediaDrmCodecBlockModelTest.java
index 2f96f18..ebb6629 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmCodecBlockModelTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmCodecBlockModelTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import android.content.res.AssetFileDescriptor;
 import android.media.MediaDrm;
@@ -23,6 +23,7 @@
 import android.media.cts.MediaCodecBlockModelHelper;
 import android.media.cts.NonMediaMainlineTest;
 import android.media.cts.Preconditions;
+import android.media.cts.Utils;
 import android.net.Uri;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmCodecTest.java b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmCodecTest.java
similarity index 99%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmCodecTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/MediaDrmCodecTest.java
index fcdb8f1..2fb107e 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmCodecTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmCodecTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import android.media.MediaCodec;
 import android.media.MediaCodec.CryptoException;
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmExtractorTest.java b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmExtractorTest.java
similarity index 98%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmExtractorTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/MediaDrmExtractorTest.java
index 6d4002e..eca036b 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmExtractorTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmExtractorTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import android.media.DrmInitData;
 
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmMetricsTest.java b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmMetricsTest.java
similarity index 99%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmMetricsTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/MediaDrmMetricsTest.java
index af0d4bf..3f8de70 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmMetricsTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmMetricsTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsInAnyOrder;
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmMockTest.java b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmMockTest.java
similarity index 99%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmMockTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/MediaDrmMockTest.java
index 97db312..35130aa 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmMockTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmMockTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import android.media.MediaDrm;
 import android.media.MediaDrm.CryptoSession;
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmTest.java b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmTest.java
similarity index 99%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/MediaDrmTest.java
index 72ca602..5dacfee 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaDrmTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/MediaDrmTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import android.media.MediaCrypto;
 import android.media.MediaCryptoException;
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaPlayerDrmTest.java b/tests/tests/media/drm/src/android/media/drm/cts/MediaPlayerDrmTest.java
similarity index 98%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/MediaPlayerDrmTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/MediaPlayerDrmTest.java
index 2d93836..deaef06 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaPlayerDrmTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/MediaPlayerDrmTest.java
@@ -13,9 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.mediadrm.cts;
-
-import android.media.cts.R;
+package android.media.drm.cts;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -39,6 +37,7 @@
 import android.media.audiofx.AudioEffect;
 import android.media.audiofx.Visualizer;
 import android.media.cts.Preconditions;
+import android.media.cts.Utils;
 import android.net.Uri;
 import android.os.Environment;
 import android.os.IBinder;
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaPlayerDrmTestBase.java b/tests/tests/media/drm/src/android/media/drm/cts/MediaPlayerDrmTestBase.java
similarity index 99%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/MediaPlayerDrmTestBase.java
rename to tests/tests/media/drm/src/android/media/drm/cts/MediaPlayerDrmTestBase.java
index 4854753..c5c402c 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaPlayerDrmTestBase.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/MediaPlayerDrmTestBase.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import android.app.DownloadManager;
 import android.app.DownloadManager.Request;
@@ -74,8 +74,6 @@
  * Base class for tests which use MediaPlayer to play audio or video.
  */
 public class MediaPlayerDrmTestBase extends ActivityInstrumentationTestCase2<MediaStubActivity> {
-    private static final Logger LOG = Logger.getLogger(MediaPlayerTestBase.class.getName());
-
     protected static final int STREAM_RETRIES = 3;
 
     protected Monitor mOnVideoSizeChangedCalled = new Monitor();
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/NativeMediaDrmClearkeyTest.java b/tests/tests/media/drm/src/android/media/drm/cts/NativeMediaDrmClearkeyTest.java
similarity index 98%
rename from tests/tests/mediadrm/src/android/mediadrm/cts/NativeMediaDrmClearkeyTest.java
rename to tests/tests/media/drm/src/android/media/drm/cts/NativeMediaDrmClearkeyTest.java
index fef0be4..c16ef04 100644
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/NativeMediaDrmClearkeyTest.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/NativeMediaDrmClearkeyTest.java
@@ -13,13 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.mediadrm.cts;
+package android.media.drm.cts;
 
 import android.content.pm.PackageManager;
 import android.media.MediaDrm;
 import android.media.cts.ConnectionStatus;
 import android.media.cts.IConnectionStatus;
 import android.media.cts.MediaCodecBlockModelHelper;
+import android.media.cts.Utils;
 import android.net.Uri;
 import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
@@ -41,7 +42,7 @@
  * this test only tests the APIs that are supported by ClearKey system.
  */
 @AppModeFull(reason = "TODO: evaluate and port to instant")
-public class NativeMediaDrmClearkeyTest extends MediaPlayerTestBase {
+public class NativeMediaDrmClearkeyTest extends MediaPlayerDrmTestBase {
     private static final String TAG = NativeMediaDrmClearkeyTest.class.getSimpleName();
 
     private static final int CONNECTION_RETRIES = 10;
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java b/tests/tests/media/drm/src/android/media/drm/cts/WorkDir.java
similarity index 95%
rename from tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
rename to tests/tests/media/drm/src/android/media/drm/cts/WorkDir.java
index e3fb1b5..6ebbabe 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
+++ b/tests/tests/media/drm/src/android/media/drm/cts/WorkDir.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.audio.cts;
+package android.media.drm.cts;
 
 import android.media.cts.WorkDirBase;
 
diff --git a/tests/tests/media/encoder/Android.bp b/tests/tests/media/encoder/Android.bp
new file mode 100644
index 0000000..cd860cc
--- /dev/null
+++ b/tests/tests/media/encoder/Android.bp
@@ -0,0 +1,64 @@
+// 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 {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license",
+    ],
+}
+
+android_test {
+    name: "CtsMediaEncoderTestCases",
+    defaults: ["cts_defaults"],
+    // include both the 32 and 64 bit versions
+    compile_multilib: "both",
+    static_libs: [
+        "ctstestrunner-axt",
+        "cts-media-common",
+    ],
+    jni_libs: [
+        "libctscodecutils_jni"
+    ],
+    aaptflags: [
+      // Do not compress these files:
+        "-0 .vp9",
+        "-0 .ts",
+        "-0 .heic",
+        "-0 .trp",
+        "-0 .ota",
+        "-0 .mxmf",
+    ],
+    srcs: [
+        "src/**/*.java",
+        "aidl/**/*.aidl",
+    ],
+    // This test uses private APIs
+    platform_apis: true,
+    jni_uses_sdk_apis: true,
+    libs: [
+        "org.apache.http.legacy",
+        "android.test.base",
+        "android.test.runner",
+    ],
+    test_suites: [
+        "cts",
+        "general-tests",
+        "mts-media",
+    ],
+    host_required: ["cts-dynamic-config"],
+    min_sdk_version: "29",
+    target_sdk_version: "31",
+}
diff --git a/tests/tests/media/encoder/AndroidManifest.xml b/tests/tests/media/encoder/AndroidManifest.xml
new file mode 100644
index 0000000..b6d56f3
--- /dev/null
+++ b/tests/tests/media/encoder/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="android.media.encoder.cts"
+     android:targetSandboxVersion="2">
+
+    <uses-sdk android:minSdkVersion="29"
+         android:targetSdkVersion="31"/>
+
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+
+    <application android:requestLegacyExternalStorage="true"
+         android:largeHeap="true">
+        <uses-library android:name="android.test.runner"/>
+
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="android.media.encoder.cts"
+         android:label="CTS tests of android.media">
+        <meta-data android:name="listener"
+             android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/mediadrm/AndroidTest.xml b/tests/tests/media/encoder/AndroidTest.xml
similarity index 68%
copy from tests/tests/mediadrm/AndroidTest.xml
copy to tests/tests/media/encoder/AndroidTest.xml
index f45ddbf..65d8d6d 100644
--- a/tests/tests/mediadrm/AndroidTest.xml
+++ b/tests/tests/media/encoder/AndroidTest.xml
@@ -13,38 +13,42 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Config for CTS Media Drm test cases">
+<configuration description="Config for CTS Media test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="media" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+        <option name="set-test-harness" value="false" />
+        <option name="screen-always-on" value="on" />
+        <option name="screen-adaptive-brightness" value="off" />
+        <option name="disable-audio" value="false"/>
+        <option name="screen-saver" value="off"/>
+    </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="host" />
-        <option name="config-filename" value="CtsMediaDrmTestCases" />
-        <option name="dynamic-config-name" value="CtsMediaDrmTestCases" />
-        <option name="version" value="9.0_r1"/>
+        <option name="config-filename" value="CtsMediaEncoderTestCases" />
+        <option name="dynamic-config-name" value="CtsMediaEncoderTestCases" />
+        <option name="version" value="1.0"/>
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="push-all" value="true" />
-        <option name="media-folder-name" value="CtsMediaDrmTestCases-1.4" />
-        <option name="dynamic-config-module" value="CtsMediaDrmTestCases" />
+        <option name="media-folder-name" value="CtsMediaEncoderTestCases-1.0" />
+        <option name="dynamic-config-module" value="CtsMediaEncoderTestCases" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsMediaDrmTestCases.apk" />
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <!-- MediaProjectionTest needs this one to not be granted, SuiteApkInstaller grants all of them by default.-->
-        <option name="run-command" value="pm revoke android.mediadrm.cts android.permission.SYSTEM_ALERT_WINDOW"/>
+        <option name="test-file-name" value="CtsMediaEncoderTestCases.apk" />
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
         <option name="target" value="device" />
-        <option name="config-filename" value="CtsMediaDrmTestCases" />
-        <option name="version" value="7.0"/>
+        <option name="config-filename" value="CtsMediaEncoderTestCases" />
+        <option name="version" value="1.0"/>
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="android.mediadrm.cts" />
+        <option name="package" value="android.media.encoder.cts" />
         <!-- setup can be expensive so limit the number of shards -->
         <option name="ajur-max-shard" value="5" />
         <!-- test-timeout unit is ms, value = 30 min -->
diff --git a/tests/tests/media/audio/DynamicConfig.xml b/tests/tests/media/encoder/DynamicConfig.xml
similarity index 92%
rename from tests/tests/media/audio/DynamicConfig.xml
rename to tests/tests/media/encoder/DynamicConfig.xml
index 53528de..4dd9839 100644
--- a/tests/tests/media/audio/DynamicConfig.xml
+++ b/tests/tests/media/encoder/DynamicConfig.xml
@@ -15,6 +15,6 @@
 
 <dynamicConfig>
     <entry key="media_files_url">
-    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/CtsMediaTestCases-1.4.zip</value>
+    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/encoder/CtsMediaEncoderTestCases-1.0.zip</value>
     </entry>
 </dynamicConfig>
diff --git a/tests/tests/media/encoder/OWNERS b/tests/tests/media/encoder/OWNERS
new file mode 100644
index 0000000..f3d95c44
--- /dev/null
+++ b/tests/tests/media/encoder/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+include ../../../media/OWNERS
diff --git a/tests/tests/media/src/android/media/cts/EncoderTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/EncoderTest.java
rename to tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java
index 9ce55d0..fc3a89f 100644
--- a/tests/tests/media/src/android/media/cts/EncoderTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/EncoderTest.java
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.encoder.cts;
 
 import android.content.Context;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaFormat;
 import android.media.MediaMuxer;
+import android.media.cts.Preconditions;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.RequiresDevice;
 import android.test.AndroidTestCase;
diff --git a/tests/tests/media/src/android/media/cts/SurfaceEncodeTimestampTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/SurfaceEncodeTimestampTest.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/SurfaceEncodeTimestampTest.java
rename to tests/tests/media/encoder/src/android/media/encoder/cts/SurfaceEncodeTimestampTest.java
index f15bb6e..7d5a955 100644
--- a/tests/tests/media/src/android/media/cts/SurfaceEncodeTimestampTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/SurfaceEncodeTimestampTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.encoder.cts;
 
 import static org.junit.Assert.assertTrue;
 
@@ -26,6 +26,7 @@
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecInfo.CodecCapabilities;
 import android.media.MediaFormat;
+import android.media.cts.InputSurface;
 import android.opengl.GLES20;
 import android.os.Build;
 import android.os.Bundle;
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
similarity index 99%
rename from tests/tests/media/src/android/media/cts/VideoEncoderTest.java
rename to tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
index 1ec50c5..707bde2 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.media.cts;
+package android.media.encoder.cts;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -35,6 +35,13 @@
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.MediaMuxer;
+import android.media.cts.CodecUtils;
+import android.media.cts.InputSurface;
+import android.media.cts.MediaHeavyPresubmitTest;
+import android.media.cts.MediaTestBase;
+import android.media.cts.NonMediaMainlineTest;
+import android.media.cts.OutputSurface;
+import android.media.cts.Preconditions;
 import android.net.Uri;
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
@@ -71,7 +78,7 @@
 @MediaHeavyPresubmitTest
 @AppModeFull(reason = "TODO: evaluate and port to instant")
 @RunWith(AndroidJUnit4.class)
-public class VideoEncoderTest extends MediaPlayerTestBase {
+public class VideoEncoderTest extends MediaTestBase {
     private static final int MAX_SAMPLE_SIZE = 256 * 1024;
     private static final String TAG = "VideoEncoderTest";
     private static final long FRAME_TIMEOUT_MS = 1000;
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java b/tests/tests/media/encoder/src/android/media/encoder/cts/WorkDir.java
similarity index 87%
copy from tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
copy to tests/tests/media/encoder/src/android/media/encoder/cts/WorkDir.java
index e3fb1b5..60d307d 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/WorkDir.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/WorkDir.java
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-package android.media.audio.cts;
+package android.media.encoder.cts;
 
 import android.media.cts.WorkDirBase;
 
 class WorkDir extends WorkDirBase {
     public static final String getMediaDirString() {
-        return getMediaDirString("CtsMediaTestCases-1.4");
+        return getMediaDirString("CtsMediaEncoderTestCases-1.0");
     }
 }
diff --git a/tests/tests/media/extractor/Android.bp b/tests/tests/media/extractor/Android.bp
index bf48cb8..cdda629 100644
--- a/tests/tests/media/extractor/Android.bp
+++ b/tests/tests/media/extractor/Android.bp
@@ -14,13 +14,10 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-CC-BY
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license", // CC-BY
+    ],
 }
 
 android_test {
diff --git a/tests/tests/media/extractor/AndroidTest.xml b/tests/tests/media/extractor/AndroidTest.xml
index fef91e6..09c79fc 100644
--- a/tests/tests/media/extractor/AndroidTest.xml
+++ b/tests/tests/media/extractor/AndroidTest.xml
@@ -35,7 +35,7 @@
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="push-all" value="true" />
-        <option name="media-folder-name" value="CtsMediaTestCases-1.4" />
+        <option name="media-folder-name" value="CtsMediaExtractorTestCases-1.0" />
         <option name="dynamic-config-module" value="CtsMediaExtractorTestCases" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/media/extractor/DynamicConfig.xml b/tests/tests/media/extractor/DynamicConfig.xml
index 53528de..1f3f5b0 100644
--- a/tests/tests/media/extractor/DynamicConfig.xml
+++ b/tests/tests/media/extractor/DynamicConfig.xml
@@ -15,6 +15,6 @@
 
 <dynamicConfig>
     <entry key="media_files_url">
-    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/CtsMediaTestCases-1.4.zip</value>
+    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/extractor/CtsMediaExtractorTestCases-1.0.zip</value>
     </entry>
 </dynamicConfig>
diff --git a/tests/tests/media/extractor/src/android/media/extractor/cts/MediaExtractorTest.java b/tests/tests/media/extractor/src/android/media/extractor/cts/MediaExtractorTest.java
index 88001e4..5d67c62 100644
--- a/tests/tests/media/extractor/src/android/media/extractor/cts/MediaExtractorTest.java
+++ b/tests/tests/media/extractor/src/android/media/extractor/cts/MediaExtractorTest.java
@@ -31,30 +31,41 @@
 import android.media.cts.TestMediaDataSource;
 import android.media.cts.StreamUtils;
 import static android.media.MediaFormat.MIMETYPE_VIDEO_DOLBY_VISION;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.platform.test.annotations.AppModeFull;
-import android.test.AndroidTestCase;
 import android.util.Log;
 import android.view.Display;
 import android.view.Display.HdrCapabilities;
 import android.webkit.cts.CtsTestServer;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.ApiLevelUtil;
 import com.android.compatibility.common.util.CddTest;
 import com.android.compatibility.common.util.MediaUtils;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.BufferedReader;
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StreamTokenizer;
@@ -65,29 +76,31 @@
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
-import java.util.UUID;
 
 @AppModeFull(reason = "Instant apps cannot access the SD card")
-public class MediaExtractorTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class MediaExtractorTest {
     private static final String TAG = "MediaExtractorTest";
     private static final boolean IS_AT_LEAST_S = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
 
     static final String mInpPrefix = WorkDir.getMediaDirString();
     protected MediaExtractor mExtractor;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         mExtractor = new MediaExtractor();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    @After
+    public void tearDown() throws Exception {
         mExtractor.release();
     }
 
-    protected AssetFileDescriptor getAssetFileDescriptorFor(final String res)
+    private Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
+    private AssetFileDescriptor getAssetFileDescriptorFor(final String res)
             throws FileNotFoundException {
         File inpFile = new File(mInpPrefix + res);
         Preconditions.assertTestFileExists(mInpPrefix + res);
@@ -96,48 +109,18 @@
         return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
     }
 
-    protected TestMediaDataSource getDataSourceFor(final String res) throws Exception {
+    private TestMediaDataSource getDataSourceFor(final String res) throws Exception {
         AssetFileDescriptor afd = getAssetFileDescriptorFor(res);
         return TestMediaDataSource.fromAssetFd(afd);
     }
 
-    protected TestMediaDataSource setDataSource(final String res) throws Exception {
+    private TestMediaDataSource setDataSource(final String res) throws Exception {
         TestMediaDataSource ds = getDataSourceFor(res);
         mExtractor.setDataSource(ds);
         return ds;
     }
 
-    public void SKIP_testNullMediaDataSourceIsRejected() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorUnitTest$TestApi
-        // #testIfNullMediaDataSourceIsRejectedBySetDataSource
-        try {
-            mExtractor.setDataSource((MediaDataSource)null);
-            fail("Expected IllegalArgumentException.");
-        } catch (IllegalArgumentException ex) {
-            // Expected, test passed.
-        }
-    }
-
-    public void SKIP_testMediaDataSourceIsClosedOnRelease() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$SetDataSourceTest#testMediaDataSource
-        TestMediaDataSource dataSource = setDataSource("testvideo.3gp");
-        mExtractor.release();
-        assertTrue(dataSource.isClosed());
-    }
-
-    public void SKIP_testExtractorFailsIfMediaDataSourceThrows() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorUnitTest$TestApi
-        // #testIfInvalidDataSourceIsRejectedBySetDataSource
-        TestMediaDataSource dataSource = getDataSourceFor("testvideo.3gp");
-        dataSource.throwFromReadAt();
-        try {
-            mExtractor.setDataSource(dataSource);
-            fail("Expected IOException.");
-        } catch (IOException e) {
-            // Expected.
-        }
-    }
-
+    @Test
     public void testExtractorFailsIfMediaDataSourceReturnsAnError() throws Exception {
         TestMediaDataSource dataSource = getDataSourceFor("testvideo.3gp");
         dataSource.returnFromReadAt(-2);
@@ -149,24 +132,6 @@
         }
     }
 
-    // Smoke test MediaExtractor reading from a DataSource.
-    public void SKIP_testExtractFromAMediaDataSource() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$SetDataSourceTest#testMediaDataSource and
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest#testMetrics
-        TestMediaDataSource dataSource = setDataSource("testvideo.3gp");
-        checkExtractorSamplesAndMetrics();
-    }
-
-    // Smoke test MediaExtractor reading from an AssetFileDescriptor.
-    public void SKIP_testExtractFromAssetFileDescriptor() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$SetDataSourceTest#testAssetFD and
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest#testMetrics
-       AssetFileDescriptor afd = getAssetFileDescriptorFor("testvideo.3gp");
-        mExtractor.setDataSource(afd);
-        checkExtractorSamplesAndMetrics();
-        afd.close();
-    }
-
     private boolean advertisesDolbyVision() {
         // Device advertises support for DV if 1) it has a DV decoder, OR
         // 2) it lists DV on the Display HDR capabilities.
@@ -174,8 +139,7 @@
             return true;
         }
 
-        Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+        DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
         Display defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
         HdrCapabilities cap = defaultDisplay.getHdrCapabilities();
         for (int type : cap.getSupportedHdrTypes()) {
@@ -188,6 +152,7 @@
 
     // DolbyVisionMediaExtractor for profile-level (DvheDtr/Fhd30).
     @CddTest(requirement="5.3.8")
+    @Test
     public void testDolbyVisionMediaExtractorProfileDvheDtr() throws Exception {
         TestMediaDataSource dataSource = setDataSource("video_dovi_1920x1080_30fps_dvhe_04.mp4");
 
@@ -234,32 +199,9 @@
         assertEquals("video/hevc", mimeType);
     }
 
-    // DolbyVisionMediaExtractor for profile-level (DvheStn/Fhd60).
-    @CddTest(requirement="5.3.8")
-    public void SKIP_testDolbyVisionMediaExtractorProfileDvheStn() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$ValidateKeyValuePairs[video/dolby-vision]
-        TestMediaDataSource dataSource = setDataSource("video_dovi_1920x1080_60fps_dvhe_05.mp4");
-
-        if (advertisesDolbyVision()) {
-            // DvheStn exposes only a single non-backward compatible Dolby Vision HDR track.
-            assertEquals("There must be 1 track", 1, mExtractor.getTrackCount());
-            final MediaFormat trackFormat = mExtractor.getTrackFormat(0);
-
-            final String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
-            assertEquals("video/dolby-vision", mimeType);
-
-            final int profile = trackFormat.getInteger(MediaFormat.KEY_PROFILE);
-            assertEquals(MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvheStn, profile);
-
-            final int level = trackFormat.getInteger(MediaFormat.KEY_LEVEL);
-            assertEquals(MediaCodecInfo.CodecProfileLevel.DolbyVisionLevelFhd60, level);
-        } else {
-            MediaUtils.skipTest("Device does not provide a Dolby Vision decoder");
-        }
-    }
-
     // DolbyVisionMediaExtractor for profile-level (DvheSt/Fhd60).
     @CddTest(requirement="5.3.8")
+    @Test
     public void testDolbyVisionMediaExtractorProfileDvheSt() throws Exception {
         TestMediaDataSource dataSource = setDataSource("video_dovi_1920x1080_60fps_dvhe_08.mp4");
 
@@ -308,6 +250,7 @@
 
     // DolbyVisionMediaExtractor for profile-level (DvavSe/Fhd60).
     @CddTest(requirement="5.3.8")
+    @Test
     public void testDolbyVisionMediaExtractorProfileDvavSe() throws Exception {
         TestMediaDataSource dataSource = setDataSource("video_dovi_1920x1080_60fps_dvav_09.mp4");
 
@@ -357,6 +300,7 @@
     // DolbyVisionMediaExtractor for profile-level (Dvav1 10.0/Uhd30)
     @SmallTest
     @CddTest(requirement="5.3.8")
+    @Test
     public void testDolbyVisionMediaExtractorProfileDvav1() throws Exception {
         TestMediaDataSource dataSource = setDataSource("video_dovi_3840x2160_30fps_dav1_10.mp4");
 
@@ -382,6 +326,7 @@
     // DolbyVisionMediaExtractor for profile-level (Dvav1 10.1/Uhd30)
     @SmallTest
     @CddTest(requirement="5.3.8")
+    @Test
     public void testDolbyVisionMediaExtractorProfileDvav1_2() throws Exception {
         TestMediaDataSource dataSource = setDataSource("video_dovi_3840x2160_30fps_dav1_10_2.mp4");
 
@@ -429,6 +374,7 @@
     }
 
     //MPEG-H 3D Audio single stream (mha1)
+    @Test
     public void testMpegh3dAudioMediaExtractorMha1() throws Exception {
         // TODO(b/186267251) move file to cloud storage.
         AssetFileDescriptor afd = getContext().getResources()
@@ -451,6 +397,7 @@
     }
 
     //MPEG-H 3D Audio single stream encapsulated in MHAS (mhm1)
+    @Test
     public void testMpegh3dAudioMediaExtractorMhm1() throws Exception {
         // TODO(b/186267251) move file to cloud storage.
         AssetFileDescriptor afd = getContext().getResources()
@@ -526,6 +473,7 @@
         return true;
     }
 
+    @Test
     public void testGetAudioPresentations() throws Exception {
         Preconditions.assertTestFileExists(mInpPrefix +
                         "MultiLangPerso_1PID_PC0_Select_AC4_H265_DVB_50fps_Audio_Only.ts");
@@ -625,65 +573,6 @@
         }
     }
 
-    @AppModeFull(reason = "Instant apps cannot bind sockets.")
-    public void SKIP_testExtractorGetCachedDuration() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$SetDataSourceTest#testUrlDataSource
-        CtsTestServer foo = new CtsTestServer(getContext());
-        String url = foo.getAssetUrl("ringer.mp3");
-        mExtractor.setDataSource(url);
-        long cachedDurationUs = mExtractor.getCachedDuration();
-        assertTrue("cached duration should be non-negative", cachedDurationUs >= 0);
-        foo.shutdown();
-    }
-
-    public void SKIP_testExtractorHasCacheReachedEndOfStream() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$SetDataSourceTest#testUrlDataSource
-        // Using file source to get deterministic result.
-        AssetFileDescriptor afd = getAssetFileDescriptorFor("testvideo.3gp");
-        mExtractor.setDataSource(afd);
-        assertTrue(mExtractor.hasCacheReachedEndOfStream());
-        afd.close();
-    }
-
-    /*
-     * Makes sure if PTS(order) of a video file with BFrames matches the expected values in
-     * the corresponding text file with just PTS values.
-     */
-    public void SKIP_testVideoPresentationTimeStampsMatch() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$ExtractorTimeStampTest
-        setDataSource("binary_counter_320x240_30fps_600frames.mp4");
-        // Select the only video track present in the file.
-        final int trackCount = mExtractor.getTrackCount();
-        for (int i = 0; i < trackCount; i++) {
-            mExtractor.selectTrack(i);
-        }
-
-        Reader txtRdr = new BufferedReader(new InputStreamReader(new FileInputStream(
-                mInpPrefix + "timestamps_binary_counter_320x240_30fps_600frames.txt")));
-        StreamTokenizer strTok = new StreamTokenizer(txtRdr);
-        strTok.parseNumbers();
-
-        boolean srcAdvance = false;
-        long srcSampleTimeUs = -1;
-        long testSampleTimeUs = -1;
-
-        strTok.nextToken();
-        do {
-            srcSampleTimeUs = mExtractor.getSampleTime();
-            testSampleTimeUs = (long) strTok.nval;
-
-            // Ignore round-off error if any.
-            if (Math.abs(srcSampleTimeUs - testSampleTimeUs) > 1) {
-                Log.d(TAG, "srcSampleTimeUs:" + srcSampleTimeUs + " testSampleTimeUs:" +
-                        testSampleTimeUs);
-                fail("video presentation timestamp not equal");
-            }
-
-            srcAdvance = mExtractor.advance();
-            strTok.nextToken();
-        } while (srcAdvance);
-    }
-
     /* package */ static class ByteBufferDataSource extends MediaDataSource {
         private final long mSize;
         private TreeMap<Long, ByteBuffer> mMap = new TreeMap<Long, ByteBuffer>();
@@ -834,74 +723,7 @@
         }
     }
 
-    @SmallTest
-    public void SKIP_testFlacIdentity() throws Exception {
-        // duplicate of CtsMediaV2TestCases:CodecEncoderTest$testLosslessEncodeDecode[audio/flac]
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FusedExtractorDecoderTest[audio/flac]
-        final int PCM_FRAMES = 1152 * 4; // FIXME: requires 4 flac frames to work with OMX codecs.
-        final int CHANNEL_COUNT = 2;
-        final int SAMPLES = PCM_FRAMES * CHANNEL_COUNT;
-        final int[] SAMPLE_RATES = {44100, 192000}; // ensure 192kHz supported.
-
-        for (int sampleRate : SAMPLE_RATES) {
-            final MediaFormat format = new MediaFormat();
-            format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_FLAC);
-            format.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRate);
-            format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNEL_COUNT);
-
-            Log.d(TAG, "Trying sample rate: " + sampleRate
-                    + " channel count: " + CHANNEL_COUNT);
-            format.setInteger(MediaFormat.KEY_FLAC_COMPRESSION_LEVEL, 5);
-
-            // TODO: Add float mode when MediaExtractor supports float configuration.
-            final StreamUtils.PcmAudioBufferStream audioStream =
-                    new StreamUtils.PcmAudioBufferStream(
-                            SAMPLES, sampleRate, 1000 /* frequency */, 100 /* sweep */,
-                          false /* useFloat */);
-
-            final StreamUtils.MediaCodecStream rawToFlac =
-                    new StreamUtils.MediaCodecStream(
-                            new StreamUtils.ByteBufferInputStream(audioStream),
-                            format, true /* encode */);
-            final MediaExtractorStream flacToRaw =
-                    new MediaExtractorStream(MediaFormat.MIMETYPE_AUDIO_FLAC /* inMime */,
-                            MediaFormat.MIMETYPE_AUDIO_RAW /* outMime */, rawToFlac);
-
-            // Note: the existence of signed zero (as well as NAN) may make byte
-            // comparisons invalid for floating point output. In our case, since the
-            // floats come through integer to float conversion, it does not matter.
-            assertEquals("Audio data not identical after compression",
-                audioStream.sizeInBytes(),
-                StreamUtils.compareStreams(new StreamUtils.ByteBufferInputStream(flacToRaw),
-                    new StreamUtils.ByteBufferInputStream(
-                            new StreamUtils.PcmAudioBufferStream(audioStream))));
-        }
-    }
-
-    public void SKIP_testFlacMovExtraction() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FusedExtractorDecoderTest[audio/flac]
-        AssetFileDescriptor testFd = getAssetFileDescriptorFor("sinesweepalac.mov");
-        MediaExtractor extractor = new MediaExtractor();
-        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
-                testFd.getLength());
-        testFd.close();
-        extractor.selectTrack(0);
-        boolean lastAdvanceResult = true;
-        boolean lastReadResult = true;
-        ByteBuffer buf = ByteBuffer.allocate(2*1024*1024);
-        int totalSize = 0;
-        while(true) {
-            int n = extractor.readSampleData(buf, 0);
-            if (n > 0) {
-                totalSize += n;
-            }
-            if (!extractor.advance()) {
-                break;
-            }
-        }
-        assertTrue("could not read alac mov", totalSize > 0);
-    }
-
+    @Test
     public void testProgramStreamExtraction() throws Exception {
         AssetFileDescriptor testFd = getAssetFileDescriptorFor("programstream.mpeg");
 
@@ -1026,27 +848,6 @@
         extractor.release();
     }
 
-    public void SKIP_testAdvance() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$SetDataSourceTest and
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest#testExtract[*]
-        // audio-only
-        doTestAdvance("sinesweepm4a.m4a");
-        doTestAdvance("sinesweepmp3lame.mp3");
-        doTestAdvance("sinesweepmp3smpb.mp3");
-        doTestAdvance("sinesweepwav.wav");
-        doTestAdvance("sinesweepflac.flac");
-        doTestAdvance("sinesweepogg.ogg");
-        doTestAdvance("sinesweepoggmkv.mkv");
-
-        // video-only
-        doTestAdvance("swirl_144x136_mpeg4.mp4");
-        doTestAdvance("video_640x360_mp4_hevc_450kbps_no_b.mp4");
-
-        // audio+video
-        doTestAdvance("video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz.mp4");
-        doTestAdvance("video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
-    }
-
     private void readAllData() {
         // 1MB is enough for any sample.
         final ByteBuffer buf = ByteBuffer.allocate(1024*1024);
@@ -1064,56 +865,31 @@
         } while (mExtractor.advance());
     }
 
-    public void SKIP_testAC3inMP4() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest[audio/ac3]
-        setDataSource("testac3mp4.mp4");
-        readAllData();
-    }
-
-    public void SKIP_testEAC3inMP4() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest[audio/eac3]
-        setDataSource("testeac3mp4.mp4");
-        readAllData();
-    }
-
-    public void SKIP_testAC3inTS() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest[audio/ac3]
-        setDataSource("testac3ts.ts");
-        readAllData();
-    }
-
-    public void SKIP_testEAC3inTS() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest[audio/eac3]
-        setDataSource("testeac3ts.ts");
-        readAllData();
-    }
-
-    public void SKIP_testAC4inMP4() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest[audio/ac4]
-        setDataSource("multi0.mp4");
-        readAllData();
-    }
-
+    @Test
     public void testAV1InMP4() throws Exception {
         setDataSource("video_dovi_3840x2160_30fps_dav1_10_2.mp4");
         readAllData();
     }
 
+    @Test
     public void testDolbyVisionInMP4() throws Exception {
         setDataSource("video_dovi_3840x2160_30fps_dav1_10.mp4");
         readAllData();
     }
 
+    @Test
     public void testPcmLeInMov() throws Exception {
         setDataSource("sinesweeppcmlemov.mov");
         readAllData();
     }
 
+    @Test
     public void testPcmBeInMov() throws Exception {
         setDataSource("sinesweeppcmbemov.mov");
         readAllData();
     }
 
+    @Test
     public void testFragmentedRead() throws Exception {
         Preconditions.assertTestFileExists(mInpPrefix + "psshtest.mp4");
         setDataSource("psshtest.mp4");
@@ -1121,6 +897,7 @@
     }
 
     @AppModeFull(reason = "Instant apps cannot bind sockets.")
+    @Test
     public void testFragmentedHttpRead() throws Exception {
         CtsTestServer server = new CtsTestServer(getContext());
         Preconditions.assertTestFileExists(mInpPrefix + "psshtest.mp4");
diff --git a/tests/tests/media/extractor/src/android/media/extractor/cts/WorkDir.java b/tests/tests/media/extractor/src/android/media/extractor/cts/WorkDir.java
index 74ba802..749645a 100644
--- a/tests/tests/media/extractor/src/android/media/extractor/cts/WorkDir.java
+++ b/tests/tests/media/extractor/src/android/media/extractor/cts/WorkDir.java
@@ -20,6 +20,6 @@
 
 class WorkDir extends WorkDirBase {
     public static final String getMediaDirString() {
-        return getMediaDirString("CtsMediaTestCases-1.4");
+        return getMediaDirString("CtsMediaExtractorTestCases-1.0");
     }
 }
diff --git a/tests/tests/media/libaudiojni/Android.bp b/tests/tests/media/libaudiojni/Android.bp
deleted file mode 100644
index daade0d..0000000
--- a/tests/tests/media/libaudiojni/Android.bp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_test_library {
-    name: "libaudio_jni",
-    srcs: [
-        "audio-record-native.cpp",
-        "audio-track-native.cpp",
-        "sl-utils.cpp",
-    ],
-    include_dirs: ["system/core/include"],
-    shared_libs: [
-        "libandroid",
-        "liblog",
-        "libnativehelper_compat_libc++",
-        "libOpenSLES",
-    ],
-    header_libs: [
-        "libaudioutils_headers",
-        "liblog_headers",
-    ],
-    stl: "libc++_static",
-    cflags: [
-        "-Werror",
-        "-Wall",
-        "-Wno-deprecated-declarations",
-    ],
-    gtest: false,
-    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
-    // (revisit if/when we add features to this library that require newer sdk.
-    sdk_version: "29",
-}
diff --git a/tests/tests/media/libmediandkjni/Android.bp b/tests/tests/media/libmediandkjni/Android.bp
index a5aaea4..02dcc3c 100644
--- a/tests/tests/media/libmediandkjni/Android.bp
+++ b/tests/tests/media/libmediandkjni/Android.bp
@@ -23,30 +23,20 @@
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
     //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_libmediaandkjni_license",
+    ],
 }
 
-cc_test_library {
-    name: "libctscodecutils_jni",
-    srcs: [
-        "codec-utils-jni.cpp",
-        "md5_utils.cpp",
-    ],
-    shared_libs: [
-        "libnativehelper_compat_libc++",
-        "liblog",
-    ],
-    header_libs: ["liblog_headers"],
-    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
-    // (revisit if/when we add features to this library that require newer sdk.
-    sdk_version: "29",
-    cflags: [
-        "-Werror",
-        "-Wall",
-        "-DEGL_EGLEXT_PROTOTYPES",
-    ],
-    stl: "libc++_static",
-    gtest: false,
+license_kind {
+    name: "libmediaandkjni_public_domain",
+    conditions: ["unencumbered"],
+}
+
+license {
+    name: "cts_tests_tests_media_libmediaandkjni_license",
+    license_kinds: ["libmediaandkjni_public_domain"],
 }
 
 //------------------------------------------------------------------------------
@@ -56,8 +46,6 @@
     name: "libctsmediacodec_jni",
     srcs: [
         "native-media-jni.cpp",
-        "native_media_utils.cpp",
-        "native_media_decoder_source.cpp",
     ],
     shared_libs: [
         "libandroid",
diff --git a/tests/tests/media/libmediandkjni/native-media-jni.cpp b/tests/tests/media/libmediandkjni/native-media-jni.cpp
index b8ff13a..a38ca17b 100644
--- a/tests/tests/media/libmediandkjni/native-media-jni.cpp
+++ b/tests/tests/media/libmediandkjni/native-media-jni.cpp
@@ -22,808 +22,14 @@
 
 #include <assert.h>
 #include <jni.h>
-#include <mutex>
-#include <pthread.h>
-#include <queue>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
-#include <semaphore.h>
-
-#include <android/native_window_jni.h>
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
 
 #include "media/NdkMediaExtractor.h"
-#include "media/NdkMediaCodec.h"
 #include "media/NdkMediaCrypto.h"
 #include "media/NdkMediaDataSource.h"
 #include "media/NdkMediaFormat.h"
-#include "media/NdkMediaMuxer.h"
-
-template <class T>
-class simplevector {
-    T *storage;
-    int capacity;
-    int numfilled;
-public:
-    simplevector() {
-        capacity = 16;
-        numfilled = 0;
-        storage = new T[capacity];
-    }
-    ~simplevector() {
-        delete[] storage;
-    }
-
-    void add(T item) {
-        if (numfilled == capacity) {
-            T *old = storage;
-            capacity *= 2;
-            storage = new T[capacity];
-            for (int i = 0; i < numfilled; i++) {
-                storage[i] = old[i];
-            }
-            delete[] old;
-        }
-        storage[numfilled] = item;
-        numfilled++;
-    }
-
-    int size() {
-        return numfilled;
-    }
-
-    T* data() {
-        return storage;
-    }
-};
-
-struct FdDataSource {
-
-    FdDataSource(int fd, jlong offset, jlong size)
-        : mFd(dup(fd)),
-          mOffset(offset),
-          mSize(size) {
-    }
-
-    ssize_t readAt(off64_t offset, void *data, size_t size) {
-        ssize_t ssize = size;
-        if (!data || offset < 0 || offset + ssize < offset) {
-            return -1;
-        }
-        if (offset >= mSize) {
-            return 0; // EOS
-        }
-        if (offset + ssize > mSize) {
-            ssize = mSize - offset;
-        }
-        if (lseek(mFd, mOffset + offset, SEEK_SET) < 0) {
-            return -1;
-        }
-        return read(mFd, data, ssize);
-    }
-
-    ssize_t getSize() {
-        return mSize;
-    }
-
-    void close() {
-        ::close(mFd);
-    }
-
-private:
-
-    int mFd;
-    off64_t mOffset;
-    int64_t mSize;
-
-};
-
-static ssize_t FdSourceReadAt(void *userdata, off64_t offset, void *data, size_t size) {
-    FdDataSource *src = (FdDataSource*) userdata;
-    return src->readAt(offset, data, size);
-}
-
-static ssize_t FdSourceGetSize(void *userdata) {
-    FdDataSource *src = (FdDataSource*) userdata;
-    return src->getSize();
-}
-
-static void FdSourceClose(void *userdata) {
-    FdDataSource *src = (FdDataSource*) userdata;
-    src->close();
-}
-
-class CallbackData {
-    std::mutex mMutex;
-    std::queue<int32_t> mInputBufferIds;
-    std::queue<int32_t> mOutputBufferIds;
-    std::queue<AMediaCodecBufferInfo> mOutputBufferInfos;
-    std::queue<AMediaFormat*> mFormats;
-
-public:
-    CallbackData() { }
-
-    ~CallbackData() {
-        mMutex.lock();
-        while (!mFormats.empty()) {
-            AMediaFormat* format = mFormats.front();
-            mFormats.pop();
-            AMediaFormat_delete(format);
-        }
-        mMutex.unlock();
-    }
-
-    void addInputBufferId(int32_t index) {
-        mMutex.lock();
-        mInputBufferIds.push(index);
-        mMutex.unlock();
-    }
-
-    int32_t getInputBufferId() {
-        int32_t id = -1;
-        mMutex.lock();
-        if (!mInputBufferIds.empty()) {
-            id = mInputBufferIds.front();
-            mInputBufferIds.pop();
-        }
-        mMutex.unlock();
-        return id;
-    }
-
-    void addOutputBuffer(int32_t index, AMediaCodecBufferInfo *bufferInfo) {
-        mMutex.lock();
-        mOutputBufferIds.push(index);
-        mOutputBufferInfos.push(*bufferInfo);
-        mMutex.unlock();
-    }
-
-    void addOutputFormat(AMediaFormat *format) {
-        mMutex.lock();
-        mOutputBufferIds.push(AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED);
-        mFormats.push(format);
-        mMutex.unlock();
-    }
-
-    int32_t getOutput(AMediaCodecBufferInfo *bufferInfo, AMediaFormat **format) {
-        int32_t id = AMEDIACODEC_INFO_TRY_AGAIN_LATER;
-        mMutex.lock();
-        if (!mOutputBufferIds.empty()) {
-            id = mOutputBufferIds.front();
-            mOutputBufferIds.pop();
-
-            if (id >= 0) {
-                *bufferInfo = mOutputBufferInfos.front();
-                mOutputBufferInfos.pop();
-            } else {  // AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED
-                *format = mFormats.front();
-                mFormats.pop();
-            }
-        }
-        mMutex.unlock();
-        return id;
-    }
-};
-
-static void OnInputAvailableCB(
-        AMediaCodec * /* aMediaCodec */,
-        void *userdata,
-        int32_t index) {
-    ALOGV("OnInputAvailableCB: index(%d)", index);
-    CallbackData *callbackData = (CallbackData *)userdata;
-    callbackData->addInputBufferId(index);
-}
-
-static void OnOutputAvailableCB(
-        AMediaCodec * /* aMediaCodec */,
-        void *userdata,
-        int32_t index,
-        AMediaCodecBufferInfo *bufferInfo) {
-    ALOGV("OnOutputAvailableCB: index(%d), (%d, %d, %lld, 0x%x)",
-          index, bufferInfo->offset, bufferInfo->size,
-          (long long)bufferInfo->presentationTimeUs, bufferInfo->flags);
-    CallbackData *callbackData = (CallbackData *)userdata;
-    callbackData->addOutputBuffer(index, bufferInfo);
-}
-
-static void OnFormatChangedCB(
-        AMediaCodec * /* aMediaCodec */,
-        void *userdata,
-        AMediaFormat *format) {
-    ALOGV("OnFormatChangedCB: format(%s)", AMediaFormat_toString(format));
-    CallbackData *callbackData = (CallbackData *)userdata;
-    callbackData->addOutputFormat(format);
-}
-
-static void OnErrorCB(
-        AMediaCodec * /* aMediaCodec */,
-        void * /* userdata */,
-        media_status_t err,
-        int32_t actionCode,
-        const char *detail) {
-    ALOGV("OnErrorCB: err(%d), actionCode(%d), detail(%s)", err, actionCode, detail);
-}
-
-static int adler32(const uint8_t *input, int len) {
-
-    int a = 1;
-    int b = 0;
-    for (int i = 0; i < len; i++) {
-        a += input[i];
-        b += a;
-        a = a % 65521;
-        b = b % 65521;
-    }
-    int ret = b * 65536 + a;
-    ALOGV("adler %d/%d", len, ret);
-    return ret;
-}
-
-jobject testExtractor(AMediaExtractor *ex, JNIEnv *env) {
-
-    simplevector<int> sizes;
-    int numtracks = AMediaExtractor_getTrackCount(ex);
-    sizes.add(numtracks);
-    for (int i = 0; i < numtracks; i++) {
-        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
-        const char *s = AMediaFormat_toString(format);
-        ALOGI("track %d format: %s", i, s);
-        const char *mime;
-        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
-            ALOGE("no mime type");
-            return NULL;
-        } else if (!strncmp(mime, "audio/", 6)) {
-            sizes.add(0);
-            int32_t val32;
-            int64_t val64;
-            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &val32);
-            sizes.add(val32);
-            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &val32);
-            sizes.add(val32);
-            AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &val64);
-            sizes.add(val64);
-        } else if (!strncmp(mime, "video/", 6)) {
-            sizes.add(1);
-            int32_t val32;
-            int64_t val64;
-            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &val32);
-            sizes.add(val32);
-            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &val32);
-            sizes.add(val32);
-            AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &val64);
-            sizes.add(val64);
-        } else {
-            ALOGE("expected audio or video mime type, got %s", mime);
-        }
-        AMediaFormat_delete(format);
-        AMediaExtractor_selectTrack(ex, i);
-    }
-    int bufsize = 1024*1024;
-    uint8_t *buf = new uint8_t[bufsize];
-    while(true) {
-        int n = AMediaExtractor_readSampleData(ex, buf, bufsize);
-        ssize_t sampleSize = AMediaExtractor_getSampleSize(ex);
-        if (n < 0 || n != sampleSize) {
-            break;
-        }
-        sizes.add(n);
-        sizes.add(AMediaExtractor_getSampleTrackIndex(ex));
-        sizes.add(AMediaExtractor_getSampleFlags(ex));
-        sizes.add(AMediaExtractor_getSampleTime(ex));
-        sizes.add(adler32(buf, n));
-        AMediaExtractor_advance(ex);
-    }
-
-    // allocate java int array for result and return it
-    int *data = sizes.data();
-    int numsamples = sizes.size();
-    jintArray ret = env->NewIntArray(numsamples);
-    jboolean isCopy;
-    jint *dst = env->GetIntArrayElements(ret, &isCopy);
-    for (int i = 0; i < numsamples; ++i) {
-        dst[i] = data[i];
-    }
-    env->ReleaseIntArrayElements(ret, dst, 0);
-
-    delete[] buf;
-    AMediaExtractor_delete(ex);
-    return ret;
-}
-
-
-// get the sample sizes for the file
-extern "C" jobject Java_android_media_cts_NativeDecoderTest_getSampleSizesNative(JNIEnv *env,
-        jclass /*clazz*/, int fd, jlong offset, jlong size)
-{
-    AMediaExtractor *ex = AMediaExtractor_new();
-    int err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
-    if (err != 0) {
-        ALOGE("setDataSource error: %d", err);
-        return NULL;
-    }
-    return testExtractor(ex, env);
-}
-
-// get the sample sizes for the path
-extern "C" jobject Java_android_media_cts_NativeDecoderTest_getSampleSizesNativePath(JNIEnv *env,
-        jclass /*clazz*/, jstring jpath, jobjectArray jkeys, jobjectArray jvalues,
-        jboolean testNativeSource)
-{
-    AMediaExtractor *ex = AMediaExtractor_new();
-
-    const char *tmp = env->GetStringUTFChars(jpath, NULL);
-    if (tmp == NULL) {  // Out of memory
-        return NULL;
-    }
-
-    int numkeys = jkeys ? env->GetArrayLength(jkeys) : 0;
-    int numvalues = jvalues ? env->GetArrayLength(jvalues) : 0;
-    int numheaders = numkeys < numvalues ? numkeys : numvalues;
-    const char **key_values = numheaders ? new const char *[numheaders * 2] : NULL;
-    for (int i = 0; i < numheaders; i++) {
-        jstring jkey = (jstring) (env->GetObjectArrayElement(jkeys, i));
-        jstring jvalue = (jstring) (env->GetObjectArrayElement(jvalues, i));
-        const char *key = env->GetStringUTFChars(jkey, NULL);
-        const char *value = env->GetStringUTFChars(jvalue, NULL);
-        key_values[i * 2] = key;
-        key_values[i * 2 + 1] = value;
-    }
-
-    int err;
-    AMediaDataSource *src = NULL;
-    if (testNativeSource) {
-        src = AMediaDataSource_newUri(tmp, numheaders, key_values);
-        err = src ? AMediaExtractor_setDataSourceCustom(ex, src) : -1;
-    } else {
-        err = AMediaExtractor_setDataSource(ex, tmp);
-    }
-
-    for (int i = 0; i < numheaders; i++) {
-        jstring jkey = (jstring) (env->GetObjectArrayElement(jkeys, i));
-        jstring jvalue = (jstring) (env->GetObjectArrayElement(jvalues, i));
-        env->ReleaseStringUTFChars(jkey, key_values[i * 2]);
-        env->ReleaseStringUTFChars(jvalue, key_values[i * 2 + 1]);
-    }
-
-    env->ReleaseStringUTFChars(jpath, tmp);
-    delete[] key_values;
-
-    if (err != 0) {
-        ALOGE("setDataSource error: %d", err);
-        AMediaExtractor_delete(ex);
-        AMediaDataSource_delete(src);
-        return NULL;
-    }
-
-    jobject ret = testExtractor(ex, env);
-    AMediaDataSource_delete(src);
-    return ret;
-}
-
-static int checksum(const uint8_t *in, int len, AMediaFormat *format) {
-    int width, stride, height;
-    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width)) {
-        width = len;
-    }
-    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_STRIDE, &stride)) {
-        stride = width;
-    }
-    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height)) {
-        height = 1;
-    }
-    uint8_t *bb = new uint8_t[width * height];
-    for (int i = 0; i < height; i++) {
-        memcpy(bb + i * width, in + i * stride, width);
-    }
-    // bb is filled with data
-    int sum = adler32(bb, width * height);
-    delete[] bb;
-    return sum;
-}
-
-extern "C" jlong Java_android_media_cts_NativeDecoderTest_getExtractorFileDurationNative(
-        JNIEnv * /*env*/, jclass /*clazz*/, int fd, jlong offset, jlong size)
-{
-    AMediaExtractor *ex = AMediaExtractor_new();
-    int err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
-    if (err != 0) {
-        ALOGE("setDataSource error: %d", err);
-        AMediaExtractor_delete(ex);
-        return -1;
-    }
-    int64_t durationUs = -1;
-    AMediaFormat *format = AMediaExtractor_getFileFormat(ex);
-    AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &durationUs);
-    AMediaFormat_delete(format);
-    AMediaExtractor_delete(ex);
-    return durationUs;
-}
-
-extern "C" jlong Java_android_media_cts_NativeDecoderTest_getExtractorCachedDurationNative(
-        JNIEnv * env, jclass /*clazz*/, jstring jpath, jboolean testNativeSource)
-{
-    AMediaExtractor *ex = AMediaExtractor_new();
-
-    const char *tmp = env->GetStringUTFChars(jpath, NULL);
-    if (tmp == NULL) {  // Out of memory
-        AMediaExtractor_delete(ex);
-        return -1;
-    }
-
-    int err;
-    AMediaDataSource *src = NULL;
-    if (testNativeSource) {
-        src = AMediaDataSource_newUri(tmp, 0, NULL);
-        err = src ? AMediaExtractor_setDataSourceCustom(ex, src) : -1;
-    } else {
-        err = AMediaExtractor_setDataSource(ex, tmp);
-    }
-
-    env->ReleaseStringUTFChars(jpath, tmp);
-
-    if (err != 0) {
-        ALOGE("setDataSource error: %d", err);
-        AMediaExtractor_delete(ex);
-        AMediaDataSource_delete(src);
-        return -1;
-    }
-
-    int64_t cachedDurationUs = AMediaExtractor_getCachedDuration(ex);
-    AMediaExtractor_delete(ex);
-    AMediaDataSource_delete(src);
-    return cachedDurationUs;
-
-}
-
-extern "C" jobject Java_android_media_cts_NativeDecoderTest_getDecodedDataNative(JNIEnv *env,
-        jclass /*clazz*/, int fd, jlong offset, jlong size, jboolean wrapFd, jboolean useCallback) {
-    ALOGV("getDecodedDataNative");
-
-    FdDataSource fdSrc(fd, offset, size);
-    AMediaExtractor *ex = AMediaExtractor_new();
-    AMediaDataSource *ndkSrc = AMediaDataSource_new();
-
-    int err;
-    if (wrapFd) {
-        AMediaDataSource_setUserdata(ndkSrc, &fdSrc);
-        AMediaDataSource_setReadAt(ndkSrc, FdSourceReadAt);
-        AMediaDataSource_setGetSize(ndkSrc, FdSourceGetSize);
-        AMediaDataSource_setClose(ndkSrc, FdSourceClose);
-        err = AMediaExtractor_setDataSourceCustom(ex, ndkSrc);
-    } else {
-        err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
-    }
-    if (err != 0) {
-        ALOGE("setDataSource error: %d", err);
-        return NULL;
-    }
-
-    int numtracks = AMediaExtractor_getTrackCount(ex);
-
-    AMediaCodec **codec = new AMediaCodec*[numtracks];
-    AMediaFormat **format = new AMediaFormat*[numtracks];
-    memset(format, 0, sizeof(AMediaFormat*) * numtracks);
-    bool *sawInputEOS = new bool[numtracks];
-    bool *sawOutputEOS = new bool[numtracks];
-    simplevector<int> *sizes = new simplevector<int>[numtracks];
-    CallbackData *callbackData = new CallbackData[numtracks];
-
-    ALOGV("input has %d tracks", numtracks);
-    for (int i = 0; i < numtracks; i++) {
-        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
-        const char *s = AMediaFormat_toString(format);
-        ALOGI("track %d format: %s", i, s);
-        const char *mime;
-        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
-            ALOGE("no mime type");
-            return NULL;
-        } else if (!strncmp(mime, "audio/", 6) || !strncmp(mime, "video/", 6)) {
-            codec[i] = AMediaCodec_createDecoderByType(mime);
-            AMediaCodec_configure(codec[i], format, NULL /* surface */, NULL /* crypto */, 0);
-            if (useCallback) {
-                AMediaCodecOnAsyncNotifyCallback aCB = {
-                    OnInputAvailableCB,
-                    OnOutputAvailableCB,
-                    OnFormatChangedCB,
-                    OnErrorCB
-                };
-                AMediaCodec_setAsyncNotifyCallback(codec[i], aCB, &callbackData[i]);
-            }
-            AMediaCodec_start(codec[i]);
-            sawInputEOS[i] = false;
-            sawOutputEOS[i] = false;
-        } else {
-            ALOGE("expected audio or video mime type, got %s", mime);
-            return NULL;
-        }
-        AMediaFormat_delete(format);
-        AMediaExtractor_selectTrack(ex, i);
-    }
-    int eosCount = 0;
-    while(eosCount < numtracks) {
-        int t = AMediaExtractor_getSampleTrackIndex(ex);
-        if (t >=0) {
-            ssize_t bufidx;
-            if (useCallback) {
-                bufidx = callbackData[t].getInputBufferId();
-            } else {
-                bufidx = AMediaCodec_dequeueInputBuffer(codec[t], 5000);
-            }
-            ALOGV("track %d, input buffer %zd", t, bufidx);
-            if (bufidx >= 0) {
-                size_t bufsize;
-                uint8_t *buf = AMediaCodec_getInputBuffer(codec[t], bufidx, &bufsize);
-                int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
-                ALOGV("read %d", sampleSize);
-                if (sampleSize < 0) {
-                    sampleSize = 0;
-                    sawInputEOS[t] = true;
-                    ALOGV("EOS");
-                    //break;
-                }
-                int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
-
-                AMediaCodec_queueInputBuffer(codec[t], bufidx, 0, sampleSize, presentationTimeUs,
-                        sawInputEOS[t] ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
-                AMediaExtractor_advance(ex);
-            }
-        } else {
-            ALOGV("@@@@ no more input samples");
-            for (int tt = 0; tt < numtracks; tt++) {
-                if (!sawInputEOS[tt]) {
-                    // we ran out of samples without ever signaling EOS to the codec,
-                    // so do that now
-                    int bufidx;
-                    if (useCallback) {
-                        bufidx = callbackData[tt].getInputBufferId();
-                    } else {
-                        bufidx = AMediaCodec_dequeueInputBuffer(codec[tt], 5000);
-                    }
-                    if (bufidx >= 0) {
-                        AMediaCodec_queueInputBuffer(codec[tt], bufidx, 0, 0, 0,
-                                AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
-                        sawInputEOS[tt] = true;
-                    }
-                }
-            }
-        }
-
-        // check all codecs for available data
-        AMediaCodecBufferInfo info;
-        AMediaFormat *outputFormat;
-        for (int tt = 0; tt < numtracks; tt++) {
-            if (!sawOutputEOS[tt]) {
-                int status;
-                if (useCallback) {
-                    status = callbackData[tt].getOutput(&info, &outputFormat);
-                } else {
-                    status = AMediaCodec_dequeueOutputBuffer(codec[tt], &info, 1);
-                }
-                ALOGV("dequeueoutput on track %d: %d", tt, status);
-                if (status >= 0) {
-                    if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
-                        ALOGV("EOS on track %d", tt);
-                        sawOutputEOS[tt] = true;
-                        eosCount++;
-                    }
-                    ALOGV("got decoded buffer for track %d, size %d", tt, info.size);
-                    if (info.size > 0) {
-                        size_t bufsize;
-                        uint8_t *buf = AMediaCodec_getOutputBuffer(codec[tt], status, &bufsize);
-                        int adler = checksum(buf, info.size, format[tt]);
-                        sizes[tt].add(adler);
-                    }
-                    AMediaCodec_releaseOutputBuffer(codec[tt], status, false);
-                } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
-                    ALOGV("output buffers changed for track %d", tt);
-                } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
-                    if (format[tt] != NULL) {
-                        AMediaFormat_delete(format[tt]);
-                    }
-                    if (useCallback) {
-                        format[tt] = outputFormat;
-                    } else {
-                        format[tt] = AMediaCodec_getOutputFormat(codec[tt]);
-                    }
-                    ALOGV("format changed for track %d: %s", tt, AMediaFormat_toString(format[tt]));
-                } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
-                    ALOGV("no output buffer right now for track %d", tt);
-                } else {
-                    ALOGV("unexpected info code for track %d : %d", tt, status);
-                }
-            } else {
-                ALOGV("already at EOS on track %d", tt);
-            }
-        }
-    }
-    ALOGV("decoding loop done");
-
-    // allocate java int array for result and return it
-    int numsamples = 0;
-    for (int i = 0; i < numtracks; i++) {
-        numsamples += sizes[i].size();
-    }
-    ALOGV("checksums: %d", numsamples);
-    jintArray ret = env->NewIntArray(numsamples);
-    jboolean isCopy;
-    jint *org = env->GetIntArrayElements(ret, &isCopy);
-    jint *dst = org;
-    for (int i = 0; i < numtracks; i++) {
-        int *data = sizes[i].data();
-        int len = sizes[i].size();
-        ALOGV("copying %d", len);
-        for (int j = 0; j < len; j++) {
-            *dst++ = data[j];
-        }
-    }
-    env->ReleaseIntArrayElements(ret, org, 0);
-
-    delete[] callbackData;
-    delete[] sizes;
-    delete[] sawOutputEOS;
-    delete[] sawInputEOS;
-    for (int i = 0; i < numtracks; i++) {
-        AMediaFormat_delete(format[i]);
-        AMediaCodec_stop(codec[i]);
-        AMediaCodec_delete(codec[i]);
-    }
-    delete[] format;
-    delete[] codec;
-    AMediaExtractor_delete(ex);
-    AMediaDataSource_delete(ndkSrc);
-    return ret;
-}
-
-extern "C" jboolean Java_android_media_cts_NativeDecoderTest_testPlaybackNative(JNIEnv *env,
-        jclass /*clazz*/, jobject surface, int fd, jlong offset, jlong size) {
-
-    ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
-    ALOGI("@@@@ native window: %p", window);
-
-    AMediaExtractor *ex = AMediaExtractor_new();
-    int err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
-    if (err != 0) {
-        ALOGE("setDataSource error: %d", err);
-        return false;
-    }
-
-    int numtracks = AMediaExtractor_getTrackCount(ex);
-
-    AMediaCodec *codec = NULL;
-    AMediaFormat *format = NULL;
-    bool sawInputEOS = false;
-    bool sawOutputEOS = false;
-
-    ALOGV("input has %d tracks", numtracks);
-    for (int i = 0; i < numtracks; i++) {
-        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
-        const char *s = AMediaFormat_toString(format);
-        ALOGI("track %d format: %s", i, s);
-        const char *mime;
-        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
-            ALOGE("no mime type");
-            return false;
-        } else if (!strncmp(mime, "video/", 6)) {
-            codec = AMediaCodec_createDecoderByType(mime);
-            AMediaCodec_configure(codec, format, window, NULL, 0);
-            AMediaCodec_start(codec);
-            AMediaExtractor_selectTrack(ex, i);
-        }
-        AMediaFormat_delete(format);
-    }
-
-    while (!sawOutputEOS) {
-        ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec, 5000);
-        ALOGV("input buffer %zd", bufidx);
-        if (bufidx >= 0) {
-            size_t bufsize;
-            uint8_t *buf = AMediaCodec_getInputBuffer(codec, bufidx, &bufsize);
-            int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
-            ALOGV("read %d", sampleSize);
-            if (sampleSize < 0) {
-                sampleSize = 0;
-                sawInputEOS = true;
-                ALOGV("EOS");
-            }
-            int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
-
-            AMediaCodec_queueInputBuffer(codec, bufidx, 0, sampleSize, presentationTimeUs,
-                    sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
-            AMediaExtractor_advance(ex);
-        }
-
-        AMediaCodecBufferInfo info;
-        int status = AMediaCodec_dequeueOutputBuffer(codec, &info, 1);
-        ALOGV("dequeueoutput returned: %d", status);
-        if (status >= 0) {
-            if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
-                ALOGV("output EOS");
-                sawOutputEOS = true;
-            }
-            ALOGV("got decoded buffer size %d", info.size);
-            AMediaCodec_releaseOutputBuffer(codec, status, true);
-            usleep(20000);
-        } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
-            ALOGV("output buffers changed");
-        } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
-            format = AMediaCodec_getOutputFormat(codec);
-            ALOGV("format changed to: %s", AMediaFormat_toString(format));
-        } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
-            ALOGV("no output buffer right now");
-        } else {
-            ALOGV("unexpected info code: %d", status);
-        }
-    }
-
-    AMediaCodec_stop(codec);
-    AMediaCodec_delete(codec);
-    AMediaExtractor_delete(ex);
-    return true;
-}
-
-extern "C" jboolean Java_android_media_cts_NativeDecoderTest_testMuxerNative(JNIEnv */*env*/,
-        jclass /*clazz*/, int infd, jlong inoffset, jlong insize, int outfd, jboolean webm) {
-
-
-    AMediaMuxer *muxer = AMediaMuxer_new(outfd,
-            webm ? AMEDIAMUXER_OUTPUT_FORMAT_WEBM : AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
-
-    AMediaExtractor *ex = AMediaExtractor_new();
-    int err = AMediaExtractor_setDataSourceFd(ex, infd, inoffset, insize);
-    if (err != 0) {
-        ALOGE("setDataSource error: %d", err);
-        return false;
-    }
-
-    int numtracks = AMediaExtractor_getTrackCount(ex);
-    ALOGI("input tracks: %d", numtracks);
-    for (int i = 0; i < numtracks; i++) {
-        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
-        const char *s = AMediaFormat_toString(format);
-        ALOGI("track %d format: %s", i, s);
-        const char *mime;
-        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
-            ALOGE("no mime type");
-            return false;
-        } else if (!strncmp(mime, "audio/", 6) || !strncmp(mime, "video/", 6)) {
-            ssize_t tidx = AMediaMuxer_addTrack(muxer, format);
-            ALOGI("track %d -> %zd format %s", i, tidx, s);
-            AMediaExtractor_selectTrack(ex, i);
-        } else {
-            ALOGE("expected audio or video mime type, got %s", mime);
-            return false;
-        }
-        AMediaFormat_delete(format);
-        AMediaExtractor_selectTrack(ex, i);
-    }
-    AMediaMuxer_start(muxer);
-
-    int bufsize = 1024*1024;
-    uint8_t *buf = new uint8_t[bufsize];
-    AMediaCodecBufferInfo info;
-    while(true) {
-        int n = AMediaExtractor_readSampleData(ex, buf, bufsize);
-        if (n < 0) {
-            break;
-        }
-        info.offset = 0;
-        info.size = n;
-        info.presentationTimeUs = AMediaExtractor_getSampleTime(ex);
-        info.flags = AMediaExtractor_getSampleFlags(ex);
-
-        size_t idx = (size_t) AMediaExtractor_getSampleTrackIndex(ex);
-        AMediaMuxer_writeSampleData(muxer, idx, buf, &info);
-
-        AMediaExtractor_advance(ex);
-    }
-
-    AMediaExtractor_delete(ex);
-    AMediaMuxer_stop(muxer);
-    AMediaMuxer_delete(muxer);
-    return true;
-
-}
 
 extern "C" jboolean Java_android_media_cts_NativeDecoderTest_testFormatNative(JNIEnv * /*env*/,
         jclass /*clazz*/) {
@@ -1005,490 +211,6 @@
         JNIEnv * /*env*/, jclass /*clazz*/, jlong ds) {
     AMediaDataSource_delete(reinterpret_cast<AMediaDataSource *>(ds));
 }
-//
-// === NdkMediaCodec
-
-extern "C" jlong Java_android_media_cts_NdkMediaCodec_AMediaCodecCreateCodecByName(
-        JNIEnv *env, jclass /*clazz*/, jstring name) {
-
-    if (name == NULL) {
-        return 0;
-    }
-
-    const char *tmp = env->GetStringUTFChars(name, NULL);
-    if (tmp == NULL) {
-        return 0;
-    }
-
-    AMediaCodec *codec = AMediaCodec_createCodecByName(tmp);
-    if (codec == NULL) {
-        env->ReleaseStringUTFChars(name, tmp);
-        return 0;
-    }
-
-    env->ReleaseStringUTFChars(name, tmp);
-    return reinterpret_cast<jlong>(codec);
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecDelete(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong codec) {
-    media_status_t err = AMediaCodec_delete(reinterpret_cast<AMediaCodec *>(codec));
-    return err == AMEDIA_OK;
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecStart(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong codec) {
-    media_status_t err = AMediaCodec_start(reinterpret_cast<AMediaCodec *>(codec));
-    return err == AMEDIA_OK;
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecStop(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong codec) {
-    media_status_t err = AMediaCodec_stop(reinterpret_cast<AMediaCodec *>(codec));
-    return err == AMEDIA_OK;
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecConfigure(
-        JNIEnv *env,
-        jclass /*clazz*/,
-        jlong codec,
-        jstring mime,
-        jint width,
-        jint height,
-        jint colorFormat,
-        jint bitRate,
-        jint frameRate,
-        jint iFrameInterval,
-        jobject csd0,
-        jobject csd1,
-        jint flags,
-        jint lowLatency,
-        jobject surface,
-        jint range,
-        jint standard,
-        jint transfer) {
-
-    AMediaFormat* format = AMediaFormat_new();
-    if (format == NULL) {
-        return false;
-    }
-
-    const char *tmp = env->GetStringUTFChars(mime, NULL);
-    if (tmp == NULL) {
-        AMediaFormat_delete(format);
-        return false;
-    }
-
-    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, tmp);
-    env->ReleaseStringUTFChars(mime, tmp);
-
-    const char *keys[] = {
-            AMEDIAFORMAT_KEY_WIDTH,
-            AMEDIAFORMAT_KEY_HEIGHT,
-            AMEDIAFORMAT_KEY_COLOR_FORMAT,
-            AMEDIAFORMAT_KEY_BIT_RATE,
-            AMEDIAFORMAT_KEY_FRAME_RATE,
-            AMEDIAFORMAT_KEY_I_FRAME_INTERVAL,
-            // need to specify the actual string, since this test needs
-            // to run on API 29, where the symbol doesn't exist
-            "low-latency", // AMEDIAFORMAT_KEY_LOW_LATENCY
-            AMEDIAFORMAT_KEY_COLOR_RANGE,
-            AMEDIAFORMAT_KEY_COLOR_STANDARD,
-            AMEDIAFORMAT_KEY_COLOR_TRANSFER,
-    };
-
-    jint values[] = {width, height, colorFormat, bitRate, frameRate, iFrameInterval, lowLatency,
-                     range, standard, transfer};
-    for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) {
-        if (values[i] >= 0) {
-            AMediaFormat_setInt32(format, keys[i], values[i]);
-        }
-    }
-
-    if (csd0 != NULL) {
-        void *csd0Ptr = env->GetDirectBufferAddress(csd0);
-        jlong csd0Size = env->GetDirectBufferCapacity(csd0);
-        AMediaFormat_setBuffer(format, "csd-0", csd0Ptr, csd0Size);
-    }
-
-    if (csd1 != NULL) {
-        void *csd1Ptr = env->GetDirectBufferAddress(csd1);
-        jlong csd1Size = env->GetDirectBufferCapacity(csd1);
-        AMediaFormat_setBuffer(format, "csd-1", csd1Ptr, csd1Size);
-    }
-
-    media_status_t err = AMediaCodec_configure(
-            reinterpret_cast<AMediaCodec *>(codec),
-            format,
-            surface == NULL ? NULL : ANativeWindow_fromSurface(env, surface),
-            NULL,
-            flags);
-
-    AMediaFormat_delete(format);
-    return err == AMEDIA_OK;
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSetInputSurface(
-        JNIEnv* env, jclass /*clazz*/, jlong codec, jobject surface) {
-
-    media_status_t err = AMediaCodec_setInputSurface(
-            reinterpret_cast<AMediaCodec *>(codec),
-            ANativeWindow_fromSurface(env, surface));
-
-    return err == AMEDIA_OK;
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSetNativeInputSurface(
-        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec, jlong nativeWindow) {
-
-    media_status_t err = AMediaCodec_setInputSurface(
-            reinterpret_cast<AMediaCodec *>(codec),
-            reinterpret_cast<ANativeWindow *>(nativeWindow));
-
-    return err == AMEDIA_OK;
-
-}
-
-extern "C" jlong Java_android_media_cts_NdkMediaCodec_AMediaCodecCreateInputSurface(
-        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec) {
-
-    ANativeWindow *nativeWindow;
-    media_status_t err = AMediaCodec_createInputSurface(
-            reinterpret_cast<AMediaCodec *>(codec),
-            &nativeWindow);
-
-     if (err == AMEDIA_OK) {
-         return reinterpret_cast<jlong>(nativeWindow);
-     }
-
-     return 0;
-
-}
-
-extern "C" jlong Java_android_media_cts_NdkMediaCodec_AMediaCodecCreatePersistentInputSurface(
-        JNIEnv* /*env*/, jclass /*clazz*/) {
-
-    ANativeWindow *nativeWindow;
-    media_status_t err = AMediaCodec_createPersistentInputSurface(&nativeWindow);
-
-     if (err == AMEDIA_OK) {
-         return reinterpret_cast<jlong>(nativeWindow);
-     }
-
-     return 0;
-
-}
-
-extern "C" jstring Java_android_media_cts_NdkMediaCodec_AMediaCodecGetOutputFormatString(
-        JNIEnv* env, jclass /*clazz*/, jlong codec) {
-
-    AMediaFormat *format = AMediaCodec_getOutputFormat(reinterpret_cast<AMediaCodec *>(codec));
-    const char *str = AMediaFormat_toString(format);
-    jstring jstr = env->NewStringUTF(str);
-    AMediaFormat_delete(format);
-    return jstr;
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSignalEndOfInputStream(
-        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec) {
-
-    media_status_t err = AMediaCodec_signalEndOfInputStream(reinterpret_cast<AMediaCodec *>(codec));
-    return err == AMEDIA_OK;
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecReleaseOutputBuffer(
-        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec, jint index, jboolean render) {
-
-    media_status_t err = AMediaCodec_releaseOutputBuffer(
-            reinterpret_cast<AMediaCodec *>(codec),
-            index,
-            render);
-
-    return err == AMEDIA_OK;
-
-}
-
-static jobject AMediaCodecGetBuffer(
-        JNIEnv* env,
-        jlong codec,
-        jint index,
-        uint8_t *(*getBuffer)(AMediaCodec*, size_t, size_t*)) {
-
-    size_t bufsize;
-    uint8_t *buf = getBuffer(
-            reinterpret_cast<AMediaCodec *>(codec),
-            index,
-            &bufsize);
-
-    return env->NewDirectByteBuffer(buf, bufsize);
-
-}
-
-extern "C" jobject Java_android_media_cts_NdkMediaCodec_AMediaCodecGetOutputBuffer(
-        JNIEnv* env, jclass /*clazz*/, jlong codec, jint index) {
-
-    return AMediaCodecGetBuffer(env, codec, index, AMediaCodec_getOutputBuffer);
-
-}
-
-extern "C" jlongArray Java_android_media_cts_NdkMediaCodec_AMediaCodecDequeueOutputBuffer(
-        JNIEnv* env, jclass /*clazz*/, jlong codec, jlong timeoutUs) {
-
-    AMediaCodecBufferInfo info;
-    memset(&info, 0, sizeof(info));
-    int status = AMediaCodec_dequeueOutputBuffer(
-        reinterpret_cast<AMediaCodec *>(codec),
-        &info,
-        timeoutUs);
-
-    jlong ret[5] = {0};
-    ret[0] = status;
-    ret[1] = 0; // NdkMediaCodec calls ABuffer::data, which already adds offset
-    ret[2] = info.size;
-    ret[3] = info.presentationTimeUs;
-    ret[4] = info.flags;
-
-    jlongArray jret = env->NewLongArray(5);
-    env->SetLongArrayRegion(jret, 0, 5, ret);
-    return jret;
-
-}
-
-extern "C" jobject Java_android_media_cts_NdkMediaCodec_AMediaCodecGetInputBuffer(
-        JNIEnv* env, jclass /*clazz*/, jlong codec, jint index) {
-
-    return AMediaCodecGetBuffer(env, codec, index, AMediaCodec_getInputBuffer);
-
-}
-
-extern "C" jint Java_android_media_cts_NdkMediaCodec_AMediaCodecDequeueInputBuffer(
-        JNIEnv* /*env*/, jclass /*clazz*/, jlong codec, jlong timeoutUs) {
-
-    return AMediaCodec_dequeueInputBuffer(
-            reinterpret_cast<AMediaCodec *>(codec),
-            timeoutUs);
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecQueueInputBuffer(
-        JNIEnv* /*env*/,
-        jclass /*clazz*/,
-        jlong codec,
-        jint index,
-        jint offset,
-        jint size,
-        jlong presentationTimeUs,
-        jint flags) {
-
-    media_status_t err = AMediaCodec_queueInputBuffer(
-            reinterpret_cast<AMediaCodec *>(codec),
-            index,
-            offset,
-            size,
-            presentationTimeUs,
-            flags);
-
-    return err == AMEDIA_OK;
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkMediaCodec_AMediaCodecSetParameter(
-        JNIEnv* env, jclass /*clazz*/, jlong codec, jstring jkey, jint value) {
-
-    AMediaFormat* params = AMediaFormat_new();
-    if (params == NULL) {
-        return false;
-    }
-
-    const char *key = env->GetStringUTFChars(jkey, NULL);
-    if (key == NULL) {
-        AMediaFormat_delete(params);
-        return false;
-    }
-
-    AMediaFormat_setInt32(params, key, value);
-    media_status_t err = AMediaCodec_setParameters(
-            reinterpret_cast<AMediaCodec *>(codec),
-            params);
-    env->ReleaseStringUTFChars(jkey, key);
-    AMediaFormat_delete(params);
-    return err == AMEDIA_OK;
-
-}
-
-// === NdkInputSurface
-
-extern "C" jlong Java_android_media_cts_NdkInputSurface_eglGetDisplay(JNIEnv * /*env*/, jclass /*clazz*/) {
-
-    EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (eglDisplay == EGL_NO_DISPLAY) {
-        return 0;
-    }
-
-    EGLint major, minor;
-    if (!eglInitialize(eglDisplay, &major, &minor)) {
-        return 0;
-    }
-
-    return reinterpret_cast<jlong>(eglDisplay);
-
-}
-
-extern "C" jlong Java_android_media_cts_NdkInputSurface_eglChooseConfig(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay) {
-
-    // Configure EGL for recordable and OpenGL ES 2.0.  We want enough RGB bits
-    // to minimize artifacts from possible YUV conversion.
-    EGLint attribList[] = {
-            EGL_RED_SIZE, 8,
-            EGL_GREEN_SIZE, 8,
-            EGL_BLUE_SIZE, 8,
-            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-            EGL_RECORDABLE_ANDROID, 1,
-            EGL_NONE
-    };
-
-    EGLConfig configs[1];
-    EGLint numConfigs[1];
-    if (!eglChooseConfig(reinterpret_cast<EGLDisplay>(eglDisplay), attribList, configs, 1, numConfigs)) {
-        return 0;
-    }
-    return reinterpret_cast<jlong>(configs[0]);
-
-}
-
-extern "C" jlong Java_android_media_cts_NdkInputSurface_eglCreateContext(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglConfig) {
-
-    // Configure context for OpenGL ES 2.0.
-    int attrib_list[] = {
-            EGL_CONTEXT_CLIENT_VERSION, 2,
-            EGL_NONE
-    };
-
-    EGLConfig eglContext = eglCreateContext(
-            reinterpret_cast<EGLDisplay>(eglDisplay),
-            reinterpret_cast<EGLConfig>(eglConfig),
-            EGL_NO_CONTEXT,
-            attrib_list);
-
-    if (eglGetError() != EGL_SUCCESS) {
-        return 0;
-    }
-
-    return reinterpret_cast<jlong>(eglContext);
-
-}
-
-extern "C" jlong Java_android_media_cts_NdkInputSurface_createEGLSurface(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglConfig, jlong nativeWindow) {
-
-    int surfaceAttribs[] = {EGL_NONE};
-    EGLSurface eglSurface = eglCreateWindowSurface(
-            reinterpret_cast<EGLDisplay>(eglDisplay),
-            reinterpret_cast<EGLConfig>(eglConfig),
-            reinterpret_cast<EGLNativeWindowType>(nativeWindow),
-            surfaceAttribs);
-
-    if (eglGetError() != EGL_SUCCESS) {
-        return 0;
-    }
-
-    return reinterpret_cast<jlong>(eglSurface);
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkInputSurface_eglMakeCurrent(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface, jlong eglContext) {
-
-    return eglMakeCurrent(
-            reinterpret_cast<EGLDisplay>(eglDisplay),
-            reinterpret_cast<EGLSurface>(eglSurface),
-            reinterpret_cast<EGLSurface>(eglSurface),
-            reinterpret_cast<EGLContext>(eglContext));
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkInputSurface_eglSwapBuffers(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface) {
-
-    return eglSwapBuffers(
-            reinterpret_cast<EGLDisplay>(eglDisplay),
-            reinterpret_cast<EGLSurface>(eglSurface));
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkInputSurface_eglPresentationTimeANDROID(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface, jlong nsecs) {
-
-    return eglPresentationTimeANDROID(
-            reinterpret_cast<EGLDisplay>(eglDisplay),
-            reinterpret_cast<EGLSurface>(eglSurface),
-            reinterpret_cast<EGLnsecsANDROID>(nsecs));
-
-}
-
-extern "C" jint Java_android_media_cts_NdkInputSurface_eglGetWidth(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface) {
-
-    EGLint width;
-    eglQuerySurface(
-            reinterpret_cast<EGLDisplay>(eglDisplay),
-            reinterpret_cast<EGLSurface>(eglSurface),
-            EGL_WIDTH,
-            &width);
-
-    return width;
-
-}
-
-extern "C" jint Java_android_media_cts_NdkInputSurface_eglGetHeight(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface) {
-
-    EGLint height;
-    eglQuerySurface(
-            reinterpret_cast<EGLDisplay>(eglDisplay),
-            reinterpret_cast<EGLSurface>(eglSurface),
-            EGL_HEIGHT,
-            &height);
-
-    return height;
-
-}
-
-extern "C" jboolean Java_android_media_cts_NdkInputSurface_eglDestroySurface(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface) {
-
-    return eglDestroySurface(
-            reinterpret_cast<EGLDisplay>(eglDisplay),
-            reinterpret_cast<EGLSurface>(eglSurface));
-
-}
-
-extern "C" void Java_android_media_cts_NdkInputSurface_nativeRelease(
-        JNIEnv * /*env*/, jclass /*clazz*/, jlong eglDisplay, jlong eglSurface, jlong eglContext, jlong nativeWindow) {
-
-    if (eglDisplay != 0) {
-
-        EGLDisplay _eglDisplay = reinterpret_cast<EGLDisplay>(eglDisplay);
-        EGLSurface _eglSurface = reinterpret_cast<EGLSurface>(eglSurface);
-        EGLContext _eglContext = reinterpret_cast<EGLContext>(eglContext);
-
-        eglDestroySurface(_eglDisplay, _eglSurface);
-        eglDestroyContext(_eglDisplay, _eglContext);
-        eglReleaseThread();
-        eglTerminate(_eglDisplay);
-
-    }
-
-    ANativeWindow_release(reinterpret_cast<ANativeWindow *>(nativeWindow));
-
-}
 
 extern "C" jboolean Java_android_media_cts_NativeDecoderTest_testMediaFormatNative(
         JNIEnv * /*env*/, jclass /*clazz*/) {
diff --git a/tests/tests/media/libmediandkjni/native_media_decoder_source.cpp b/tests/tests/media/libmediandkjni/native_media_decoder_source.cpp
deleted file mode 100644
index 040e78b..0000000
--- a/tests/tests/media/libmediandkjni/native_media_decoder_source.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "NativeMediaEnc-Source"
-#include <log/log.h>
-
-#include "native_media_source.h"
-
-using namespace Utils;
-
-class DecoderSource : public Thread, public Source {
-public:
-    DecoderSource(
-        int32_t w, int32_t h, int32_t colorFormat, float fps, bool looping, bool regulate);
-    ~DecoderSource();
-    DecoderSource(const DecoderSource& ) = delete;
-
-    Status setDataSourceFd(int sourceFileFd, off64_t sourceFileOffset, off64_t sourceFileSize);
-    Status prepare(std::shared_ptr<Listener> l, std::shared_ptr<ANativeWindow> n) override;
-    Status start() override;
-    Status stop() override;
-
-protected:
-    void run() override;
-
-private:
-    // seek the extractor back to beginning
-    void rewindExtractor();
-
-    // When setting dynamic params, if the source is faster than the encoder,
-    // there is a possibility of param set via setParameters() will get delayed.
-    // Simulate a real-time source by slowing down the feeding rate (up to configured fps)
-    bool mRegulateFramerate;
-
-    std::shared_ptr<AMediaExtractor> mEx;
-    std::shared_ptr<AMediaCodec> mDec;
-    std::shared_ptr<AMediaFormat> mFormat;
-    std::string mMime;
-    int mVideoTrackIndex;
-    int mFrameCount;
-    bool mStopRequest;
-    bool mStarted;
-};
-
-std::shared_ptr<Source> createDecoderSource(
-        int32_t w, int32_t h, int32_t colorFormat, float fps, bool looping,
-        bool regulateFeedingRate, /* WA for dynamic settings */
-        int sourceFileFd, off64_t sourceFileOffset, off64_t sourceFileSize) {
-    DecoderSource *d = new DecoderSource(w, h, colorFormat, fps, looping, regulateFeedingRate);
-    d->setDataSourceFd(sourceFileFd, sourceFileOffset, sourceFileSize);
-    std::shared_ptr<Source> src(d);
-    return src;
-}
-
-DecoderSource::DecoderSource(
-        int32_t w, int32_t h, int32_t colorFormat, float fps, bool looping, bool regulate)
-    : Source(w, h, colorFormat, fps, looping),
-      mRegulateFramerate(regulate),
-      mEx(nullptr),
-      mDec(nullptr),
-      mFormat(nullptr),
-      mMime(""),
-      mVideoTrackIndex(-1),
-      mFrameCount(0),
-      mStopRequest(false),
-      mStarted(false) {
-}
-
-Status DecoderSource::setDataSourceFd(
-        int sourceFileFd, off64_t sourceFileOffset, off64_t sourceFileSize) {
-
-    mEx = std::shared_ptr<AMediaExtractor>(AMediaExtractor_new(), deleter_AMediExtractor);
-    int err = AMediaExtractor_setDataSourceFd(mEx.get(), sourceFileFd, sourceFileOffset, sourceFileSize);
-    if (err != 0) {
-        ALOGE("setDataSource error: %d", err);
-        return FAIL;
-    }
-
-    const char *mime;
-    mVideoTrackIndex = -1;
-    int numtracks = AMediaExtractor_getTrackCount(mEx.get());
-    for (int i = 0; i < numtracks; i++) {
-        AMediaFormat *format = AMediaExtractor_getTrackFormat(mEx.get(), i);
-        const char *s = AMediaFormat_toString(format);
-        ALOGV("track %d format: %s", i, s);
-        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
-            ALOGE("no mime type");
-            mEx = nullptr;
-            AMediaFormat_delete(format);
-            return FAIL;
-        } else if (!strncmp(mime, "video/", 6)) {
-            mVideoTrackIndex = i;
-            mFormat = std::shared_ptr<AMediaFormat>(format, deleter_AMediaFormat);
-            mMime = mime;
-            break;
-        } else {
-            ALOGE("expected video mime type, got %s", mime);
-            mEx = nullptr;
-        }
-        AMediaFormat_delete(format);
-    }
-    return mVideoTrackIndex == -1 ? FAIL : OK;
-}
-
-DecoderSource::~DecoderSource() {
-    mDec = nullptr;
-    mEx = nullptr;
-    mFormat = nullptr;
-}
-
-void DecoderSource::run() {
-    while(!mStopRequest) {
-        int t = AMediaExtractor_getSampleTrackIndex(mEx.get());
-        if (t < 0) {
-            if (mLooping) {
-                rewindExtractor();
-                continue;
-            } else {
-                ALOGV("no more samples");
-                break;
-            }
-        } else if (t != mVideoTrackIndex) {
-            continue;
-        }
-
-        ssize_t bufidx = AMediaCodec_dequeueInputBuffer(mDec.get(), 5000);
-        if (bufidx >= 0) {
-            size_t bufsize;
-            uint8_t *buf = AMediaCodec_getInputBuffer(mDec.get(), bufidx, &bufsize);
-            int sampleSize = AMediaExtractor_readSampleData(mEx.get(), buf, bufsize);
-            int32_t flags = 0;
-            if (sampleSize < 0) {
-                if (mLooping) {
-                    rewindExtractor();
-                    continue;
-                } else {
-                    flags = AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
-                }
-            }
-            // synthesize timestamps based on required fps
-            int64_t timeStampUs = 1e6 / mFps * mFrameCount;
-            AMediaCodec_queueInputBuffer(mDec.get(), bufidx, 0, sampleSize, timeStampUs, flags);
-
-            AMediaExtractor_advance(mEx.get());
-            ++mFrameCount;
-        }
-
-        AMediaCodecBufferInfo info;
-        int status = AMediaCodec_dequeueOutputBuffer(mDec.get(), &info, 1000);
-        if (status >= 0) {
-            ALOGV("got decoded buffer of size=%d @%lld us",
-                    info.size, (long long)info.presentationTimeUs);
-            bool render = info.size > 0;
-            if (mBufListener != nullptr) {
-                //TBD
-                //mBufListener->onBufferAvailable(..);
-            }
-
-            // WA: if decoder runs free, dynamic settings applied by
-            //     MediaCodec.setParameters() are off
-            if (mRegulateFramerate) {
-                usleep(1e6/mFps);
-            }
-
-            AMediaCodec_releaseOutputBuffer(mDec.get(), status, render);
-
-            if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
-                ALOGV("saw EOS");
-                break;
-            }
-
-        } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
-        } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
-            mFormat = std::shared_ptr<AMediaFormat>(
-                    AMediaCodec_getOutputFormat(mDec.get()), deleter_AMediaFormat);
-            ALOGV("format changed: %s", AMediaFormat_toString(mFormat.get()));
-        } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
-        } else {
-            ALOGV("Invalid status : %d", status);
-        }
-    }
-}
-
-Status DecoderSource::prepare(
-        std::shared_ptr<Listener> l, std::shared_ptr<ANativeWindow> n) {
-
-    mBufListener = l;
-    mSurface = n;
-
-    if (mVideoTrackIndex < 0) {
-        ALOGE("Video track not found !");
-        return FAIL;
-    }
-
-    assert(mEx.get() != nullptr);
-    AMediaExtractor_selectTrack(mEx.get(), mVideoTrackIndex);
-
-    AMediaCodec *dec = AMediaCodec_createDecoderByType(mMime.c_str());
-    mDec = std::shared_ptr<AMediaCodec>(dec, deleter_AMediaCodec);
-
-    ALOGI("configure decoder. surface = %p", mSurface.get());
-    media_status_t status = AMediaCodec_configure(
-            mDec.get(), mFormat.get(), mSurface.get(), NULL /* crypto */, 0);
-    if (status != AMEDIA_OK) {
-        ALOGE("failed to configure decoder");
-        return FAIL;
-    }
-    return OK;
-}
-
-Status DecoderSource::start() {
-    ALOGV("start");
-    media_status_t status = AMediaCodec_start(mDec.get());
-    if (status != AMEDIA_OK) {
-        ALOGE("failed to start decoder");
-        return FAIL;
-    }
-    if (startThread() != OK) {
-        return FAIL;
-    }
-    mStarted = true;
-    return OK;
-}
-
-Status DecoderSource::stop() {
-    if (!mStarted) {
-        return FAIL;
-    }
-
-    ALOGV("Stopping decoder source..");
-    mStopRequest = true;
-    joinThread();
-
-    media_status_t status = AMediaCodec_stop(mDec.get());
-    if (status != AMEDIA_OK) {
-        ALOGE("failed to stop decoder");
-    }
-
-    mDec = nullptr;
-    mEx = nullptr;
-    mFormat = nullptr;
-    return OK;
-}
-
-void DecoderSource::rewindExtractor() {
-    assert(mEx.get() != nullptr);
-    media_status_t status = AMediaExtractor_seekTo(mEx.get(), 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
-    if (status != AMEDIA_OK) {
-        ALOGE("failed to seek Extractor to 0");
-    }
-}
-
diff --git a/tests/tests/media/libmediandkjni/native_media_source.h b/tests/tests/media/libmediandkjni/native_media_source.h
deleted file mode 100644
index 4320aed..0000000
--- a/tests/tests/media/libmediandkjni/native_media_source.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NATIVE_MEDIA_SOURCE_H_
-#define _NATIVE_MEDIA_SOURCE_H_
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <memory>
-#include <string>
-
-#include <android/native_window_jni.h>
-
-#include "media/NdkMediaFormat.h"
-#include "media/NdkMediaExtractor.h"
-#include "media/NdkMediaCodec.h"
-#include "media/NdkMediaMuxer.h"
-
-#include "native_media_utils.h"
-using Utils::Thread;
-using Utils::Status;
-
-class Source {
-public:
-    Source(int32_t w, int32_t h, int32_t colorFormat, float fps, bool looping)
-        : mWidth(w),
-          mHeight(h),
-          mColorFormat(colorFormat),
-          mFps(fps),
-          mLooping(looping),
-          mBufListener(nullptr) {
-    }
-    virtual ~Source() = default;
-
-    class Listener {
-    public:
-        virtual void onBufferAvailable(
-            uint8_t *buffer, int32_t size, int64_t timeStampUs, uint32_t flags) = 0;
-        virtual ~Listener() = default;
-    };
-    virtual Status prepare(std::shared_ptr<Listener> l, std::shared_ptr<ANativeWindow> n) = 0;
-    virtual Status start() = 0;
-    virtual Status stop() = 0;
-
-protected:
-    int32_t mWidth;
-    int32_t mHeight;
-    int32_t mColorFormat;
-    float mFps;
-    bool mLooping;
-    std::shared_ptr<Listener> mBufListener;
-    std::shared_ptr<ANativeWindow> mSurface;
-};
-
-std::shared_ptr<Source> createDecoderSource(
-        int32_t w, int32_t h, int32_t colorFormat, float fps, bool looping,
-        bool regulateFeedingRate, /* WA for dynamic settings */
-        int sourceFileFd, off64_t sourceFileOffset, off64_t sourceFileSize);
-
-#endif // _NATIVE_MEDIA_SOURCE_H_
diff --git a/tests/tests/media/libmediandkjni/native_media_utils.cpp b/tests/tests/media/libmediandkjni/native_media_utils.cpp
deleted file mode 100644
index 21b7f7f..0000000
--- a/tests/tests/media/libmediandkjni/native_media_utils.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "NativeMedia"
-#include <log/log.h>
-
-#include <stdlib.h>
-#include <math.h>
-#include <string>
-#include <algorithm>
-#include <iterator>
-#include "native_media_utils.h"
-
-namespace Utils {
-
-Status Thread::startThread() {
-    assert(mHandle == 0);
-    if (pthread_create(&mHandle, nullptr, Thread::thread_wrapper, this) != 0) {
-        ALOGE("Failed to create thread");
-        return FAIL;
-    }
-    return OK;
-}
-
-Status Thread::joinThread() {
-    assert(mHandle != 0);
-    void *ret;
-    pthread_join(mHandle, &ret);
-    return OK;
-}
-
-//static
-void* Thread::thread_wrapper(void *obj) {
-    assert(obj != nullptr);
-    Thread *t = reinterpret_cast<Thread *>(obj);
-    t->run();
-    return nullptr;
-}
-
-}; // namespace Utils
diff --git a/tests/tests/media/libmediandkjni/native_media_utils.h b/tests/tests/media/libmediandkjni/native_media_utils.h
deleted file mode 100644
index e5842f7..0000000
--- a/tests/tests/media/libmediandkjni/native_media_utils.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _NATIVE_MEDIA_UTILS_H_
-#define _NATIVE_MEDIA_UTILS_H_
-
-#include <pthread.h>
-#include <sys/cdefs.h>
-#include <stddef.h>
-#include <assert.h>
-#include <vector>
-
-#include <android/native_window_jni.h>
-
-#include "media/NdkMediaFormat.h"
-#include "media/NdkMediaExtractor.h"
-#include "media/NdkMediaCodec.h"
-#include "media/NdkMediaMuxer.h"
-
-namespace Utils {
-
-enum Status : int32_t {
-    FAIL = -1,
-    OK = 0,
-};
-
-class Thread {
-public:
-    Thread()
-        : mHandle(0) {
-    }
-    virtual ~Thread() {
-        assert(mExited);
-        mHandle = 0;
-    }
-    Thread(const Thread& ) = delete;
-    Status startThread();
-    Status joinThread();
-
-protected:
-    virtual void run() = 0;
-
-private:
-    static void* thread_wrapper(void *);
-    pthread_t mHandle;
-};
-
-static inline void deleter_AMediExtractor(AMediaExtractor *_a) {
-    AMediaExtractor_delete(_a);
-}
-
-static inline void deleter_AMediaCodec(AMediaCodec *_a) {
-    AMediaCodec_delete(_a);
-}
-
-static inline void deleter_AMediaFormat(AMediaFormat *_a) {
-    AMediaFormat_delete(_a);
-}
-
-static inline void deleter_AMediaMuxer(AMediaMuxer *_a) {
-    AMediaMuxer_delete(_a);
-}
-
-static inline void deleter_ANativeWindow(ANativeWindow *_a) {
-    ANativeWindow_release(_a);
-}
-
-}; //namespace Utils
-
-#endif // _NATIVE_MEDIA_UTILS_H_
diff --git a/tests/tests/media/libndkaudio/Android.bp b/tests/tests/media/libndkaudio/Android.bp
deleted file mode 100644
index 08552d1..0000000
--- a/tests/tests/media/libndkaudio/Android.bp
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (C) 2016 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_test_library {
-    name: "libndkaudioLib",
-    include_dirs: [
-        "frameworks/wilhelm/include",
-        "frameworks/wilhelm/src/android",
-    ],
-    srcs: [
-        "OpenSLESUtils.cpp",
-        "AudioPlayer.cpp",
-        "AudioSource.cpp",
-        "PeriodicAudioSource.cpp",
-        "SystemParams.cpp",
-        "WaveTableGenerator.cpp",
-        "WaveTableOscillator.cpp",
-        "com_android_ndkaudio_AudioPlayer.cpp",
-        "AudioRecorder.cpp",
-        "com_android_ndkaudio_AudioRecorder.cpp",
-    ],
-    stl: "libc++_static",
-    shared_libs: [
-        "liblog",
-        "libOpenSLES",
-    ],
-    cflags: [
-        "-Werror",
-        "-Wall",
-        "-Wno-deprecated-declarations",
-    ],
-    gtest: false,
-    sdk_version: "29",
-}
-
-//
-// ndkaudio - java
-//
-java_library {
-    name: "ndkaudio",
-    srcs: ["**/*.java"],
-    sdk_version: "current",
-    min_sdk_version: "23",
-}
diff --git a/tests/tests/media/libndkaudio/AndroidManifest.xml b/tests/tests/media/libndkaudio/AndroidManifest.xml
deleted file mode 100644
index 3fbb9ad..0000000
--- a/tests/tests/media/libndkaudio/AndroidManifest.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.ndkaudio"
-    android:versionCode="1"
-    android:versionName="1.0" >
-
-    <uses-sdk
-        android:minSdkVersion="23"
-        android:targetSdkVersion="23" />
-
-</manifest>
diff --git a/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioPlayer.h b/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioPlayer.h
deleted file mode 100644
index 551349c..0000000
--- a/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioPlayer.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class com_android_ndkaudio_AudioPlayer */
-
-#ifndef _Included_com_android_ndkaudio_AudioPlayer
-#define _Included_com_android_ndkaudio_AudioPlayer
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    InitN
- * Signature: ()V
- */
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    Create
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_Create
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    Destroy
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_Destroy
-  (JNIEnv*, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    RealizePlayer
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_RealizePlayer
-  (JNIEnv*, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    RealizeRoutingProxy
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_RealizeRoutingProxy
-  (JNIEnv*, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    Start
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_Start
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    Stop
- * Signature: ()android/
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_Stop
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    GetRoutingInterface
- * Signature: ()Landroid/media/AudioRouting;
- */
-JNIEXPORT jobject JNICALL Java_com_android_ndkaudio_AudioPlayer_GetRoutingInterface
-  (JNIEnv*, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    GetRoutingInterface
- * Signature: (Landroid/media/AudioRouting;)V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_ReleaseRoutingInterface
-  (JNIEnv*, jobject, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    GetLastSLResult
- * Signature: ()J
- */
-JNIEXPORT jlong JNICALL Java_com_android_ndkaudio_AudioPlayer_GetLastSLResult
-  (JNIEnv*, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioPlayer
- * Method:    ClearLastSLResult
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioPlayer_ClearLastSLResult
-  (JNIEnv*, jobject);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioRecorder.h b/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioRecorder.h
deleted file mode 100644
index 98a2932..0000000
--- a/tests/tests/media/libndkaudio/com_android_ndkaudio_AudioRecorder.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class com_android_ndkaudio_AudioRecorder */
-
-#ifndef _Included_com_android_ndkaudio_AudioRecorder
-#define _Included_com_android_ndkaudio_AudioRecorder
-#ifdef __cplusplus
-extern "C" {
-#endif
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    Create
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_Create
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    Destroy
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_Destroy
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    RealizeRecorder
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_RealizeRecorder
-  (JNIEnv*, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    RealizeRoutingProxy
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_RealizeRoutingProxy
-  (JNIEnv*, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    Start
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_Start
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    Stop
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_Stop
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    GetRoutingInterface
- * Signature: ()Landroid/media/AudioRouting;
- */
-JNIEXPORT jobject JNICALL Java_com_android_ndkaudio_AudioRecorder_GetRoutingInterface
-  (JNIEnv*, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    ReleaseRoutingInterface
- * Signature: (Landroid/media/AudioRouting;)V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_ReleaseRoutingInterface
-  (JNIEnv*, jobject, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    GetNumBufferFrames
- * Signature: ()I
- */
-JNIEXPORT jint JNICALL Java_com_android_ndkaudio_AudioRecorder_GetNumBufferFrames
-  (JNIEnv *, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    GetBufferData
- * Signature: ([F)V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_GetBufferData
-  (JNIEnv *, jobject, jfloatArray);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    GetLastSLResult
- * Signature: ()J
- */
-JNIEXPORT jlong JNICALL Java_com_android_ndkaudio_AudioRecorder_GetLastSLResult
-  (JNIEnv*, jobject);
-
-/*
- * Class:     com_android_ndkaudio_AudioRecorder
- * Method:    ClearLastSLResult
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_com_android_ndkaudio_AudioRecorder_ClearLastSLResult
-  (JNIEnv*, jobject);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/tests/tests/media/muxer/Android.bp b/tests/tests/media/muxer/Android.bp
index 37cf20e..3eb38a6 100644
--- a/tests/tests/media/muxer/Android.bp
+++ b/tests/tests/media/muxer/Android.bp
@@ -14,13 +14,28 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-CC-BY
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: ["cts_tests_tests_media_license"],
+}
+
+cc_test_library {
+    name: "libctsmediamuxertest_jni",
+    srcs: [
+        "jni/native_muxer_jni.cpp",
+    ],
+    shared_libs: [
+        "liblog",
+        "libmediandk",
+    ],
+    header_libs: ["liblog_headers"],
+    stl: "libc++_static",
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+    gtest: false,
+    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
+    // (revisit if/when we add features to this library that require newer sdk.
+    sdk_version: "29",
 }
 
 android_test {
@@ -32,6 +47,9 @@
         "ctstestrunner-axt",
         "cts-media-common",
     ],
+    jni_libs: [
+        "libctsmediamuxertest_jni",
+    ],
     // do not compress media files
     aaptflags: [
         "-0 .vp9",
diff --git a/tests/tests/media/muxer/AndroidTest.xml b/tests/tests/media/muxer/AndroidTest.xml
index e2e91dd..02d777b 100644
--- a/tests/tests/media/muxer/AndroidTest.xml
+++ b/tests/tests/media/muxer/AndroidTest.xml
@@ -35,7 +35,7 @@
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="push-all" value="true" />
-        <option name="media-folder-name" value="CtsMediaTestCases-1.4" />
+        <option name="media-folder-name" value="CtsMediaMuxerTestCases-1.0" />
         <option name="dynamic-config-module" value="CtsMediaMuxerTestCases" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/media/muxer/DynamicConfig.xml b/tests/tests/media/muxer/DynamicConfig.xml
index 53528de..01b2a7f 100644
--- a/tests/tests/media/muxer/DynamicConfig.xml
+++ b/tests/tests/media/muxer/DynamicConfig.xml
@@ -15,6 +15,6 @@
 
 <dynamicConfig>
     <entry key="media_files_url">
-    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/CtsMediaTestCases-1.4.zip</value>
+    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/muxer/CtsMediaMuxerTestCases-1.0.zip</value>
     </entry>
 </dynamicConfig>
diff --git a/tests/tests/media/muxer/jni/native_muxer_jni.cpp b/tests/tests/media/muxer/jni/native_muxer_jni.cpp
new file mode 100644
index 0000000..b45dba1
--- /dev/null
+++ b/tests/tests/media/muxer/jni/native_muxer_jni.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMuxer"
+#include <log/log.h>
+#include <assert.h>
+#include <jni.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaMuxer.h"
+
+extern "C" jboolean Java_android_media_muxer_cts_NativeMuxerTest_testMuxerNative(
+        JNIEnv */*env*/, jclass /*clazz*/, int infd, jlong inoffset, jlong insize, int outfd,
+        jboolean webm) {
+    AMediaMuxer *muxer = AMediaMuxer_new(outfd,
+            webm ? AMEDIAMUXER_OUTPUT_FORMAT_WEBM : AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
+    AMediaExtractor *ex = AMediaExtractor_new();
+    int err = AMediaExtractor_setDataSourceFd(ex, infd, inoffset, insize);
+    if (err != 0) {
+        ALOGE("setDataSource error: %d", err);
+        return false;
+    }
+    int numtracks = AMediaExtractor_getTrackCount(ex);
+    ALOGI("input tracks: %d", numtracks);
+    for (int i = 0; i < numtracks; i++) {
+        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+        const char *s = AMediaFormat_toString(format);
+        ALOGI("track %d format: %s", i, s);
+        const char *mime;
+        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+            ALOGE("no mime type");
+            return false;
+        } else if (!strncmp(mime, "audio/", 6) || !strncmp(mime, "video/", 6)) {
+            ssize_t tidx = AMediaMuxer_addTrack(muxer, format);
+            ALOGI("track %d -> %zd format %s", i, tidx, s);
+            AMediaExtractor_selectTrack(ex, i);
+        } else {
+            ALOGE("expected audio or video mime type, got %s", mime);
+            return false;
+        }
+        AMediaFormat_delete(format);
+        AMediaExtractor_selectTrack(ex, i);
+    }
+    AMediaMuxer_start(muxer);
+    int bufsize = 1024*1024;
+    uint8_t *buf = new uint8_t[bufsize];
+    AMediaCodecBufferInfo info;
+    while(true) {
+        int n = AMediaExtractor_readSampleData(ex, buf, bufsize);
+        if (n < 0) {
+            break;
+        }
+        info.offset = 0;
+        info.size = n;
+        info.presentationTimeUs = AMediaExtractor_getSampleTime(ex);
+        info.flags = AMediaExtractor_getSampleFlags(ex);
+        size_t idx = (size_t) AMediaExtractor_getSampleTrackIndex(ex);
+        AMediaMuxer_writeSampleData(muxer, idx, buf, &info);
+        AMediaExtractor_advance(ex);
+    }
+    AMediaExtractor_delete(ex);
+    AMediaMuxer_stop(muxer);
+    AMediaMuxer_delete(muxer);
+    return true;
+}
diff --git a/tests/tests/media/muxer/src/android/media/muxer/cts/MediaMuxerTest.java b/tests/tests/media/muxer/src/android/media/muxer/cts/MediaMuxerTest.java
index a31a80f..322e2fc 100644
--- a/tests/tests/media/muxer/src/android/media/muxer/cts/MediaMuxerTest.java
+++ b/tests/tests/media/muxer/src/android/media/muxer/cts/MediaMuxerTest.java
@@ -35,7 +35,6 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -53,8 +52,9 @@
     private static final float BAD_LONGITUDE = -181.0f;
     private static final float TOLERANCE = 0.0002f;
     private static final long OFFSET_TIME_US = 29 * 60 * 1000000L; // 29 minutes
-    static final String mInpPrefix = WorkDir.getMediaDirString();
-    private boolean mAndroid11 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
+    private static final String MEDIA_DIR = WorkDir.getMediaDirString();
+
+    private final boolean mAndroid11 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
 
     @Override
     public void setContext(Context context) {
@@ -63,116 +63,13 @@
 
     protected AssetFileDescriptor getAssetFileDescriptorFor(final String res)
             throws FileNotFoundException {
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        File inpFile = new File(mInpPrefix + res);
+        Preconditions.assertTestFileExists(MEDIA_DIR + res);
+        File inpFile = new File(MEDIA_DIR + res);
         ParcelFileDescriptor parcelFD =
                 ParcelFileDescriptor.open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY);
         return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
     }
 
-    /**
-     * Test: make sure the muxer handles both video and audio tracks correctly.
-     */
-    public void SKIP_testVideoAudio() throws Exception {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestMultiTrack#testMultiTrack[*]
-        // numTracks @ {1, 1}
-        final String source = "video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz.3gp";
-        String outputFilePath = File.createTempFile("MediaMuxerTest_testAudioVideo", ".mp4")
-                .getAbsolutePath();
-        cloneAndVerify(source, outputFilePath, 2, 90, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-    }
-
-    public void SKIP_testDualVideoTrack() throws Exception {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestMultiTrack#testMultiTrack[*]
-        // numTracks @ {2, 0}
-        final String source = "video_176x144_h264_408kbps_30fps_352x288_h264_122kbps_30fps.mp4";
-        String outputFilePath = File.createTempFile("MediaMuxerTest_testDualVideo", ".mp4")
-                .getAbsolutePath();
-        cloneAndVerify(source, outputFilePath, 2, 90, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-    }
-
-    public void SKIP_testDualAudioTrack() throws Exception {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestMultiTrack#testMultiTrack[*]
-        // numTracks @ {0, 2}
-        if (!MediaUtils.check(mAndroid11, "test needs Android 11")) return;
-
-        final String source = "audio_aac_mono_70kbs_44100hz_aac_mono_70kbs_44100hz.mp4";
-        String outputFilePath = File.createTempFile("MediaMuxerTest_testDualAudio", ".mp4")
-                .getAbsolutePath();
-        cloneAndVerify(source, outputFilePath, 2, 90, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-    }
-
-    public void SKIP_testDualVideoAndAudioTrack() throws Exception {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestMultiTrack#testMultiTrack[*]
-        // numTracks @ {2, 2}
-        if (!MediaUtils.check(mAndroid11, "test needs Android 11")) return;
-
-        final String source = "video_h264_30fps_video_h264_30fps_aac_44100hz_aac_44100hz.mp4";
-        String outputFilePath = File.createTempFile("MediaMuxerTest_testDualVideoAudio", ".mp4")
-                .getAbsolutePath();
-        cloneAndVerify(source, outputFilePath, 4, 90, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-    }
-
-    /**
-     * Test: make sure the muxer handles video, audio and non standard compliant metadata tracks
-     * that generated before API29 correctly. This test will use extractor to extract the video
-     * track, audio and the non standard compliant metadata track from the source file, then
-     * remuxes them into a new file with standard compliant metadata track. Finally, it will check
-     * to make sure the new file's metadata track matches the source file's metadata track for the
-     * mime format and data payload.
-     */
-    public void SKIP_testVideoAudioMedatadataWithNonCompliantMetadataTrack() throws Exception {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[application/gyro]
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[video/h263]
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[audio/aac]
-        final String source =
-                "video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz_metadata_gyro_non_compliant.3gp";
-        String outputFilePath = File.createTempFile("MediaMuxerTest_testAudioVideoMetadata", ".mp4")
-                .getAbsolutePath();
-        cloneAndVerify(source, outputFilePath, 3, 90, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-    }
-
-    /**
-     * Test: make sure the muxer handles video, audio and standard compliant metadata tracks that
-     * generated from API29 correctly. This test will use extractor to extract the video track,
-     * audio and the standard compliant metadata track from the source file, then remuxes them
-     * into a new file with standard compliant metadata track. Finally, it will check to make sure
-     * the new file's metadata track matches the source file's metadata track for the mime format
-     * and data payload.
-     */
-     public void SKIP_testVideoAudioMedatadataWithCompliantMetadataTrack() throws Exception {
-         // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[application/gyro]
-         // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[video/h263]
-         // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[audio/aac]
-         final String source =
-                 "video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz_metadata_gyro_compliant.3gp";
-        String outputFilePath = File.createTempFile("MediaMuxerTest_testAudioVideoMetadata", ".mp4")
-                .getAbsolutePath();
-        cloneAndVerify(source, outputFilePath, 3, 90, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-    }
-
-    /**
-     * Test: make sure the muxer handles audio track only file correctly.
-     */
-    public void SKIP_testAudioOnly() throws Exception {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[audio/*]
-        final String source = "sinesweepm4a.m4a";
-        String outputFilePath = File.createTempFile("MediaMuxerTest_testAudioOnly", ".mp4")
-                .getAbsolutePath();
-        cloneAndVerify(source, outputFilePath, 1, -1, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-    }
-
-    /**
-     * Test: make sure the muxer handles video track only file correctly.
-     */
-        public void SKIP_testVideoOnly() throws Exception {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[video/*]
-        final String source = "video_only_176x144_3gp_h263_25fps.mp4";
-        String outputFilePath = File.createTempFile("MediaMuxerTest_videoOnly", ".mp4")
-                .getAbsolutePath();
-        cloneAndVerify(source, outputFilePath, 1, 180, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-    }
-
     public void testWebmOutput() throws Exception {
         final String source =
                 "video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm";
@@ -181,142 +78,6 @@
         cloneAndVerify(source, outputFilePath, 2, 90, MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
     }
 
-    public void SKIP_testThreegppOutput() throws Exception {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[*]
-        final String source = "video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz.3gp";
-        String outputFilePath = File.createTempFile("testThreegppOutput", ".3gp")
-                .getAbsolutePath();
-        cloneAndVerify(source, outputFilePath, 2, 90, MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP);
-    }
-
-    /**
-     * Tests: make sure the muxer handles exceptions correctly.
-     * <br> Throws exception b/c start() is not called.
-     * <br> Throws exception b/c 2 video tracks were added.
-     * <br> Throws exception b/c 2 audio tracks were added.
-     * <br> Throws exception b/c 3 tracks were added.
-     * <br> Throws exception b/c no tracks was added.
-     * <br> Throws exception b/c a wrong format.
-     */
-    public void SKIP_testIllegalStateExceptions() throws IOException {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestMultiTrack#testMultiTrack[*] and
-        // duplicate of CtsMediaV2TestCases:MuxerUnitTest$TestApi
-        String outputFilePath = File.createTempFile("MediaMuxerTest_testISEs", ".mp4")
-                .getAbsolutePath();
-        MediaMuxer muxer;
-
-        // Throws exception b/c start() is not called.
-        muxer = new MediaMuxer(outputFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-        muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
-
-        try {
-            muxer.stop();
-            fail("should throw IllegalStateException.");
-        } catch (IllegalStateException e) {
-            // expected
-        } finally {
-            muxer.release();
-        }
-
-        // Should not throw exception when 2 video tracks were added.
-        muxer = new MediaMuxer(outputFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-        muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
-
-        try {
-            muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
-        } catch (IllegalStateException e) {
-            fail("should not throw IllegalStateException.");
-        } finally {
-            muxer.release();
-        }
-
-        // Should not throw exception when 2 audio tracks were added.
-        muxer = new MediaMuxer(outputFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-        muxer.addTrack(MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 48000, 1));
-        try {
-            muxer.addTrack(MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 48000, 1));
-        } catch (IllegalStateException e) {
-            fail("should not throw IllegalStateException.");
-        } finally {
-            muxer.release();
-        }
-
-        // Should not throw exception when 3 tracks were added.
-        muxer = new MediaMuxer(outputFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-        muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
-        muxer.addTrack(MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 48000, 1));
-        try {
-            muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
-        } catch (IllegalStateException e) {
-            fail("should not throw IllegalStateException.");
-        } finally {
-            muxer.release();
-        }
-
-        // Throws exception b/c no tracks was added.
-        muxer = new MediaMuxer(outputFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-        try {
-            muxer.start();
-            fail("should throw IllegalStateException.");
-        } catch (IllegalStateException e) {
-            // expected
-        } finally {
-            muxer.release();
-        }
-
-        // Throws exception b/c a wrong format.
-        muxer = new MediaMuxer(outputFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-        try {
-            muxer.addTrack(MediaFormat.createVideoFormat("vidoe/mp4", 480, 320));
-            fail("should throw IllegalStateException.");
-        } catch (IllegalStateException e) {
-            // expected
-        } finally {
-            muxer.release();
-        }
-
-        // Test FileDescriptor Constructor expect sucess.
-        RandomAccessFile file = null;
-        try {
-            file = new RandomAccessFile(outputFilePath, "rws");
-            muxer = new MediaMuxer(file.getFD(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-            muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
-        } finally {
-            file.close();
-            muxer.release();
-        }
-
-        // Test FileDescriptor Constructor expect exception with read only mode.
-        RandomAccessFile file2 = null;
-        try {
-            file2 = new RandomAccessFile(outputFilePath, "r");
-            muxer = new MediaMuxer(file2.getFD(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-            fail("should throw IOException.");
-        } catch (IOException e) {
-            // expected
-        } finally {
-            file2.close();
-            // No need to release the muxer.
-        }
-
-        // Test FileDescriptor Constructor expect NO exception with write only mode.
-        ParcelFileDescriptor out = null;
-        try {
-            out = ParcelFileDescriptor.open(new File(outputFilePath),
-                    ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE);
-            muxer = new MediaMuxer(out.getFileDescriptor(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
-        } catch (IllegalArgumentException e) {
-            fail("should not throw IllegalArgumentException.");
-        } catch (IOException e) {
-            fail("should not throw IOException.");
-        } finally {
-            out.close();
-            muxer.release();
-        }
-
-        new File(outputFilePath).delete();
-    }
-
     /**
      * Test: makes sure if audio and video muxing using MPEG4Writer works well when there are frame
      * drops as in b/63590381 and b/64949961 while B Frames encoding is enabled.
@@ -338,27 +99,6 @@
     }
 
     /**
-     * Test: makes sure if video only muxing using MPEG4Writer works well when there are B Frames.
-     */
-    public void SKIP_testAllTimestampsBVideoOnly() throws Exception {
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[video/avc] and
-        // duplicate of CtsMediaV2TestCases:MuxerTest$TestSimpleMux#testSimpleMux[video/hevc]
-        final String source = "video_480x360_mp4_h264_bframes_495kbps_30fps_editlist.mp4";
-        String outputFilePath = File.createTempFile("MediaMuxerTest_testAllTimestampsBVideoOnly",
-            ".mp4").getAbsolutePath();
-        try {
-            // No samples to drop in this case.
-            // No start offsets for any track.
-            cloneMediaWithSamplesDropAndStartOffsets(source, outputFilePath,
-                MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4, null, null);
-            verifyTSWithSamplesDropAndStartOffset(
-                    source, true /* has B frames */, outputFilePath, null, null);
-        } finally {
-            new File(outputFilePath).delete();
-        }
-    }
-
-    /**
      * Test: makes sure muxing works well when video with B Frames are muxed using MPEG4Writer
      * and a few frames drop.
      */
diff --git a/tests/tests/media/muxer/src/android/media/muxer/cts/NativeMuxerTest.java b/tests/tests/media/muxer/src/android/media/muxer/cts/NativeMuxerTest.java
new file mode 100644
index 0000000..f8e7b88
--- /dev/null
+++ b/tests/tests/media/muxer/src/android/media/muxer/cts/NativeMuxerTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.muxer.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.res.AssetFileDescriptor;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaPlayer;
+import android.media.cts.MediaTestBase;
+import android.media.cts.NonMediaMainlineTest;
+import android.media.cts.Preconditions;
+import android.net.Uri;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresDevice;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.compatibility.common.util.MediaUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Set;
+
+@SmallTest
+@RequiresDevice
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+@RunWith(AndroidJUnit4.class)
+public class NativeMuxerTest extends MediaTestBase {
+    private static final String TAG = "NativeMuxerTest";
+
+    private static final boolean sIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
+
+    private static final String MEDIA_DIR = WorkDir.getMediaDirString();
+
+    static {
+        // Load jni on initialization.
+        Log.i("@@@", "before loadlibrary");
+        System.loadLibrary("ctsmediamuxertest_jni");
+        Log.i("@@@", "after loadlibrary");
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Throwable {
+        super.setUp();
+    }
+
+    @After
+    @Override
+    public void tearDown() {
+        super.tearDown();
+    }
+
+    private static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
+            throws FileNotFoundException {
+        Preconditions.assertTestFileExists(MEDIA_DIR + res);
+        File inpFile = new File(MEDIA_DIR + res);
+        ParcelFileDescriptor parcelFD =
+                ParcelFileDescriptor.open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY);
+        return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
+    }
+
+    // check that native extractor behavior matches java extractor
+    @Presubmit
+    @NonMediaMainlineTest
+    @Test
+    public void testMuxerAvc() throws Exception {
+        // IMPORTANT: this file must not have B-frames
+        testMuxer("video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4", false);
+    }
+
+    @NonMediaMainlineTest
+    @Test
+    public void testMuxerH263() throws Exception {
+        // IMPORTANT: this file must not have B-frames
+        testMuxer("video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz.3gp", false);
+    }
+
+    @NonMediaMainlineTest
+    @Test
+    public void testMuxerHevc() throws Exception {
+        // IMPORTANT: this file must not have B-frames
+        testMuxer("video_640x360_mp4_hevc_450kbps_no_b.mp4", false);
+    }
+
+    @NonMediaMainlineTest
+    @Test
+    public void testMuxerVp8() throws Exception {
+        testMuxer("bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz.webm", true);
+    }
+
+    @NonMediaMainlineTest
+    @Test
+    public void testMuxerVp9() throws Exception {
+        testMuxer("video_1280x720_webm_vp9_csd_309kbps_25fps_vorbis_stereo_128kbps_48000hz.webm",
+                true);
+    }
+
+    @NonMediaMainlineTest
+    @Test
+    public void testMuxerVp9NoCsd() throws Exception {
+        testMuxer("bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm",
+                true);
+    }
+
+    @NonMediaMainlineTest
+    @Test
+    public void testMuxerVp9Hdr() throws Exception {
+        testMuxer("video_256x144_webm_vp9_hdr_83kbps_24fps.webm", true);
+    }
+
+    // We do not support MPEG-2 muxing as of yet
+    @Ignore
+    @Test
+    public void SKIP_testMuxerMpeg2() throws Exception {
+        // IMPORTANT: this file must not have B-frames
+        testMuxer("video_176x144_mp4_mpeg2_105kbps_25fps_aac_stereo_128kbps_44100hz.mp4", false);
+    }
+
+    @NonMediaMainlineTest
+    @Test
+    public void testMuxerMpeg4() throws Exception {
+        // IMPORTANT: this file must not have B-frames
+        testMuxer("video_176x144_mp4_mpeg4_300kbps_25fps_aac_stereo_128kbps_44100hz.mp4", false);
+    }
+
+    private void testMuxer(final String res, boolean webm) throws Exception {
+        Preconditions.assertTestFileExists(MEDIA_DIR + res);
+        if (!MediaUtils.checkCodecsForResource(MEDIA_DIR + res)) {
+            return; // skip
+        }
+
+        AssetFileDescriptor infd = getAssetFileDescriptorFor(res);
+
+        File base = mContext.getExternalFilesDir(null);
+        String tmpFile = base.getPath() + "/tmp.dat";
+        Log.i("@@@", "using tmp file " + tmpFile);
+        new File(tmpFile).delete();
+        ParcelFileDescriptor out = ParcelFileDescriptor.open(new File(tmpFile),
+                ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE);
+
+        assertTrue("muxer failed", testMuxerNative(
+                infd.getParcelFileDescriptor().getFd(), infd.getStartOffset(), infd.getLength(),
+                out.getFd(), webm));
+
+        // compare the original with the remuxed
+        MediaExtractor org = new MediaExtractor();
+        org.setDataSource(infd.getFileDescriptor(),
+                infd.getStartOffset(), infd.getLength());
+
+        MediaExtractor remux = new MediaExtractor();
+        remux.setDataSource(out.getFileDescriptor());
+
+        assertEquals("mismatched numer of tracks", org.getTrackCount(), remux.getTrackCount());
+        // allow duration mismatch for webm files as ffmpeg does not consider the duration of the
+        // last frame while libwebm (and our framework) does.
+        final long maxDurationDiffUs = webm ? 50000 : 0; // 50ms for webm
+        for (int i = 0; i < org.getTrackCount(); i++) {
+            MediaFormat format1 = org.getTrackFormat(i);
+            MediaFormat format2 = remux.getTrackFormat(i);
+            Log.i("@@@", "org: " + format1);
+            Log.i("@@@", "remux: " + format2);
+            assertTrue("different formats", compareFormats(format1, format2, maxDurationDiffUs));
+        }
+
+        org.release();
+        remux.release();
+
+        Preconditions.assertTestFileExists(MEDIA_DIR + res);
+        MediaPlayer player1 =
+                MediaPlayer.create(mContext, Uri.fromFile(new File(MEDIA_DIR + res)));
+        MediaPlayer player2 = MediaPlayer.create(mContext, Uri.parse("file://" + tmpFile));
+        assertEquals("duration is different",
+                player1.getDuration(), player2.getDuration(), maxDurationDiffUs * 0.001);
+        player1.release();
+        player2.release();
+        new File(tmpFile).delete();
+    }
+
+    private String hexString(ByteBuffer buf) {
+        if (buf == null) {
+            return "(null)";
+        }
+        final char[] digits =
+                {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+        StringBuilder hex = new StringBuilder();
+        for (int i = buf.position(); i < buf.limit(); ++i) {
+            byte c = buf.get(i);
+            hex.append(digits[(c >> 4) & 0xf]);
+            hex.append(digits[c & 0xf]);
+        }
+        return hex.toString();
+    }
+
+    /**
+     * returns: null if key is in neither formats, true if they match and false otherwise
+     */
+    private Boolean compareByteBufferInFormats(MediaFormat f1, MediaFormat f2, String key) {
+        ByteBuffer bufF1 = f1.containsKey(key) ? f1.getByteBuffer(key) : null;
+        ByteBuffer bufF2 = f2.containsKey(key) ? f2.getByteBuffer(key) : null;
+        if (bufF1 == null && bufF2 == null) {
+            return null;
+        }
+        if (bufF1 == null || !bufF1.equals(bufF2)) {
+            Log.i("@@@", "org " + key + ": " + hexString(bufF1));
+            Log.i("@@@", "rmx " + key + ": " + hexString(bufF2));
+            return false;
+        }
+        return true;
+    }
+
+    private boolean compareFormats(MediaFormat f1, MediaFormat f2, long maxDurationDiffUs) {
+        final String KEY_DURATION = MediaFormat.KEY_DURATION;
+
+        // allow some difference in durations
+        if (maxDurationDiffUs > 0
+                && f1.containsKey(KEY_DURATION) && f2.containsKey(KEY_DURATION)
+                && Math.abs(f1.getLong(KEY_DURATION)
+                - f2.getLong(KEY_DURATION)) <= maxDurationDiffUs) {
+            f2.setLong(KEY_DURATION, f1.getLong(KEY_DURATION));
+        }
+
+        // verify hdr-static-info
+        if (Boolean.FALSE.equals(compareByteBufferInFormats(f1, f2, "hdr-static-info"))) {
+            return false;
+        }
+
+        // verify CSDs
+        for (int i = 0; ; ++i) {
+            String key = "csd-" + i;
+            Boolean match = compareByteBufferInFormats(f1, f2, key);
+            if (match == null) {
+                break;
+            } else if (!match) {
+                return false;
+            }
+        }
+
+        // before S, mpeg4 writers jammed a fixed SAR value into the output;
+        // this was fixed in S
+        if (!sIsAtLeastS) {
+            if (f1.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT)
+                    && f2.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT)) {
+                f2.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT,
+                        f1.getInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT));
+            }
+            if (f1.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH)
+                    && f2.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH)) {
+                f2.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH,
+                        f1.getInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH));
+            }
+        }
+
+        // look for f2 (the new) being a superset (>=) of f1 (the original)
+        // ensure that all of our fields in f1 appear in f2 with the same
+        // value. We allow f2 to contain extra fields.
+        Set<String> keys = f1.getKeys();
+        for (String key : keys) {
+            if (key == null) {
+                continue;
+            }
+            if (!f2.containsKey(key)) {
+                return false;
+            }
+            int f1Type = f1.getValueTypeForKey(key);
+            if (f1Type != f2.getValueTypeForKey(key)) {
+                return false;
+            }
+            switch (f1Type) {
+                case MediaFormat.TYPE_INTEGER:
+                    int f1Int = f1.getInteger(key);
+                    int f2Int = f2.getInteger(key);
+                    if (f1Int != f2Int) {
+                        return false;
+                    }
+                    break;
+                case MediaFormat.TYPE_LONG:
+                    long f1Long = f1.getLong(key);
+                    long f2Long = f2.getLong(key);
+                    if (f1Long != f2Long) {
+                        return false;
+                    }
+                    break;
+                case MediaFormat.TYPE_FLOAT:
+                    float f1Float = f1.getFloat(key);
+                    float f2Float = f2.getFloat(key);
+                    if (f1Float != f2Float) {
+                        return false;
+                    }
+                    break;
+                case MediaFormat.TYPE_STRING:
+                    String f1String = f1.getString(key);
+                    String f2String = f2.getString(key);
+                    if (!f1String.equals(f2String)) {
+                        return false;
+                    }
+                    break;
+                case MediaFormat.TYPE_BYTE_BUFFER:
+                    ByteBuffer f1ByteBuffer = f1.getByteBuffer(key);
+                    ByteBuffer f2ByteBuffer = f2.getByteBuffer(key);
+                    if (!f1ByteBuffer.equals(f2ByteBuffer)) {
+                        return false;
+                    }
+                    break;
+                default:
+                    return false;
+            }
+        }
+
+        // repeat for getFeatures
+        // (which we don't use in this test, but include for completeness)
+        Set<String> features = f1.getFeatures();
+        for (String key : features) {
+            if (key == null) {
+                continue;
+            }
+            if (!f2.containsKey(key)) {
+                return false;
+            }
+            int f1Type = f1.getValueTypeForKey(key);
+            if (f1Type != f2.getValueTypeForKey(key)) {
+                return false;
+            }
+            switch (f1Type) {
+                case MediaFormat.TYPE_INTEGER:
+                    int f1Int = f1.getInteger(key);
+                    int f2Int = f2.getInteger(key);
+                    if (f1Int != f2Int) {
+                        return false;
+                    }
+                    break;
+                case MediaFormat.TYPE_LONG:
+                    long f1Long = f1.getLong(key);
+                    long f2Long = f2.getLong(key);
+                    if (f1Long != f2Long) {
+                        return false;
+                    }
+                    break;
+                case MediaFormat.TYPE_FLOAT:
+                    float f1Float = f1.getFloat(key);
+                    float f2Float = f2.getFloat(key);
+                    if (f1Float != f2Float) {
+                        return false;
+                    }
+                    break;
+                case MediaFormat.TYPE_STRING:
+                    String f1String = f1.getString(key);
+                    String f2String = f2.getString(key);
+                    if (!f1String.equals(f2String)) {
+                        return false;
+                    }
+                    break;
+                case MediaFormat.TYPE_BYTE_BUFFER:
+                    ByteBuffer f1ByteBuffer = f1.getByteBuffer(key);
+                    ByteBuffer f2ByteBuffer = f2.getByteBuffer(key);
+                    if (!f1ByteBuffer.equals(f2ByteBuffer)) {
+                        return false;
+                    }
+                    break;
+                default:
+                    return false;
+            }
+        }
+
+        // not otherwise disqualified
+        return true;
+    }
+
+    private static native boolean testMuxerNative(int in, long inoffset, long insize,
+                                                  int out, boolean webm);
+}
diff --git a/tests/tests/media/muxer/src/android/media/muxer/cts/WorkDir.java b/tests/tests/media/muxer/src/android/media/muxer/cts/WorkDir.java
index a770675..9f086f3 100644
--- a/tests/tests/media/muxer/src/android/media/muxer/cts/WorkDir.java
+++ b/tests/tests/media/muxer/src/android/media/muxer/cts/WorkDir.java
@@ -20,6 +20,6 @@
 
 class WorkDir extends WorkDirBase {
     public static final String getMediaDirString() {
-        return getMediaDirString("CtsMediaTestCases-1.4");
+        return getMediaDirString("CtsMediaMuxerTestCases-1.0");
     }
 }
diff --git a/tests/tests/media/player/Android.bp b/tests/tests/media/player/Android.bp
index 464ddef..ecda2ff 100644
--- a/tests/tests/media/player/Android.bp
+++ b/tests/tests/media/player/Android.bp
@@ -14,13 +14,10 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-CC-BY
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license", // CC-BY
+    ],
 }
 
 android_test {
@@ -33,9 +30,6 @@
         "ctstestserver",
         "cts-media-common",
     ],
-    jni_libs: [
-        "libctsmediadrm_jni",
-    ],
     resource_dirs: ["res"],
     // do not compress media files
     aaptflags: [
diff --git a/tests/tests/media/player/AndroidTest.xml b/tests/tests/media/player/AndroidTest.xml
index 81aaf92..7b92b12 100644
--- a/tests/tests/media/player/AndroidTest.xml
+++ b/tests/tests/media/player/AndroidTest.xml
@@ -35,7 +35,7 @@
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="push-all" value="true" />
-        <option name="media-folder-name" value="CtsMediaTestCases-1.4" />
+        <option name="media-folder-name" value="CtsMediaPlayerTestCases-1.0" />
         <option name="dynamic-config-module" value="CtsMediaPlayerTestCases" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/media/player/DynamicConfig.xml b/tests/tests/media/player/DynamicConfig.xml
index 8ee89ac..3f0c534 100644
--- a/tests/tests/media/player/DynamicConfig.xml
+++ b/tests/tests/media/player/DynamicConfig.xml
@@ -33,6 +33,6 @@
         <value>http://redirector.gvt1.com/videoplayback?id=c80658495af60617&amp;itag=17&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=70E979A621001201BC18622BDBF914FA870BDA40.6E78890B80F4A33A18835F775B1FF64F0A4D0003&amp;key=ik0&amp;user=android-device-test</value>
     </entry>
     <entry key="media_files_url">
-    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/CtsMediaTestCases-1.4.zip</value>
+    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/player/CtsMediaPlayerTestCases-1.0.zip</value>
     </entry>
 </dynamicConfig>
diff --git a/tests/tests/media/player/jni/Android.bp b/tests/tests/media/player/jni/Android.bp
deleted file mode 100644
index 33bc17b..0000000
--- a/tests/tests/media/player/jni/Android.bp
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2012 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
-}
-
-//------------------------------------------------------------------------------
-// Builds libctsmediadrm_jni.so
-//
-cc_test_library {
-    name: "libctsmediadrm_jni",
-    srcs: [
-        "CtsMediaDrmJniOnLoad.cpp",
-        "native-mediadrm-jni.cpp",
-    ],
-    include_dirs: ["system/core/include"],
-    shared_libs: [
-        "libandroid",
-        "libnativehelper_compat_libc++",
-        "liblog",
-        "libmediandk",
-    ],
-    header_libs: ["liblog_headers"],
-    cflags: [
-        "-Werror",
-        "-Wall",
-        "-DEGL_EGLEXT_PROTOTYPES",
-    ],
-    stl: "libc++_static",
-    gtest: false,
-    // this test suite will run on sdk 29 as part of MTS, make sure it's compatible
-    // (revisit if/when we add features to this library that require newer sdk.
-    sdk_version: "29",
-}
diff --git a/tests/tests/media/player/jni/CtsMediaDrmJniOnLoad.cpp b/tests/tests/media/player/jni/CtsMediaDrmJniOnLoad.cpp
deleted file mode 100644
index 709936c..0000000
--- a/tests/tests/media/player/jni/CtsMediaDrmJniOnLoad.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <jni.h>
-#include <stdio.h>
-
-extern int register_android_media_player_cts_NativeMediaDrmClearkeyTest(JNIEnv*);
-
-jint JNI_OnLoad(JavaVM *vm, void */*reserved*/) {
-    JNIEnv *env = NULL;
-
-    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
-        return JNI_ERR;
-    }
-
-    if (register_android_media_player_cts_NativeMediaDrmClearkeyTest(env)) {
-        return JNI_ERR;
-    }
-
-    return JNI_VERSION_1_4;
-}
diff --git a/tests/tests/media/player/jni/native-mediadrm-jni.cpp b/tests/tests/media/player/jni/native-mediadrm-jni.cpp
deleted file mode 100644
index bd1d6af..0000000
--- a/tests/tests/media/player/jni/native-mediadrm-jni.cpp
+++ /dev/null
@@ -1,1038 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TAG "NativeMediaDrm"
-
-#include <utils/Log.h>
-#include <sys/types.h>
-
-#include <list>
-#include <string>
-#include <vector>
-
-#include <assert.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <sys/stat.h>
-
-#include <android/native_window_jni.h>
-
-#include "AMediaObjects.h"
-
-#include "media/NdkMediaCodec.h"
-#include "media/NdkMediaCrypto.h"
-#include "media/NdkMediaDrm.h"
-#include "media/NdkMediaExtractor.h"
-#include "media/NdkMediaFormat.h"
-#include "media/NdkMediaMuxer.h"
-
-typedef std::vector<uint8_t> Uuid;
-
-struct fields_t {
-    jfieldID surface;
-    jfieldID mimeType;
-    jfieldID audioUrl;
-    jfieldID videoUrl;
-};
-
-struct PlaybackParams {
-    jobject surface;
-    jstring mimeType;
-    jstring audioUrl;
-    jstring videoUrl;
-};
-
-static fields_t gFieldIds;
-static bool gGotVendorDefinedEvent = false;
-static bool gListenerGotValidExpiryTime = false;
-static bool gOnKeyChangeListenerOK = false;
-
-static const char kFileScheme[] = "file://";
-static constexpr size_t kFileSchemeStrLen = sizeof(kFileScheme) - 1;
-static constexpr size_t kPlayTimeSeconds = 30;
-static constexpr size_t kUuidSize = 16;
-
-static const uint8_t kClearKeyUuid[kUuidSize] = {
-    0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
-    0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b
-};
-
-// The test content is not packaged with clearkey UUID,
-// we have to use a canned clearkey pssh for the test.
-static const uint8_t kClearkeyPssh[] = {
-    // BMFF box header (4 bytes size + 'pssh')
-    0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
-    // full box header (version = 1 flags = 0)
-    0x01, 0x00, 0x00, 0x00,
-    // system id
-    0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
-    0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
-    // number of key ids
-    0x00, 0x00, 0x00, 0x01,
-    // key id
-    0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
-    0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
-    // size of data, must be zero
-    0x00, 0x00, 0x00, 0x00
-};
-
-static const uint8_t kKeyRequestData[] = {
-    0x7b, 0x22, 0x6b, 0x69, 0x64,
-    0x73, 0x22, 0x3a, 0x5b, 0x22,
-    0x4d, 0x44, 0x41, 0x77, 0x4d,
-    0x44, 0x41, 0x77, 0x4d, 0x44,
-    0x41, 0x77, 0x4d, 0x44, 0x41,
-    0x77, 0x4d, 0x44, 0x41, 0x77,
-    0x4d, 0x41, 0x22, 0x5d, 0x2c,
-    0x22, 0x74, 0x79, 0x70, 0x65,
-    0x22, 0x3a, 0x22, 0x74, 0x65,
-    0x6d, 0x70, 0x6f, 0x72, 0x61,
-    0x72, 0x79, 0x22, 0x7d
-};
-
-static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
-
-// base 64 encoded JSON response string, must not contain padding character '='
-static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
-        "\"kid\":\"MDAwMDAwMDAwMDAwMDAwMA\",\"k\":" \
-        "\"Pwoz80CYueIrwHjgobXoVA\"}]}";
-
-static bool isUuidSizeValid(Uuid uuid) {
-    return (uuid.size() == kUuidSize);
-}
-
-static std::vector<uint8_t> jbyteArrayToVector(
-    JNIEnv* env, jbyteArray const &byteArray) {
-    uint8_t* buffer = reinterpret_cast<uint8_t*>(
-        env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
-    std::vector<uint8_t> vector;
-    for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
-        vector.push_back(buffer[i]);
-    }
-    return vector;
-}
-
-static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
-    Uuid juuid;
-    juuid.resize(0);
-    if (uuid != NULL) {
-        juuid = jbyteArrayToVector(env, uuid);
-    }
-    return juuid;
-}
-
-static media_status_t setDataSourceFdFromUrl(
-    AMediaExtractor* extractor, const char* url) {
-
-    const char *path = url + kFileSchemeStrLen;
-    FILE* fp = fopen(path, "r");
-    struct stat buf {};
-    media_status_t status = AMEDIA_ERROR_BASE;
-    if (fp && !fstat(fileno(fp), &buf)) {
-      status = AMediaExtractor_setDataSourceFd(
-          extractor,
-          fileno(fp), 0, buf.st_size);
-    } else {
-        status = AMEDIA_ERROR_IO;
-        ALOGE("Failed to convert URL to Fd");
-    }
-    if (fp && fclose(fp) == EOF) {
-        // 0 indicate success, EOF for error
-        ALOGE("Failed to close file pointer");
-    }
-    return status;
-}
-
-static media_status_t setDataSourceFdOrUrl(
-    AMediaExtractor* extractor, const char* url) {
-
-    media_status_t status = AMEDIA_ERROR_BASE;
-    if (strlen(url) > kFileSchemeStrLen && strncmp(url, kFileScheme, kFileSchemeStrLen) == 0) {
-        status = setDataSourceFdFromUrl(extractor, url);
-    } else {
-       status = AMediaExtractor_setDataSource(extractor, url);
-    }
-    return status;
-}
-
-extern "C" jboolean
-Java_android_media_player_cts_NativeMediaDrmClearkeyTest_isCryptoSchemeSupportedNative(
-    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
-
-    if (NULL == uuid) {
-        jniThrowException(env, "java/lang/NullPointerException", "null uuid");
-        return JNI_FALSE;
-    }
-
-    Uuid juuid = jbyteArrayToUuid(env, uuid);
-    if (isUuidSizeValid(juuid)) {
-         return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
-    } else {
-          jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                  "invalid UUID size, expected %u bytes", kUuidSize);
-    }
-    return JNI_FALSE;
-}
-
-void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams &params) {
-    params.surface = env->GetObjectField(
-        playbackParams, gFieldIds.surface);
-
-    params.mimeType = static_cast<jstring>(env->GetObjectField(
-        playbackParams, gFieldIds.mimeType));
-
-    params.audioUrl = static_cast<jstring>(env->GetObjectField(
-        playbackParams, gFieldIds.audioUrl));
-
-    params.videoUrl = static_cast<jstring>(env->GetObjectField(
-        playbackParams, gFieldIds.videoUrl));
-}
-
-extern "C" jboolean
-Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testGetPropertyStringNative(
-    JNIEnv* env, jclass clazz, jbyteArray uuid,
-    jstring name, jobject outValue) {
-
-    if (NULL == uuid || NULL == name || NULL == outValue) {
-        jniThrowException(env, "java/lang/NullPointerException",
-                "One or more null input parameters");
-        return JNI_FALSE;
-    }
-
-    Uuid juuid = jbyteArrayToUuid(env, uuid);
-    if (!isUuidSizeValid(juuid)) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                "invalid UUID size, expected %u bytes", kUuidSize);
-        return JNI_FALSE;
-    }
-
-    AMediaObjects aMediaObjects;
-    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
-    if (NULL == aMediaObjects.getDrm()) {
-        jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
-        return JNI_FALSE;
-    }
-
-    const char *utf8_name = env->GetStringUTFChars(name, NULL);
-    const char *utf8_outValue = NULL;
-    media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
-            utf8_name, &utf8_outValue);
-    env->ReleaseStringUTFChars(name, utf8_name);
-
-    if (NULL != utf8_outValue) {
-        clazz = env->GetObjectClass(outValue);
-        jmethodID mId = env->GetMethodID (clazz, "append",
-                "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
-        jstring outString = env->NewStringUTF(
-                static_cast<const char *>(utf8_outValue));
-        env->CallObjectMethod(outValue, mId, outString);
-    } else {
-        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                "get property string returns %d", status);
-        return JNI_FALSE;
-    }
-    return JNI_TRUE;
-}
-
-extern "C" jboolean
-Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testPropertyByteArrayNative(
-        JNIEnv* env, jclass /* clazz */, jbyteArray uuid) {
-
-    if (NULL == uuid) {
-        jniThrowException(env, "java/lang/NullPointerException",
-                "uuid is NULL");
-        return JNI_FALSE;
-    }
-
-    Uuid juuid = jbyteArrayToUuid(env, uuid);
-    if (!isUuidSizeValid(juuid)) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                "invalid UUID size, expected %u bytes", kUuidSize);
-        return JNI_FALSE;
-    }
-
-    AMediaDrm* drm = AMediaDrm_createByUUID(&juuid[0]);
-    const char *propertyName = "clientId";
-    const uint8_t value[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
-    media_status_t status = AMediaDrm_setPropertyByteArray(drm, propertyName, value, sizeof(value));
-    if (status != AMEDIA_OK) {
-        jniThrowException(env, "java/lang/RuntimeException", "setPropertyByteArray failed");
-        AMediaDrm_release(drm);
-        return JNI_FALSE;
-    }
-    AMediaDrmByteArray array;
-    status = AMediaDrm_getPropertyByteArray(drm, propertyName, &array);
-    if (status != AMEDIA_OK) {
-        jniThrowException(env, "java/lang/RuntimeException", "getPropertyByteArray failed");
-        AMediaDrm_release(drm);
-        return JNI_FALSE;
-    }
-    if (array.length != sizeof(value)) {
-        jniThrowException(env, "java/lang/RuntimeException", "byte array size differs");
-        AMediaDrm_release(drm);
-        return JNI_FALSE;
-    }
-    if (!array.ptr) {
-        jniThrowException(env, "java/lang/RuntimeException", "byte array pointer is null");
-        AMediaDrm_release(drm);
-        return JNI_FALSE;
-    }
-    if (memcmp(array.ptr, value, sizeof(value)) != 0) {
-        jniThrowException(env, "java/lang/RuntimeException", "byte array content differs");
-        AMediaDrm_release(drm);
-        return JNI_FALSE;
-    }
-    AMediaDrm_release(drm);
-    return JNI_TRUE;
-}
-
-extern "C" jboolean Java_android_media_player_cts_NativeMediaDrmClearkeyTest__testPsshNative(
-    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
-
-    if (NULL == uuid || NULL == videoUrl) {
-        jniThrowException(env, "java/lang/NullPointerException",
-                "null uuid or null videoUrl");
-        return JNI_FALSE;
-    }
-
-    Uuid juuid = jbyteArrayToUuid(env, uuid);
-    if (!isUuidSizeValid(juuid)) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                "invalid UUID size, expected %u bytes", kUuidSize);
-        return JNI_FALSE;
-    }
-
-    AMediaObjects aMediaObjects;
-    aMediaObjects.setVideoExtractor(AMediaExtractor_new());
-    const char* url = env->GetStringUTFChars(videoUrl, 0);
-    if (url) {
-        media_status_t status = setDataSourceFdOrUrl(
-            aMediaObjects.getVideoExtractor(), url);
-        env->ReleaseStringUTFChars(videoUrl, url);
-
-        if (status != AMEDIA_OK) {
-            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                    "set video data source error=%d", status);
-            return JNI_FALSE;
-        }
-    }
-
-    PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
-    if (psshInfo == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
-        return JNI_FALSE;
-    }
-
-    jboolean testResult = JNI_FALSE;
-    for (size_t i = 0; i < psshInfo->numentries; i++) {
-        PsshEntry *entry = &psshInfo->entries[i];
-
-        if (0 == memcmp(entry->uuid, kClearKeyUuid, sizeof(entry->uuid))) {
-            aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
-            if (aMediaObjects.getDrm()) {
-                testResult = JNI_TRUE;
-            } else {
-                ALOGE("Failed to create media drm=%zd", i);
-                testResult = JNI_FALSE;
-            }
-            break;
-        }
-    }
-    return testResult;
-}
-
-static bool isVideo(const char* mime) {
-    return !strncmp(mime, "video/", 6) ? true : false;
-}
-
-static bool isAudio(const char* mime) {
-    return !strncmp(mime, "audio/", 6) ? true : false;
-}
-
-static void addTrack(const AMediaFormat* format,
-        const char* mime, const AMediaCrypto* crypto,
-        const ANativeWindow* window, AMediaCodec** codec) {
-
-    *codec = AMediaCodec_createDecoderByType(mime);
-    if (codec == NULL) {
-        ALOGE("cannot create codec for %s", mime);
-        return;
-    }
-
-    AMediaCodec_configure(*codec, format,
-            const_cast<ANativeWindow*>(window),
-            const_cast<AMediaCrypto*>(crypto), 0);
-}
-
-static void addTracks(const AMediaExtractor* extractor,
-        const AMediaCrypto* crypto, const ANativeWindow* window,
-        AMediaCodec** codec) {
-    size_t numTracks = AMediaExtractor_getTrackCount(
-        const_cast<AMediaExtractor*>(extractor));
-
-    AMediaFormat* trackFormat = NULL;
-    for (size_t i = 0; i < numTracks; ++i) {
-        trackFormat = AMediaExtractor_getTrackFormat(
-            const_cast<AMediaExtractor*>(extractor), i);
-        if (trackFormat) {
-            ALOGV("track %zd format: %s", i,
-                    AMediaFormat_toString(trackFormat));
-
-            const char* mime = "";
-            if (!AMediaFormat_getString(
-                trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
-                ALOGE("no mime type");
-
-                AMediaFormat_delete(trackFormat);
-                return;
-            } else if (isAudio(mime) || isVideo(mime)) {
-                AMediaExtractor_selectTrack(
-                    const_cast<AMediaExtractor*>(extractor), i);
-                ALOGV("track %zd codec format: %s", i,
-                        AMediaFormat_toString(trackFormat));
-
-                addTrack(trackFormat, mime, crypto, window, codec);
-                AMediaCodec_start(*codec);
-                AMediaCodec_flush(*codec);
-                AMediaExtractor_seekTo(
-                    const_cast<AMediaExtractor*>(extractor), 0,
-                            AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
-            }
-            AMediaFormat_delete(trackFormat);
-        }
-    }
-}
-
-static int64_t getSystemNanoTime() {
-    timespec now;
-    clock_gettime(CLOCK_MONOTONIC, &now);
-    return now.tv_sec * 1000000000LL + now.tv_nsec;
-}
-
-static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
-        int64_t* presentationTimeUs, bool* eosReached) {
-    media_status_t status = AMEDIA_OK;
-
-    ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
-    if (bufferIndex >= 0) {
-        size_t bufsize;
-        uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
-
-        int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
-        if (sampleSize < 0) {
-            sampleSize = 0;
-            *eosReached = true;
-        }
-
-        *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
-
-        AMediaCodecCryptoInfo *cryptoInfo =
-            AMediaExtractor_getSampleCryptoInfo(extractor);
-
-        if (cryptoInfo) {
-            status = AMediaCodec_queueSecureInputBuffer(
-                codec, bufferIndex, 0, cryptoInfo,
-                *presentationTimeUs,
-                *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
-            AMediaCodecCryptoInfo_delete(cryptoInfo);
-        } else {
-            status = AMediaCodec_queueInputBuffer(
-                codec, bufferIndex, 0, sampleSize,
-                *presentationTimeUs,
-                *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
-        }
-        AMediaExtractor_advance(extractor);
-    }
-}
-
-static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
-    int64_t* startTimeNano) {
-
-    AMediaCodecBufferInfo info;
-    ssize_t bufferIndex  = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
-    if (bufferIndex >= 0) {
-        if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
-            return true;  // eos reached
-        }
-
-        if (*startTimeNano < 0) {
-            *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
-        }
-        int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
-                getSystemNanoTime();
-        if (delay > 0) {
-            usleep(delay / 1000);
-        }
-
-        AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
-    } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
-        ALOGV("output buffers changed");
-    } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
-        AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
-        ALOGV("format changed to: %s", AMediaFormat_toString(format));
-        AMediaFormat_delete(format);
-    } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
-         ALOGV("no output buffer right now");
-         usleep(20000);
-    } else {
-         ALOGV("unexpected info code: %zd", bufferIndex);
-    }
-    return false;
-}
-
-static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
-        PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
-
-    ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
-    AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
-    AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
-
-    AMediaCodec* audioCodec = NULL;
-    AMediaCodec* videoCodec = NULL;
-    AMediaCrypto* crypto = NULL;
-
-    crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
-    if (crypto == NULL) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "failed to create crypto object");
-        return JNI_FALSE;
-    }
-
-    addTracks(audioExtractor, NULL, NULL, &audioCodec);
-
-    addTracks(videoExtractor, crypto, window, &videoCodec);
-
-    bool sawAudioInputEos = false;
-    bool sawAudioOutputEos = false;
-    bool sawVideoInputEos = false;
-    bool sawVideoOutputEos = false;
-    int64_t videoPresentationTimeUs = 0;
-    int64_t videoStartTimeNano = -1;
-    struct timespec timeSpec;
-    clock_gettime(CLOCK_MONOTONIC, &timeSpec);
-    time_t startTimeSec = timeSpec.tv_sec;
-
-    while (!sawAudioOutputEos && !sawVideoOutputEos) {
-        if (!sawVideoInputEos) {
-            fillDecoder(videoCodec, videoExtractor,
-                    &videoPresentationTimeUs, &sawVideoInputEos);
-        }
-
-        if (!sawAudioInputEos) {
-            // skip audio, still need to advance the audio extractor
-            AMediaExtractor_advance(audioExtractor);
-        }
-
-        if (!sawVideoOutputEos) {
-            sawVideoOutputEos = drainDecoder(videoCodec, videoPresentationTimeUs,
-                    &videoStartTimeNano);
-        }
-
-        clock_gettime(CLOCK_MONOTONIC, &timeSpec);
-        if (timeSpec.tv_sec >= static_cast<time_t>(
-            (startTimeSec + kPlayTimeSeconds))) {
-            // stop reading samples and drain the output buffers
-            sawAudioInputEos = sawVideoInputEos = true;
-            sawAudioOutputEos = true; // ignore audio
-        }
-    }
-
-    if (audioCodec) {
-        AMediaCodec_stop(audioCodec);
-        AMediaCodec_delete(audioCodec);
-    }
-    if (videoCodec) {
-        AMediaCodec_stop(videoCodec);
-        AMediaCodec_delete(videoCodec);
-    }
-
-    AMediaCrypto_delete(crypto);
-    ANativeWindow_release(window);
-    return JNI_TRUE;
-}
-
-static void listener(
-    AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
-    AMediaDrmEventType eventType,
-    int /*extra*/, const uint8_t* /*data*/, size_t /*dataSize*/) {
-
-    switch (eventType) {
-        case EVENT_PROVISION_REQUIRED:
-            ALOGD("EVENT_PROVISION_REQUIRED received");
-            break;
-        case EVENT_KEY_REQUIRED:
-            ALOGD("EVENT_KEY_REQUIRED received");
-            break;
-        case EVENT_KEY_EXPIRED:
-            ALOGD("EVENT_KEY_EXPIRED received");
-            break;
-        case EVENT_VENDOR_DEFINED:
-            gGotVendorDefinedEvent = true;
-            ALOGD("EVENT_VENDOR_DEFINED received");
-            break;
-        case EVENT_SESSION_RECLAIMED:
-            ALOGD("EVENT_SESSION_RECLAIMED received");
-            break;
-        default:
-            ALOGD("Unknown event received");
-            break;
-    }
-}
-
-static void onExpirationUpdateListener(
-    AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
-    int64_t expiryTimeInMS) {
-
-    if (expiryTimeInMS == 100) {
-        ALOGD("Updates new expiration time to %" PRId64 " ms", expiryTimeInMS);
-        gListenerGotValidExpiryTime = true;
-    } else {
-        ALOGE("Expects 100 ms for expiry time, received: %" PRId64 " ms", expiryTimeInMS);
-        gListenerGotValidExpiryTime = false;
-    }
-}
-
-static void onKeysChangeListener(
-    AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
-    const AMediaDrmKeyStatus* keysStatus, size_t numKeys, bool hasNewUsableKey) {
-
-    gOnKeyChangeListenerOK = false;
-    if (numKeys != 3) {
-        ALOGE("Expects 3 keys, received %zd keys", numKeys);
-        return;
-    }
-
-    if (!hasNewUsableKey) {
-        ALOGE("Expects hasNewUsableKey to be true");
-        return;
-    }
-
-    ALOGD("Number of keys changed=%zd", numKeys);
-    AMediaDrmKeyStatus keyStatus;
-    for (size_t i = 0; i < numKeys; ++i) {
-        keyStatus.keyId.ptr = keysStatus[i].keyId.ptr;
-        keyStatus.keyId.length = keysStatus[i].keyId.length;
-        keyStatus.keyType = keysStatus[i].keyType;
-
-        ALOGD("key[%zd]: key: %0x, %0x, %0x", i, keyStatus.keyId.ptr[0], keyStatus.keyId.ptr[1],
-                keyStatus.keyId.ptr[2]);
-        ALOGD("key[%zd]: key type=%d", i, keyStatus.keyType);
-    }
-    gOnKeyChangeListenerOK = true;
-}
-
-static void acquireLicense(
-    JNIEnv* env, const AMediaObjects& aMediaObjects, const AMediaDrmSessionId& sessionId,
-    AMediaDrmKeyType keyType) {
-    // Pointer to keyRequest memory, which remains until the next
-    // AMediaDrm_getKeyRequest call or until the drm object is released.
-    const uint8_t* keyRequest;
-    size_t keyRequestSize = 0;
-    std::string errorMessage;
-
-    // The server recognizes "video/mp4" but not "video/avc".
-    media_status_t status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(),
-            &sessionId, kClearkeyPssh, sizeof(kClearkeyPssh),
-            "video/mp4" /*mimeType*/, keyType,
-            NULL, 0, &keyRequest, &keyRequestSize);
-    if (status != AMEDIA_OK) {
-        errorMessage.assign("getKeyRequest failed, error = %d");
-        goto errorOut;
-    }
-
-    if (kKeyRequestSize != keyRequestSize) {
-        ALOGE("Invalid keyRequestSize %zd", kKeyRequestSize);
-        errorMessage.assign("Invalid key request size, error = %d");
-        status = AMEDIA_DRM_NEED_KEY;
-        goto errorOut;
-    }
-
-    if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
-        errorMessage.assign("Invalid key request data is returned, error = %d");
-        status = AMEDIA_DRM_NEED_KEY;
-        goto errorOut;
-    }
-
-    AMediaDrmKeySetId keySetId;
-    gGotVendorDefinedEvent = false;
-    gListenerGotValidExpiryTime = false;
-    gOnKeyChangeListenerOK = false;
-    status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
-            reinterpret_cast<const uint8_t*>(kResponse),
-            sizeof(kResponse), &keySetId);
-    if (status == AMEDIA_OK) {
-        return;  // success
-    }
-
-    errorMessage.assign("provideKeyResponse failed, error = %d");
-
-errorOut:
-    AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-    jniThrowExceptionFmt(env, "java/lang/RuntimeException", errorMessage.c_str(), status);
-}
-
-extern "C" jboolean
-Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testClearKeyPlaybackNative(
-    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
-    if (NULL == uuid || NULL == playbackParams) {
-        jniThrowException(env, "java/lang/NullPointerException",
-                "null uuid or null playback parameters");
-        return JNI_FALSE;
-    }
-
-    Uuid juuid = jbyteArrayToUuid(env, uuid);
-    if (!isUuidSizeValid(juuid)) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                "invalid UUID size, expected %u bytes", kUuidSize);
-        return JNI_FALSE;
-    }
-
-    PlaybackParams params;
-    initPlaybackParams(env, playbackParams, params);
-
-    AMediaObjects aMediaObjects;
-    media_status_t status = AMEDIA_OK;
-    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
-    if (NULL == aMediaObjects.getDrm()) {
-        jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
-        return JNI_FALSE;
-    }
-
-    status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
-    if (status != AMEDIA_OK) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "setOnEventListener failed");
-        return JNI_FALSE;
-    }
-
-    status = AMediaDrm_setOnExpirationUpdateListener(aMediaObjects.getDrm(),
-            onExpirationUpdateListener);
-    if (status != AMEDIA_OK) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "setOnExpirationUpdateListener failed");
-        return JNI_FALSE;
-    }
-
-    status = AMediaDrm_setOnKeysChangeListener(aMediaObjects.getDrm(),
-            onKeysChangeListener);
-    if (status != AMEDIA_OK) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "setOnKeysChangeListener failed");
-        return JNI_FALSE;
-    }
-
-    aMediaObjects.setAudioExtractor(AMediaExtractor_new());
-    const char* url = env->GetStringUTFChars(params.audioUrl, 0);
-    if (url) {
-        status = setDataSourceFdOrUrl(
-            aMediaObjects.getAudioExtractor(), url);
-        env->ReleaseStringUTFChars(params.audioUrl, url);
-
-        if (status != AMEDIA_OK) {
-            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                    "set audio data source error=%d", status);
-            return JNI_FALSE;
-        }
-    }
-
-    aMediaObjects.setVideoExtractor(AMediaExtractor_new());
-    url = env->GetStringUTFChars(params.videoUrl, 0);
-    if (url) {
-        status = setDataSourceFdOrUrl(
-            aMediaObjects.getVideoExtractor(), url);
-        env->ReleaseStringUTFChars(params.videoUrl, url);
-
-        if (status != AMEDIA_OK) {
-            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                    "set video data source error=%d", status);
-            return JNI_FALSE;
-        }
-    }
-
-    AMediaDrmSessionId sessionId;
-    status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
-    if (status != AMEDIA_OK) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "openSession failed");
-        return JNI_FALSE;
-    }
-
-    acquireLicense(env, aMediaObjects, sessionId, KEY_TYPE_STREAMING);
-
-    // Checks if the event listener has received the expected event sent by
-    // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
-    const char *utf8_outValue = NULL;
-    status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
-            "listenerTestSupport", &utf8_outValue);
-    if (status == AMEDIA_OK && NULL != utf8_outValue) {
-        std::string eventType(utf8_outValue);
-        if (eventType.compare("true") == 0) {
-            int count = 0;
-            while ((!gGotVendorDefinedEvent ||
-                    !gListenerGotValidExpiryTime ||
-                    !gOnKeyChangeListenerOK) && count++ < 5) {
-               // Prevents race condition when the event arrives late
-               usleep(10000);
-            }
-
-            if (!gGotVendorDefinedEvent) {
-                ALOGE("Event listener did not receive the expected event.");
-                jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                        "Event listener did not receive the expected event.");
-                AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-                return JNI_FALSE;
-           }
-
-          // Checks if onExpirationUpdateListener received the correct expiry time.
-           if (!gListenerGotValidExpiryTime) {
-               jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                       "onExpirationUpdateListener received incorrect expiry time.");
-               AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-               return JNI_FALSE;
-           }
-
-          // Checks if onKeysChangeListener succeeded.
-          if (!gOnKeyChangeListenerOK) {
-              jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                      "onKeysChangeListener failed");
-              AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-              return JNI_FALSE;
-          }
-        }
-    }
-
-    playContent(env, aMediaObjects, params, sessionId, juuid);
-
-    status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-    if (status != AMEDIA_OK) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "closeSession failed");
-        return JNI_FALSE;
-    }
-    return JNI_TRUE;
-}
-
-extern "C" jboolean
-Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testQueryKeyStatusNative(
-    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
-
-    if (NULL == uuid) {
-        jniThrowException(env, "java/lang/NullPointerException", "null uuid");
-        return JNI_FALSE;
-    }
-
-    Uuid juuid = jbyteArrayToUuid(env, uuid);
-    if (!isUuidSizeValid(juuid)) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                "invalid UUID size, expected %u bytes", kUuidSize);
-        return JNI_FALSE;
-    }
-
-    AMediaObjects aMediaObjects;
-    media_status_t status = AMEDIA_OK;
-    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
-    if (NULL == aMediaObjects.getDrm()) {
-        jniThrowException(env, "java/lang/RuntimeException", "failed to create drm");
-        return JNI_FALSE;
-    }
-
-    AMediaDrmSessionId sessionId;
-    status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
-    if (status != AMEDIA_OK) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "openSession failed");
-        return JNI_FALSE;
-    }
-
-    size_t numPairs = 3;
-    AMediaDrmKeyValue keyStatus[numPairs];
-
-    // Test default key status, should return zero key status
-    status = AMediaDrm_queryKeyStatus(aMediaObjects.getDrm(), &sessionId, keyStatus, &numPairs);
-    if (status != AMEDIA_OK) {
-        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                "AMediaDrm_queryKeyStatus failed, error = %d", status);
-        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-        return JNI_FALSE;
-    }
-
-    if (numPairs != 0) {
-        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                "AMediaDrm_queryKeyStatus failed, no policy should be defined");
-        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-        return JNI_FALSE;
-    }
-
-    acquireLicense(env, aMediaObjects, sessionId, KEY_TYPE_STREAMING);
-
-    // Test short buffer
-    numPairs = 2;
-    status = AMediaDrm_queryKeyStatus(aMediaObjects.getDrm(), &sessionId, keyStatus, &numPairs);
-    if (status != AMEDIA_DRM_SHORT_BUFFER) {
-        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                "AMediaDrm_queryKeyStatus should return AMEDIA_DRM_SHORT_BUFFER, error = %d",
-                        status);
-        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-        return JNI_FALSE;
-    }
-
-    // Test valid key status
-    numPairs = 3;
-    status = AMediaDrm_queryKeyStatus(aMediaObjects.getDrm(), &sessionId, keyStatus, &numPairs);
-    if (status != AMEDIA_OK) {
-        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                "AMediaDrm_queryKeyStatus failed, error = %d", status);
-        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-        return JNI_FALSE;
-    }
-
-    for (size_t i = 0; i < numPairs; ++i) {
-        ALOGI("AMediaDrm_queryKeyStatus: key=%s, value=%s", keyStatus[i].mKey, keyStatus[i].mValue);
-    }
-
-    if (numPairs != 3) {
-        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
-                "AMediaDrm_queryKeyStatus returns %zd key status, expecting 3", numPairs);
-        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-        return JNI_FALSE;
-    }
-
-    status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-    if (status != AMEDIA_OK) {
-        jniThrowException(env, "java/lang/RuntimeException",
-                "closeSession failed");
-        return JNI_FALSE;
-    }
-    return JNI_TRUE;
-}
-
-extern "C" jboolean
-Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testFindSessionIdNative(
-    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
-
-    if (NULL == uuid) {
-        jniThrowException(env, "java/lang/NullPointerException", "null uuid");
-        return JNI_FALSE;
-    }
-
-    Uuid juuid = jbyteArrayToUuid(env, uuid);
-    if (!isUuidSizeValid(juuid)) {
-        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                "invalid UUID size, expected %u bytes", kUuidSize);
-        return JNI_FALSE;
-    }
-
-    AMediaObjects aMediaObjects;
-    media_status_t status = AMEDIA_OK;
-    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
-    if (NULL == aMediaObjects.getDrm()) {
-        jniThrowException(env, "java/lang/RuntimeException", "failed to create drm");
-        return JNI_FALSE;
-    }
-
-    // Stores duplicates of session id.
-    std::vector<std::vector<uint8_t> > sids;
-
-    std::list<AMediaDrmSessionId> sessionIds;
-    AMediaDrmSessionId sessionId;
-    for (int i = 0; i < 5; ++i) {
-        status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
-        if (status != AMEDIA_OK) {
-            jniThrowException(env, "java/lang/RuntimeException", "openSession failed");
-            return JNI_FALSE;
-        }
-
-        // Allocates a new pointer to duplicate the session id returned by
-        // AMediaDrm_openSession. These new pointers will be passed to
-        // AMediaDrm_closeSession, which verifies that the ndk
-        // can find the session id even if the pointer has changed.
-        sids.push_back(std::vector<uint8_t>(sessionId.length));
-        memcpy(sids.at(i).data(), sessionId.ptr, sessionId.length);
-        sessionId.ptr = static_cast<uint8_t *>(sids.at(i).data());
-        sessionIds.push_back(sessionId);
-    }
-
-    for (auto sessionId : sessionIds) {
-        status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
-        if (status != AMEDIA_OK) {
-            jniThrowException(env, "java/lang/RuntimeException", "closeSession failed");
-            return JNI_FALSE;
-        }
-    }
-
-    return JNI_TRUE;
-}
-
-static JNINativeMethod gMethods[] = {
-    { "isCryptoSchemeSupportedNative", "([B)Z",
-            (void *)Java_android_media_player_cts_NativeMediaDrmClearkeyTest_isCryptoSchemeSupportedNative },
-
-    { "testClearKeyPlaybackNative",
-            "([BLandroid/media/player/cts/NativeMediaDrmClearkeyTest$PlaybackParams;)Z",
-            (void *)Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testClearKeyPlaybackNative },
-
-    { "testGetPropertyStringNative",
-            "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
-            (void *)Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testGetPropertyStringNative },
-
-    { "testPropertyByteArrayNative",
-            "([B)Z",
-            (void *)Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testPropertyByteArrayNative },
-
-    { "testPsshNative", "([BLjava/lang/String;)Z",
-            (void *)Java_android_media_player_cts_NativeMediaDrmClearkeyTest__testPsshNative },
-
-    { "testQueryKeyStatusNative", "([B)Z",
-            (void *)Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testQueryKeyStatusNative },
-
-    { "testFindSessionIdNative", "([B)Z",
-            (void *)Java_android_media_player_cts_NativeMediaDrmClearkeyTest_testFindSessionIdNative },
-};
-
-int register_android_media_player_cts_NativeMediaDrmClearkeyTest(JNIEnv* env) {
-    jint result = JNI_ERR;
-    jclass testClass =
-        env->FindClass("android/media/player/cts/NativeMediaDrmClearkeyTest");
-    if (testClass) {
-        jclass playbackParamsClass = env->FindClass(
-            "android/media/player/cts/NativeMediaDrmClearkeyTest$PlaybackParams");
-        if (playbackParamsClass) {
-            jclass surfaceClass =
-                env->FindClass("android/view/Surface");
-            if (surfaceClass) {
-                gFieldIds.surface = env->GetFieldID(playbackParamsClass,
-                        "surface", "Landroid/view/Surface;");
-            } else {
-                gFieldIds.surface = NULL;
-            }
-            gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
-                    "mimeType", "Ljava/lang/String;");
-            gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
-                    "audioUrl", "Ljava/lang/String;");
-            gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
-                    "videoUrl", "Ljava/lang/String;");
-        } else {
-            ALOGE("PlaybackParams class not found");
-        }
-
-    } else {
-        ALOGE("NativeMediaDrmClearkeyTest class not found");
-    }
-
-    result = env->RegisterNatives(testClass, gMethods,
-            sizeof(gMethods) / sizeof(JNINativeMethod));
-    return result;
-}
diff --git a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java
index b6d8f90..b530f66 100644
--- a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java
+++ b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java
@@ -153,7 +153,7 @@
         testIfMediaServerDied("heap_oob_flac.mp3");
     }
 
-    protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
+    private static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
             throws FileNotFoundException {
         Preconditions.assertTestFileExists(mInpPrefix + res);
         File inpFile = new File(mInpPrefix + res);
@@ -162,6 +162,48 @@
         return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
     }
 
+    private void loadSubtitleSource(String res) throws Exception {
+        try (AssetFileDescriptor afd = getAssetFileDescriptorFor(res)) {
+            mMediaPlayer.addTimedTextSource(afd.getFileDescriptor(), afd.getStartOffset(),
+                    afd.getLength(), MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP);
+        }
+    }
+
+    // returns true on success
+    private boolean loadResource(final String res) throws Exception {
+        Preconditions.assertTestFileExists(mInpPrefix + res);
+        if (!MediaUtils.hasCodecsForResource(mInpPrefix + res)) {
+            return false;
+        }
+
+        try (AssetFileDescriptor afd = getAssetFileDescriptorFor(res)) {
+            mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
+                    afd.getLength());
+
+            // Although it is only meant for video playback, it should not
+            // cause issues for audio-only playback.
+            int videoScalingMode = sUseScaleToFitMode ?
+                    MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT
+                    : MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
+
+            mMediaPlayer.setVideoScalingMode(videoScalingMode);
+        }
+        sUseScaleToFitMode = !sUseScaleToFitMode;  // Alternate the scaling mode
+        return true;
+    }
+
+    private boolean checkLoadResource(String res) throws Exception {
+        return MediaUtils.check(loadResource(res), "no decoder found");
+    }
+
+    private void playLoadedVideoTest(final String res, int width, int height) throws Exception {
+        if (!checkLoadResource(res)) {
+            return; // skip
+        }
+
+        playLoadedVideo(width, height, 0);
+    }
+
     private void testIfMediaServerDied(final String res) throws Exception {
         mMediaPlayer.setOnErrorListener((mp, what, extra) -> {
             assertSame(mp, mMediaPlayer);
diff --git a/tests/tests/media/player/src/android/media/player/cts/WorkDir.java b/tests/tests/media/player/src/android/media/player/cts/WorkDir.java
index bf8a839..622d733 100644
--- a/tests/tests/media/player/src/android/media/player/cts/WorkDir.java
+++ b/tests/tests/media/player/src/android/media/player/cts/WorkDir.java
@@ -20,6 +20,6 @@
 
 class WorkDir extends WorkDirBase {
     public static final String getMediaDirString() {
-        return getMediaDirString("CtsMediaTestCases-1.4");
+        return getMediaDirString("CtsMediaPlayerTestCases-1.0");
     }
 }
diff --git a/tests/tests/media/recorder/Android.bp b/tests/tests/media/recorder/Android.bp
index 3db7dd5..9e17567 100644
--- a/tests/tests/media/recorder/Android.bp
+++ b/tests/tests/media/recorder/Android.bp
@@ -14,13 +14,10 @@
 
 package {
     // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "cts_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    //   SPDX-license-identifier-CC-BY
-    //   legacy_unencumbered
-    default_applicable_licenses: ["cts_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "cts_tests_tests_media_license", // CC-BY
+    ],
 }
 
 android_test {
diff --git a/tests/tests/media/recorder/AndroidTest.xml b/tests/tests/media/recorder/AndroidTest.xml
index d443d1d..772e6ec 100644
--- a/tests/tests/media/recorder/AndroidTest.xml
+++ b/tests/tests/media/recorder/AndroidTest.xml
@@ -27,30 +27,10 @@
         <option name="disable-audio" value="false"/>
         <option name="screen-saver" value="off"/>
     </target_preparer>
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
-        <option name="target" value="host" />
-        <option name="config-filename" value="CtsMediaRecorderTestCases" />
-        <option name="dynamic-config-name" value="CtsMediaRecorderTestCases" />
-        <option name="version" value="1.0"/>
-    </target_preparer>
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
-        <option name="push-all" value="true" />
-        <option name="media-folder-name" value="CtsMediaTestCases-1.4" />
-        <option name="dynamic-config-module" value="CtsMediaRecorderTestCases" />
-    </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsMediaRecorderTestCases.apk" />
     </target_preparer>
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
-        <option name="target" value="device" />
-        <option name="config-filename" value="CtsMediaRecorderTestCases" />
-        <option name="version" value="1.0"/>
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <!-- MediaProjectionTest needs this one to not be granted, SuiteApkInstaller grants all of them by default.-->
-        <option name="run-command" value="pm revoke android.media.recorder.cts android.permission.SYSTEM_ALERT_WINDOW"/>
-    </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.media.recorder.cts" />
         <!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/recorder/DynamicConfig.xml b/tests/tests/media/recorder/DynamicConfig.xml
deleted file mode 100644
index 53528de..0000000
--- a/tests/tests/media/recorder/DynamicConfig.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<dynamicConfig>
-    <entry key="media_files_url">
-    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/CtsMediaTestCases-1.4.zip</value>
-    </entry>
-</dynamicConfig>
diff --git a/tests/tests/media/res/values/exifinterface.xml b/tests/tests/media/res/values/exifinterface.xml
index d19d128..be002e5 100644
--- a/tests/tests/media/res/values/exifinterface.xml
+++ b/tests/tests/media/res/values/exifinterface.xml
@@ -229,7 +229,7 @@
         <item>337</item>
         <item>600</item>
         <item>800</item>
-        <item>1</item>
+        <item>0</item>
         <item>0</item>
         <item>true</item>
         <item>826</item>
@@ -493,7 +493,7 @@
         <item>3072</item>
         <item>4608</item>
         <item>200</item>
-        <item>8</item>
+        <item>0</item>
         <item>0</item>
         <item>true</item>
         <item>472</item>
@@ -537,7 +537,7 @@
         <item>3000</item>
         <item>4000</item>
         <item>3200</item>
-        <item>8</item>
+        <item>0</item>
         <item>0</item>
         <item>false</item>
         <item />
diff --git a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
index 1811cb2..75ba4b1 100644
--- a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
+++ b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
@@ -643,6 +643,7 @@
 
     public void testReadExifDataFromLgG4Iso800Dng() throws Throwable {
         readFromFilesWithExif(DNG_WITH_EXIF_WITH_XMP, R.array.dng_with_exif_with_xmp);
+        writeToFilesWithExif(DNG_WITH_EXIF_WITH_XMP, R.array.dng_with_exif_with_xmp);
     }
 
     public void testReadExifDataFromLgG4Iso800Jpg() throws Throwable {
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
index 0e23ebf..7203df4 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
@@ -40,6 +40,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Test {@link android.service.media.MediaBrowserService}.
@@ -51,98 +53,86 @@
     private static final long WAIT_TIME_FOR_NO_RESPONSE_MS = 500L;
     private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
             "android.media.cts", "android.media.cts.StubMediaBrowserService");
-    private final Object mWaitLock = new Object();
 
-    private final MediaBrowser.ConnectionCallback mConnectionCallback =
-            new MediaBrowser.ConnectionCallback() {
-        @Override
-        public void onConnected() {
-            synchronized (mWaitLock) {
-                mMediaBrowserService = StubMediaBrowserService.sInstance;
-                mWaitLock.notify();
-            }
-        }
-    };
+    private final TestCountDownLatch mOnChildrenLoadedLatch = new TestCountDownLatch();
+    private final TestCountDownLatch mOnChildrenLoadedWithOptionsLatch = new TestCountDownLatch();
+    private final TestCountDownLatch mOnItemLoadedLatch = new TestCountDownLatch();
 
     private final MediaBrowser.SubscriptionCallback mSubscriptionCallback =
             new MediaBrowser.SubscriptionCallback() {
             @Override
             public void onChildrenLoaded(String parentId, List<MediaItem> children) {
-                synchronized (mWaitLock) {
-                    mOnChildrenLoaded = true;
-                    if (children != null) {
-                        for (MediaItem item : children) {
-                            assertRootHints(item);
-                        }
+                if (children != null) {
+                    for (MediaItem item : children) {
+                        assertRootHints(item);
                     }
-                    mWaitLock.notify();
                 }
+                mOnChildrenLoadedLatch.countDown();
             }
 
             @Override
             public void onChildrenLoaded(String parentId, List<MediaItem> children,
                     Bundle options) {
-                synchronized (mWaitLock) {
-                    mOnChildrenLoadedWithOptions = true;
-                    if (children != null) {
-                        for (MediaItem item : children) {
-                            assertRootHints(item);
-                        }
+                if (children != null) {
+                    for (MediaItem item : children) {
+                        assertRootHints(item);
                     }
-                    mWaitLock.notify();
                 }
+                mOnChildrenLoadedWithOptionsLatch.countDown();
             }
         };
 
     private final MediaBrowser.ItemCallback mItemCallback = new MediaBrowser.ItemCallback() {
         @Override
         public void onItemLoaded(MediaItem item) {
-            synchronized (mWaitLock) {
-                mOnItemLoaded = true;
-                assertRootHints(item);
-                mWaitLock.notify();
-            }
+            assertRootHints(item);
+            mOnItemLoadedLatch.countDown();
         }
     };
 
     private MediaBrowser mMediaBrowser;
     private RemoteUserInfo mBrowserInfo;
     private StubMediaBrowserService mMediaBrowserService;
-    private boolean mOnChildrenLoaded;
-    private boolean mOnChildrenLoadedWithOptions;
-    private boolean mOnItemLoaded;
     private Bundle mRootHints;
 
     @Override
     public void setUp() throws Exception {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mRootHints = new Bundle();
-                mRootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
-                mRootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_OFFLINE, true);
-                mRootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_SUGGESTED, true);
-                mMediaBrowser = new MediaBrowser(getInstrumentation().getTargetContext(),
-                        TEST_BROWSER_SERVICE, mConnectionCallback, mRootHints);
-                mBrowserInfo = new RemoteUserInfo(
-                        getInstrumentation().getTargetContext().getPackageName(),
-                        Process.myPid(),
-                        Process.myUid());
-            }
-        });
-        synchronized (mWaitLock) {
+        mRootHints = new Bundle();
+        mRootHints.putBoolean(BrowserRoot.EXTRA_RECENT, true);
+        mRootHints.putBoolean(BrowserRoot.EXTRA_OFFLINE, true);
+        mRootHints.putBoolean(BrowserRoot.EXTRA_SUGGESTED, true);
+        mBrowserInfo = new RemoteUserInfo(
+                getInstrumentation().getTargetContext().getPackageName(),
+                Process.myPid(),
+                Process.myUid());
+        mOnChildrenLoadedLatch.reset();
+        mOnChildrenLoadedWithOptionsLatch.reset();
+        mOnItemLoadedLatch.reset();
+
+        final CountDownLatch onConnectedLatch = new CountDownLatch(1);
+        getInstrumentation().runOnMainSync(()-> {
+            mMediaBrowser = new MediaBrowser(getInstrumentation().getTargetContext(),
+                    TEST_BROWSER_SERVICE, new MediaBrowser.ConnectionCallback() {
+                @Override
+                public void onConnected() {
+                    mMediaBrowserService = StubMediaBrowserService.sInstance;
+                    onConnectedLatch.countDown();
+                }
+            }, mRootHints);
             mMediaBrowser.connect();
-            mWaitLock.wait(TIME_OUT_MS);
-        }
+        });
+        assertTrue(onConnectedLatch.await(TIME_OUT_MS, TimeUnit.MILLISECONDS));
         assertNotNull(mMediaBrowserService);
     }
 
     @Override
     public void tearDown() {
-        if (mMediaBrowser != null) {
-            mMediaBrowser.disconnect();
-            mMediaBrowser = null;
-        }
+        getInstrumentation().runOnMainSync(()-> {
+            if (mMediaBrowser != null) {
+                mMediaBrowser.disconnect();
+                mMediaBrowser = null;
+            }
+        });
     }
 
     public void testGetSessionToken() {
@@ -151,16 +141,14 @@
     }
 
     public void testNotifyChildrenChanged() throws Exception {
-        synchronized (mWaitLock) {
+        getInstrumentation().runOnMainSync(()-> {
             mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnChildrenLoaded);
+        });
+        assertTrue(mOnChildrenLoadedLatch.await(TIME_OUT_MS));
 
-            mOnChildrenLoaded = false;
-            mMediaBrowserService.notifyChildrenChanged(StubMediaBrowserService.MEDIA_ID_ROOT);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnChildrenLoaded);
-        }
+        mOnChildrenLoadedLatch.reset();
+        mMediaBrowserService.notifyChildrenChanged(StubMediaBrowserService.MEDIA_ID_ROOT);
+        assertTrue(mOnChildrenLoadedLatch.await(TIME_OUT_MS));
     }
 
     public void testNotifyChildrenChangedWithNullOptionsThrowsIAE() {
@@ -173,103 +161,91 @@
         }
     }
 
-    public void testNotifyChildrenChangedWithPagination() throws Exception {
-        synchronized (mWaitLock) {
-            final int pageSize = 5;
-            final int page = 2;
-            Bundle options = new Bundle();
-            options.putInt(MediaBrowser.EXTRA_PAGE_SIZE, pageSize);
-            options.putInt(MediaBrowser.EXTRA_PAGE, page);
+    public void testNotifyChildrenChangedWithPagination() {
+        final int pageSize = 5;
+        final int page = 2;
+        Bundle options = new Bundle();
+        options.putInt(MediaBrowser.EXTRA_PAGE_SIZE, pageSize);
+        options.putInt(MediaBrowser.EXTRA_PAGE, page);
 
+        getInstrumentation().runOnMainSync(()-> {
             mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, options,
                     mSubscriptionCallback);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnChildrenLoadedWithOptions);
+        });
+        assertTrue(mOnChildrenLoadedWithOptionsLatch.await(TIME_OUT_MS));
 
-            mOnChildrenLoadedWithOptions = false;
-            mMediaBrowserService.notifyChildrenChanged(StubMediaBrowserService.MEDIA_ID_ROOT);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnChildrenLoadedWithOptions);
+        mOnChildrenLoadedWithOptionsLatch.reset();
+        mMediaBrowserService.notifyChildrenChanged(StubMediaBrowserService.MEDIA_ID_ROOT);
+        assertTrue(mOnChildrenLoadedWithOptionsLatch.await(TIME_OUT_MS));
 
-            // Notify that the items overlapping with the given options are changed.
-            mOnChildrenLoadedWithOptions = false;
-            final int newPageSize = 3;
-            final int overlappingNewPage = pageSize * page / newPageSize;
-            Bundle overlappingOptions = new Bundle();
-            overlappingOptions.putInt(MediaBrowser.EXTRA_PAGE_SIZE, newPageSize);
-            overlappingOptions.putInt(MediaBrowser.EXTRA_PAGE, overlappingNewPage);
-            mMediaBrowserService.notifyChildrenChanged(
-                    StubMediaBrowserService.MEDIA_ID_ROOT, overlappingOptions);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnChildrenLoadedWithOptions);
+        // Notify that the items overlapping with the given options are changed.
+        mOnChildrenLoadedWithOptionsLatch.reset();
+        final int newPageSize = 3;
+        final int overlappingNewPage = pageSize * page / newPageSize;
+        Bundle overlappingOptions = new Bundle();
+        overlappingOptions.putInt(MediaBrowser.EXTRA_PAGE_SIZE, newPageSize);
+        overlappingOptions.putInt(MediaBrowser.EXTRA_PAGE, overlappingNewPage);
+        mMediaBrowserService.notifyChildrenChanged(
+                StubMediaBrowserService.MEDIA_ID_ROOT, overlappingOptions);
+        assertTrue(mOnChildrenLoadedWithOptionsLatch.await(TIME_OUT_MS));
 
-            // Notify that the items non-overlapping with the given options are changed.
-            mOnChildrenLoadedWithOptions = false;
-            Bundle nonOverlappingOptions = new Bundle();
-            nonOverlappingOptions.putInt(MediaBrowser.EXTRA_PAGE_SIZE, pageSize);
-            nonOverlappingOptions.putInt(MediaBrowser.EXTRA_PAGE, page + 1);
-            mMediaBrowserService.notifyChildrenChanged(
-                    StubMediaBrowserService.MEDIA_ID_ROOT, nonOverlappingOptions);
-            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
-            assertFalse(mOnChildrenLoadedWithOptions);
-        }
+        // Notify that the items non-overlapping with the given options are changed.
+        mOnChildrenLoadedWithOptionsLatch.reset();
+        Bundle nonOverlappingOptions = new Bundle();
+        nonOverlappingOptions.putInt(MediaBrowser.EXTRA_PAGE_SIZE, pageSize);
+        nonOverlappingOptions.putInt(MediaBrowser.EXTRA_PAGE, page + 1);
+        mMediaBrowserService.notifyChildrenChanged(
+                StubMediaBrowserService.MEDIA_ID_ROOT, nonOverlappingOptions);
+        assertFalse(mOnChildrenLoadedWithOptionsLatch.await(WAIT_TIME_FOR_NO_RESPONSE_MS));
     }
 
     public void testDelayedNotifyChildrenChanged() throws Exception {
-        synchronized (mWaitLock) {
-            mOnChildrenLoaded = false;
+        getInstrumentation().runOnMainSync(()-> {
             mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED,
                     mSubscriptionCallback);
-            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
-            assertFalse(mOnChildrenLoaded);
+        });
+        assertFalse(mOnChildrenLoadedLatch.await(WAIT_TIME_FOR_NO_RESPONSE_MS));
 
-            mMediaBrowserService.sendDelayedNotifyChildrenChanged();
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnChildrenLoaded);
+        mMediaBrowserService.sendDelayedNotifyChildrenChanged();
+        assertTrue(mOnChildrenLoadedLatch.await(TIME_OUT_MS));
 
-            mOnChildrenLoaded = false;
-            mMediaBrowserService.notifyChildrenChanged(
-                    StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED);
-            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
-            assertFalse(mOnChildrenLoaded);
+        mOnChildrenLoadedLatch.reset();
+        mMediaBrowserService.notifyChildrenChanged(
+                StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED);
+        assertFalse(mOnChildrenLoadedLatch.await(WAIT_TIME_FOR_NO_RESPONSE_MS));
 
-            mMediaBrowserService.sendDelayedNotifyChildrenChanged();
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnChildrenLoaded);
-        }
+        mMediaBrowserService.sendDelayedNotifyChildrenChanged();
+        assertTrue(mOnChildrenLoadedLatch.await(TIME_OUT_MS));
     }
 
     public void testDelayedItem() throws Exception {
-        synchronized (mWaitLock) {
-            mOnItemLoaded = false;
+        getInstrumentation().runOnMainSync(()-> {
             mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED,
                     mItemCallback);
-            mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
-            assertFalse(mOnItemLoaded);
+        });
+        assertFalse(mOnItemLoadedLatch.await(WAIT_TIME_FOR_NO_RESPONSE_MS));
 
-            mMediaBrowserService.sendDelayedItemLoaded();
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnItemLoaded);
-        }
+        mMediaBrowserService.sendDelayedItemLoaded();
+        assertTrue(mOnItemLoadedLatch.await(TIME_OUT_MS));
     }
 
     public void testGetBrowserInfo() throws Exception {
-        synchronized (mWaitLock) {
-            // StubMediaBrowserService stores the browser info in its onGetRoot().
-            assertTrue(compareRemoteUserInfo(mBrowserInfo, StubMediaBrowserService.sBrowserInfo));
+        // StubMediaBrowserService stores the browser info in its onGetRoot().
+        assertTrue(compareRemoteUserInfo(mBrowserInfo, StubMediaBrowserService.sBrowserInfo));
 
-            StubMediaBrowserService.clearBrowserInfo();
+        StubMediaBrowserService.clearBrowserInfo();
+        getInstrumentation().runOnMainSync(()-> {
             mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnChildrenLoaded);
-            assertTrue(compareRemoteUserInfo(mBrowserInfo, StubMediaBrowserService.sBrowserInfo));
+        });
+        assertTrue(mOnChildrenLoadedLatch.await(TIME_OUT_MS));
+        assertTrue(compareRemoteUserInfo(mBrowserInfo, StubMediaBrowserService.sBrowserInfo));
 
-            StubMediaBrowserService.clearBrowserInfo();
+        StubMediaBrowserService.clearBrowserInfo();
+        getInstrumentation().runOnMainSync(()-> {
             mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
-            mWaitLock.wait(TIME_OUT_MS);
-            assertTrue(mOnItemLoaded);
-            assertTrue(compareRemoteUserInfo(mBrowserInfo, StubMediaBrowserService.sBrowserInfo));
-        }
+        });
+        assertTrue(mOnItemLoadedLatch.await(TIME_OUT_MS));
+        assertTrue(compareRemoteUserInfo(mBrowserInfo, StubMediaBrowserService.sBrowserInfo));
     }
 
     public void testBrowserRoot() {
@@ -329,4 +305,28 @@
         assertEquals(mRootHints.getBoolean(BrowserRoot.EXTRA_SUGGESTED),
                 rootHints.getBoolean(BrowserRoot.EXTRA_SUGGESTED));
     }
+
+    private static class TestCountDownLatch {
+        private CountDownLatch mLatch;
+
+        TestCountDownLatch() {
+            mLatch = new CountDownLatch(1);
+        }
+
+        void reset() {
+            mLatch = new CountDownLatch(1);
+        }
+
+        void countDown() {
+            mLatch.countDown();
+        }
+
+        boolean await(long timeoutMs) {
+            try {
+                return mLatch.await(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
index fc832fb..8f3d299 100644
--- a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -25,6 +25,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Test {@link android.media.browse.MediaBrowser}.
@@ -57,86 +58,107 @@
     @Override
     public void tearDown() {
         if (mMediaBrowser != null) {
-            mMediaBrowser.disconnect();
+            try {
+                disconnectMediaBrowser();
+            } catch (Throwable t) {
+                // Ignore.
+            }
             mMediaBrowser = null;
         }
     }
 
-    public void testMediaBrowser() {
+    public void testMediaBrowser() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
-        assertEquals(false, mMediaBrowser.isConnected());
+        runOnMainThread(() -> {
+            assertEquals(false, mMediaBrowser.isConnected());
+        });
 
         connectMediaBrowserService();
-        assertEquals(true, mMediaBrowser.isConnected());
+        runOnMainThread(() -> {
+            assertEquals(true, mMediaBrowser.isConnected());
+        });
 
-        assertEquals(TEST_BROWSER_SERVICE, mMediaBrowser.getServiceComponent());
-        assertEquals(StubMediaBrowserService.MEDIA_ID_ROOT, mMediaBrowser.getRoot());
-        assertEquals(StubMediaBrowserService.EXTRAS_VALUE,
-                mMediaBrowser.getExtras().getString(StubMediaBrowserService.EXTRAS_KEY));
-        assertEquals(StubMediaBrowserService.sSession.getSessionToken(),
-                mMediaBrowser.getSessionToken());
+        runOnMainThread(() -> {
+            assertEquals(TEST_BROWSER_SERVICE, mMediaBrowser.getServiceComponent());
+            assertEquals(StubMediaBrowserService.MEDIA_ID_ROOT, mMediaBrowser.getRoot());
+            assertEquals(StubMediaBrowserService.EXTRAS_VALUE,
+                    mMediaBrowser.getExtras().getString(StubMediaBrowserService.EXTRAS_KEY));
+            assertEquals(StubMediaBrowserService.sSession.getSessionToken(),
+                    mMediaBrowser.getSessionToken());
+        });
 
-        mMediaBrowser.disconnect();
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return !mMediaBrowser.isConnected();
+        disconnectMediaBrowser();
+        runOnMainThread(() -> {
+            new PollingCheck(TIME_OUT_MS) {
+                @Override
+                protected boolean check() {
+                    return !mMediaBrowser.isConnected();
+                }
+            }.run();
+        });
+    }
+
+    public void testThrowingISEWhileNotConnected() throws Throwable {
+        resetCallbacks();
+        createMediaBrowser(TEST_BROWSER_SERVICE);
+        runOnMainThread(() -> {
+            assertEquals(false, mMediaBrowser.isConnected());
+        });
+
+        runOnMainThread(() -> {
+            try {
+                mMediaBrowser.getExtras();
+                fail();
+            } catch (IllegalStateException e) {
+                // Expected
             }
-        }.run();
+
+            try {
+                mMediaBrowser.getRoot();
+                fail();
+            } catch (IllegalStateException e) {
+                // Expected
+            }
+
+            try {
+                mMediaBrowser.getServiceComponent();
+                fail();
+            } catch (IllegalStateException e) {
+                // Expected
+            }
+
+            try {
+                mMediaBrowser.getSessionToken();
+                fail();
+            } catch (IllegalStateException e) {
+                // Expected
+            }
+        });
     }
 
-    public void testThrowingISEWhileNotConnected() {
-        resetCallbacks();
-        createMediaBrowser(TEST_BROWSER_SERVICE);
-        assertEquals(false, mMediaBrowser.isConnected());
-
-        try {
-            mMediaBrowser.getExtras();
-            fail();
-        } catch (IllegalStateException e) {
-            // Expected
-        }
-
-        try {
-            mMediaBrowser.getRoot();
-            fail();
-        } catch (IllegalStateException e) {
-            // Expected
-        }
-
-        try {
-            mMediaBrowser.getServiceComponent();
-            fail();
-        } catch (IllegalStateException e) {
-            // Expected
-        }
-
-        try {
-            mMediaBrowser.getSessionToken();
-            fail();
-        } catch (IllegalStateException e) {
-            // Expected
-        }
-    }
-
-    public void testConnectTwice() {
+    public void testConnectTwice() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        try {
-            mMediaBrowser.connect();
-            fail();
-        } catch (IllegalStateException e) {
-            // expected
-        }
+        runOnMainThread(() -> {
+            try {
+                mMediaBrowser.connect();
+                fail();
+            } catch (IllegalStateException e) {
+                // expected
+            }
+        });
     }
 
-    public void testConnectionFailed() {
+    public void testConnectionFailed() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_INVALID_BROWSER_SERVICE);
 
-        mMediaBrowser.connect();
+        runOnMainThread(() -> {
+            mMediaBrowser.connect();
+        });
+
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -147,18 +169,22 @@
         }.run();
     }
 
-    public void testReconnection() {
+    public void testReconnection() throws Throwable {
         createMediaBrowser(TEST_BROWSER_SERVICE);
 
-        // Reconnect before the first connection was established.
-        mMediaBrowser.connect();
-        mMediaBrowser.disconnect();
+        runOnMainThread(() -> {
+            // Reconnect before the first connection was established.
+            mMediaBrowser.connect();
+            mMediaBrowser.disconnect();
+        });
         resetCallbacks();
         connectMediaBrowserService();
 
         // Test subscribe.
         resetCallbacks();
-        mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+        runOnMainThread(() -> {
+            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+        });
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -168,7 +194,9 @@
 
         // Test getItem.
         resetCallbacks();
-        mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+        runOnMainThread(() -> {
+            mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+        });
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -177,13 +205,15 @@
         }.run();
 
         // Reconnect after connection was established.
-        mMediaBrowser.disconnect();
+        disconnectMediaBrowser();
         resetCallbacks();
         connectMediaBrowserService();
 
         // Test getItem.
         resetCallbacks();
-        mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+        runOnMainThread(() -> {
+            mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+        });
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -192,10 +222,12 @@
         }.run();
     }
 
-    public void testConnectionCallbackNotCalledAfterDisconnect() {
+    public void testConnectionCallbackNotCalledAfterDisconnect() throws Throwable {
         createMediaBrowser(TEST_BROWSER_SERVICE);
-        mMediaBrowser.connect();
-        mMediaBrowser.disconnect();
+        runOnMainThread(() -> {
+            mMediaBrowser.connect();
+            mMediaBrowser.disconnect();
+        });
         resetCallbacks();
         try {
             Thread.sleep(SLEEP_MS);
@@ -207,11 +239,13 @@
         assertEquals(0, mConnectionCallback.mConnectionSuspendedCount);
     }
 
-    public void testSubscribe() {
+    public void testSubscribe() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+        runOnMainThread(() -> {
+            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+        });
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -229,7 +263,9 @@
 
         // Test unsubscribe.
         resetCallbacks();
-        mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT);
+        runOnMainThread(() -> {
+            mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT);
+        });
 
         // After unsubscribing, make StubMediaBrowserService notify that the children are changed.
         StubMediaBrowserService.sInstance.notifyChildrenChanged(
@@ -243,44 +279,46 @@
         assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
     }
 
-    public void testSubscribeWithIllegalArguments() {
+    public void testSubscribeWithIllegalArguments() throws Throwable {
         createMediaBrowser(TEST_BROWSER_SERVICE);
 
-        try {
-            final String nullMediaId = null;
-            mMediaBrowser.subscribe(nullMediaId, mSubscriptionCallback);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
+        runOnMainThread(() -> {
+            try {
+                final String nullMediaId = null;
+                mMediaBrowser.subscribe(nullMediaId, mSubscriptionCallback);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
 
-        try {
-            final String emptyMediaId = "";
-            mMediaBrowser.subscribe(emptyMediaId, mSubscriptionCallback);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
+            try {
+                final String emptyMediaId = "";
+                mMediaBrowser.subscribe(emptyMediaId, mSubscriptionCallback);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
 
-        try {
-            final MediaBrowser.SubscriptionCallback nullCallback = null;
-            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, nullCallback);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
+            try {
+                final MediaBrowser.SubscriptionCallback nullCallback = null;
+                mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, nullCallback);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
 
-        try {
-            final Bundle nullOptions = null;
-            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, nullOptions,
-                    mSubscriptionCallback);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
+            try {
+                final Bundle nullOptions = null;
+                mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, nullOptions,
+                        mSubscriptionCallback);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
+        });
     }
 
-    public void testSubscribeWithOptions() {
+    public void testSubscribeWithOptions() throws Throwable {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
         final int pageSize = 3;
@@ -290,8 +328,10 @@
         for (int page = 0; page <= lastPage; ++page) {
             resetCallbacks();
             options.putInt(MediaBrowser.EXTRA_PAGE, page);
-            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, options,
-                    mSubscriptionCallback);
+            runOnMainThread(() -> {
+                mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, options,
+                        mSubscriptionCallback);
+            });
             new PollingCheck(TIME_OUT_MS) {
                 @Override
                 protected boolean check() {
@@ -315,7 +355,9 @@
 
         // Test unsubscribe with callback argument.
         resetCallbacks();
-        mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+        runOnMainThread(() -> {
+            mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+        });
 
         // After unsubscribing, make StubMediaBrowserService notify that the children are changed.
         StubMediaBrowserService.sInstance.notifyChildrenChanged(
@@ -329,11 +371,14 @@
         assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
     }
 
-    public void testSubscribeInvalidItem() {
+    public void testSubscribeInvalidItem() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_INVALID, mSubscriptionCallback);
+        runOnMainThread(() -> {
+            mMediaBrowser.subscribe(
+                    StubMediaBrowserService.MEDIA_ID_INVALID, mSubscriptionCallback);
+        });
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -344,7 +389,7 @@
         assertEquals(StubMediaBrowserService.MEDIA_ID_INVALID, mSubscriptionCallback.mLastErrorId);
     }
 
-    public void testSubscribeInvalidItemWithOptions() {
+    public void testSubscribeInvalidItemWithOptions() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
@@ -354,8 +399,10 @@
         Bundle options = new Bundle();
         options.putInt(MediaBrowser.EXTRA_PAGE_SIZE, pageSize);
         options.putInt(MediaBrowser.EXTRA_PAGE, page);
-        mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_INVALID, options,
-                mSubscriptionCallback);
+        runOnMainThread(() -> {
+            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_INVALID, options,
+                    mSubscriptionCallback);
+        });
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -369,11 +416,13 @@
                 mSubscriptionCallback.mLastOptions.getInt(MediaBrowser.EXTRA_PAGE_SIZE));
     }
 
-    public void testSubscriptionCallbackNotCalledAfterDisconnect() {
+    public void testSubscriptionCallbackNotCalledAfterDisconnect() throws Throwable {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
-        mMediaBrowser.disconnect();
+        runOnMainThread(() -> {
+            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+            mMediaBrowser.disconnect();
+        });
         resetCallbacks();
         StubMediaBrowserService.sInstance.notifyChildrenChanged(
                 StubMediaBrowserService.MEDIA_ID_ROOT);
@@ -387,35 +436,36 @@
         assertNull(mSubscriptionCallback.mLastParentId);
     }
 
-    public void testUnsubscribeWithIllegalArguments() {
+    public void testUnsubscribeWithIllegalArguments() throws Throwable {
         createMediaBrowser(TEST_BROWSER_SERVICE);
+        runOnMainThread(() -> {
+            try {
+                final String nullMediaId = null;
+                mMediaBrowser.unsubscribe(nullMediaId);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
 
-        try {
-            final String nullMediaId = null;
-            mMediaBrowser.unsubscribe(nullMediaId);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
+            try {
+                final String emptyMediaId = "";
+                mMediaBrowser.unsubscribe(emptyMediaId);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
 
-        try {
-            final String emptyMediaId = "";
-            mMediaBrowser.unsubscribe(emptyMediaId);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
-
-        try {
-            final MediaBrowser.SubscriptionCallback nullCallback = null;
-            mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT, nullCallback);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
+            try {
+                final MediaBrowser.SubscriptionCallback nullCallback = null;
+                mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT, nullCallback);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
+        });
     }
 
-    public void testUnsubscribeForMultipleSubscriptions() {
+    public void testUnsubscribeForMultipleSubscriptions() throws Throwable {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
         final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
@@ -429,7 +479,9 @@
             Bundle options = new Bundle();
             options.putInt(MediaBrowser.EXTRA_PAGE, page);
             options.putInt(MediaBrowser.EXTRA_PAGE_SIZE, pageSize);
-            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, options, callback);
+            runOnMainThread(() -> {
+                mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, options, callback);
+            });
 
             // Each onChildrenLoaded() must be called.
             new PollingCheck(TIME_OUT_MS) {
@@ -444,7 +496,9 @@
         for (StubSubscriptionCallback callback : subscriptionCallbacks) {
             callback.reset();
         }
-        mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT);
+        runOnMainThread(() -> {
+            mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT);
+        });
 
         // After unsubscribing, make StubMediaBrowserService notify that the children are changed.
         StubMediaBrowserService.sInstance.notifyChildrenChanged(
@@ -461,7 +515,7 @@
         }
     }
 
-    public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() {
+    public void testUnsubscribeWithSubscriptionCallbackForMultipleSubscriptions() throws Throwable {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
         final List<StubSubscriptionCallback> subscriptionCallbacks = new ArrayList<>();
@@ -475,7 +529,9 @@
             Bundle options = new Bundle();
             options.putInt(MediaBrowser.EXTRA_PAGE, page);
             options.putInt(MediaBrowser.EXTRA_PAGE_SIZE, pageSize);
-            mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, options, callback);
+            runOnMainThread(() -> {
+                mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, options, callback);
+            });
 
             // Each onChildrenLoaded() must be called.
             new PollingCheck(TIME_OUT_MS) {
@@ -494,9 +550,12 @@
                 callback.reset();
             }
 
-            // Remove one subscription
-            mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT,
-                    subscriptionCallbacks.get(orderOfRemovingCallbacks[i]));
+            final int index = i;
+            runOnMainThread(() -> {
+                // Remove one subscription
+                mMediaBrowser.unsubscribe(StubMediaBrowserService.MEDIA_ID_ROOT,
+                        subscriptionCallbacks.get(orderOfRemovingCallbacks[index]));
+            });
 
             // Make StubMediaBrowserService notify that the children are changed.
             StubMediaBrowserService.sInstance.notifyChildrenChanged(
@@ -520,11 +579,14 @@
         }
     }
 
-    public void testGetItem() {
+    public void testGetItem() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+
+        runOnMainThread(() -> {
+            mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+        });
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -536,41 +598,45 @@
                 mItemCallback.mLastMediaItem.getMediaId());
     }
 
-    public void testGetItemThrowsIAE() {
+    public void testGetItemThrowsIAE() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
 
-        try {
-            // Calling getItem() with empty mediaId will throw IAE.
-            mMediaBrowser.getItem("",  mItemCallback);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
+        runOnMainThread(() -> {
+            try {
+                // Calling getItem() with empty mediaId will throw IAE.
+                mMediaBrowser.getItem("",  mItemCallback);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
 
-        try {
-            // Calling getItem() with null mediaId will throw IAE.
-            mMediaBrowser.getItem(null,  mItemCallback);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
+            try {
+                // Calling getItem() with null mediaId will throw IAE.
+                mMediaBrowser.getItem(null,  mItemCallback);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
 
-        try {
-            // Calling getItem() with null itemCallback will throw IAE.
-            mMediaBrowser.getItem("media_id",  null);
-            fail();
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
+            try {
+                // Calling getItem() with null itemCallback will throw IAE.
+                mMediaBrowser.getItem("media_id",  null);
+                fail();
+            } catch (IllegalArgumentException e) {
+                // Expected
+            }
+        });
     }
 
-    public void testGetItemWhileNotConnected() {
+    public void testGetItemWhileNotConnected() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
 
         final String mediaId = "test_media_id";
-        mMediaBrowser.getItem(mediaId, mItemCallback);
+        runOnMainThread(() -> {
+            mMediaBrowser.getItem(mediaId, mItemCallback);
+        });
 
         // Calling getItem while not connected will invoke ItemCallback.onError().
         new PollingCheck(TIME_OUT_MS) {
@@ -583,11 +649,13 @@
         assertEquals(mItemCallback.mLastErrorId, mediaId);
     }
 
-    public void testGetItemFailure() {
+    public void testGetItemFailure() throws Throwable {
         resetCallbacks();
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_INVALID, mItemCallback);
+        runOnMainThread(() -> {
+            mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_INVALID, mItemCallback);
+        });
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -598,11 +666,13 @@
         assertEquals(StubMediaBrowserService.MEDIA_ID_INVALID, mItemCallback.mLastErrorId);
     }
 
-    public void testItemCallbackNotCalledAfterDisconnect() {
+    public void testItemCallbackNotCalledAfterDisconnect() throws Throwable {
         createMediaBrowser(TEST_BROWSER_SERVICE);
         connectMediaBrowserService();
-        mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
-        mMediaBrowser.disconnect();
+        runOnMainThread(() -> {
+            mMediaBrowser.getItem(StubMediaBrowserService.MEDIA_ID_CHILDREN[0], mItemCallback);
+            mMediaBrowser.disconnect();
+        });
         resetCallbacks();
         try {
             Thread.sleep(SLEEP_MS);
@@ -613,18 +683,17 @@
         assertNull(mItemCallback.mLastErrorId);
     }
 
-    private void createMediaBrowser(final ComponentName component) {
-        getInstrumentation().runOnMainSync(new Runnable() {
-            @Override
-            public void run() {
-                mMediaBrowser = new MediaBrowser(getInstrumentation().getTargetContext(),
-                        component, mConnectionCallback, null);
-            }
+    private void createMediaBrowser(final ComponentName component) throws Throwable {
+        runOnMainThread(() -> {
+            mMediaBrowser = new MediaBrowser(getInstrumentation().getTargetContext(),
+                    component, mConnectionCallback, null);
         });
     }
 
-    private void connectMediaBrowserService() {
-        mMediaBrowser.connect();
+    private void connectMediaBrowserService() throws Throwable {
+        runOnMainThread(() -> {
+            mMediaBrowser.connect();
+        });
         new PollingCheck(TIME_OUT_MS) {
             @Override
             protected boolean check() {
@@ -633,12 +702,35 @@
         }.run();
     }
 
+    private void disconnectMediaBrowser() throws Throwable {
+        runOnMainThread(() -> {
+            mMediaBrowser.disconnect();
+        });
+    }
+
     private void resetCallbacks() {
         mConnectionCallback.reset();
         mSubscriptionCallback.reset();
         mItemCallback.reset();
     }
 
+    private void runOnMainThread(Runnable runnable) throws Throwable {
+        AtomicReference<Throwable> throwableRef = new AtomicReference<>();
+
+        getInstrumentation().runOnMainSync(() -> {
+            try {
+                runnable.run();
+            } catch (Throwable t) {
+                throwableRef.set(t);
+            }
+        });
+
+        Throwable t = throwableRef.get();
+        if (t != null) {
+            throw t;
+        }
+    }
+
     private static class StubConnectionCallback extends MediaBrowser.ConnectionCallback {
         volatile int mConnectedCount;
         volatile int mConnectionFailedCount;
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataTest.java
index 0483c9d..ed7e0a3 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataTest.java
@@ -21,7 +21,11 @@
 import static org.junit.Assert.fail;
 
 import android.graphics.Bitmap;
+import android.media.MediaDescription;
 import android.media.MediaMetadata;
+import android.media.Rating;
+import android.os.Parcel;
+import android.text.TextUtils;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -29,16 +33,246 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Set;
+
 /**
  * Tests {@link MediaMetadata}.
  */
-// TODO(b/168668505): Add tests for other methods.
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @NonMediaMainlineTest
 public class MediaMetadataTest {
 
     @Test
+    public void builder_defaultConstructor_hasNoData() {
+        MediaMetadata metadata = new MediaMetadata.Builder().build();
+
+        assertEquals(0, metadata.size());
+        assertTrue(metadata.keySet().isEmpty());
+    }
+
+    @Test
+    public void builder_putText() {
+        String testTitle = "test_title";
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putText(MediaMetadata.METADATA_KEY_TITLE, testTitle)
+                .build();
+
+        assertTrue(metadata.containsKey(MediaMetadata.METADATA_KEY_TITLE));
+        CharSequence titleOut = metadata.getText(MediaMetadata.METADATA_KEY_TITLE);
+        assertTrue(TextUtils.equals(testTitle, titleOut));
+    }
+
+    @Test
+    public void builder_putString() {
+        String testTitle = "test_title";
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_TITLE, testTitle)
+                .build();
+
+        assertTrue(metadata.containsKey(MediaMetadata.METADATA_KEY_TITLE));
+        String titleOut = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
+        assertTrue(TextUtils.equals(testTitle, titleOut));
+    }
+
+    @Test
+    public void builder_putLong() {
+        long testYear = 2021;
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putLong(MediaMetadata.METADATA_KEY_YEAR, testYear)
+                .build();
+
+        assertTrue(metadata.containsKey(MediaMetadata.METADATA_KEY_YEAR));
+        long yearOut = metadata.getLong(MediaMetadata.METADATA_KEY_YEAR);
+        assertEquals(testYear, yearOut);
+    }
+
+    @Test
+    public void builder_putRating() {
+        Rating testHeartRating = Rating.newHeartRating(/*hasHeart=*/ true);
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putRating(MediaMetadata.METADATA_KEY_RATING, testHeartRating)
+                .build();
+
+        assertTrue(metadata.containsKey(MediaMetadata.METADATA_KEY_RATING));
+        Rating ratingOut = metadata.getRating(MediaMetadata.METADATA_KEY_RATING);
+        assertEquals(testHeartRating, ratingOut);
+    }
+
+    @Test
+    public void builder_putText_throwsIAE_withNonTextKey() {
+        MediaMetadata.Builder builder = new MediaMetadata.Builder();
+        try {
+            builder.putText(MediaMetadata.METADATA_KEY_YEAR, "test");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void builder_putString_throwsIAE_withNonTextKey() {
+        MediaMetadata.Builder builder = new MediaMetadata.Builder();
+        try {
+            builder.putString(MediaMetadata.METADATA_KEY_YEAR, "test");
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void builder_putLong_throwsIAE_withNonLongKey() {
+        MediaMetadata.Builder builder = new MediaMetadata.Builder();
+        try {
+            builder.putLong(MediaMetadata.METADATA_KEY_TITLE, 2021);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void builder_putRating_throwsIAE_withNonRatingKey() {
+        Rating testHeartRating = Rating.newHeartRating(/*hasHeart=*/ true);
+        MediaMetadata.Builder builder = new MediaMetadata.Builder();
+        try {
+            builder.putRating(MediaMetadata.METADATA_KEY_TITLE, testHeartRating);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void builder_putBitmap_throwsIAE_withNonBitmapKey() {
+        Bitmap testBitmap = Bitmap.createBitmap(/*width=*/ 16, /*height=*/16,
+                Bitmap.Config.ARGB_8888);
+        MediaMetadata.Builder builder = new MediaMetadata.Builder();
+        try {
+            builder.putBitmap(MediaMetadata.METADATA_KEY_TITLE, testBitmap);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void builder_copyConstructor() {
+        long testYear = 2021;
+        MediaMetadata originalMetadata = new MediaMetadata.Builder()
+                .putLong(MediaMetadata.METADATA_KEY_YEAR, testYear)
+                .build();
+
+        MediaMetadata copiedMetadata = new MediaMetadata.Builder(originalMetadata).build();
+        assertEquals(originalMetadata, copiedMetadata);
+    }
+
+    @Test
+    public void equalsAndHashCode() {
+        String testTitle = "test_title";
+        long testYear = 2021;
+        MediaMetadata originalMetadata = new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_TITLE, testTitle)
+                .putLong(MediaMetadata.METADATA_KEY_YEAR, testYear)
+                .build();
+        MediaMetadata metadataToCompare = new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_TITLE, testTitle)
+                .putLong(MediaMetadata.METADATA_KEY_YEAR, testYear)
+                .build();
+
+        assertEquals(originalMetadata, metadataToCompare);
+        assertEquals(originalMetadata.hashCode(), metadataToCompare.hashCode());
+    }
+
+    @Test
+    public void equalsAndHashCode_ignoreRatingAndBitmap() {
+        Rating testHeartRating = Rating.newHeartRating(/*hasHeart=*/ true);
+        Bitmap testBitmap = Bitmap.createBitmap(/*width=*/ 16, /*height=*/16,
+                Bitmap.Config.ARGB_8888);
+        MediaMetadata originalMetadata = new MediaMetadata.Builder()
+                .putRating(MediaMetadata.METADATA_KEY_RATING, testHeartRating)
+                .putBitmap(MediaMetadata.METADATA_KEY_ART, testBitmap)
+                .build();
+        MediaMetadata emptyMetadata = new MediaMetadata.Builder().build();
+
+        assertEquals(originalMetadata, emptyMetadata);
+        assertEquals(originalMetadata.hashCode(), emptyMetadata.hashCode());
+    }
+
+    @Test
+    public void sizeAndKeySet() {
+        Rating testHeartRating = Rating.newHeartRating(/*hasHeart=*/ true);
+        Bitmap testBitmap = Bitmap.createBitmap(/*width=*/ 16, /*height=*/16,
+                Bitmap.Config.ARGB_8888);
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putRating(MediaMetadata.METADATA_KEY_RATING, testHeartRating)
+                .putBitmap(MediaMetadata.METADATA_KEY_ART, testBitmap)
+                .build();
+
+        assertEquals(2, metadata.size());
+        Set<String> keySet = metadata.keySet();
+        assertEquals(2, keySet.size());
+        assertTrue(keySet.contains(MediaMetadata.METADATA_KEY_RATING));
+        assertTrue(keySet.contains(MediaMetadata.METADATA_KEY_ART));
+    }
+
+    @Test
+    public void describeContents() {
+        long testYear = 2021;
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putLong(MediaMetadata.METADATA_KEY_YEAR, testYear)
+                .build();
+
+        assertEquals(0, metadata.describeContents());
+    }
+
+    @Test
+    public void writeToParcel() {
+        String testTitle = "test_title";
+        long testYear = 2021;
+        MediaMetadata originalMetadata = new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_TITLE, testTitle)
+                .putLong(MediaMetadata.METADATA_KEY_YEAR, testYear)
+                .build();
+
+        Parcel parcel = Parcel.obtain();
+        originalMetadata.writeToParcel(parcel, 0 /* flags */);
+        parcel.setDataPosition(0);
+        MediaMetadata metadataOut = MediaMetadata.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertEquals(originalMetadata, metadataOut);
+    }
+
+    @Test
+    public void getDescription() {
+        String testMediaId = "media_id";
+        String testTitle = "test_title";
+        String testSubtitle = "test_subtitle";
+        String testDescription = "test_description";
+        Bitmap testIcon = Bitmap.createBitmap(/*width=*/ 16, /*height=*/16,
+                Bitmap.Config.ARGB_8888);
+        String testMediaUri = "https://www.google.com";
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, testMediaId)
+                .putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, testTitle)
+                .putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, testSubtitle)
+                .putString(MediaMetadata.METADATA_KEY_DISPLAY_DESCRIPTION, testDescription)
+                .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, testIcon)
+                .putString(MediaMetadata.METADATA_KEY_MEDIA_URI, testMediaUri)
+                .build();
+
+        MediaDescription mediaDescription = metadata.getDescription();
+        assertTrue(TextUtils.equals(testMediaId, mediaDescription.getMediaId()));
+        assertTrue(TextUtils.equals(testTitle, mediaDescription.getTitle()));
+        assertTrue(TextUtils.equals(testSubtitle, mediaDescription.getSubtitle()));
+        assertTrue(TextUtils.equals(testDescription, mediaDescription.getDescription()));
+        assertNotNull(mediaDescription.getIconBitmap());
+        assertTrue(TextUtils.equals(testMediaUri, mediaDescription.getMediaUri().toString()));
+    }
+
+    @Test
     public void getBitmapDimensionLimit_returnsIntegerMaxWhenNotSet() {
         MediaMetadata metadata = new MediaMetadata.Builder().build();
         assertEquals(Integer.MAX_VALUE, metadata.getBitmapDimensionLimit());
@@ -97,7 +331,7 @@
                 .build();
         assertEquals(testBitmapDimensionLimit, metadata.getBitmapDimensionLimit());
 
-        // Using copy constructor, unset the limit by passing zero to the limit.
+        // Using copy constructor, unset the limit by passing Integer.MAX_VALUE to the limit.
         MediaMetadata copiedMetadataWithLimitUnset = new MediaMetadata.Builder()
                 .setBitmapDimensionLimit(Integer.MAX_VALUE)
                 .build();
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerTest.java b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
index eb85c42..f984381 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
@@ -613,17 +613,24 @@
             c.close();
 
             // also test with the MediaMetadataRetriever API
-            MediaMetadataRetriever woodly = new MediaMetadataRetriever();
-            AssetFileDescriptor afd = getAssetFileDescriptorFor(entry.fileName);
-            woodly.setDataSource(afd.getFileDescriptor(),
-                    afd.getStartOffset(), afd.getDeclaredLength());
+            String[] actual;
+            try (MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever()) {
+                AssetFileDescriptor afd = getAssetFileDescriptorFor(entry.fileName);
+                metadataRetriever.setDataSource(afd.getFileDescriptor(),
+                        afd.getStartOffset(), afd.getDeclaredLength());
 
-            String[] actual = new String[5];
-            actual[0] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
-            actual[1] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
-            actual[2] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
-            actual[3] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
-            actual[4] = woodly.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER);
+                actual = new String[5];
+                actual[0] = metadataRetriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_ARTIST);
+                actual[1] = metadataRetriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_ALBUM);
+                actual[2] = metadataRetriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST);
+                actual[3] = metadataRetriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_TITLE);
+                actual[4] = metadataRetriever.extractMetadata(
+                        MediaMetadataRetriever.METADATA_KEY_COMPOSER);
+            }
 
             for (int j = 0; j < 5; j++) {
                 if ("".equals(entry.tags[j])) {
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
index 6065a0e..3349206 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
@@ -35,8 +35,10 @@
 import android.os.Looper;
 import android.os.Process;
 import android.platform.test.annotations.AppModeFull;
+import android.provider.Settings;
 import android.test.InstrumentationTestCase;
 import android.test.UiThreadTest;
+import android.text.TextUtils;
 import android.view.KeyEvent;
 
 import com.android.compatibility.common.util.ApiLevelUtil;
@@ -56,7 +58,9 @@
     private static final String TAG = "MediaSessionManagerTest";
     private static final int TIMEOUT_MS = 3000;
     private static final int WAIT_MS = 500;
+    private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
 
+    private Context mContext;
     private AudioManager mAudioManager;
     private MediaSessionManager mSessionManager;
 
@@ -66,6 +70,7 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        mContext = getInstrumentation().getTargetContext();
         mAudioManager = (AudioManager) getInstrumentation().getTargetContext()
                 .getSystemService(Context.AUDIO_SERVICE);
         mSessionManager = (MediaSessionManager) getInstrumentation().getTargetContext()
@@ -564,6 +569,27 @@
         }
     }
 
+    public void testIsTrustedForMediaControl_withEnabledNotificationListener() throws Exception {
+        List<String> packageNames = getEnabledNotificationListenerPackages();
+        for (String packageName : packageNames) {
+            int packageUid =
+                    mContext.getPackageManager().getPackageUid(packageName, /* flags= */ 0);
+            MediaSessionManager.RemoteUserInfo info =
+                    new MediaSessionManager.RemoteUserInfo(packageName, /* pid= */ 0, packageUid);
+            assertTrue(mSessionManager.isTrustedForMediaControl(info));
+        }
+    }
+
+    public void testIsTrustedForMediaControl_withInvalidUid() throws Exception {
+        List<String> packageNames = getEnabledNotificationListenerPackages();
+        for (String packageName : packageNames) {
+            MediaSessionManager.RemoteUserInfo info =
+                    new MediaSessionManager.RemoteUserInfo(
+                            packageName, /* pid= */ 0, Process.myUid());
+            assertFalse(mSessionManager.isTrustedForMediaControl(info));
+        }
+    }
+
     private boolean listContainsToken(List<Session2Token> tokens, Session2Token token) {
         for (int i = 0; i < tokens.size(); i++) {
             if (tokens.get(i).equals(token)) {
@@ -596,6 +622,24 @@
                 new KeyEvent(downTime, System.currentTimeMillis(), KeyEvent.ACTION_UP, keyCode, 0));
     }
 
+    private List<String> getEnabledNotificationListenerPackages() {
+        List<String> listeners = new ArrayList<>();
+        String enabledNotificationListeners =
+                Settings.Secure.getString(
+                        mContext.getContentResolver(),
+                        ENABLED_NOTIFICATION_LISTENERS);
+        if (!TextUtils.isEmpty(enabledNotificationListeners)) {
+            String[] components = enabledNotificationListeners.split(":");
+            for (String componentString : components) {
+                ComponentName component = ComponentName.unflattenFromString(componentString);
+                if (component != null) {
+                    listeners.add(component.getPackageName());
+                }
+            }
+        }
+        return listeners;
+    }
+
     private class VolumeKeyLongPressListener
             implements MediaSessionManager.OnVolumeKeyLongPressListener {
         private final List<KeyEvent> mKeyEvents = new ArrayList<>();
diff --git a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
index 9a2fe27..c0330cb 100644
--- a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
@@ -72,25 +72,14 @@
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.zip.Adler32;
 
 @SmallTest
 @RequiresDevice
 @AppModeFull(reason = "TODO: evaluate and port to instant")
 @RunWith(AndroidJUnit4.class)
-public class NativeDecoderTest extends MediaPlayerTestBase {
+public class NativeDecoderTest extends MediaTestBase {
     private static final String TAG = "DecoderTest";
 
-    private static final int RESET_MODE_NONE = 0;
-    private static final int RESET_MODE_RECONFIGURE = 1;
-    private static final int RESET_MODE_FLUSH = 2;
-    private static final int RESET_MODE_EOS_FLUSH = 3;
-
-    private static final String[] CSD_KEYS = new String[] { "csd-0", "csd-1" };
-
-    private static final int CONFIG_MODE_NONE = 0;
-    private static final int CONFIG_MODE_QUEUE = 1;
-
     private static final boolean sIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
 
     static final String mInpPrefix = WorkDir.getMediaDirString();
@@ -115,94 +104,6 @@
         super.tearDown();
     }
 
-    // check that native extractor behavior matches java extractor
-
-    private void compareArrays(String message, int[] a1, int[] a2) {
-        if (a1 == a2) {
-            return;
-        }
-
-        assertNotNull(message + ": array 1 is null", a1);
-        assertNotNull(message + ": array 2 is null", a2);
-
-        assertEquals(message + ": arraylengths differ", a1.length, a2.length);
-        int length = a1.length;
-
-        for (int i = 0; i < length; i++)
-            if (a1[i] != a2[i]) {
-                Log.i("@@@@", Arrays.toString(a1));
-                Log.i("@@@@", Arrays.toString(a2));
-                fail(message + ": at index " + i);
-            }
-    }
-
-    @Ignore
-    @Test
-    public void SKIP_testExtractor() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest#testExtract where
-        // checksum is computed over track format attributes, track buffer and buffer
-        // info in both SDK and NDK side and checked for equality
-        testExtractor("sinesweepogg.ogg");
-        testExtractor("sinesweepoggmkv.mkv");
-        testExtractor("sinesweepoggmp4.mp4");
-        testExtractor("sinesweepmp3lame.mp3");
-        testExtractor("sinesweepmp3smpb.mp3");
-        testExtractor("sinesweepopus.mkv");
-        testExtractor("sinesweepopusmp4.mp4");
-        testExtractor("sinesweepm4a.m4a");
-        testExtractor("sinesweepflacmkv.mkv");
-        testExtractor("sinesweepflac.flac");
-        testExtractor("sinesweepflacmp4.mp4");
-        testExtractor("sinesweepwav.wav");
-
-        testExtractor("video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
-        testExtractor("bbb_s3_1280x720_webm_vp8_8mbps_60fps_opus_6ch_384kbps_48000hz.webm");
-        testExtractor("bbb_s4_1280x720_webm_vp9_0p31_4mbps_30fps_opus_stereo_128kbps_48000hz.webm");
-        testExtractor("video_1280x720_webm_av1_2000kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
-        testExtractor("video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz.3gp");
-        testExtractor("video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz.mp4");
-        testExtractor("video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
-
-        CtsTestServer foo = new CtsTestServer(mContext);
-        testExtractor(foo.getAssetUrl("noiseandchirps.ogg"), null, null);
-        testExtractor(foo.getAssetUrl("ringer.mp3"), null, null);
-        testExtractor(foo.getRedirectingAssetUrl("ringer.mp3"), null, null);
-
-        String[] keys = new String[] {"header0", "header1"};
-        String[] values = new String[] {"value0", "value1"};
-        testExtractor(foo.getAssetUrl("noiseandchirps.ogg"), keys, values);
-        HttpRequest req = foo.getLastRequest("noiseandchirps.ogg");
-        for (int i = 0; i < keys.length; i++) {
-            String key = keys[i];
-            String value = values[i];
-            Header[] header = req.getHeaders(key);
-            assertTrue("expecting " + key + ":" + value + ", saw " + Arrays.toString(header),
-                    header.length == 1 && header[0].getValue().equals(value));
-        }
-
-        String[] emptyArray = new String[0];
-        testExtractor(foo.getAssetUrl("noiseandchirps.ogg"), emptyArray, emptyArray);
-    }
-
-    /**
-     * |keys| and |values| should be arrays of the same length.
-     *
-     * If keys or values is null, test {@link MediaExtractor#setDataSource(String)}
-     * and NDK counter part, i.e. set data source without headers.
-     *
-     * If keys or values is zero length, test {@link MediaExtractor#setDataSource(String, Map))}
-     * and NDK counter part with null headers.
-     *
-     */
-    private void testExtractor(String path, String[] keys, String[] values) throws Exception {
-        int[] jsizes = getSampleSizes(path, keys, values);
-        int[] nsizes = getSampleSizesNativePath(path, keys, values, /* testNativeSource = */ false);
-        int[] nsizes2 = getSampleSizesNativePath(path, keys, values, /* testNativeSource = */ true);
-
-        compareArrays("different samplesizes", jsizes, nsizes);
-        compareArrays("different samplesizes native source", jsizes, nsizes2);
-    }
-
     protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
             throws FileNotFoundException {
         Preconditions.assertTestFileExists(mInpPrefix + res);
@@ -212,746 +113,6 @@
         return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
     }
 
-    private void testExtractor(final String res) throws Exception {
-        AssetFileDescriptor fd = getAssetFileDescriptorFor(res);
-
-        int[] jsizes = getSampleSizes(
-                fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
-        int[] nsizes = getSampleSizesNative(
-                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
-
-        fd.close();
-        compareArrays("different samples", jsizes, nsizes);
-    }
-
-    private static int[] getSampleSizes(String path, String[] keys, String[] values) throws IOException {
-        MediaExtractor ex = new MediaExtractor();
-        if (keys == null || values == null) {
-            ex.setDataSource(path);
-        } else {
-            Map<String, String> headers = null;
-            int numheaders = Math.min(keys.length, values.length);
-            for (int i = 0; i < numheaders; i++) {
-                if (headers == null) {
-                    headers = new HashMap<>();
-                }
-                String key = keys[i];
-                String value = values[i];
-                headers.put(key, value);
-            }
-            ex.setDataSource(path, headers);
-        }
-
-        return getSampleSizes(ex);
-    }
-
-    private static int[] getSampleSizes(FileDescriptor fd, long offset, long size)
-            throws IOException {
-        MediaExtractor ex = new MediaExtractor();
-        ex.setDataSource(fd, offset, size);
-        return getSampleSizes(ex);
-    }
-
-    private static int[] getSampleSizes(MediaExtractor ex) {
-        ArrayList<Integer> foo = new ArrayList<Integer>();
-        ByteBuffer buf = ByteBuffer.allocate(1024*1024);
-        int numtracks = ex.getTrackCount();
-        assertTrue("no tracks", numtracks > 0);
-        foo.add(numtracks);
-        for (int i = 0; i < numtracks; i++) {
-            MediaFormat format = ex.getTrackFormat(i);
-            String mime = format.getString(MediaFormat.KEY_MIME);
-            if (mime.startsWith("audio/")) {
-                foo.add(0);
-                foo.add(format.getInteger(MediaFormat.KEY_SAMPLE_RATE));
-                foo.add(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
-                foo.add((int)format.getLong(MediaFormat.KEY_DURATION));
-            } else if (mime.startsWith("video/")) {
-                foo.add(1);
-                foo.add(format.getInteger(MediaFormat.KEY_WIDTH));
-                foo.add(format.getInteger(MediaFormat.KEY_HEIGHT));
-                foo.add((int)format.getLong(MediaFormat.KEY_DURATION));
-            } else {
-                fail("unexpected mime type: " + mime);
-            }
-            ex.selectTrack(i);
-        }
-        while(true) {
-            int n = ex.readSampleData(buf, 0);
-            if (n < 0) {
-                break;
-            }
-            foo.add(n);
-            foo.add(ex.getSampleTrackIndex());
-            foo.add(ex.getSampleFlags());
-            foo.add((int)ex.getSampleTime()); // just the low bits should be OK
-            byte[] foobar = new byte[n];
-            buf.get(foobar, 0, n);
-            foo.add(adler32(foobar));
-            ex.advance();
-        }
-
-        int [] ret = new int[foo.size()];
-        for (int i = 0; i < ret.length; i++) {
-            ret[i] = foo.get(i);
-        }
-        return ret;
-    }
-
-    private static native int[] getSampleSizesNative(int fd, long offset, long size);
-    private static native int[] getSampleSizesNativePath(
-            String path, String[] keys, String[] values, boolean testNativeSource);
-
-    @Presubmit
-    @Ignore
-    @Test
-    public void SKIP_testExtractorFileDurationNative() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$FunctionalityTest#testExtract where
-        // checksum is computed over track format attributes, track buffer and buffer
-        // info in both SDK and NDK side and checked for equality. KEY_DURATION for each track is
-        // part of the checksum.
-        testExtractorFileDurationNative(
-                "video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
-    }
-
-    private void testExtractorFileDurationNative(final String res) throws Exception {
-        AssetFileDescriptor fd = getAssetFileDescriptorFor(res);
-        long durationUs = getExtractorFileDurationNative(
-                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
-
-        MediaExtractor ex = new MediaExtractor();
-        ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
-
-        int numtracks = ex.getTrackCount();
-        long aDurationUs = -1, vDurationUs = -1;
-        for (int i = 0; i < numtracks; i++) {
-            MediaFormat format = ex.getTrackFormat(i);
-            String mime = format.getString(MediaFormat.KEY_MIME);
-            if (mime.startsWith("audio/")) {
-                aDurationUs = format.getLong(MediaFormat.KEY_DURATION);
-            } else if (mime.startsWith("video/")) {
-                vDurationUs = format.getLong(MediaFormat.KEY_DURATION);
-            }
-        }
-
-        assertTrue("duration inconsistency",
-                durationUs < 0 || durationUs >= aDurationUs && durationUs >= vDurationUs);
-
-    }
-
-    private static native long getExtractorFileDurationNative(int fd, long offset, long size);
-
-    @Presubmit
-    public void SKIP_testExtractorCachedDurationNative() throws Exception {
-        // duplicate of CtsMediaV2TestCases:ExtractorTest$SetDataSourceTest#testDataSourceNative
-        CtsTestServer foo = new CtsTestServer(mContext);
-        String url = foo.getAssetUrl("ringer.mp3");
-        long cachedDurationUs = getExtractorCachedDurationNative(url, /* testNativeSource = */ false);
-        assertTrue("cached duration negative", cachedDurationUs >= 0);
-        cachedDurationUs = getExtractorCachedDurationNative(url, /* testNativeSource = */ true);
-        assertTrue("cached duration negative native source", cachedDurationUs >= 0);
-    }
-
-    private static native long getExtractorCachedDurationNative(String uri, boolean testNativeSource);
-
-    @Ignore
-    @Test
-    public void SKIP_testDecoder() throws Exception {
-        // duplicate of CtsMediaV2TestCases:CodecDecoderTest#testSimpleDecode where checksum  is
-        // computed over decoded output in both SDK and NDK side and checked for equality.
-        int testsRun =
-            testDecoder("sinesweepogg.ogg") +
-            testDecoder("sinesweepoggmkv.mkv") +
-            testDecoder("sinesweepoggmp4.mp4") +
-            testDecoder("sinesweepmp3lame.mp3") +
-            testDecoder("sinesweepmp3smpb.mp3") +
-            testDecoder("sinesweepopus.mkv") +
-            testDecoder("sinesweepopusmp4.mp4") +
-            testDecoder("sinesweepm4a.m4a") +
-            testDecoder("sinesweepflacmkv.mkv") +
-            testDecoder("sinesweepflac.flac") +
-            testDecoder("sinesweepflacmp4.mp4") +
-            testDecoder("sinesweepwav.wav") +
-            testDecoder("video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4") +
-            testDecoder("bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz.webm") +
-            testDecoder("bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm") +
-            testDecoder("video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz.3gp") +
-            testDecoder("video_480x360_mp4_mpeg2_1500kbps_30fps_aac_stereo_128kbps_48000hz.mp4");
-            testDecoder("video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
-        if (testsRun == 0) {
-            MediaUtils.skipTest("no decoders found");
-        }
-    }
-
-    @Test
-    public void testDataSource() throws Exception {
-        int testsRun = testDecoder(
-                "video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz.3gp", /* wrapFd */
-                true, /* useCallback */ false);
-        if (testsRun == 0) {
-            MediaUtils.skipTest("no decoders found");
-        }
-    }
-
-    @Test
-    public void testDataSourceAudioOnly() throws Exception {
-        int testsRun = testDecoder(
-                "loudsoftmp3.mp3",
-                /* wrapFd */ true, /* useCallback */ false) +
-                testDecoder(
-                        "loudsoftaac.aac",
-                        /* wrapFd */ false, /* useCallback */ false);
-        if (testsRun == 0) {
-            MediaUtils.skipTest("no decoders found");
-        }
-    }
-
-    @Test
-    public void testDataSourceWithCallback() throws Exception {
-        int testsRun = testDecoder(
-                "video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz.3gp",/* wrapFd */
-                true, /* useCallback */ true);
-        if (testsRun == 0) {
-            MediaUtils.skipTest("no decoders found");
-        }
-    }
-
-    private int testDecoder(final String res) throws Exception {
-        return testDecoder(res, /* wrapFd */ false, /* useCallback */ false);
-    }
-
-    private int testDecoder(final String res, boolean wrapFd, boolean useCallback)
-            throws Exception {
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        if (!MediaUtils.hasCodecsForResource(mInpPrefix  + res)) {
-            return 0; // skip
-        }
-
-        AssetFileDescriptor fd = getAssetFileDescriptorFor(res);
-
-        int[] jdata1 = getDecodedData(
-                fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
-        int[] jdata2 = getDecodedData(
-                fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
-        int[] ndata1 = getDecodedDataNative(
-                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength(), wrapFd,
-                useCallback);
-        int[] ndata2 = getDecodedDataNative(
-                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength(), wrapFd,
-                useCallback);
-
-        fd.close();
-        compareArrays("inconsistent java decoder", jdata1, jdata2);
-        compareArrays("inconsistent native decoder", ndata1, ndata2);
-        compareArrays("different decoded data", jdata1, ndata1);
-        return 1;
-    }
-
-    private static int[] getDecodedData(FileDescriptor fd, long offset, long size)
-            throws IOException {
-        MediaExtractor ex = new MediaExtractor();
-        ex.setDataSource(fd, offset, size);
-        return getDecodedData(ex);
-    }
-    private static int[] getDecodedData(MediaExtractor ex) throws IOException {
-        int numtracks = ex.getTrackCount();
-        assertTrue("no tracks", numtracks > 0);
-        ArrayList<Integer>[] trackdata = new ArrayList[numtracks];
-        MediaCodec[] codec = new MediaCodec[numtracks];
-        MediaFormat[] format = new MediaFormat[numtracks];
-        ByteBuffer[][] inbuffers = new ByteBuffer[numtracks][];
-        ByteBuffer[][] outbuffers = new ByteBuffer[numtracks][];
-        for (int i = 0; i < numtracks; i++) {
-            format[i] = ex.getTrackFormat(i);
-            String mime = format[i].getString(MediaFormat.KEY_MIME);
-            if (mime.startsWith("audio/") || mime.startsWith("video/")) {
-                codec[i] = MediaCodec.createDecoderByType(mime);
-                codec[i].configure(format[i], null, null, 0);
-                codec[i].start();
-                inbuffers[i] = codec[i].getInputBuffers();
-                outbuffers[i] = codec[i].getOutputBuffers();
-                trackdata[i] = new ArrayList<>();
-            } else {
-                fail("unexpected mime type: " + mime);
-            }
-            ex.selectTrack(i);
-        }
-
-        boolean[] sawInputEOS = new boolean[numtracks];
-        boolean[] sawOutputEOS = new boolean[numtracks];
-        int eosCount = 0;
-        BufferInfo info = new BufferInfo();
-        while(eosCount < numtracks) {
-            int t = ex.getSampleTrackIndex();
-            if (t >= 0) {
-                assertFalse("saw input EOS twice", sawInputEOS[t]);
-                int bufidx = codec[t].dequeueInputBuffer(5000);
-                if (bufidx >= 0) {
-                    Log.i("@@@@", "track " + t + " buffer " + bufidx);
-                    ByteBuffer buf = inbuffers[t][bufidx];
-                    int sampleSize = ex.readSampleData(buf, 0);
-                    Log.i("@@@@", "read " + sampleSize + " @ " + ex.getSampleTime());
-                    if (sampleSize < 0) {
-                        sampleSize = 0;
-                        sawInputEOS[t] = true;
-                        Log.i("@@@@", "EOS");
-                        //break;
-                    }
-                    long presentationTimeUs = ex.getSampleTime();
-
-                    codec[t].queueInputBuffer(bufidx, 0, sampleSize, presentationTimeUs,
-                            sawInputEOS[t] ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-                    ex.advance();
-                }
-            } else {
-                Log.i("@@@@", "no more input samples");
-                for (int tt = 0; tt < codec.length; tt++) {
-                    if (!sawInputEOS[tt]) {
-                        // we ran out of samples without ever signaling EOS to the codec,
-                        // so do that now
-                        int bufidx = codec[tt].dequeueInputBuffer(5000);
-                        if (bufidx >= 0) {
-                            codec[tt].queueInputBuffer(bufidx, 0, 0, 0,
-                                    MediaCodec.BUFFER_FLAG_END_OF_STREAM);
-                            sawInputEOS[tt] = true;
-                        }
-                    }
-                }
-            }
-
-            // see if any of the codecs have data available
-            for (int tt = 0; tt < codec.length; tt++) {
-                if (!sawOutputEOS[tt]) {
-                    int status = codec[tt].dequeueOutputBuffer(info, 1);
-                    if (status >= 0) {
-                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                            Log.i("@@@@", "EOS on track " + tt);
-                            sawOutputEOS[tt] = true;
-                            eosCount++;
-                        }
-                        Log.i("@@@@", "got decoded buffer for track " + tt + ", size " + info.size);
-                        if (info.size > 0) {
-                            addSampleData(trackdata[tt], outbuffers[tt][status], info.size, format[tt]);
-                        }
-                        codec[tt].releaseOutputBuffer(status, false);
-                    } else if (status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                        Log.i("@@@@", "output buffers changed for track " + tt);
-                        outbuffers[tt] = codec[tt].getOutputBuffers();
-                    } else if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                        format[tt] = codec[tt].getOutputFormat();
-                        Log.i("@@@@", "format changed for track " + t + ": " + format[tt].toString());
-                    } else if (status == MediaCodec.INFO_TRY_AGAIN_LATER) {
-                        Log.i("@@@@", "no buffer right now for track " + tt);
-                    } else {
-                        Log.i("@@@@", "unexpected info code for track " + tt + ": " + status);
-                    }
-                } else {
-                    Log.i("@@@@", "already at EOS on track " + tt);
-                }
-            }
-        }
-
-        int totalsize = 0;
-        for (int i = 0; i < numtracks; i++) {
-            totalsize += trackdata[i].size();
-        }
-        int[] trackbytes = new int[totalsize];
-        int idx = 0;
-        for (int i = 0; i < numtracks; i++) {
-            ArrayList<Integer> src = trackdata[i];
-            for (Integer integer : src) {
-                trackbytes[idx++] = integer;
-            }
-        }
-
-        for (MediaCodec mediaCodec : codec) {
-            mediaCodec.release();
-        }
-
-        return trackbytes;
-    }
-
-    static void addSampleData(ArrayList<Integer> dst,
-            ByteBuffer buf, int size, MediaFormat format) throws IOException{
-
-        Log.i("@@@", "addsample " + dst.size() + "/" + size);
-        int width = format.getInteger(MediaFormat.KEY_WIDTH, size);
-        int stride = format.getInteger(MediaFormat.KEY_STRIDE, width);
-        int height = format.getInteger(MediaFormat.KEY_HEIGHT, 1);
-        byte[] bb = new byte[width * height];
-        int offset = buf.position();
-        for (int i = 0; i < height; i++) {
-            buf.position(i * stride + offset);
-            buf.get(bb, i * width, width);
-        }
-        // bb is filled with data
-        long sum = adler32(bb);
-        dst.add( (int) (sum & 0xffffffff));
-    }
-
-    private final static Adler32 checksummer = new Adler32(); 
-    // simple checksum computed over every decoded buffer
-    static int adler32(byte[] input) {
-        checksummer.reset();
-        checksummer.update(input);
-        int ret = (int) checksummer.getValue();
-        Log.i("@@@", "adler " + input.length + "/" + ret);
-        return ret;
-    }
-
-    private static native int[] getDecodedDataNative(int fd, long offset, long size, boolean wrapFd,
-            boolean useCallback)
-            throws IOException;
-
-    @Ignore
-    @Test
-    public void SKIP_testVideoPlayback() throws Exception {
-        // duplicate of
-        // CtsMediaV2TestCases:CodecDecoderSurfaceTest#testSimpleDecodeToSurfaceNative[*]
-        int testsRun =
-            testVideoPlayback(
-                    "video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4") +
-            testVideoPlayback(
-                    "bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz.webm") +
-            testVideoPlayback(
-                    "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm") +
-            testVideoPlayback(
-                    "video_640x360_webm_av1_470kbps_30fps_vorbis_stereo_128kbps_48000hz.webm") +
-            testVideoPlayback(
-                    "video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz.3gp") +
-            testVideoPlayback(
-                    "video_176x144_mp4_mpeg2_105kbps_25fps_aac_stereo_128kbps_44100hz.mp4") +
-            testVideoPlayback(
-                    "video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
-        if (testsRun == 0) {
-            MediaUtils.skipTest("no decoders found");
-        }
-    }
-
-    private int testVideoPlayback(final String res) throws Exception {
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        if (!MediaUtils.checkCodecsForResource(mInpPrefix + res)) {
-            return 0; // skip
-        }
-
-        AssetFileDescriptor fd = getAssetFileDescriptorFor(res);
-
-        boolean ret = testPlaybackNative(mActivity.getSurfaceHolder().getSurface(),
-                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
-        assertTrue("native playback error", ret);
-        return 1;
-    }
-
-    private static native boolean testPlaybackNative(Surface surface,
-            int fd, long startOffset, long length);
-
-    @Presubmit
-    @NonMediaMainlineTest
-    @Test
-    public void testMuxerAvc() throws Exception {
-        // IMPORTANT: this file must not have B-frames
-        testMuxer("video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4", false);
-    }
-
-    @NonMediaMainlineTest
-    @Test
-    public void testMuxerH263() throws Exception {
-        // IMPORTANT: this file must not have B-frames
-        testMuxer("video_176x144_3gp_h263_300kbps_25fps_aac_stereo_128kbps_11025hz.3gp", false);
-    }
-
-    @NonMediaMainlineTest
-    @Test
-    public void testMuxerHevc() throws Exception {
-        // IMPORTANT: this file must not have B-frames
-        testMuxer("video_640x360_mp4_hevc_450kbps_no_b.mp4", false);
-    }
-
-    @NonMediaMainlineTest
-    @Test
-    public void testMuxerVp8() throws Exception {
-        testMuxer("bbb_s1_640x360_webm_vp8_2mbps_30fps_vorbis_5ch_320kbps_48000hz.webm", true);
-    }
-
-    @NonMediaMainlineTest
-    @Test
-    public void testMuxerVp9() throws Exception {
-        testMuxer("video_1280x720_webm_vp9_csd_309kbps_25fps_vorbis_stereo_128kbps_48000hz.webm",
-                true);
-    }
-
-    @NonMediaMainlineTest
-    @Test
-    public void testMuxerVp9NoCsd() throws Exception {
-        testMuxer("bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm",
-                true);
-    }
-
-    @NonMediaMainlineTest
-    @Test
-    public void testMuxerVp9Hdr() throws Exception {
-        testMuxer("video_256x144_webm_vp9_hdr_83kbps_24fps.webm", true);
-    }
-
-    // We do not support MPEG-2 muxing as of yet
-    @Ignore
-    @Test
-    public void SKIP_testMuxerMpeg2() throws Exception {
-        // IMPORTANT: this file must not have B-frames
-        testMuxer("video_176x144_mp4_mpeg2_105kbps_25fps_aac_stereo_128kbps_44100hz.mp4", false);
-    }
-
-    @NonMediaMainlineTest
-    @Test
-    public void testMuxerMpeg4() throws Exception {
-        // IMPORTANT: this file must not have B-frames
-        testMuxer("video_176x144_mp4_mpeg4_300kbps_25fps_aac_stereo_128kbps_44100hz.mp4", false);
-    }
-
-    private void testMuxer(final String res, boolean webm) throws Exception {
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        if (!MediaUtils.checkCodecsForResource(mInpPrefix + res)) {
-            return; // skip
-        }
-
-        AssetFileDescriptor infd = getAssetFileDescriptorFor(res);
-
-        File base = mContext.getExternalFilesDir(null);
-        String tmpFile = base.getPath() + "/tmp.dat";
-        Log.i("@@@", "using tmp file " + tmpFile);
-        new File(tmpFile).delete();
-        ParcelFileDescriptor out = ParcelFileDescriptor.open(new File(tmpFile),
-                ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE);
-
-        assertTrue("muxer failed", testMuxerNative(
-                infd.getParcelFileDescriptor().getFd(), infd.getStartOffset(), infd.getLength(),
-                out.getFd(), webm));
-
-        // compare the original with the remuxed
-        MediaExtractor org = new MediaExtractor();
-        org.setDataSource(infd.getFileDescriptor(),
-                infd.getStartOffset(), infd.getLength());
-
-        MediaExtractor remux = new MediaExtractor();
-        remux.setDataSource(out.getFileDescriptor());
-
-        assertEquals("mismatched numer of tracks", org.getTrackCount(), remux.getTrackCount());
-        // allow duration mismatch for webm files as ffmpeg does not consider the duration of the
-        // last frame while libwebm (and our framework) does.
-        final long maxDurationDiffUs = webm ? 50000 : 0; // 50ms for webm
-        for (int i = 0; i < org.getTrackCount(); i++) {
-            MediaFormat format1 = org.getTrackFormat(i);
-            MediaFormat format2 = remux.getTrackFormat(i);
-            Log.i("@@@", "org: " + format1);
-            Log.i("@@@", "remux: " + format2);
-            assertTrue("different formats", compareFormats(format1, format2, maxDurationDiffUs));
-        }
-
-        org.release();
-        remux.release();
-
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        MediaPlayer player1 =
-                MediaPlayer.create(mContext, Uri.fromFile(new File(mInpPrefix + res)));
-        MediaPlayer player2 = MediaPlayer.create(mContext, Uri.parse("file://" + tmpFile));
-        assertEquals("duration is different",
-                     player1.getDuration(), player2.getDuration(), maxDurationDiffUs * 0.001);
-        player1.release();
-        player2.release();
-        new File(tmpFile).delete();
-    }
-
-    private String hexString(ByteBuffer buf) {
-        if (buf == null) {
-            return "(null)";
-        }
-        final char[] digits =
-            { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
-
-        StringBuilder hex = new StringBuilder();
-        for (int i = buf.position(); i < buf.limit(); ++i) {
-            byte c = buf.get(i);
-            hex.append(digits[(c >> 4) & 0xf]);
-            hex.append(digits[c & 0xf]);
-        }
-        return hex.toString();
-    }
-
-    /** returns: null if key is in neither formats, true if they match and false otherwise */
-    private Boolean compareByteBufferInFormats(MediaFormat f1, MediaFormat f2, String key) {
-        ByteBuffer bufF1 = f1.containsKey(key) ? f1.getByteBuffer(key) : null;
-        ByteBuffer bufF2 = f2.containsKey(key) ? f2.getByteBuffer(key) : null;
-        if (bufF1 == null && bufF2 == null) {
-            return null;
-        }
-        if (bufF1 == null || !bufF1.equals(bufF2)) {
-            Log.i("@@@", "org " + key + ": " + hexString(bufF1));
-            Log.i("@@@", "rmx " + key + ": " + hexString(bufF2));
-            return false;
-        }
-        return true;
-    }
-
-    private boolean compareFormats(MediaFormat f1, MediaFormat f2, long maxDurationDiffUs) {
-        final String KEY_DURATION = MediaFormat.KEY_DURATION;
-
-        // allow some difference in durations
-        if (maxDurationDiffUs > 0
-                && f1.containsKey(KEY_DURATION) && f2.containsKey(KEY_DURATION)
-                && Math.abs(f1.getLong(KEY_DURATION)
-                        - f2.getLong(KEY_DURATION)) <= maxDurationDiffUs) {
-            f2.setLong(KEY_DURATION, f1.getLong(KEY_DURATION));
-        }
-
-        // verify hdr-static-info
-        if (Boolean.FALSE.equals(compareByteBufferInFormats(f1, f2, "hdr-static-info"))) {
-            return false;
-        }
-
-        // verify CSDs
-        for (int i = 0;; ++i) {
-            String key = "csd-" + i;
-            Boolean match = compareByteBufferInFormats(f1, f2, key);
-            if (match == null) {
-                break;
-            } else if (!match) {
-                return false;
-            }
-        }
-
-        // before S, mpeg4 writers jammed a fixed SAR value into the output;
-        // this was fixed in S
-        if (!sIsAtLeastS) {
-            if (f1.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT)
-                            && f2.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT)) {
-                f2.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT,
-                                f1.getInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT));
-            }
-            if (f1.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH)
-                            && f2.containsKey(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH)) {
-                f2.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH,
-                                f1.getInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH));
-            }
-        }
-
-        // look for f2 (the new) being a superset (>=) of f1 (the original)
-        // ensure that all of our fields in f1 appear in f2 with the same
-        // value. We allow f2 to contain extra fields.
-        Set<String> keys = f1.getKeys();
-        for (String key: keys) {
-            if (key == null) {
-                continue;
-            }
-            if (!f2.containsKey(key)) {
-                return false;
-            }
-            int f1Type = f1.getValueTypeForKey(key);
-            if (f1Type != f2.getValueTypeForKey(key)) {
-                return false;
-            }
-            switch (f1Type) {
-                case MediaFormat.TYPE_INTEGER:
-                    int f1Int = f1.getInteger(key);
-                    int f2Int = f2.getInteger(key);
-                    if (f1Int != f2Int) {
-                        return false;
-                    }
-                    break;
-                case MediaFormat.TYPE_LONG:
-                    long f1Long = f1.getLong(key);
-                    long f2Long = f2.getLong(key);
-                    if (f1Long != f2Long) {
-                        return false;
-                    }
-                    break;
-                case MediaFormat.TYPE_FLOAT:
-                    float f1Float = f1.getFloat(key);
-                    float f2Float = f2.getFloat(key);
-                    if (f1Float != f2Float) {
-                        return false;
-                    }
-                    break;
-                case MediaFormat.TYPE_STRING:
-                    String f1String = f1.getString(key);
-                    String f2String = f2.getString(key);
-                    if (!f1String.equals(f2String)) {
-                        return false;
-                    }
-                    break;
-                case MediaFormat.TYPE_BYTE_BUFFER:
-                    ByteBuffer f1ByteBuffer = f1.getByteBuffer(key);
-                    ByteBuffer f2ByteBuffer = f2.getByteBuffer(key);
-                    if (!f1ByteBuffer.equals(f2ByteBuffer)) {
-                        return false;
-                    }
-                    break;
-                default:
-                    return false;
-            }
-        }
-
-        // repeat for getFeatures
-        // (which we don't use in this test, but include for completeness)
-        Set<String> features = f1.getFeatures();
-        for (String key: features) {
-            if (key == null) {
-                continue;
-            }
-            if (!f2.containsKey(key)) {
-                return false;
-            }
-            int f1Type = f1.getValueTypeForKey(key);
-            if (f1Type != f2.getValueTypeForKey(key)) {
-                return false;
-            }
-            switch (f1Type) {
-                case MediaFormat.TYPE_INTEGER:
-                    int f1Int = f1.getInteger(key);
-                    int f2Int = f2.getInteger(key);
-                    if (f1Int != f2Int) {
-                        return false;
-                    }
-                    break;
-                case MediaFormat.TYPE_LONG:
-                    long f1Long = f1.getLong(key);
-                    long f2Long = f2.getLong(key);
-                    if (f1Long != f2Long) {
-                        return false;
-                    }
-                    break;
-                case MediaFormat.TYPE_FLOAT:
-                    float f1Float = f1.getFloat(key);
-                    float f2Float = f2.getFloat(key);
-                    if (f1Float != f2Float) {
-                        return false;
-                    }
-                    break;
-                case MediaFormat.TYPE_STRING:
-                    String f1String = f1.getString(key);
-                    String f2String = f2.getString(key);
-                    if (!f1String.equals(f2String)) {
-                        return false;
-                    }
-                    break;
-                case MediaFormat.TYPE_BYTE_BUFFER:
-                    ByteBuffer f1ByteBuffer = f1.getByteBuffer(key);
-                    ByteBuffer f2ByteBuffer = f2.getByteBuffer(key);
-                    if (!f1ByteBuffer.equals(f2ByteBuffer)) {
-                        return false;
-                    }
-                    break;
-                default:
-                    return false;
-            }
-        }
-
-        // not otherwise disqualified
-        return true;
-    }
-
-    private static native boolean testMuxerNative(int in, long inoffset, long insize,
-            int out, boolean webm);
-
     @Presubmit
     @Test
     public void testFormat() throws Exception {
diff --git a/tests/tests/media/src/android/media/cts/SystemMediaRouter2Test.java b/tests/tests/media/src/android/media/cts/SystemMediaRouter2Test.java
index 23e2494..3623f15 100644
--- a/tests/tests/media/src/android/media/cts/SystemMediaRouter2Test.java
+++ b/tests/tests/media/src/android/media/cts/SystemMediaRouter2Test.java
@@ -112,7 +112,8 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
         mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL);
+        mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL,
+                                                   Manifest.permission.QUERY_AUDIO_STATE);
 
         mExecutor = Executors.newSingleThreadExecutor();
         mAudioManager = (AudioManager) mContext.getSystemService(AUDIO_SERVICE);
@@ -334,7 +335,7 @@
 
     @Test
     public void testRouteCallbackOnRoutesChanged_whenLocalVolumeChanged() throws Exception {
-        if (mAudioManager.isVolumeFixed()) {
+        if (mAudioManager.isVolumeFixed() || mAudioManager.isFullVolumeDevice()) {
             return;
         }
 
diff --git a/tests/tests/media/src/android/media/cts/VideoEditorTest.java b/tests/tests/media/src/android/media/cts/VideoEditorTest.java
deleted file mode 100644
index b95431e..0000000
--- a/tests/tests/media/src/android/media/cts/VideoEditorTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.cts;
-
-import android.media.cts.R;
-
-import android.platform.test.annotations.AppModeFull;
-import android.test.ActivityInstrumentationTestCase2;
-
-@AppModeFull(reason = "TODO: evaluate and port to instant")
-public class VideoEditorTest extends ActivityInstrumentationTestCase2<MediaStubActivity> {
-
-    public VideoEditorTest() {
-        super(MediaStubActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        //setup for each test case.
-        super.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        //Test case clean up.
-        super.tearDown();
-    }
-}
diff --git a/tests/tests/media/common/src/android/media/cts/WorkDir.java b/tests/tests/media/src/android/media/cts/WorkDir.java
similarity index 100%
rename from tests/tests/media/common/src/android/media/cts/WorkDir.java
rename to tests/tests/media/src/android/media/cts/WorkDir.java
diff --git a/tests/tests/mediadrm/AndroidManifest.xml b/tests/tests/mediadrm/AndroidManifest.xml
deleted file mode 100644
index 5deca4e..0000000
--- a/tests/tests/mediadrm/AndroidManifest.xml
+++ /dev/null
@@ -1,180 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-     package="android.mediadrm.cts"
-     android:targetSandboxVersion="2">
-
-    <uses-sdk android:minSdkVersion="29"
-         android:targetSdkVersion="31"/>
-
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
-    <uses-permission android:name="android.permission.CAMERA"/>
-    <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
-    <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
-    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
-    <uses-permission android:name="android.permission.WAKE_LOCK"/>
-    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
-    <uses-permission android:name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER"/>
-    <uses-permission android:name="android.permission.SET_MEDIA_KEY_LISTENER"/>
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
-    <uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"/>
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
-
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
-
-    <uses-permission android:name="android.permission.VIBRATE"/>
-
-    <application android:networkSecurityConfig="@xml/network_security_config"
-         android:requestLegacyExternalStorage="true"
-         android:largeHeap="true">
-        <uses-library android:name="android.test.runner"/>
-        <uses-library android:name="org.apache.http.legacy"
-             android:required="false"/>
-
-        <activity android:name="android.mediadrm.cts.MediaProjectionActivity"
-             android:label="MediaProjectionActivity"
-             android:screenOrientation="locked"/>
-        <activity android:name="android.mediadrm.cts.AudioManagerStub"
-             android:label="AudioManagerStub"/>
-        <activity android:name="android.mediadrm.cts.AudioManagerStubHelper"
-             android:label="AudioManagerStubHelper"/>
-        <activity android:name="android.mediadrm.cts.DecodeAccuracyTestActivity"
-             android:label="DecodeAccuracyTestActivity"
-             android:screenOrientation="locked"
-             android:configChanges="mcc|mnc|keyboard|keyboardHidden|orientation|screenSize|navigation"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
-            </intent-filter>
-        </activity>
-        <activity android:name="android.mediadrm.cts.MediaSessionTestActivity"
-             android:label="MediaSessionTestActivity"
-             android:screenOrientation="nosensor"
-             android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
-            </intent-filter>
-        </activity>
-        <activity android:name="android.mediadrm.cts.MediaStubActivity"
-             android:label="MediaStubActivity"
-             android:screenOrientation="nosensor"
-             android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
-            </intent-filter>
-        </activity>
-        <activity android:name="android.mediadrm.cts.MediaStubActivity2"
-             android:label="MediaStubActivity2"
-             android:screenOrientation="nosensor"
-             android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
-            </intent-filter>
-        </activity>
-        <activity android:name="android.mediadrm.cts.FaceDetectorStub"
-             android:label="FaceDetectorStub"/>
-        <activity android:name="android.mediadrm.cts.MediaPlayerSurfaceStubActivity"
-             android:label="MediaPlayerSurfaceStubActivity"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
-            </intent-filter>
-        </activity>
-        <activity android:name="android.mediadrm.cts.ResourceManagerStubActivity"
-             android:label="ResourceManagerStubActivity"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
-            </intent-filter>
-        </activity>
-        <activity android:name="android.mediadrm.cts.ResourceManagerTestActivity1"
-             android:label="ResourceManagerTestActivity1"
-             android:process=":mediaCodecTestProcess1">
-        </activity>
-        <activity android:name="android.mediadrm.cts.ResourceManagerTestActivity2"
-             android:label="ResourceManagerTestActivity2"
-             android:process=":mediaCodecTestProcess2">
-        </activity>
-        <activity android:name="android.mediadrm.cts.RingtonePickerActivity"
-             android:label="RingtonePickerActivity"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"/>
-            </intent-filter>
-        </activity>
-        <activity android:name="android.mediadrm.cts.MockActivity"/>
-        <activity android:name="android.mediadrm.cts.MediaRouter2TestActivity"/>
-        <service android:name="android.mediadrm.cts.RemoteVirtualDisplayService"
-             android:process=":remoteService"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-            </intent-filter>
-        </service>
-        <service android:name="android.mediadrm.cts.StubMediaBrowserService"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.media.browse.MediaBrowserService"/>
-            </intent-filter>
-        </service>
-        <service android:name="android.mediadrm.cts.StubMediaSession2Service"
-             android:permission="android.mediadrm.cts"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.media.MediaSession2Service"/>
-            </intent-filter>
-        </service>
-        <service android:name="android.mediadrm.cts.LocalMediaProjectionService"
-             android:foregroundServiceType="mediaProjection"
-             android:enabled="true">
-        </service>
-        <service android:name=".StubMediaRoute2ProviderService"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.media.MediaRoute2ProviderService"/>
-            </intent-filter>
-        </service>
-        <service android:name="android.mediadrm.cts.MediaButtonReceiverService"
-             android:exported="true"/>
-        <service android:name=".MediaBrowserServiceTestService"
-             android:process=":mediaBrowserServiceTestService"/>
-        <service android:name=".MediaSessionTestService"
-             android:process=":mediaSessionTestService"/>
-        <receiver android:name="android.mediadrm.cts.MediaButtonBroadcastReceiver"/>
-    </application>
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-         android:targetPackage="android.mediadrm.cts"
-         android:label="CTS tests of android.media DRM">
-        <meta-data android:name="listener"
-             android:value="com.android.cts.runner.CtsTestRunListener"/>
-    </instrumentation>
-
-</manifest>
diff --git a/tests/tests/mediadrm/libmediadrmndkjni/AMediaObjects.h b/tests/tests/mediadrm/libmediadrmndkjni/AMediaObjects.h
deleted file mode 100644
index c4d5397..0000000
--- a/tests/tests/mediadrm/libmediadrmndkjni/AMediaObjects.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef AMEDIAOBJECTS_H_
-#define AMEDIAOBJECTS_H_
-
-#include <utils/Log.h>
-
-#include "media/NdkMediaCrypto.h"
-#include "media/NdkMediaDrm.h"
-#include "media/NdkMediaExtractor.h"
-
-namespace {
-
-// Simple class to manage deletion of AMedia objects
-class AMediaObjects {
- public:
-    AMediaObjects();
-    virtual ~AMediaObjects();
-
-    void setCrypto(AMediaCrypto* const theCrypto) {
-        mCrypto = theCrypto;
-    }
-    void setDrm(AMediaDrm* const theDrm) {
-        mDrm = theDrm;
-    }
-    void setVideoExtractor(AMediaExtractor* const theExtractor) {
-        mVideoExtractor = theExtractor;
-    }
-    void setAudioExtractor(AMediaExtractor* const theExtractor) {
-        mAudioExtractor = theExtractor;
-    }
-
-    AMediaCrypto* getCrypto() const { return mCrypto; }
-    AMediaDrm* getDrm() const { return mDrm; }
-    AMediaExtractor* getAudioExtractor() const { return mAudioExtractor; }
-    AMediaExtractor* getVideoExtractor() const { return mVideoExtractor; }
-
- private:
-    AMediaCrypto *mCrypto;
-    AMediaDrm* mDrm;
-    AMediaExtractor* mAudioExtractor;
-    AMediaExtractor* mVideoExtractor;
-
-    // Disallow copy and assignment
-    AMediaObjects(const AMediaObjects&);
-    void operator=(const AMediaObjects&);
-};
-
-AMediaObjects::AMediaObjects(void) : mCrypto(NULL), mDrm(NULL),
-        mAudioExtractor(NULL), mVideoExtractor(NULL) {
-}
-
-AMediaObjects::~AMediaObjects() {
-    if (mCrypto) {
-        AMediaCrypto_delete(mCrypto);
-    }
-    if (mAudioExtractor) {
-        AMediaExtractor_delete(mAudioExtractor);
-    }
-    if (mVideoExtractor) {
-        AMediaExtractor_delete(mVideoExtractor);
-    }
-    if (mDrm) {
-        AMediaDrm_release(mDrm);
-    }
-}
-
-}  // anonymous namespace
-#endif  // AMEDIAOBJECTS_H_
-
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaPlayerTestBase.java b/tests/tests/mediadrm/src/android/mediadrm/cts/MediaPlayerTestBase.java
deleted file mode 100644
index 25d7ef7..0000000
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/MediaPlayerTestBase.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.mediadrm.cts;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.AssetFileDescriptor;
-import android.media.MediaPlayer;
-import android.media.cts.MediaStubActivity;
-import android.media.cts.Preconditions;
-import android.media.cts.TestUtils.Monitor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.os.PersistableBundle;
-import android.test.ActivityInstrumentationTestCase2;
-
-import com.android.compatibility.common.util.MediaUtils;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.net.HttpCookie;
-import java.util.List;
-import java.util.logging.Logger;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Base class for tests which use MediaPlayer to play audio or video.
- */
-public class MediaPlayerTestBase extends ActivityInstrumentationTestCase2<MediaStubActivity> {
-    private static final Logger LOG = Logger.getLogger(MediaPlayerTestBase.class.getName());
-
-    static final String mInpPrefix = WorkDir.getMediaDirString();
-
-    protected static final int SLEEP_TIME = 1000;
-    protected static final int LONG_SLEEP_TIME = 6000;
-    protected static final int STREAM_RETRIES = 20;
-    protected static boolean sUseScaleToFitMode = false;
-
-    protected Monitor mOnVideoSizeChangedCalled = new Monitor();
-    protected Monitor mOnVideoRenderingStartCalled = new Monitor();
-    protected Monitor mOnBufferingUpdateCalled = new Monitor();
-    protected Monitor mOnPrepareCalled = new Monitor();
-    protected Monitor mOnSeekCompleteCalled = new Monitor();
-    protected Monitor mOnCompletionCalled = new Monitor();
-    protected Monitor mOnInfoCalled = new Monitor();
-    protected Monitor mOnErrorCalled = new Monitor();
-
-    protected Context mContext;
-
-    protected MediaPlayer mMediaPlayer = null;
-    protected MediaPlayer mMediaPlayer2 = null;
-    protected MediaStubActivity mActivity;
-
-    public MediaPlayerTestBase() {
-        super(MediaStubActivity.class);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mActivity = getActivity();
-        getInstrumentation().waitForIdleSync();
-        try {
-            runTestOnUiThread(new Runnable() {
-                public void run() {
-                    mMediaPlayer = new MediaPlayer();
-                    mMediaPlayer2 = new MediaPlayer();
-                }
-            });
-        } catch (Throwable e) {
-            e.printStackTrace();
-            fail();
-        }
-        mContext = getInstrumentation().getTargetContext();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        if (mMediaPlayer != null) {
-            mMediaPlayer.release();
-            mMediaPlayer = null;
-        }
-        if (mMediaPlayer2 != null) {
-            mMediaPlayer2.release();
-            mMediaPlayer2 = null;
-        }
-        mActivity = null;
-        super.tearDown();
-    }
-
-    protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
-            throws FileNotFoundException {
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        File inpFile = new File(mInpPrefix + res);
-        ParcelFileDescriptor parcelFD =
-                ParcelFileDescriptor.open(inpFile, ParcelFileDescriptor.MODE_READ_ONLY);
-        return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize());
-    }
-
-    // returns true on success
-    protected boolean loadResource(final String res) throws Exception {
-        Preconditions.assertTestFileExists(mInpPrefix + res);
-        if (!MediaUtils.hasCodecsForResource(mInpPrefix + res)) {
-            return false;
-        }
-
-        AssetFileDescriptor afd = getAssetFileDescriptorFor(res);
-        try {
-            mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
-                    afd.getLength());
-
-            // Although it is only meant for video playback, it should not
-            // cause issues for audio-only playback.
-            int videoScalingMode = sUseScaleToFitMode?
-                                    MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT
-                                  : MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING;
-
-            mMediaPlayer.setVideoScalingMode(videoScalingMode);
-        } finally {
-            afd.close();
-        }
-        sUseScaleToFitMode = !sUseScaleToFitMode;  // Alternate the scaling mode
-        return true;
-    }
-
-    protected boolean checkLoadResource(String res) throws Exception {
-        return MediaUtils.check(loadResource(res), "no decoder found");
-    }
-
-    protected void loadSubtitleSource(String res) throws Exception {
-        AssetFileDescriptor afd = getAssetFileDescriptorFor(res);
-        try {
-            mMediaPlayer.addTimedTextSource(afd.getFileDescriptor(), afd.getStartOffset(),
-                      afd.getLength(), MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP);
-        } finally {
-            afd.close();
-        }
-    }
-
-    protected void playLiveVideoTest(String path, int playTime) throws Exception {
-        playVideoWithRetries(path, null, null, playTime);
-    }
-
-    protected void playLiveAudioOnlyTest(String path, int playTime) throws Exception {
-        playVideoWithRetries(path, -1, -1, playTime);
-    }
-
-    protected void playVideoTest(String path, int width, int height) throws Exception {
-        playVideoWithRetries(path, width, height, 0);
-    }
-
-    protected void playVideoWithRetries(String path, Integer width, Integer height, int playTime)
-            throws Exception {
-        boolean playedSuccessfully = false;
-        for (int i = 0; i < STREAM_RETRIES; i++) {
-          try {
-            mMediaPlayer.reset();
-            mMediaPlayer.setDataSource(path);
-            playLoadedVideo(width, height, playTime);
-            playedSuccessfully = true;
-            break;
-          } catch (PrepareFailedException e) {
-            // prepare() can fail because of network issues, so try again
-            LOG.warning("prepare() failed on try " + i + ", trying playback again");
-          }
-        }
-        assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
-    }
-
-    protected void playLoadedVideoTest(final String res, int width, int height) throws Exception {
-        if (!checkLoadResource(res)) {
-            return; // skip
-        }
-
-        playLoadedVideo(width, height, 0);
-    }
-
-    protected void playLiveVideoTest(
-            Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
-            int playTime) throws Exception {
-        playVideoWithRetries(uri, headers, cookies, null /* width */, null /* height */, playTime);
-    }
-
-    protected void playLiveAudioOnlyTest(
-            Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
-            int playTime) throws Exception {
-        playVideoWithRetries(uri, headers, cookies, -1 /* width */, -1 /* height */, playTime);
-    }
-
-    protected void playVideoWithRetries(
-            Uri uri, Map<String, String> headers, List<HttpCookie> cookies,
-            Integer width, Integer height, int playTime) throws Exception {
-        boolean playedSuccessfully = false;
-        for (int i = 0; i < STREAM_RETRIES; i++) {
-            try {
-                mMediaPlayer.reset();
-                mMediaPlayer.setDataSource(getInstrumentation().getTargetContext(),
-                        uri, headers, cookies);
-                playLoadedVideo(width, height, playTime);
-                playedSuccessfully = true;
-                break;
-            } catch (PrepareFailedException e) {
-                // prepare() can fail because of network issues, so try again
-                // playLoadedVideo already has reset the player so we can try again safely.
-                LOG.warning("prepare() failed on try " + i + ", trying playback again");
-            }
-        }
-        assertTrue("Stream did not play successfully after all attempts", playedSuccessfully);
-    }
-
-    /**
-     * Play a video which has already been loaded with setDataSource().
-     *
-     * @param width width of the video to verify, or null to skip verification
-     * @param height height of the video to verify, or null to skip verification
-     * @param playTime length of time to play video, or 0 to play entire video.
-     * with a non-negative value, this method stops the playback after the length of
-     * time or the duration the video is elapsed. With a value of -1,
-     * this method simply starts the video and returns immediately without
-     * stoping the video playback.
-     */
-    protected void playLoadedVideo(final Integer width, final Integer height, int playTime)
-            throws Exception {
-        final float leftVolume = 0.5f;
-        final float rightVolume = 0.5f;
-
-        boolean audioOnly = (width != null && width.intValue() == -1) ||
-                (height != null && height.intValue() == -1);
-
-        mMediaPlayer.setDisplay(mActivity.getSurfaceHolder());
-        mMediaPlayer.setScreenOnWhilePlaying(true);
-        mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {
-            @Override
-            public void onVideoSizeChanged(MediaPlayer mp, int w, int h) {
-                if (w == 0 && h == 0) {
-                    // A size of 0x0 can be sent initially one time when using NuPlayer.
-                    assertFalse(mOnVideoSizeChangedCalled.isSignalled());
-                    return;
-                }
-                mOnVideoSizeChangedCalled.signal();
-                if (width != null) {
-                    assertEquals(width.intValue(), w);
-                }
-                if (height != null) {
-                    assertEquals(height.intValue(), h);
-                }
-            }
-        });
-        mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
-            @Override
-            public boolean onError(MediaPlayer mp, int what, int extra) {
-                fail("Media player had error " + what + " playing video");
-                return true;
-            }
-        });
-        mMediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
-            @Override
-            public boolean onInfo(MediaPlayer mp, int what, int extra) {
-                if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
-                    mOnVideoRenderingStartCalled.signal();
-                }
-                return true;
-            }
-        });
-        try {
-          mMediaPlayer.prepare();
-        } catch (IOException e) {
-          mMediaPlayer.reset();
-          throw new PrepareFailedException();
-        }
-
-        mMediaPlayer.start();
-        if (!audioOnly) {
-            mOnVideoSizeChangedCalled.waitForSignal();
-            mOnVideoRenderingStartCalled.waitForSignal();
-        }
-        mMediaPlayer.setVolume(leftVolume, rightVolume);
-
-        // waiting to complete
-        if (playTime == -1) {
-            return;
-        } else if (playTime == 0) {
-            while (mMediaPlayer.isPlaying()) {
-                Thread.sleep(SLEEP_TIME);
-            }
-        } else {
-            Thread.sleep(playTime);
-        }
-
-        // validate a few MediaMetrics.
-        PersistableBundle metrics = mMediaPlayer.getMetrics();
-        if (metrics == null) {
-            fail("MediaPlayer.getMetrics() returned null metrics");
-        } else if (metrics.isEmpty()) {
-            fail("MediaPlayer.getMetrics() returned empty metrics");
-        } else {
-
-            int size = metrics.size();
-            Set<String> keys = metrics.keySet();
-
-            if (keys == null) {
-                fail("MediaMetricsSet returned no keys");
-            } else if (keys.size() != size) {
-                fail("MediaMetricsSet.keys().size() mismatch MediaMetricsSet.size()");
-            }
-
-            // we played something; so one of these should be non-null
-            String vmime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_VIDEO, null);
-            String amime = metrics.getString(MediaPlayer.MetricsConstants.MIME_TYPE_AUDIO, null);
-            if (vmime == null && amime == null) {
-                fail("getMetrics() returned neither video nor audio mime value");
-            }
-
-            long duration = metrics.getLong(MediaPlayer.MetricsConstants.DURATION, -2);
-            if (duration == -2) {
-                fail("getMetrics() didn't return a duration");
-            }
-            long playing = metrics.getLong(MediaPlayer.MetricsConstants.PLAYING, -2);
-            if (playing == -2) {
-                fail("getMetrics() didn't return a playing time");
-            }
-            if (!keys.contains(MediaPlayer.MetricsConstants.PLAYING)) {
-                fail("MediaMetricsSet.keys() missing: " + MediaPlayer.MetricsConstants.PLAYING);
-            }
-        }
-
-        mMediaPlayer.stop();
-    }
-
-    private static class PrepareFailedException extends Exception {}
-
-    public boolean isTv() {
-        PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
-        return pm.hasSystemFeature(pm.FEATURE_TELEVISION)
-                && pm.hasSystemFeature(pm.FEATURE_LEANBACK);
-    }
-
-    public boolean checkTv() {
-        return MediaUtils.check(isTv(), "not a TV");
-    }
-
-    protected void setOnErrorListener() {
-        mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
-            @Override
-            public boolean onError(MediaPlayer mp, int what, int extra) {
-                mOnErrorCalled.signal();
-                return false;
-            }
-        });
-    }
-}
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/Utils.java b/tests/tests/mediadrm/src/android/mediadrm/cts/Utils.java
deleted file mode 100644
index 88fdd06..0000000
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/Utils.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.mediadrm.cts;
-
-import android.app.Instrumentation;
-import android.app.NotificationManager;
-import android.app.UiAutomation;
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.AudioPlaybackConfiguration;
-import android.media.MediaPlayer;
-import android.media.session.MediaSessionManager.RemoteUserInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.ParcelFileDescriptor;
-import android.platform.test.annotations.AppModeFull;
-import android.util.Log;
-import androidx.test.platform.app.InstrumentationRegistry;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.Scanner;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import junit.framework.Assert;
-
-public class Utils {
-    private static final String TAG = "CtsMediaTestUtil";
-    private static final int TEST_TIMING_TOLERANCE_MS = 500;
-    private static final String MEDIA_PATH_INSTR_ARG_KEY = "media-path";
-
-    public static void enableAppOps(String packageName, String operation,
-            Instrumentation instrumentation) {
-        setAppOps(packageName, operation, instrumentation, true);
-    }
-
-    public static void disableAppOps(String packageName, String operation,
-            Instrumentation instrumentation) {
-        setAppOps(packageName, operation, instrumentation, false);
-    }
-
-    public static String convertStreamToString(InputStream is) {
-        try (Scanner scanner = new Scanner(is).useDelimiter("\\A")) {
-            return scanner.hasNext() ? scanner.next() : "";
-        }
-    }
-
-    public static String getMediaPath() {
-        Bundle bundle = InstrumentationRegistry.getArguments();
-        String mediaPath = bundle.getString(MEDIA_PATH_INSTR_ARG_KEY);
-        Log.i(TAG, "Media Path value is: " + mediaPath);
-
-        if (mediaPath != null && !mediaPath.isEmpty()) {
-            if (mediaPath.startsWith("http") || mediaPath.startsWith("file")) {
-                return mediaPath;
-            }
-            // Otherwise, assume a file path that is not already Uri formatted
-            return Uri.fromFile(new File(mediaPath)).toString();
-        }
-        return "https://storage.googleapis.com/wvmedia";
-    }
-
-    private static void setAppOps(String packageName, String operation,
-            Instrumentation instrumentation, boolean enable) {
-        StringBuilder cmd = new StringBuilder();
-        cmd.append("appops set ");
-        cmd.append(packageName);
-        cmd.append(" ");
-        cmd.append(operation);
-        cmd.append(enable ? " allow" : " deny");
-        instrumentation.getUiAutomation().executeShellCommand(cmd.toString());
-
-        StringBuilder query = new StringBuilder();
-        query.append("appops get ");
-        query.append(packageName);
-        query.append(" ");
-        query.append(operation);
-        String queryStr = query.toString();
-
-        String expectedResult = enable ? "allow" : "deny";
-        String result = "";
-        while(!result.contains(expectedResult)) {
-            ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(
-                                                            queryStr);
-            InputStream inputStream = new FileInputStream(pfd.getFileDescriptor());
-            result = convertStreamToString(inputStream);
-        }
-    }
-
-    protected static void toggleNotificationPolicyAccess(String packageName,
-            Instrumentation instrumentation, boolean on) throws IOException {
-
-        String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName;
-
-        // Get permission to enable accessibility
-        UiAutomation uiAutomation = instrumentation.getUiAutomation();
-        // Execute command
-        try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
-            Assert.assertNotNull("Failed to execute shell command: " + command, fd);
-            // Wait for the command to finish by reading until EOF
-            try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
-                byte[] buffer = new byte[4096];
-                while (in.read(buffer) > 0) {}
-            } catch (IOException e) {
-                throw new IOException("Could not read stdout of command: " + command, e);
-            }
-        } finally {
-            uiAutomation.destroy();
-        }
-
-        NotificationManager nm = (NotificationManager) instrumentation.getContext()
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-        Assert.assertEquals("Wrote setting should be the same as the read one", on,
-                nm.isNotificationPolicyAccessGranted());
-    }
-
-    static boolean compareRemoteUserInfo(RemoteUserInfo a, RemoteUserInfo b) {
-        if (a == null && b == null) {
-            return true;
-        } else if (a == null || b == null) {
-            return false;
-        }
-        return a.getPackageName().equals(b.getPackageName())
-                && a.getPid() == b.getPid()
-                && a.getUid() == b.getUid();
-    }
-
-    /**
-     * Assert that a media playback is started and an active {@link AudioPlaybackConfiguration}
-     * is created once. The playback will be stopped immediately after that.
-     * <p>For a media session to receive media button events, an actual playback is needed.
-     */
-    @AppModeFull(reason = "Instant apps cannot access the SD card")
-    static void assertMediaPlaybackStarted(Context context) {
-        final AudioManager am = new AudioManager(context);
-        final HandlerThread handlerThread = new HandlerThread(TAG);
-        handlerThread.start();
-        final TestAudioPlaybackCallback callback = new TestAudioPlaybackCallback();
-        MediaPlayer mediaPlayer = null;
-        final String mInpPrefix = WorkDir.getMediaDirString();
-
-        try {
-            final int activeConfigSizeBeforeStart = am.getActivePlaybackConfigurations().size();
-            final Handler handler = new Handler(handlerThread.getLooper());
-
-            am.registerAudioPlaybackCallback(callback, handler);
-            File testAudioFile = new File(mInpPrefix + "sine1khzm40db.wav");
-            Assert.assertTrue("Test audio file does not exist! path="
-                            + testAudioFile.getAbsolutePath(), testAudioFile.exists());
-            mediaPlayer = MediaPlayer.create(context, Uri.fromFile(testAudioFile));
-            mediaPlayer.start();
-            if (!callback.mCountDownLatch.await(TEST_TIMING_TOLERANCE_MS, TimeUnit.MILLISECONDS)
-                    || callback.mActiveConfigSize != activeConfigSizeBeforeStart + 1) {
-                Assert.fail("Failed to create an active AudioPlaybackConfiguration");
-            }
-        } catch (InterruptedException e) {
-            Assert.fail("Failed to create an active AudioPlaybackConfiguration");
-        } finally {
-            am.unregisterAudioPlaybackCallback(callback);
-            if (mediaPlayer != null) {
-                mediaPlayer.stop();
-                mediaPlayer.release();
-                mediaPlayer = null;
-            }
-            handlerThread.quitSafely();
-        }
-    }
-
-    private static class TestAudioPlaybackCallback extends AudioManager.AudioPlaybackCallback {
-        private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
-        private int mActiveConfigSize;
-
-        @Override
-        public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
-            // For non-framework apps, only anonymized active AudioPlaybackCallbacks will be
-            // notified.
-            mActiveConfigSize = configs.size();
-            mCountDownLatch.countDown();
-        }
-    }
-}
diff --git a/tests/tests/mediadrm/src/android/mediadrm/cts/WorkDir.java b/tests/tests/mediadrm/src/android/mediadrm/cts/WorkDir.java
deleted file mode 100644
index 48637da..0000000
--- a/tests/tests/mediadrm/src/android/mediadrm/cts/WorkDir.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.mediadrm.cts;
-
-import android.os.Environment;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Assert;
-
-import java.io.File;
-
-public class WorkDir {
-    private static final String MEDIA_PATH_INSTR_ARG_KEY = "media-path";
-    static private final File getTopDir() {
-        Assert.assertEquals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED);
-        return Environment.getExternalStorageDirectory();
-    }
-    static private final String getTopDirString() {
-        return (getTopDir().getAbsolutePath() + File.separator);
-    }
-    public static final String getMediaDirString() {
-        android.os.Bundle bundle = InstrumentationRegistry.getArguments();
-        String mediaDirString = bundle.getString(MEDIA_PATH_INSTR_ARG_KEY);
-        if (mediaDirString == null) {
-            return (getTopDirString() + "test/CtsMediaDrmTestCases-1.4/");
-        } else if (!mediaDirString.endsWith(File.separator)) {
-            // user has specified the mediaDirString via instrumentation-arg
-            return mediaDirString + File.separator;
-        } else {
-            // user has specified the mediaDirString via instrumentation-arg
-            return mediaDirString;
-        }
-    }
-}
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/CodecTest.java b/tests/tests/mediastress/src/android/mediastress/cts/CodecTest.java
index b33259f..fbf5cd9 100644
--- a/tests/tests/mediastress/src/android/mediastress/cts/CodecTest.java
+++ b/tests/tests/mediastress/src/android/mediastress/cts/CodecTest.java
@@ -507,14 +507,16 @@
         try {
             BitmapFactory mBitmapFactory = new BitmapFactory();
 
-            MediaMetadataRetriever mMediaMetadataRetriever = new MediaMetadataRetriever();
-            try {
-                mMediaMetadataRetriever.setDataSource(filePath);
-            } catch(Exception e) {
-                e.printStackTrace();
-                return false;
+            Bitmap outThumbnail;
+            try (MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever()) {
+                try {
+                    mediaMetadataRetriever.setDataSource(filePath);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    return false;
+                }
+                outThumbnail = mediaMetadataRetriever.getFrameAtTime(-1);
             }
-            Bitmap outThumbnail = mMediaMetadataRetriever.getFrameAtTime(-1);
 
             //Verify the thumbnail
             Bitmap goldenBitmap = mBitmapFactory.decodeFile(goldenPath);
diff --git a/tests/tests/multiuser/OWNERS b/tests/tests/multiuser/OWNERS
index 2b344eb..cbd8eb1 100644
--- a/tests/tests/multiuser/OWNERS
+++ b/tests/tests/multiuser/OWNERS
@@ -1,4 +1,3 @@
 # Bug component: 71510
 include /tests/app/OWNERS
-
-bookatz@google.com
+include platform/frameworks/base:/MULTIUSER_OWNERS
diff --git a/tests/tests/net/OWNERS b/tests/tests/net/OWNERS
new file mode 100644
index 0000000..8dfa455
--- /dev/null
+++ b/tests/tests/net/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 31808
+set noparent
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking_xts
diff --git a/tests/tests/net/native/Android.bp b/tests/tests/net/native/Android.bp
new file mode 100644
index 0000000..06ae4d2
--- /dev/null
+++ b/tests/tests/net/native/Android.bp
@@ -0,0 +1,63 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Build the unit tests.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "CtsNativeNetPlatformTestCases",
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+
+    srcs: [
+        "src/TagSocketTest.cpp",
+    ],
+
+    header_libs: [
+        "bpf_headers",
+    ],
+
+    static_libs: [
+        "libbase",
+        "libutils",
+        "libnettestutils",
+    ],
+
+    shared_libs: [
+        "libandroid_net",
+        "libbinder",
+        "liblog",
+    ],
+
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+    ],
+}
diff --git a/tests/tests/net/native/AndroidTest.xml b/tests/tests/net/native/AndroidTest.xml
new file mode 100644
index 0000000..8dde2cd
--- /dev/null
+++ b/tests/tests/net/native/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<!-- Soong does not autogenerate test config files for tests tagged with "cts". -->
+<configuration description="Config for CTS Native Network Platform test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="networking" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsNativeNetPlatformTestCases->/data/local/tmp/CtsNativeNetPlatformTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsNativeNetPlatformTestCases" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/tests/net/native/src/TagSocketTest.cpp b/tests/tests/net/native/src/TagSocketTest.cpp
new file mode 100644
index 0000000..9826747
--- /dev/null
+++ b/tests/tests/net/native/src/TagSocketTest.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless requied by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <android-base/format.h>
+#include <android/multinetwork.h>
+#include <binder/IServiceManager.h>
+#include <bpf/BpfUtils.h>
+#include <gtest/gtest.h>
+#include <nettestutils/DumpService.h>
+
+using android::IBinder;
+using android::IServiceManager;
+using android::bpf::getSocketCookie;
+using android::bpf::NONEXISTENT_COOKIE;
+using android::sp;
+using android::String16;
+using android::Vector;
+
+class TagSocketTest : public ::testing::Test {
+ public:
+  TagSocketTest() {
+    sp<IServiceManager> sm = android::defaultServiceManager();
+    mBinder = sm->getService(String16("connectivity"));
+  }
+
+  void SetUp() override { ASSERT_NE(nullptr, mBinder.get()); }
+
+ protected:
+  sp<IBinder> mBinder;
+};
+
+namespace {
+
+constexpr uid_t TEST_UID = 10086;
+constexpr uint32_t TEST_TAG = 42;
+
+[[maybe_unused]] void dumpBpfMaps(const sp<IBinder>& binder,
+                                  std::vector<std::string>& output) {
+  Vector<String16> vec;
+  android::status_t ret = dumpService(binder, {"trafficcontroller"}, output);
+  ASSERT_EQ(android::OK, ret)
+      << "Error dumping service: " << android::statusToString(ret);
+}
+
+[[maybe_unused]] bool socketIsTagged(const sp<IBinder>& binder, uint64_t cookie,
+                                     uid_t uid, uint32_t tag) {
+  std::string match =
+      fmt::format("cookie={} tag={:#x} uid={}", cookie, tag, uid);
+  std::vector<std::string> lines = {};
+  dumpBpfMaps(binder, lines);
+  for (const auto& line : lines) {
+    if (std::string::npos != line.find(match)) return true;
+  }
+  return false;
+}
+
+[[maybe_unused]] bool socketIsNotTagged(const sp<IBinder>& binder,
+                                        uint64_t cookie) {
+  std::string match = fmt::format("cookie={}", cookie);
+  std::vector<std::string> lines = {};
+  dumpBpfMaps(binder, lines);
+  for (const auto& line : lines) {
+    if (std::string::npos != line.find(match)) return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+TEST_F(TagSocketTest, TagSocket) {
+  int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  ASSERT_LE(0, sock);
+  uint64_t cookie = getSocketCookie(sock);
+  EXPECT_NE(NONEXISTENT_COOKIE, cookie);
+
+  // TODO(b/214338829): Uncomment lines that check tagging result from BPF map when the
+  // connectivity service is in charge of dumping BPF maps. Currently, it
+  // doesn't work because BPF maps are dumped by netd. The shell does not have
+  // permission to find netd service in user build. So, it only verify API
+  // return codes for now.
+  // EXPECT_TRUE(socketIsNotTagged(mBinder, cookie));
+
+  EXPECT_EQ(0, android_tag_socket(sock, TEST_TAG));
+  // EXPECT_TRUE(socketIsTagged(mBinder, cookie, geteuid(), TEST_TAG));
+  EXPECT_EQ(0, android_untag_socket(sock));
+  // EXPECT_TRUE(socketIsNotTagged(mBinder, cookie));
+
+  EXPECT_EQ(0, android_tag_socket_with_uid(sock, TEST_TAG, TEST_UID));
+  // EXPECT_TRUE(socketIsTagged(mBinder, cookie, TEST_UID, TEST_TAG));
+  EXPECT_EQ(0, android_untag_socket(sock));
+  // EXPECT_TRUE(socketIsNotTagged(mBinder, cookie));
+}
+
+TEST_F(TagSocketTest, TagSocketErrors) {
+  int sock = socket(AF_INET6, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  ASSERT_LE(0, sock);
+  uint64_t cookie = getSocketCookie(sock);
+  EXPECT_NE(NONEXISTENT_COOKIE, cookie);
+
+  // Untag an untagged socket.
+  EXPECT_EQ(-ENOENT, android_untag_socket(sock));
+  // EXPECT_TRUE(socketIsNotTagged(mBinder, cookie));
+
+  // Untag a closed socket.
+  close(sock);
+  EXPECT_EQ(-EBADF, android_untag_socket(sock));
+}
diff --git a/tests/tests/neuralnetworks/Android.bp b/tests/tests/neuralnetworks/Android.bp
index a4a508f..00298e6 100644
--- a/tests/tests/neuralnetworks/Android.bp
+++ b/tests/tests/neuralnetworks/Android.bp
@@ -19,6 +19,7 @@
 
 cc_test {
     name: "CtsNNAPITestCases",
+    host_supported: true,
     compile_multilib: "both",
     multilib: {
         lib32: {
@@ -30,23 +31,40 @@
     },
     whole_static_libs: ["CtsNNAPITests_static"],
     shared_libs: [
-        "libandroid",
         "liblog",
         "libneuralnetworks",
-        "libvulkan",
     ],
-    static_libs: [
-        "libbase_ndk",
-        "libgtest_ndk_c++",
-        "libgmock_ndk",
-    ],
-    // Tag this module as a cts test artifact
     test_suites: [
-        "cts",
-        "mts",
-        "mts-neuralnetworks",
         "general-tests",
     ],
+    target: {
+        android: {
+            shared_libs: [
+                "libandroid",
+                "libvulkan",
+            ],
+            static_libs: [
+                "libbase_ndk",
+                "libgtest_ndk_c++",
+                "libgmock_ndk",
+            ],
+            // Tag this module as a cts test artifact
+            test_suites: [
+                "cts",
+                "mts",
+                "mts-neuralnetworks",
+            ],
+            test_config: "AndroidTestDevice.xml",
+        },
+        host: {
+            static_libs: [
+                "libbase",
+                "libgmock",
+                "libgtest",
+            ],
+            test_config: "AndroidTestHost.xml",
+        },
+    },
     sdk_version: "current",
     stl: "c++_static",
     min_sdk_version: "30",
diff --git a/tests/tests/neuralnetworks/AndroidTest.xml b/tests/tests/neuralnetworks/AndroidTestDevice.xml
similarity index 100%
rename from tests/tests/neuralnetworks/AndroidTest.xml
rename to tests/tests/neuralnetworks/AndroidTestDevice.xml
diff --git a/tests/tests/media/audio/DynamicConfig.xml b/tests/tests/neuralnetworks/AndroidTestHost.xml
similarity index 61%
copy from tests/tests/media/audio/DynamicConfig.xml
copy to tests/tests/neuralnetworks/AndroidTestHost.xml
index 53528de..60a34fa 100644
--- a/tests/tests/media/audio/DynamicConfig.xml
+++ b/tests/tests/neuralnetworks/AndroidTestHost.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -12,9 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<configuration description="Runs CtsNNAPITestCases.">
+    <test class="com.android.tradefed.testtype.HostGTest" >
+        <option name="module-name" value="CtsNNAPITestCases" />
+        <option name="native-test-timeout" value="10m" />
+    </test>
+</configuration>
 
-<dynamicConfig>
-    <entry key="media_files_url">
-    <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/CtsMediaTestCases-1.4.zip</value>
-    </entry>
-</dynamicConfig>
diff --git a/tests/tests/neuralnetworks/benchmark/src/com/android/nn/benchmark/cts/NNAccuracyTest.java b/tests/tests/neuralnetworks/benchmark/src/com/android/nn/benchmark/cts/NNAccuracyTest.java
index 1851b36..6b31091 100644
--- a/tests/tests/neuralnetworks/benchmark/src/com/android/nn/benchmark/cts/NNAccuracyTest.java
+++ b/tests/tests/neuralnetworks/benchmark/src/com/android/nn/benchmark/cts/NNAccuracyTest.java
@@ -19,6 +19,7 @@
 import static junit.framework.TestCase.assertFalse;
 
 import android.app.Activity;
+import android.util.Log;
 import android.util.Pair;
 
 import androidx.test.filters.LargeTest;
@@ -49,6 +50,7 @@
  */
 @RunWith(Parameterized.class)
 public class NNAccuracyTest {
+    protected static final String TAG = NNAccuracyTest.class.getSimpleName();
 
     @Rule
     public ActivityTestRule<NNAccuracyActivity> mActivityRule =
@@ -105,7 +107,12 @@
             try (NNTestBase test = mModel.createNNTestBase(/*useNNAPI=*/true,
                         /*enableIntermediateTensorsDump=*/false)) {
                 test.setNNApiDeviceName(accelerator);
-                test.setupModel(mActivity);
+                if (!test.setupModel(mActivity)) {
+                    Log.d(TAG, String.format(
+                        "Cannot initialise test '%s' on accelerator %s, skipping",
+                        mModel.mModelName, accelerator));
+                    continue;
+                }
                 Pair<List<InferenceInOutSequence>, List<InferenceResult>> inferenceResults =
                         test.runBenchmarkCompleteInputSet(/*setRepeat=*/1, /*timeoutSec=*/3600);
                 BenchmarkResult benchmarkResult =
diff --git a/tests/tests/os/Android.bp b/tests/tests/os/Android.bp
index 3068f9f..d0513fa 100644
--- a/tests/tests/os/Android.bp
+++ b/tests/tests/os/Android.bp
@@ -60,7 +60,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
     sdk_version: "test_current",
diff --git a/tests/tests/os/AutoRevokeQApp/Android.bp b/tests/tests/os/AutoRevokeQApp/Android.bp
index 72e9f08..5c7d4b9 100644
--- a/tests/tests/os/AutoRevokeQApp/Android.bp
+++ b/tests/tests/os/AutoRevokeQApp/Android.bp
@@ -29,7 +29,7 @@
         "cts",
         "vts",
         "vts10",
-        "mts",
+        "mts-permission",
         "general-tests",
         "sts",
     ],
diff --git a/tests/tests/os/AutoRevokeRApp/Android.bp b/tests/tests/os/AutoRevokeRApp/Android.bp
index cb82a12..8e01388 100644
--- a/tests/tests/os/AutoRevokeRApp/Android.bp
+++ b/tests/tests/os/AutoRevokeRApp/Android.bp
@@ -28,7 +28,7 @@
     test_suites: [
         "cts",
         "vts",
-        "mts",
+        "mts-permission",
         "general-tests",
         "sts",
     ],
diff --git a/tests/tests/os/AutoRevokeSApp/Android.bp b/tests/tests/os/AutoRevokeSApp/Android.bp
index 77fba78..60ca80f 100644
--- a/tests/tests/os/AutoRevokeSApp/Android.bp
+++ b/tests/tests/os/AutoRevokeSApp/Android.bp
@@ -28,7 +28,7 @@
     test_suites: [
         "cts",
         "vts",
-        "mts",
+        "mts-permission",
         "general-tests",
         "sts",
     ],
diff --git a/tests/tests/os/CompanionTestApp/Android.bp b/tests/tests/os/CompanionTestApp/Android.bp
index b8d873e..3a0f07e 100644
--- a/tests/tests/os/CompanionTestApp/Android.bp
+++ b/tests/tests/os/CompanionTestApp/Android.bp
@@ -30,7 +30,7 @@
         "cts",
         "vts",
         "vts10",
-        "mts",
+        "mts-permission",
         "general-tests",
         "sts",
     ],
diff --git a/tests/tests/os/src/android/os/storage/cts/OWNERS b/tests/tests/os/src/android/os/storage/cts/OWNERS
index 948bcad..d76ba4f 100644
--- a/tests/tests/os/src/android/os/storage/cts/OWNERS
+++ b/tests/tests/os/src/android/os/storage/cts/OWNERS
@@ -1,5 +1,5 @@
-# Bug component: 141660526
+# Bug component: 95221
 per-file CrateInfoTest.java = felkachang@google.com
 per-file StorageCrateTest.java = felkachang@google.com
 per-file StorageStatsManagerTest.java = felkachang@google.com
-
+per-file StorageManagerTest.java = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
diff --git a/tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.bp b/tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.bp
index b2ddba2..fdb0be4 100644
--- a/tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.bp
+++ b/tests/tests/permission/AppThatAccessesCalendarContactsBodySensorCustomPermission/Android.bp
@@ -30,6 +30,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.bp b/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.bp
index 32bfd90..2bb3dd3 100644
--- a/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.bp
+++ b/tests/tests/permission/AppThatAccessesLocationOnCommand/Android.bp
@@ -31,7 +31,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
     srcs: [
         "src/**/*.java",
diff --git a/tests/tests/permission/AppThatAlsoDefinesPermissionA/Android.bp b/tests/tests/permission/AppThatAlsoDefinesPermissionA/Android.bp
index a8b5b08..46b7aec 100644
--- a/tests/tests/permission/AppThatAlsoDefinesPermissionA/Android.bp
+++ b/tests/tests/permission/AppThatAlsoDefinesPermissionA/Android.bp
@@ -26,7 +26,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/AppThatAlsoDefinesPermissionADifferentCert/Android.bp b/tests/tests/permission/AppThatAlsoDefinesPermissionADifferentCert/Android.bp
index 3918052..c88d0f7 100644
--- a/tests/tests/permission/AppThatAlsoDefinesPermissionADifferentCert/Android.bp
+++ b/tests/tests/permission/AppThatAlsoDefinesPermissionADifferentCert/Android.bp
@@ -26,7 +26,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/Android.bp b/tests/tests/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/Android.bp
index 08d1985..b1ef695 100644
--- a/tests/tests/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/Android.bp
+++ b/tests/tests/permission/AppThatAlsoDefinesPermissionGroupADifferentCert/Android.bp
@@ -26,7 +26,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/Android.bp b/tests/tests/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/Android.bp
index 3ef945d..52900df 100644
--- a/tests/tests/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/Android.bp
+++ b/tests/tests/permission/AppThatAlsoDefinesPermissionGroupADifferentCert30/Android.bp
@@ -26,7 +26,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/AppThatDefinesPermissionA/Android.bp b/tests/tests/permission/AppThatDefinesPermissionA/Android.bp
index f7e9089..54a575b 100644
--- a/tests/tests/permission/AppThatDefinesPermissionA/Android.bp
+++ b/tests/tests/permission/AppThatDefinesPermissionA/Android.bp
@@ -26,7 +26,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/AppThatDefinesPermissionWithInvalidGroup/Android.bp b/tests/tests/permission/AppThatDefinesPermissionWithInvalidGroup/Android.bp
index afe356a..9029b3a 100644
--- a/tests/tests/permission/AppThatDefinesPermissionWithInvalidGroup/Android.bp
+++ b/tests/tests/permission/AppThatDefinesPermissionWithInvalidGroup/Android.bp
@@ -25,7 +25,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/AppThatDefinesPermissionWithInvalidGroup30/Android.bp b/tests/tests/permission/AppThatDefinesPermissionWithInvalidGroup30/Android.bp
index 4b02006..04961e2 100644
--- a/tests/tests/permission/AppThatDefinesPermissionWithInvalidGroup30/Android.bp
+++ b/tests/tests/permission/AppThatDefinesPermissionWithInvalidGroup30/Android.bp
@@ -25,7 +25,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/AppThatDefinesPermissionWithPlatformGroup/Android.bp b/tests/tests/permission/AppThatDefinesPermissionWithPlatformGroup/Android.bp
index 57e9b9e..687ad74 100644
--- a/tests/tests/permission/AppThatDefinesPermissionWithPlatformGroup/Android.bp
+++ b/tests/tests/permission/AppThatDefinesPermissionWithPlatformGroup/Android.bp
@@ -25,7 +25,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp b/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp
index 4494a6f..c00d26d 100644
--- a/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp
+++ b/tests/tests/permission/AppThatDefinesUndefinedPermissionGroupElement/Android.bp
@@ -26,7 +26,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src/**/*.kt"],
 }
diff --git a/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/Android.bp b/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/Android.bp
index fa2ee89..f9ff21c 100644
--- a/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/Android.bp
+++ b/tests/tests/permission/AppThatDoesNotHaveBgLocationAccess/Android.bp
@@ -31,6 +31,6 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestBluetoothPermission30/Android.bp b/tests/tests/permission/AppThatRequestBluetoothPermission30/Android.bp
index a6de815..a3fe381 100644
--- a/tests/tests/permission/AppThatRequestBluetoothPermission30/Android.bp
+++ b/tests/tests/permission/AppThatRequestBluetoothPermission30/Android.bp
@@ -34,7 +34,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
     srcs: [":AppThatRequestBluetoothPermission"],
 }
diff --git a/tests/tests/permission/AppThatRequestBluetoothPermission31/Android.bp b/tests/tests/permission/AppThatRequestBluetoothPermission31/Android.bp
index f3bc59b..7dc2e24 100644
--- a/tests/tests/permission/AppThatRequestBluetoothPermission31/Android.bp
+++ b/tests/tests/permission/AppThatRequestBluetoothPermission31/Android.bp
@@ -27,7 +27,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
     srcs: [":AppThatRequestBluetoothPermission"],
 }
diff --git a/tests/tests/permission/AppThatRequestBluetoothPermissionNeverForLocation31/Android.bp b/tests/tests/permission/AppThatRequestBluetoothPermissionNeverForLocation31/Android.bp
index 0b76542..857f2e2 100644
--- a/tests/tests/permission/AppThatRequestBluetoothPermissionNeverForLocation31/Android.bp
+++ b/tests/tests/permission/AppThatRequestBluetoothPermissionNeverForLocation31/Android.bp
@@ -27,7 +27,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
     srcs: [":AppThatRequestBluetoothPermission"],
 }
diff --git a/tests/tests/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/Android.bp b/tests/tests/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/Android.bp
index c3ed047..6f635c0 100644
--- a/tests/tests/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/Android.bp
+++ b/tests/tests/permission/AppThatRequestBluetoothPermissionNeverForLocationNoProvider/Android.bp
@@ -27,6 +27,6 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestContactsAndCallLogPermission16/Android.bp b/tests/tests/permission/AppThatRequestContactsAndCallLogPermission16/Android.bp
index e7fe577..1a057d0 100644
--- a/tests/tests/permission/AppThatRequestContactsAndCallLogPermission16/Android.bp
+++ b/tests/tests/permission/AppThatRequestContactsAndCallLogPermission16/Android.bp
@@ -26,6 +26,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestContactsPermission15/Android.bp b/tests/tests/permission/AppThatRequestContactsPermission15/Android.bp
index 2436d07..5461844 100644
--- a/tests/tests/permission/AppThatRequestContactsPermission15/Android.bp
+++ b/tests/tests/permission/AppThatRequestContactsPermission15/Android.bp
@@ -26,6 +26,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestContactsPermission16/Android.bp b/tests/tests/permission/AppThatRequestContactsPermission16/Android.bp
index f52d2dd..2dca5aa 100644
--- a/tests/tests/permission/AppThatRequestContactsPermission16/Android.bp
+++ b/tests/tests/permission/AppThatRequestContactsPermission16/Android.bp
@@ -26,6 +26,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestLocationAndBackgroundPermission28/Android.bp b/tests/tests/permission/AppThatRequestLocationAndBackgroundPermission28/Android.bp
index b997271..5a60a3f 100644
--- a/tests/tests/permission/AppThatRequestLocationAndBackgroundPermission28/Android.bp
+++ b/tests/tests/permission/AppThatRequestLocationAndBackgroundPermission28/Android.bp
@@ -27,6 +27,6 @@
         "cts",
         "vts10",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestLocationAndBackgroundPermission29/Android.bp b/tests/tests/permission/AppThatRequestLocationAndBackgroundPermission29/Android.bp
index 4fdf8ec..de6c3cb 100644
--- a/tests/tests/permission/AppThatRequestLocationAndBackgroundPermission29/Android.bp
+++ b/tests/tests/permission/AppThatRequestLocationAndBackgroundPermission29/Android.bp
@@ -28,6 +28,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestLocationPermission22/Android.bp b/tests/tests/permission/AppThatRequestLocationPermission22/Android.bp
index f04fa9b..25d9893 100644
--- a/tests/tests/permission/AppThatRequestLocationPermission22/Android.bp
+++ b/tests/tests/permission/AppThatRequestLocationPermission22/Android.bp
@@ -26,6 +26,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestLocationPermission28/Android.bp b/tests/tests/permission/AppThatRequestLocationPermission28/Android.bp
index 371277c..bfeadbd 100644
--- a/tests/tests/permission/AppThatRequestLocationPermission28/Android.bp
+++ b/tests/tests/permission/AppThatRequestLocationPermission28/Android.bp
@@ -26,6 +26,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestLocationPermission29/Android.bp b/tests/tests/permission/AppThatRequestLocationPermission29/Android.bp
index ea1caa8..ee3982e 100644
--- a/tests/tests/permission/AppThatRequestLocationPermission29/Android.bp
+++ b/tests/tests/permission/AppThatRequestLocationPermission29/Android.bp
@@ -27,6 +27,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestLocationPermission29v4/Android.bp b/tests/tests/permission/AppThatRequestLocationPermission29v4/Android.bp
index 67a1613..b56bb25 100644
--- a/tests/tests/permission/AppThatRequestLocationPermission29v4/Android.bp
+++ b/tests/tests/permission/AppThatRequestLocationPermission29v4/Android.bp
@@ -27,6 +27,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestOneTimePermission/Android.bp b/tests/tests/permission/AppThatRequestOneTimePermission/Android.bp
index a653c77..c12a708 100644
--- a/tests/tests/permission/AppThatRequestOneTimePermission/Android.bp
+++ b/tests/tests/permission/AppThatRequestOneTimePermission/Android.bp
@@ -28,7 +28,7 @@
     // Tag this module as a cts test artifact
     test_suites: [
         "cts",
-        "mts",
+        "mts-permission",
         "general-tests",
     ],
     srcs: ["src/**/*.java"],
diff --git a/tests/tests/permission/AppThatRequestPermissionAandB/Android.bp b/tests/tests/permission/AppThatRequestPermissionAandB/Android.bp
index 3748fbbb..6c037b4 100644
--- a/tests/tests/permission/AppThatRequestPermissionAandB/Android.bp
+++ b/tests/tests/permission/AppThatRequestPermissionAandB/Android.bp
@@ -27,7 +27,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src/**/*.java"],
 }
diff --git a/tests/tests/permission/AppThatRequestPermissionAandC/Android.bp b/tests/tests/permission/AppThatRequestPermissionAandC/Android.bp
index 2d72428..b949653 100644
--- a/tests/tests/permission/AppThatRequestPermissionAandC/Android.bp
+++ b/tests/tests/permission/AppThatRequestPermissionAandC/Android.bp
@@ -27,7 +27,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
     srcs: ["src/**/*.java"],
 }
diff --git a/tests/tests/permission/AppThatRequestStoragePermission28/Android.bp b/tests/tests/permission/AppThatRequestStoragePermission28/Android.bp
index 9fd2e98..50ae920 100644
--- a/tests/tests/permission/AppThatRequestStoragePermission28/Android.bp
+++ b/tests/tests/permission/AppThatRequestStoragePermission28/Android.bp
@@ -26,6 +26,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRequestStoragePermission29/Android.bp b/tests/tests/permission/AppThatRequestStoragePermission29/Android.bp
index 23d3b49..4663be9 100644
--- a/tests/tests/permission/AppThatRequestStoragePermission29/Android.bp
+++ b/tests/tests/permission/AppThatRequestStoragePermission29/Android.bp
@@ -26,6 +26,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppThatRunsRationaleTests/Android.bp b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
index e326c05..30019fb 100644
--- a/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
+++ b/tests/tests/permission/AppThatRunsRationaleTests/Android.bp
@@ -28,7 +28,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 
     srcs: ["src/**/*.java"],
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp
index ef92d20..8214c42 100644
--- a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission28/Android.bp
@@ -28,6 +28,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp
index 546179f..3df5c9a 100644
--- a/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp
+++ b/tests/tests/permission/AppWithSharedUidThatRequestLocationPermission29/Android.bp
@@ -28,6 +28,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestsNoPermissions/Android.bp b/tests/tests/permission/AppWithSharedUidThatRequestsNoPermissions/Android.bp
index b043eb3..7dd3ef6 100644
--- a/tests/tests/permission/AppWithSharedUidThatRequestsNoPermissions/Android.bp
+++ b/tests/tests/permission/AppWithSharedUidThatRequestsNoPermissions/Android.bp
@@ -25,6 +25,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/AppWithSharedUidThatRequestsPermissions/Android.bp b/tests/tests/permission/AppWithSharedUidThatRequestsPermissions/Android.bp
index 5e6d68e..c58b3e8 100644
--- a/tests/tests/permission/AppWithSharedUidThatRequestsPermissions/Android.bp
+++ b/tests/tests/permission/AppWithSharedUidThatRequestsPermissions/Android.bp
@@ -25,6 +25,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/StorageEscalationApp28/Android.bp b/tests/tests/permission/StorageEscalationApp28/Android.bp
index 520625a..9ea70f5 100644
--- a/tests/tests/permission/StorageEscalationApp28/Android.bp
+++ b/tests/tests/permission/StorageEscalationApp28/Android.bp
@@ -24,7 +24,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/StorageEscalationApp29Full/Android.bp b/tests/tests/permission/StorageEscalationApp29Full/Android.bp
index 018c324..dcb64e6 100644
--- a/tests/tests/permission/StorageEscalationApp29Full/Android.bp
+++ b/tests/tests/permission/StorageEscalationApp29Full/Android.bp
@@ -24,7 +24,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/StorageEscalationApp29Scoped/Android.bp b/tests/tests/permission/StorageEscalationApp29Scoped/Android.bp
index 5c0d89a..8a780c0 100644
--- a/tests/tests/permission/StorageEscalationApp29Scoped/Android.bp
+++ b/tests/tests/permission/StorageEscalationApp29Scoped/Android.bp
@@ -24,7 +24,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
 }
diff --git a/tests/tests/permission/sdk28/Android.bp b/tests/tests/permission/sdk28/Android.bp
index 4c6aab4..3043c93 100644
--- a/tests/tests/permission/sdk28/Android.bp
+++ b/tests/tests/permission/sdk28/Android.bp
@@ -28,6 +28,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission/telephony/Android.bp b/tests/tests/permission/telephony/Android.bp
index da256dc..bbfe06c 100644
--- a/tests/tests/permission/telephony/Android.bp
+++ b/tests/tests/permission/telephony/Android.bp
@@ -25,7 +25,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
     // Include both the 32 and 64 bit versions
     compile_multilib: "both",
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.bp
index 61a0e76..6f0bcf1 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionDefinerApp/Android.bp
@@ -26,7 +26,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
     certificate: ":cts-testkey1",
 }
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.bp
index a324934..41f01e9 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/AdversarialPermissionUserApp/Android.bp
@@ -26,7 +26,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
     certificate: ":cts-testkey2",
 }
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp
index 0d2f5d57..a3e1f13 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionDefinerApp/Android.bp
@@ -25,7 +25,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
     certificate: ":cts-testkey1",
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp
index 85ce995..caaa8e8 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionEscalatorApp/Android.bp
@@ -25,7 +25,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
     certificate: ":cts-testkey1",
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp
index 9fefaf5..d9ca4c8 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/InstallPermissionUserApp/Android.bp
@@ -25,7 +25,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
     certificate: ":cts-testkey2",
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.bp
index 7aeb75f5..b4ea30a 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionDefinerApp/Android.bp
@@ -25,7 +25,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
     certificate: ":cts-testkey1",
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.bp
index 060a2dc..22c6ccd 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/RuntimePermissionUserApp/Android.bp
@@ -25,7 +25,7 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
         "sts",
     ],
     certificate: ":cts-testkey2",
diff --git a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.bp b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.bp
index 542a6b3..5627a3d 100644
--- a/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.bp
+++ b/tests/tests/permission/testapps/RevokePermissionWhenRemoved/VictimPermissionDefinerApp/Android.bp
@@ -26,7 +26,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-permission",
     ],
     certificate: ":cts-testkey1",
 }
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index edaf643..86e9e6e 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -2704,6 +2704,10 @@
     <permission android:name="android.permission.CONFIGURE_INTERACT_ACROSS_PROFILES"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Allows starting activities across profiles in the same profile group. -->
+    <permission android:name="android.permission.START_CROSS_PROFILE_ACTIVITIES"
+        android:protectionLevel="signature|role" />
+
     <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
          users on the device. This permission is not available to
          third party applications. -->
@@ -3416,6 +3420,13 @@
     <permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
         android:protectionLevel="signature|privileged|development" />
 
+    <!-- @hide @SystemApi Must be required by a
+         {@link com.android.service.tracing.TraceReportService}, to ensure that only the system
+         can bind to it.
+        <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.BIND_TRACE_REPORT_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- @hide @SystemApi @TestApi
          Allow an application to approve incident and bug reports to be
          shared off-device.  There can be only one application installed on the
diff --git a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
index 5a11331..989f79c 100755
--- a/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PrivappPermissionsTest.java
@@ -44,6 +44,8 @@
 import com.android.compatibility.common.util.PropertyUtil;
 import com.android.compatibility.common.util.SystemUtil;
 
+import com.google.common.collect.ImmutableSet;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -71,6 +73,26 @@
 @RunWith(AndroidJUnit4.class)
 public class PrivappPermissionsTest {
 
+    // TODO(b/191740932): Migrate APK-in-APEX priv-app allowlists inside their respective modules,
+    // so that {@code adb shell cmd package get-privapp-permissions <packageName>} lists them.
+    private static final Set<String> APK_IN_APEX_ALLOWLIST_MIGRATION_BURNDOWN_LIST =
+            ImmutableSet.of(
+                // Updatable APEX packages
+                "com.google.android.networkstack.tethering",
+                "com.google.android.ext.services",
+                "com.google.android.providers.media.module",
+                "com.google.android.permissioncontroller",
+                "com.google.android.cellbroadcastreceiver",
+                "com.google.android.cellbroadcastservice",
+                // AOSP APEX packages
+                "com.android.networkstack.tethering",
+                "com.android.ext.services",
+                "com.android.providers.media.module",
+                "com.android.permissioncontroller",
+                "com.android.cellbroadcastreceiver",
+                "com.android.cellbroadcastservice"
+            );
+
     private static final String TAG = "PrivappPermissionsTest";
 
     private static final String PLATFORM_PACKAGE_NAME = "android";
@@ -108,9 +130,9 @@
                 continue;
             }
 
-            // Exempt apk-in-apex as there is currently no way to get the PackageInfo of the
-            // preinstalled APK
-            if (pkg.applicationInfo.sourceDir.startsWith("/apex")) {
+            // Exempt apk-in-apexes that have not had their priv-app permission allowlist .xml
+            // migrated inside their respective APEXes.
+            if (APK_IN_APEX_ALLOWLIST_MIGRATION_BURNDOWN_LIST.contains(packageName)) {
                 continue;
             }
 
diff --git a/tests/tests/permission3/Android.bp b/tests/tests/permission3/Android.bp
index 3d91b81..ecc70dc 100644
--- a/tests/tests/permission3/Android.bp
+++ b/tests/tests/permission3/Android.bp
@@ -56,6 +56,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission4/Android.bp b/tests/tests/permission4/Android.bp
index 3f2d3db..22b0aa2 100644
--- a/tests/tests/permission4/Android.bp
+++ b/tests/tests/permission4/Android.bp
@@ -37,6 +37,6 @@
         "cts",
         "vts10",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/permission5/Android.bp b/tests/tests/permission5/Android.bp
index d7cb067..00efcf6 100644
--- a/tests/tests/permission5/Android.bp
+++ b/tests/tests/permission5/Android.bp
@@ -37,6 +37,6 @@
     test_suites: [
         "cts",
         "general-tests",
-        "mts",
+        "mts-permission",
     ],
 }
diff --git a/tests/tests/provider/Android.bp b/tests/tests/provider/Android.bp
index c979102..91566e3 100644
--- a/tests/tests/provider/Android.bp
+++ b/tests/tests/provider/Android.bp
@@ -13,7 +13,7 @@
         "cts",
         "general-tests",
         "sts",
-        "mts",
+        "mts-documentsui",
     ],
 
     libs: [
diff --git a/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp b/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp
index 424da9a..31f3a1e 100644
--- a/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp
+++ b/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp
@@ -66,7 +66,7 @@
   ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
 
   for (const auto& entry : fstab) {
-    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+    if (!entry.fs_mgr_flags.avb) {
       continue;
     }
 
diff --git a/tests/tests/telecom/src/android/telecom/cts/AdhocConferenceTest.java b/tests/tests/telecom/src/android/telecom/cts/AdhocConferenceTest.java
index 6e8acd1..9e8c150 100644
--- a/tests/tests/telecom/src/android/telecom/cts/AdhocConferenceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/AdhocConferenceTest.java
@@ -26,6 +26,7 @@
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
 import android.telecom.DisconnectCause;
+import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.util.Log;
 import android.util.Pair;
@@ -65,6 +66,8 @@
             return;
         }
         Bundle extra = new Bundle();
+        extra.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+                TestUtils.TEST_PHONE_ACCOUNT_HANDLE);
         mTelecomManager.startConference(PARTICIPANTS, extra);
         ConnectionRequest request = verifyAdhocConferenceCall().second;
         assertTrue(request.isAdhocConferenceCall());
diff --git a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
index dd4a9ac..205cb5b 100644
--- a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
@@ -239,7 +239,10 @@
         }
 
         try {
-            mTelecomManager.startConference(PARTICIPANTS, null);
+            Bundle extra = new Bundle();
+            extra.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
+                    TestUtils.TEST_PHONE_ACCOUNT_HANDLE);
+            mTelecomManager.startConference(PARTICIPANTS, extra);
             MockConference conference = verifyConference(2);
             MockConference remoteConference = verifyConferenceOnRemoteCS(2);
             RemoteConferenceTest.verifyRemoteConferenceObject(conference.getRemoteConference(),
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CarrierCapability.java b/tests/tests/telephony/current/src/android/telephony/cts/CarrierCapability.java
index fa64856..161e8f2 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CarrierCapability.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CarrierCapability.java
@@ -146,4 +146,19 @@
                     "45006",    // LGT
                     "45008"     // KT
             );
+
+
+    public static final List<String> SUPPORT_TEL_URI_PUBLISH =
+            Arrays.asList(
+                    "310410",   // AT&T Mobility
+                    "310280",   // AT&T Mobility
+                    "310030",   // AT&T Mobility
+                    "310070",   // AT&T Mobility
+                    "310170",   // AT&T Mobility
+                    "310380",   // AT&T Mobility
+                    "310560",   // AT&T Mobility
+                    "310680",   // AT&T Mobility
+                    "310950",   // AT&T Mobility
+                    "311180"    // AT&T Mobility
+            );
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java b/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java
index 7faf091..6feec7f 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/DataProfileTest.java
@@ -42,10 +42,12 @@
     private static final int APN_BITMASK = ApnSetting.TYPE_DEFAULT;
     private static final int ROAMING_PROTOCOL_TYPE = ApnSetting.PROTOCOL_IP;
     private static final int BEARER_BITMASK = (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
+    private static final long LINGERING_BEARER_BITMASK = TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
     private static final int MTU_V4 = 1440;
     private static final int MTU_V6 = 1400;
     private static final boolean IS_PREFERRED = true;
     private static final boolean IS_PERSISTENT = true;
+    private static final boolean IS_ALWAYS_ON = true;
 
     @Test
     public void testConstructorAndGetters() {
@@ -210,6 +212,8 @@
                 .setCarrierEnabled(IS_ENABLED)
                 .setProfileId(PROFILE_ID)
                 .setAuthType(AUTH_TYPE)
+                .setLingeringNetworkTypeBitmask(LINGERING_BEARER_BITMASK)
+                .setAlwaysOn(IS_ALWAYS_ON)
                 .build();
 
         byte[] osAppId = {1, 2, 3, 4};
@@ -250,6 +254,8 @@
                 .setCarrierEnabled(IS_ENABLED)
                 .setProfileId(PROFILE_ID)
                 .setAuthType(AUTH_TYPE)
+                .setLingeringNetworkTypeBitmask(LINGERING_BEARER_BITMASK)
+                .setAlwaysOn(IS_ALWAYS_ON)
                 .build();
 
         DataProfile profile = new DataProfile.Builder()
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java
index a41aff5..86a2c8f7 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java
@@ -23,10 +23,11 @@
 import android.hardware.radio.config.IRadioConfigIndication;
 import android.hardware.radio.config.IRadioConfigResponse;
 import android.hardware.radio.config.PhoneCapability;
-import android.hardware.radio.config.SimPortInfo;
 import android.hardware.radio.config.SimSlotStatus;
 import android.hardware.radio.config.SlotPortMapping;
-import android.hardware.radio.sim.CardStatus;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -34,18 +35,88 @@
     private static final String TAG = "MRCFG";
 
     private final MockModemService mService;
-    private byte mNumOfLiveModems = 1;
     private IRadioConfigResponse mRadioConfigResponse;
     private IRadioConfigIndication mRadioConfigIndication;
+    private static MockModemConfigInterface[] sMockModemConfigInterfaces;
+    private Object mCacheUpdateMutex;
+    private final Handler mHandler;
+    private int mSubId;
 
+    // ***** Events
+    static final int EVENT_NUM_OF_LIVE_MODEM_CHANGED = 1;
+    static final int EVENT_PHONE_CAPABILITY_CHANGED = 2;
+    static final int EVENT_SIM_SLOT_STATUS_CHANGED = 3;
+
+    // ***** Cache of modem attributes/status
     private int mSlotNum = 1;
-    private boolean mSimStatePresent = false;
+    private byte mNumOfLiveModems = 1;
+    private PhoneCapability mPhoneCapability = new PhoneCapability();
+    private SimSlotStatus[] mSimSlotStatus;
 
-    public IRadioConfigImpl(MockModemService service) {
+    public IRadioConfigImpl(
+            MockModemService service, MockModemConfigInterface[] interfaces, int instanceId) {
         Log.d(TAG, "Instantiated");
 
         this.mService = service;
+        sMockModemConfigInterfaces = interfaces;
         mSlotNum = mService.getNumPhysicalSlots();
+        mSimSlotStatus = new SimSlotStatus[mSlotNum];
+        mCacheUpdateMutex = new Object();
+        mHandler = new IRadioConfigHandler();
+        mSubId = instanceId;
+
+        // Register events
+        sMockModemConfigInterfaces[mSubId].registerForNumOfLiveModemChanged(
+                mHandler, EVENT_NUM_OF_LIVE_MODEM_CHANGED, null);
+        sMockModemConfigInterfaces[mSubId].registerForPhoneCapabilityChanged(
+                mHandler, EVENT_PHONE_CAPABILITY_CHANGED, null);
+        sMockModemConfigInterfaces[mSubId].registerForSimSlotStatusChanged(
+                mHandler, EVENT_SIM_SLOT_STATUS_CHANGED, null);
+    }
+
+    /** Handler class to handle callbacks */
+    private final class IRadioConfigHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            AsyncResult ar;
+            synchronized (mCacheUpdateMutex) {
+                switch (msg.what) {
+                    case EVENT_NUM_OF_LIVE_MODEM_CHANGED:
+                        Log.d(TAG, "Received EVENT_NUM_OF_LIVE_MODEM_CHANGED");
+                        ar = (AsyncResult) msg.obj;
+                        if (ar != null && ar.exception == null) {
+                            mNumOfLiveModems = (byte) ar.result;
+                            Log.i(TAG, "Number of live modem: " + mNumOfLiveModems);
+                        } else {
+                            Log.e(TAG, msg.what + " failure. Exception: " + ar.exception);
+                        }
+                        break;
+                    case EVENT_PHONE_CAPABILITY_CHANGED:
+                        Log.d(TAG, "Received EVENT_PHONE_CAPABILITY_CHANGED");
+                        ar = (AsyncResult) msg.obj;
+                        if (ar != null && ar.exception == null) {
+                            mPhoneCapability = (PhoneCapability) ar.result;
+                            Log.i(TAG, "Phone capability: " + mPhoneCapability);
+                        } else {
+                            Log.e(TAG, msg.what + " failure. Exception: " + ar.exception);
+                        }
+                        break;
+                    case EVENT_SIM_SLOT_STATUS_CHANGED:
+                        Log.d(TAG, "Received EVENT_SIM_SLOT_STATUS_CHANGED");
+                        ar = (AsyncResult) msg.obj;
+                        if (ar != null && ar.exception == null) {
+                            mSimSlotStatus = (SimSlotStatus[]) ar.result;
+                            for (int i = 0; i < mSlotNum; i++) {
+                                Log.i(TAG, "Sim slot status: " + mSimSlotStatus[i]);
+                            }
+                            unsolSimSlotsStatusChanged();
+                        } else {
+                            Log.e(TAG, msg.what + " failure. Exception: " + ar.exception);
+                        }
+                        break;
+                }
+            }
+        }
     }
 
     // Implementation of IRadioConfig functions
@@ -68,10 +139,15 @@
     @Override
     public void getNumOfLiveModems(int serial) {
         Log.d(TAG, "getNumOfLiveModems");
+        byte numoflivemodem;
+
+        synchronized (mCacheUpdateMutex) {
+            numoflivemodem = mNumOfLiveModems;
+        }
 
         RadioResponseInfo rsp = mService.makeSolRsp(serial);
         try {
-            mRadioConfigResponse.getNumOfLiveModemsResponse(rsp, mNumOfLiveModems);
+            mRadioConfigResponse.getNumOfLiveModemsResponse(rsp, numoflivemodem);
         } catch (RemoteException ex) {
             Log.e(TAG, "Failed to invoke getNumOfLiveModemsResponse from AIDL. Exception" + ex);
         }
@@ -80,10 +156,11 @@
     @Override
     public void getPhoneCapability(int serial) {
         Log.d(TAG, "getPhoneCapability");
+        PhoneCapability phoneCapability;
 
-        PhoneCapability phoneCapability = new PhoneCapability();
-        phoneCapability.logicalModemIds = new byte[2];
-        convertMockPhoneCapToHal(phoneCapability);
+        synchronized (mCacheUpdateMutex) {
+            phoneCapability = mPhoneCapability;
+        }
 
         RadioResponseInfo rsp = mService.makeSolRsp(serial);
         try {
@@ -98,14 +175,13 @@
         Log.d(TAG, "getSimSlotsStatus");
         SimSlotStatus[] slotStatus;
 
-        if (mSlotNum < 1) {
-            Log.d(TAG, "No slot information is retured.");
-            slotStatus = null;
-        } else {
-            slotStatus = new SimSlotStatus[mSlotNum];
-
-            for (int i = 0; i < mSlotNum; i++) slotStatus[i] = new SimSlotStatus();
-            convertMockSimSlotStatusToHal(slotStatus);
+        synchronized (mCacheUpdateMutex) {
+            if (mSlotNum < 1) {
+                Log.d(TAG, "No slot information is retured.");
+                slotStatus = null;
+            } else {
+                slotStatus = mSimSlotStatus;
+            }
         }
 
         RadioResponseInfo rsp = mService.makeSolRsp(serial);
@@ -170,15 +246,13 @@
         SimSlotStatus[] slotStatus;
 
         if (mRadioConfigIndication != null) {
-
-            if (mSlotNum < 1) {
-                Log.d(TAG, "No slot information is retured.");
-                slotStatus = null;
-            } else {
-                slotStatus = new SimSlotStatus[mSlotNum];
-
-                for (int i = 0; i < mSlotNum; i++) slotStatus[i] = new SimSlotStatus();
-                convertMockSimSlotStatusToHal(slotStatus);
+            synchronized (mCacheUpdateMutex) {
+                if (mSlotNum < 1) {
+                    Log.d(TAG, "No slot information is retured.");
+                    slotStatus = null;
+                } else {
+                    slotStatus = mSimSlotStatus;
+                }
             }
 
             try {
@@ -191,64 +265,4 @@
             Log.e(TAG, "null mRadioConfigIndication");
         }
     }
-
-    private void convertMockSimSlotStatusToHal(SimSlotStatus[] slotStatus) {
-
-        int portInfoListLen = 1;
-
-        if (mSlotNum >= 1) {
-            if (mSimStatePresent) {
-                slotStatus[0].cardState = CardStatus.STATE_PRESENT;
-            } else {
-                slotStatus[0].cardState = CardStatus.STATE_ABSENT;
-            }
-            slotStatus[0].atr = "";
-            slotStatus[0].eid = "";
-            SimPortInfo[] portInfoList0 = new SimPortInfo[portInfoListLen];
-            for (int i = 0; i < portInfoListLen; i++) portInfoList0[i] = new SimPortInfo();
-            portInfoList0[0].portActive = true;
-            portInfoList0[0].logicalSlotId = 0;
-            portInfoList0[0].iccId = "";
-            slotStatus[0].portInfo = portInfoList0;
-        }
-
-        if (mSlotNum >= 2) {
-            slotStatus[1].cardState = CardStatus.STATE_PRESENT;
-            slotStatus[1].atr = "3B9F97C00A3FC6828031E073FE211F65D002341512810F51";
-            slotStatus[1].eid = "89033023426200000000005430099507";
-            SimPortInfo[] portInfoList1 = new SimPortInfo[portInfoListLen];
-            for (int i = 0; i < portInfoListLen; i++) portInfoList1[i] = new SimPortInfo();
-            portInfoList1[0].portActive = false;
-            portInfoList1[0].logicalSlotId = -1;
-            portInfoList1[0].iccId = "";
-            slotStatus[1].portInfo = portInfoList1;
-        }
-
-        if (mSlotNum >= 3) {
-            slotStatus[2].cardState = CardStatus.STATE_ABSENT;
-            slotStatus[2].atr = "";
-            slotStatus[2].eid = "";
-            SimPortInfo[] portInfoList2 = new SimPortInfo[portInfoListLen];
-            for (int i = 0; i < portInfoListLen; i++) portInfoList2[i] = new SimPortInfo();
-            portInfoList2[0].portActive = false;
-            portInfoList2[0].logicalSlotId = -1;
-            portInfoList2[0].iccId = "";
-            slotStatus[2].portInfo = portInfoList2;
-        }
-    }
-
-    private void convertMockPhoneCapToHal(PhoneCapability phoneCapability) {
-
-        phoneCapability.maxActiveData = 2;
-        phoneCapability.maxActiveInternetData = 1;
-        phoneCapability.isInternetLingeringSupported = false;
-        phoneCapability.logicalModemIds[0] = 0;
-        phoneCapability.logicalModemIds[1] = 1;
-    }
-
-    // TODO: use helper function to handle
-    public void setSimPresent(int slotId) {
-        // TODO: check  slotId and Phone ID
-        mSimStatePresent = true;
-    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java
index 689f5bd..e9fff8e 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java
@@ -25,7 +25,6 @@
 import android.hardware.radio.data.KeepaliveRequest;
 import android.hardware.radio.data.LinkAddress;
 import android.hardware.radio.data.SliceInfo;
-import android.hardware.radio.data.TrafficDescriptor;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -181,7 +180,6 @@
             String[] dnses,
             int pduSessionId,
             SliceInfo sliceInfo,
-            TrafficDescriptor trafficDescriptor,
             boolean matchAllRuleAllowed) {
         Log.d(TAG, "setupDataCall");
         RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java
index 44a7ee5..4cca64b 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java
@@ -16,6 +16,8 @@
 
 package android.telephony.cts;
 
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
+
 import android.hardware.radio.RadioError;
 import android.hardware.radio.RadioIndicationType;
 import android.hardware.radio.RadioResponseInfo;
@@ -23,26 +25,119 @@
 import android.hardware.radio.modem.IRadioModemIndication;
 import android.hardware.radio.modem.IRadioModemResponse;
 import android.hardware.radio.modem.RadioState;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
 
 public class IRadioModemImpl extends IRadioModem.Stub {
     private static final String TAG = "MRMDM";
 
-    private static final String BASEBAND_VERSION = "mock-modem-service-1.0";
-    private static final String DEFAULT_IMEI = "123456789012345";
-    private static final String DEFAULT_IMEISV = "01";
-    private static final String DEFAULT_ESN = "123456789";
-    private static final String DEFAULT_MEID = "123456789012345";
-
     private final MockModemService mService;
     private IRadioModemResponse mRadioModemResponse;
     private IRadioModemIndication mRadioModemIndication;
 
-    public IRadioModemImpl(MockModemService service) {
+    private int mForceRadioPowerError = -1;
+
+    private static MockModemConfigInterface[] sMockModemConfigInterfaces;
+    private Object mCacheUpdateMutex;
+    private final Handler mHandler;
+    private int mSubId;
+
+    // ***** Events
+    static final int EVENT_BASEBAND_VERSION_CHANGED = 1;
+    static final int EVENT_DEVICE_IDENTITY_CHANGED = 2;
+    static final int EVENT_RADIO_STATE_CHANGED = 3;
+
+    // ***** Cache of modem attributes/status
+    private String mBasebandVer;
+    private String mImei;
+    private String mImeiSv;
+    private String mEsn;
+    private String mMeid;
+    private int mRadioState;
+
+    public IRadioModemImpl(
+            MockModemService service, MockModemConfigInterface[] interfaces, int instanceId) {
         Log.d(TAG, "Instantiated");
 
         this.mService = service;
+        sMockModemConfigInterfaces = interfaces;
+        mCacheUpdateMutex = new Object();
+        mHandler = new IRadioModemHandler();
+        mSubId = instanceId;
+
+        // Register events
+        sMockModemConfigInterfaces[mSubId].registerForBasebandVersionChanged(
+                mHandler, EVENT_BASEBAND_VERSION_CHANGED, null);
+        sMockModemConfigInterfaces[mSubId].registerForDeviceIdentityChanged(
+                mHandler, EVENT_DEVICE_IDENTITY_CHANGED, null);
+        sMockModemConfigInterfaces[mSubId].registerForRadioStateChanged(
+                mHandler, EVENT_RADIO_STATE_CHANGED, null);
+    }
+
+    /** Handler class to handle callbacks */
+    private final class IRadioModemHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            AsyncResult ar;
+            synchronized (mCacheUpdateMutex) {
+                switch (msg.what) {
+                    case EVENT_BASEBAND_VERSION_CHANGED:
+                        Log.d(TAG, "Received EVENT_BASEBAND_VERSION_CHANGED");
+                        ar = (AsyncResult) msg.obj;
+                        if (ar != null && ar.exception == null) {
+                            mBasebandVer = (String) ar.result;
+                            Log.i(TAG, "Basedband version = " + mBasebandVer);
+                        } else {
+                            Log.e(
+                                    TAG,
+                                    msg.what
+                                            + " failure. Not update baseband version."
+                                            + ar.exception);
+                        }
+                        break;
+                    case EVENT_DEVICE_IDENTITY_CHANGED:
+                        Log.d(TAG, "Received EVENT_DEVICE_IDENTITY_CHANGED");
+                        ar = (AsyncResult) msg.obj;
+                        if (ar != null && ar.exception == null) {
+                            String[] deviceIdentity = (String[]) ar.result;
+                            mImei = deviceIdentity[0];
+                            mImeiSv = deviceIdentity[1];
+                            mEsn = deviceIdentity[2];
+                            mMeid = deviceIdentity[3];
+                            Log.i(
+                                    TAG,
+                                    "Device identity: IMEI = "
+                                            + mImei
+                                            + " IMEISV = "
+                                            + mImeiSv
+                                            + " ESN = "
+                                            + mEsn
+                                            + " MEID ="
+                                            + mMeid);
+                        } else {
+                            Log.e(
+                                    TAG,
+                                    msg.what
+                                            + " failure. Not update device identity."
+                                            + ar.exception);
+                        }
+                        break;
+                    case EVENT_RADIO_STATE_CHANGED:
+                        Log.d(TAG, "Received EVENT_RADIO_STATE_CHANGED");
+                        ar = (AsyncResult) msg.obj;
+                        if (ar != null && ar.exception == null) {
+                            mRadioState = (int) ar.result;
+                            Log.i(TAG, "Radio state: " + mRadioState);
+                        } else {
+                            Log.e(TAG, msg.what + " failure. Exception: " + ar.exception);
+                        }
+                        break;
+                }
+            }
+        }
     }
 
     // Implementation of IRadioModem functions
@@ -72,7 +167,11 @@
     public void getBasebandVersion(int serial) {
         Log.d(TAG, "getBasebandVersion");
 
-        String baseband = BASEBAND_VERSION;
+        String baseband;
+
+        synchronized (mCacheUpdateMutex) {
+            baseband = mBasebandVer;
+        }
 
         RadioResponseInfo rsp = mService.makeSolRsp(serial);
         try {
@@ -86,10 +185,14 @@
     public void getDeviceIdentity(int serial) {
         Log.d(TAG, "getDeviceIdentity");
 
-        String imei = DEFAULT_IMEI;
-        String imeisv = DEFAULT_IMEISV;
-        String esn = DEFAULT_ESN;
-        String meid = DEFAULT_MEID;
+        String imei, imeisv, esn, meid;
+
+        synchronized (mCacheUpdateMutex) {
+            imei = mImei;
+            imeisv = mImeiSv;
+            esn = mEsn;
+            meid = mMeid;
+        }
 
         RadioResponseInfo rsp = mService.makeSolRsp(serial);
         try {
@@ -268,23 +371,35 @@
             boolean forEmergencyCall,
             boolean preferredForEmergencyCall) {
         Log.d(TAG, "setRadioPower");
+        RadioResponseInfo rsp = null;
 
-        // TODO: cache value
+        // Check if the error response needs to be modified
+        if (mForceRadioPowerError != -1) {
+            rsp = mService.makeSolRsp(serial, mForceRadioPowerError);
+        } else {
+            synchronized (mCacheUpdateMutex) {
+                if (powerOn) {
+                    mRadioState = MockModemConfigInterface.RADIO_STATE_ON;
+                } else {
+                    mRadioState = MockModemConfigInterface.RADIO_STATE_OFF;
+                }
+                sMockModemConfigInterfaces[mSubId].setRadioState(mRadioState, TAG);
+            }
+            rsp = mService.makeSolRsp(serial);
+        }
 
-        RadioResponseInfo rsp = mService.makeSolRsp(serial);
         try {
             mRadioModemResponse.setRadioPowerResponse(rsp);
         } catch (RemoteException ex) {
             Log.e(TAG, "Failed to setRadioPower from AIDL. Exception" + ex);
         }
 
-        // TODO: The below should be handled by Helper function
-        if (powerOn) {
-            radioStateChanged(RadioState.ON);
-            mService.countDownLatch(MockModemService.LATCH_MOCK_MODEM_RADIO_POWR_ON);
-        } else {
-            radioStateChanged(RadioState.OFF);
-            mService.countDownLatch(MockModemService.LATCH_MOCK_MODEM_RADIO_POWR_OFF);
+        if (rsp.error == RadioError.NONE) {
+            if (powerOn) {
+                radioStateChanged(RadioState.ON);
+            } else {
+                radioStateChanged(RadioState.OFF);
+            }
         }
     }
 
@@ -347,4 +462,14 @@
             Log.e(TAG, "null mRadioModemIndication");
         }
     }
+
+    public void forceErrorResponse(int requestId, int error) {
+        switch (requestId) {
+            case RIL_REQUEST_RADIO_POWER:
+                mForceRadioPowerError = error;
+                break;
+            default:
+                break;
+        }
+    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java
index 50b9a79..c914f79 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java
@@ -129,7 +129,11 @@
 
         android.hardware.radio.network.RegStateResult dataRegResponse =
                 new android.hardware.radio.network.RegStateResult();
-        RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+        dataRegResponse.accessTechnologySpecificInfo =
+                android.hardware.radio.network.AccessTechnologySpecificInfo.noinit(true);
+        dataRegResponse.cellIdentity = android.hardware.radio.network.CellIdentity.noinit(true);
+
+        RadioResponseInfo rsp = mService.makeSolRsp(serial);
         try {
             mRadioNetworkResponse.getDataRegistrationStateResponse(rsp, dataRegResponse);
         } catch (RemoteException ex) {
@@ -223,7 +227,11 @@
 
         android.hardware.radio.network.RegStateResult voiceRegResponse =
                 new android.hardware.radio.network.RegStateResult();
-        RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+        voiceRegResponse.accessTechnologySpecificInfo =
+                android.hardware.radio.network.AccessTechnologySpecificInfo.noinit(true);
+        voiceRegResponse.cellIdentity = android.hardware.radio.network.CellIdentity.noinit(true);
+
+        RadioResponseInfo rsp = mService.makeSolRsp(serial);
         try {
             mRadioNetworkResponse.getVoiceRegistrationStateResponse(rsp, voiceRegResponse);
         } catch (RemoteException ex) {
@@ -473,5 +481,28 @@
         }
     }
 
-    // Implementation of IRadioNetworkIndication functions
+    @Override
+    public void setUsageSetting(int serial, int usageSetting) {
+        Log.d(TAG, "setUsageSetting");
+        int remainingRetries = 0;
+
+        RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+        try {
+            mRadioNetworkResponse.setUsageSettingResponse(rsp);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Failed to setUsageSetting from AIDL. Exception" + ex);
+        }
+    }
+
+    @Override
+    public void getUsageSetting(int serial) {
+        Log.d(TAG, "getUsageSetting");
+
+        RadioResponseInfo rsp = mService.makeSolRsp(serial, RadioError.REQUEST_NOT_SUPPORTED);
+        try {
+            mRadioNetworkResponse.getUsageSettingResponse(rsp, -1 /* Invalid value */);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Failed to getUsageSetting from AIDL. Exception" + ex);
+        }
+    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java
index 7879841..8a9f241 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java
@@ -23,6 +23,9 @@
 import android.hardware.radio.sim.IRadioSim;
 import android.hardware.radio.sim.IRadioSimIndication;
 import android.hardware.radio.sim.IRadioSimResponse;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -32,14 +35,56 @@
     private final MockModemService mService;
     private IRadioSimResponse mRadioSimResponse;
     private IRadioSimIndication mRadioSimIndication;
-    private CardStatus mCardStatus = null;
-    private boolean mSimStatePresent = false;
+    private static MockModemConfigInterface[] sMockModemConfigInterfaces;
+    private Object mCacheUpdateMutex;
+    private final Handler mHandler;
+    private int mSubId;
 
-    public IRadioSimImpl(MockModemService service) {
+    // ***** Events
+    static final int EVENT_SIM_CARD_STATUS_CHANGED = 1;
+
+    // ***** Cache of modem attributes/status
+    private int mNumOfLogicalSim;
+    private CardStatus mCardStatus;
+
+    public IRadioSimImpl(
+            MockModemService service, MockModemConfigInterface[] interfaces, int instanceId) {
         Log.d(TAG, "Instantiated");
 
         this.mService = service;
-        mCardStatus = setCardAbsent();
+        sMockModemConfigInterfaces = interfaces;
+        mSubId = instanceId;
+        mCardStatus = new CardStatus();
+        mCacheUpdateMutex = new Object();
+        mHandler = new IRadioSimHandler();
+        mNumOfLogicalSim = sMockModemConfigInterfaces.length;
+
+        // Register events
+        sMockModemConfigInterfaces[mSubId].registerForCardStatusChanged(
+                mHandler, EVENT_SIM_CARD_STATUS_CHANGED, null);
+    }
+
+    /** Handler class to handle callbacks */
+    private final class IRadioSimHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            AsyncResult ar;
+            synchronized (mCacheUpdateMutex) {
+                switch (msg.what) {
+                    case EVENT_SIM_CARD_STATUS_CHANGED:
+                        Log.d(TAG, "Received EVENT_SIM_CARD_STATUS_CHANGED");
+                        ar = (AsyncResult) msg.obj;
+                        if (ar != null && ar.exception == null) {
+                            mCardStatus = (CardStatus) ar.result;
+                            Log.i(TAG, "Sim card status: " + mCardStatus);
+                            simStatusChanged();
+                        } else {
+                            Log.e(TAG, msg.what + " failure. Exception: " + ar.exception);
+                        }
+                        break;
+                }
+            }
+        }
     }
 
     // Implementation of IRadioSim functions
@@ -183,18 +228,18 @@
     @Override
     public void getIccCardStatus(int serial) {
         Log.d(TAG, "getIccCardStatus");
+        CardStatus cardStatus;
 
-        // TODO: use helper function to handle
-        if (mSimStatePresent) mCardStatus.cardState = CardStatus.STATE_PRESENT;
+        synchronized (mCacheUpdateMutex) {
+            cardStatus = mCardStatus;
+        }
 
         RadioResponseInfo rsp = mService.makeSolRsp(serial);
         try {
-            mRadioSimResponse.getIccCardStatusResponse(rsp, mCardStatus);
+            mRadioSimResponse.getIccCardStatusResponse(rsp, cardStatus);
         } catch (RemoteException ex) {
             Log.e(TAG, "Failed to getIccCardStatus from AIDL. Exception" + ex);
         }
-
-        mService.countDownLatch(MockModemService.LATCH_MOCK_MODEM_SIM_READY);
     }
 
     @Override
@@ -718,28 +763,4 @@
             Log.e(TAG, "null mRadioSimIndication");
         }
     }
-
-    private CardStatus setCardAbsent() {
-        CardStatus cardStatus = new CardStatus();
-        cardStatus.cardState = CardStatus.STATE_ABSENT;
-        cardStatus.universalPinState = 0;
-        cardStatus.gsmUmtsSubscriptionAppIndex = -1;
-        cardStatus.cdmaSubscriptionAppIndex = -1;
-        cardStatus.imsSubscriptionAppIndex = -1;
-        cardStatus.applications = new android.hardware.radio.sim.AppStatus[0];
-        cardStatus.atr = "";
-        cardStatus.iccid = "";
-        cardStatus.eid = "";
-        cardStatus.slotMap = new android.hardware.radio.config.SlotPortMapping();
-        cardStatus.slotMap.physicalSlotId = 0;
-        cardStatus.slotMap.portId = 0;
-
-        return cardStatus;
-    }
-
-    // TODO: use helper function to handle
-    public void setSimPresent(int slotId) {
-        // TODO: check  slotId and Phone ID
-        mSimStatePresent = true;
-    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigBase.java b/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigBase.java
new file mode 100644
index 0000000..6a8731d
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigBase.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.cts;
+
+import android.content.Context;
+import android.hardware.radio.config.PhoneCapability;
+import android.hardware.radio.config.SimPortInfo;
+import android.hardware.radio.config.SimSlotStatus;
+import android.hardware.radio.sim.CardStatus;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RegistrantList;
+import android.util.Log;
+
+public class MockModemConfigBase implements MockModemConfigInterface {
+    // ***** Instance Variables
+    private static final int DEFAULT_SUB_ID = 0;
+    private String mTAG = "MockModemConfigBase";
+    private final Handler mHandler;
+    private Context mContext;
+    private int mSubId;
+    private int mSimPhyicalId;
+    private Object mConfigAccess;
+    private int mNumOfSim = MockModemConfigInterface.MAX_NUM_OF_SIM_SLOT;
+    private int mNumOfPhone = MockModemConfigInterface.MAX_NUM_OF_LOGICAL_MODEM;
+
+    // ***** Events
+    static final int EVENT_SET_RADIO_POWER = 1;
+    static final int EVENT_SET_SIM_PRESENT = 2;
+
+    // ***** Modem config values
+    private String mBasebandVersion = MockModemConfigInterface.DEFAULT_BASEBAND_VERSION;
+    private String mImei = MockModemConfigInterface.DEFAULT_IMEI;
+    private String mImeiSv = MockModemConfigInterface.DEFAULT_IMEISV;
+    private String mEsn = MockModemConfigInterface.DEFAULT_ESN;
+    private String mMeid = MockModemConfigInterface.DEFAULT_MEID;
+    private int mRadioState = MockModemConfigInterface.DEFAULT_RADIO_STATE;
+    private byte mNumOfLiveModem = MockModemConfigInterface.DEFAULT_NUM_OF_LIVE_MODEM;
+    private SimSlotStatus[] mSimSlotStatus;
+    private CardStatus mCardStatus;
+    private MockSimCard[] mSIMCard;
+    private PhoneCapability mPhoneCapability = new PhoneCapability();
+
+    // ***** RegistrantLists
+    // ***** IRadioConfig RegistrantLists
+    private RegistrantList mNumOfLiveModemChangedRegistrants = new RegistrantList();
+    private RegistrantList mPhoneCapabilityChangedRegistrants = new RegistrantList();
+    private RegistrantList mSimSlotStatusChangedRegistrants = new RegistrantList();
+
+    // ***** IRadioModem RegistrantLists
+    private RegistrantList mBasebandVersionChangedRegistrants = new RegistrantList();
+    private RegistrantList mDeviceIdentityChangedRegistrants = new RegistrantList();
+    private RegistrantList mRadioStateChangedRegistrants = new RegistrantList();
+
+    // ***** IRadioSim RegistrantLists
+    private RegistrantList mCardStatusChangedRegistrants = new RegistrantList();
+
+    public MockModemConfigBase(Context context, int instanceId, int numOfSim, int numOfPhone) {
+        mContext = context;
+        mSubId = instanceId;
+        mNumOfSim =
+                (numOfSim > MockModemConfigInterface.MAX_NUM_OF_SIM_SLOT)
+                        ? MockModemConfigInterface.MAX_NUM_OF_SIM_SLOT
+                        : numOfSim;
+        mNumOfPhone =
+                (numOfPhone > MockModemConfigInterface.MAX_NUM_OF_LOGICAL_MODEM)
+                        ? MockModemConfigInterface.MAX_NUM_OF_LOGICAL_MODEM
+                        : numOfPhone;
+        mTAG = mTAG + "[" + mSubId + "]";
+        mConfigAccess = new Object();
+        mHandler = new MockModemConfigHandler();
+        mSimSlotStatus = new SimSlotStatus[mNumOfSim];
+        mCardStatus = new CardStatus();
+        mSIMCard = new MockSimCard[mNumOfSim];
+        mSimPhyicalId = mSubId; // for default mapping
+        createSIMCards();
+        setDefaultConfigValue();
+    }
+
+    public class MockModemConfigHandler extends Handler {
+        // ***** Handler implementation
+        @Override
+        public void handleMessage(Message msg) {
+            synchronized (mConfigAccess) {
+                switch (msg.what) {
+                    case EVENT_SET_RADIO_POWER:
+                        int state = msg.arg1;
+                        if (state >= RADIO_STATE_UNAVAILABLE && state <= RADIO_STATE_ON) {
+                            Log.d(
+                                    mTAG,
+                                    "EVENT_SET_RADIO_POWER: old("
+                                            + mRadioState
+                                            + "), new("
+                                            + state
+                                            + ")");
+                            if (mRadioState != state) {
+                                mRadioState = state;
+                                mRadioStateChangedRegistrants.notifyRegistrants(
+                                        new AsyncResult(null, mRadioState, null));
+                            }
+                        } else {
+                            Log.e(mTAG, "EVENT_SET_RADIO_POWER: invalid state(" + state + ")");
+                            mRadioStateChangedRegistrants.notifyRegistrants(null);
+                        }
+                        break;
+                    case EVENT_SET_SIM_PRESENT:
+                        boolean isPresent = msg.getData().getBoolean("isPresent", false);
+                        Log.d(mTAG, "EVENT_SET_SIM_PRESENT: " + (isPresent ? "Present" : "Absent"));
+                        int newCardState =
+                                isPresent ? CardStatus.STATE_PRESENT : CardStatus.STATE_ABSENT;
+                        if (mSubId == DEFAULT_SUB_ID
+                                && mSimSlotStatus[mSimPhyicalId].cardState != newCardState) {
+                            mSimSlotStatus[mSimPhyicalId].cardState = newCardState;
+                            mSimSlotStatusChangedRegistrants.notifyRegistrants(
+                                    new AsyncResult(null, mSimSlotStatus, null));
+                        }
+                        if (mCardStatus.cardState != newCardState) {
+                            mCardStatus.cardState = newCardState;
+                            mCardStatusChangedRegistrants.notifyRegistrants(
+                                    new AsyncResult(null, mCardStatus, null));
+                        }
+                        break;
+                }
+            }
+        }
+    }
+
+    private void setDefaultConfigValue() {
+        synchronized (mConfigAccess) {
+            mBasebandVersion = MockModemConfigInterface.DEFAULT_BASEBAND_VERSION;
+            mImei = MockModemConfigInterface.DEFAULT_IMEI;
+            mImeiSv = MockModemConfigInterface.DEFAULT_IMEISV;
+            mEsn = MockModemConfigInterface.DEFAULT_ESN;
+            mMeid = MockModemConfigInterface.DEFAULT_MEID;
+            mRadioState = MockModemConfigInterface.DEFAULT_RADIO_STATE;
+            mNumOfLiveModem = MockModemConfigInterface.DEFAULT_NUM_OF_LIVE_MODEM;
+            setDefaultPhoneCapability(mPhoneCapability);
+            if (mSubId == DEFAULT_SUB_ID) {
+                setDefaultSimSlotStatus();
+            }
+            setDefaultCardStatus();
+        }
+    }
+
+    private void setDefaultPhoneCapability(PhoneCapability phoneCapability) {
+        phoneCapability.logicalModemIds =
+                new byte[MockModemConfigInterface.MAX_NUM_OF_LOGICAL_MODEM];
+        phoneCapability.maxActiveData = MockModemConfigInterface.DEFAULT_MAX_ACTIVE_DATA;
+        phoneCapability.maxActiveInternetData =
+                MockModemConfigInterface.DEFAULT_MAX_ACTIVE_INTERNAL_DATA;
+        phoneCapability.isInternetLingeringSupported =
+                MockModemConfigInterface.DEFAULT_IS_INTERNAL_LINGERING_SUPPORTED;
+        phoneCapability.logicalModemIds[0] = MockModemConfigInterface.DEFAULT_LOGICAL_MODEM1_ID;
+        phoneCapability.logicalModemIds[1] = MockModemConfigInterface.DEFAULT_LOGICAL_MODEM2_ID;
+    }
+
+    private void createSIMCards() {
+        for (int i = 0; i < mNumOfSim; i++) {
+            mSIMCard[i] = new MockSimCard(i);
+        }
+    }
+
+    private void setDefaultSimSlotStatus() {
+        if (mSubId != DEFAULT_SUB_ID) {
+            // Only sub 0 needs to response SimSlotStatus
+            return;
+        }
+
+        for (int i = 0; i < mNumOfSim; i++) {
+            int portInfoListLen = mSIMCard[i].getNumOfSimPortInfo();
+            mSimSlotStatus[i] = new SimSlotStatus();
+            mSimSlotStatus[i].cardState =
+                    mSIMCard[i].isCardPresent()
+                            ? CardStatus.STATE_PRESENT
+                            : CardStatus.STATE_ABSENT;
+            mSimSlotStatus[i].atr = mSIMCard[i].getATR();
+            mSimSlotStatus[i].eid = mSIMCard[i].getEID();
+            // Current only support one Sim port in MockSimCard
+            SimPortInfo[] portInfoList0 = new SimPortInfo[portInfoListLen];
+            portInfoList0[0] = new SimPortInfo();
+            portInfoList0[0].portActive = mSIMCard[i].isSlotPortActive();
+            portInfoList0[0].logicalSlotId = mSIMCard[i].getLogicalSlotId();
+            portInfoList0[0].iccId = mSIMCard[i].getICCID();
+            mSimSlotStatus[i].portInfo = portInfoList0;
+        }
+    }
+
+    private void setDefaultCardStatus() {
+        if (mSimPhyicalId != -1) {
+            int numbOfSimApp = mSIMCard[mSimPhyicalId].getNumOfSimApp();
+            mCardStatus = new CardStatus();
+            mCardStatus.cardState =
+                    mSIMCard[mSimPhyicalId].isCardPresent()
+                            ? CardStatus.STATE_PRESENT
+                            : CardStatus.STATE_ABSENT;
+            mCardStatus.universalPinState = mSIMCard[mSimPhyicalId].getUniversalPinState();
+            mCardStatus.gsmUmtsSubscriptionAppIndex = mSIMCard[mSimPhyicalId].getGsmAppIndex();
+            mCardStatus.cdmaSubscriptionAppIndex = mSIMCard[mSimPhyicalId].getCdmaAppIndex();
+            mCardStatus.imsSubscriptionAppIndex = mSIMCard[mSimPhyicalId].getImsAppIndex();
+            mCardStatus.applications = new android.hardware.radio.sim.AppStatus[numbOfSimApp];
+            mCardStatus.atr = mSIMCard[mSimPhyicalId].getATR();
+            mCardStatus.iccid = mSIMCard[mSimPhyicalId].getICCID();
+            mCardStatus.eid = mSIMCard[mSimPhyicalId].getEID();
+            mCardStatus.slotMap = new android.hardware.radio.config.SlotPortMapping();
+            mCardStatus.slotMap.physicalSlotId = mSIMCard[mSimPhyicalId].getPhysicalSlotId();
+            mCardStatus.slotMap.portId = mSIMCard[mSimPhyicalId].getSlotPortId();
+        } else {
+            Log.e(mTAG, "Invalid Sim physical id");
+        }
+    }
+
+    private void notifyDeviceIdentityChangedRegistrants() {
+        String[] deviceIdentity = new String[4];
+        synchronized (mConfigAccess) {
+            deviceIdentity[0] = mImei;
+            deviceIdentity[1] = mImeiSv;
+            deviceIdentity[2] = mEsn;
+            deviceIdentity[3] = mMeid;
+        }
+        AsyncResult ar = new AsyncResult(null, deviceIdentity, null);
+        mDeviceIdentityChangedRegistrants.notifyRegistrants(ar);
+    }
+
+    // ***** MockModemConfigInterface implementation
+    @Override
+    public void notifyAllRegistrantNotifications() {
+        Log.d(mTAG, "notifyAllRegistrantNotifications");
+        synchronized (mConfigAccess) {
+            // IRadioConfig
+            mNumOfLiveModemChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(null, mNumOfLiveModem, null));
+            mPhoneCapabilityChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(null, mPhoneCapability, null));
+            mSimSlotStatusChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(null, mSimSlotStatus, null));
+
+            // IRadioModem
+            mBasebandVersionChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(null, mBasebandVersion, null));
+            notifyDeviceIdentityChangedRegistrants();
+            mRadioStateChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(null, mRadioState, null));
+
+            // IRadioSim
+            mCardStatusChangedRegistrants.notifyRegistrants(
+                    new AsyncResult(null, mCardStatus, null));
+        }
+    }
+
+    // ***** IRadioConfig notification implementation
+    @Override
+    public void registerForNumOfLiveModemChanged(Handler h, int what, Object obj) {
+        mNumOfLiveModemChangedRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForNumOfLiveModemChanged(Handler h) {
+        mNumOfLiveModemChangedRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForPhoneCapabilityChanged(Handler h, int what, Object obj) {
+        mPhoneCapabilityChangedRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForPhoneCapabilityChanged(Handler h) {
+        mPhoneCapabilityChangedRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForSimSlotStatusChanged(Handler h, int what, Object obj) {
+        mSimSlotStatusChangedRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForSimSlotStatusChanged(Handler h) {
+        mSimSlotStatusChangedRegistrants.remove(h);
+    }
+
+    // ***** IRadioModem notification implementation
+    @Override
+    public void registerForBasebandVersionChanged(Handler h, int what, Object obj) {
+        mBasebandVersionChangedRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForBasebandVersionChanged(Handler h) {
+        mBasebandVersionChangedRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForDeviceIdentityChanged(Handler h, int what, Object obj) {
+        mDeviceIdentityChangedRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForDeviceIdentityChanged(Handler h) {
+        mDeviceIdentityChangedRegistrants.remove(h);
+    }
+
+    @Override
+    public void registerForRadioStateChanged(Handler h, int what, Object obj) {
+        mRadioStateChangedRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForRadioStateChanged(Handler h) {
+        mRadioStateChangedRegistrants.remove(h);
+    }
+
+    // ***** IRadioSim notification implementation
+    @Override
+    public void registerForCardStatusChanged(Handler h, int what, Object obj) {
+        mCardStatusChangedRegistrants.addUnique(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForCardStatusChanged(Handler h) {
+        mCardStatusChangedRegistrants.remove(h);
+    }
+
+    // ***** IRadioConfig set APIs implementation
+
+    // ***** IRadioModem set APIs implementation
+    @Override
+    public void setRadioState(int state, String client) {
+        Log.d(mTAG, "setRadioState (" + state + ") from " + client);
+
+        Message msg = mHandler.obtainMessage(EVENT_SET_RADIO_POWER);
+        msg.arg1 = state;
+        mHandler.sendMessage(msg);
+    }
+
+    // ***** IRadioSim set APIs implementation
+
+    // ***** IRadioNetwork set APIs implementation
+
+    // ***** IRadioVoice set APIs implementation
+
+    // ***** IRadioData set APIs implementation
+
+    // ***** IRadioMessaging set APIs implementation
+
+    // ***** Helper APIs implementation
+    @Override
+    public void setSimPresent(boolean isPresent, String client) {
+        Log.d(mTAG, "setSimPresent (" + (isPresent ? "Present" : "Absent") + ") from: " + client);
+
+        Message msg = mHandler.obtainMessage(EVENT_SET_SIM_PRESENT);
+        msg.getData().putBoolean("isPresent", isPresent);
+        mHandler.sendMessage(msg);
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigInterface.java b/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigInterface.java
new file mode 100644
index 0000000..5632255
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MockModemConfigInterface.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import android.os.Handler;
+
+public interface MockModemConfigInterface {
+
+    // ***** Constants
+    int MAX_NUM_OF_SIM_SLOT = 3; // Change this needs to add more SIM SLOT NVs.
+    int MAX_NUM_OF_LOGICAL_MODEM = 2; // Change this needs to add more MODEM NVs.
+    int RADIO_STATE_UNAVAILABLE = 0;
+    int RADIO_STATE_OFF = 1;
+    int RADIO_STATE_ON = 2;
+
+    // Default config value
+    String DEFAULT_BASEBAND_VERSION = "mock-modem-service-1.0";
+    String DEFAULT_IMEI = "123456789012345";
+    String DEFAULT_IMEISV = "01";
+    String DEFAULT_ESN = "123456789";
+    String DEFAULT_MEID = "123456789012345";
+    int DEFAULT_RADIO_STATE = RADIO_STATE_UNAVAILABLE;
+    int DEFAULT_NUM_OF_LIVE_MODEM = 1; // Should <= MAX_NUM_OF_MODEM
+    int DEFAULT_MAX_ACTIVE_DATA = 2;
+    int DEFAULT_MAX_ACTIVE_INTERNAL_DATA = 1;
+    boolean DEFAULT_IS_INTERNAL_LINGERING_SUPPORTED = false;
+    int DEFAULT_LOGICAL_MODEM1_ID = 0;
+    int DEFAULT_LOGICAL_MODEM2_ID = 1;
+
+    // ***** Methods
+
+    /** Broadcast all notifications */
+    void notifyAllRegistrantNotifications();
+
+    // ***** IRadioConfig
+    /** Register/unregister notification handler for number of modem changed */
+    void registerForNumOfLiveModemChanged(Handler h, int what, Object obj);
+
+    void unregisterForNumOfLiveModemChanged(Handler h);
+
+    /** Register/unregister notification handler for sim slot status changed */
+    void registerForPhoneCapabilityChanged(Handler h, int what, Object obj);
+
+    void unregisterForPhoneCapabilityChanged(Handler h);
+
+    /** Register/unregister notification handler for sim slot status changed */
+    void registerForSimSlotStatusChanged(Handler h, int what, Object obj);
+
+    void unregisterForSimSlotStatusChanged(Handler h);
+
+    // ***** IRadioModem
+    /** Register/unregister notification handler for baseband version changed */
+    void registerForBasebandVersionChanged(Handler h, int what, Object obj);
+
+    void unregisterForBasebandVersionChanged(Handler h);
+
+    /** Register/unregister notification handler for device identity changed */
+    void registerForDeviceIdentityChanged(Handler h, int what, Object obj);
+
+    void unregisterForDeviceIdentityChanged(Handler h);
+
+    /** Register/unregister notification handler for radio state changed */
+    void registerForRadioStateChanged(Handler h, int what, Object obj);
+
+    void unregisterForRadioStateChanged(Handler h);
+
+    // ***** IRadioSim
+    /** Register/unregister notification handler for card status changed */
+    void registerForCardStatusChanged(Handler h, int what, Object obj);
+
+    void unregisterForCardStatusChanged(Handler h);
+
+    /**
+     * Sets the latest radio power state of modem
+     *
+     * @param state 0 means "unavailable", 1 means "off", 2 means "on".
+     * @param client for tracking calling client
+     */
+    void setRadioState(int state, String client);
+
+    /**
+     * Sets a specific logical SIM state to absent or present
+     *
+     * @param isPresent true means "present", false means "absent".
+     * @param client for tracking calling client
+     */
+    void setSimPresent(boolean isPresent, String client);
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemManager.java b/tests/tests/telephony/current/src/android/telephony/cts/MockModemManager.java
new file mode 100644
index 0000000..24eaa7a
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MockModemManager.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+
+import java.util.concurrent.TimeUnit;
+
+public class MockModemManager {
+    private static final String TAG = "MockModemManager";
+
+    private static Context sContext;
+    private static MockModemServiceConnector sServiceConnector;
+    private MockModemService mMockModemService;
+
+    public MockModemManager() {
+        sContext = InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
+    private void waitForTelephonyFrameworkDone(int delayInSec) throws Exception {
+        TimeUnit.SECONDS.sleep(delayInSec);
+    }
+
+    /* Public APIs */
+
+    /**
+     * Bring up Mock Modem Service and connect to it.
+     *
+     * @return boolean true if the operation is successful, otherwise false.
+     */
+    public boolean connectMockModemService() throws Exception {
+        boolean result = false;
+
+        if (sServiceConnector == null) {
+            sServiceConnector =
+                    new MockModemServiceConnector(InstrumentationRegistry.getInstrumentation());
+        }
+
+        if (sServiceConnector != null) {
+            result = sServiceConnector.connectMockModemService();
+
+            if (result) {
+                mMockModemService = sServiceConnector.getMockModemService();
+
+                if (mMockModemService != null) {
+                    /*
+                     It needs to have a delay to wait for Telephony Framework to bind with
+                     MockModemService and set radio power as a desired state for initial condition
+                     even get SIM card state. Currently, 1 sec is enough for now.
+                    */
+                    waitForTelephonyFrameworkDone(1);
+                } else {
+                    Log.e(TAG, "MockModemService get failed!");
+                    result = false;
+                }
+            }
+        } else {
+            Log.e(TAG, "Create MockModemServiceConnector failed!");
+        }
+
+        return result;
+    }
+
+    /**
+     * Disconnect from Mock Modem Service.
+     *
+     * @return boolean true if the operation is successful, otherwise false.
+     */
+    public boolean disconnectMockModemService() throws Exception {
+        boolean result = false;
+
+        if (sServiceConnector != null) {
+            result = sServiceConnector.disconnectMockModemService();
+
+            if (result) {
+                mMockModemService = null;
+            } else {
+                Log.e(TAG, "MockModemService disconnected failed!");
+            }
+        } else {
+            Log.e(TAG, "No MockModemServiceConnector exist!");
+        }
+
+        return result;
+    }
+
+    /**
+     * Set SIM card status as present.
+     *
+     * @param subId which sub needs to be set.
+     * @return boolean true if the operation is successful, otherwise false.
+     */
+    public boolean setSimPresent(int subId) throws Exception {
+        Log.d(TAG, "setSimPresent[" + subId + "]");
+        boolean result = true;
+
+        MockModemConfigInterface[] configInterfaces =
+                mMockModemService.getMockModemConfigInterfaces();
+        configInterfaces[subId].setSimPresent(true, TAG);
+        waitForTelephonyFrameworkDone(1);
+        return result;
+    }
+
+    /**
+     * Force the response error return for a specific RIL request
+     *
+     * @param subId which sub needs to be set.
+     * @param requestId the request/response message ID
+     * @param error RIL_Errno and -1 means to disable the modified mechanism, back to original mock
+     *     modem behavior
+     * @return boolean true if the operation is successful, otherwise false.
+     */
+    public boolean forceErrorResponse(int subId, int requestId, int error) throws Exception {
+        Log.d(
+                TAG,
+                "forceErrorResponse[" + subId + "] for request:" + requestId + " ,error:" + error);
+        boolean result = true;
+
+        // TODO: support DSDS
+        switch (requestId) {
+            case RIL_REQUEST_RADIO_POWER:
+                mMockModemService.getIRadioModem().forceErrorResponse(requestId, error);
+                break;
+            default:
+                Log.e(TAG, "request:" + requestId + " not support to change the response error");
+                result = false;
+                break;
+        }
+        return result;
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemService.java b/tests/tests/telephony/current/src/android/telephony/cts/MockModemService.java
index 4b09895..38e7086 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MockModemService.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MockModemService.java
@@ -24,7 +24,7 @@
 import android.hardware.radio.RadioResponseType;
 import android.os.Binder;
 import android.os.IBinder;
-import android.sysprop.TelephonyProperties;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -46,6 +46,7 @@
     public static final String PHONE_ID = "phone_id";
 
     private static Context sContext;
+    private static MockModemConfigInterface[] sMockModemConfigInterfaces;
     private static IRadioConfigImpl sIRadioConfigImpl;
     private static IRadioModemImpl sIRadioModemImpl;
     private static IRadioSimImpl sIRadioSimImpl;
@@ -58,19 +59,16 @@
     public static final byte PHONE_ID_1 = 0x01;
 
     public static final int LATCH_MOCK_MODEM_SERVICE_READY = 0;
-    public static final int LATCH_MOCK_MODEM_RADIO_POWR_ON = 1;
-    public static final int LATCH_MOCK_MODEM_RADIO_POWR_OFF = 2;
-    public static final int LATCH_MOCK_MODEM_SIM_READY = 3;
-    public static final int LATCH_MAX = 4;
+    public static final int LATCH_RADIO_INTERFACES_READY = 1;
+    public static final int LATCH_MAX = 2;
 
     private static final int IRADIO_CONFIG_INTERFACE_NUMBER = 1;
     private static final int IRADIO_INTERFACE_NUMBER = 6;
-    public static final int LATCH_RADIO_INTERFACES_READY = LATCH_MAX;
-    public static final int LATCH_MOCK_MODEM_INITIALIZATION_READY =
-            LATCH_RADIO_INTERFACES_READY + 1;
-    public static final int TOTAL_LATCH_NUMBER = LATCH_MAX + 2;
 
-    private int mSimNumber;
+    private TelephonyManager mTelephonyManager;
+    private int mNumOfSim;
+    private int mNumOfPhone;
+    private static final int DEFAULT_SUB_ID = 0;
 
     private Object mLock;
     protected static CountDownLatch[] sLatches;
@@ -87,24 +85,36 @@
     public void onCreate() {
         Log.d(TAG, "Mock Modem Service Created");
 
-        mSimNumber = 1; // TODO: Read property to know the device is single SIM or DSDS
+        sContext = InstrumentationRegistry.getInstrumentation().getContext();
+        mTelephonyManager = sContext.getSystemService(TelephonyManager.class);
+        mNumOfSim = getNumPhysicalSlots();
+        mNumOfPhone = mTelephonyManager.getActiveModemCount();
+        Log.d(TAG, "Support number of phone = " + mNumOfPhone + ", number of SIM = " + mNumOfSim);
 
         mLock = new Object();
 
-        sLatches = new CountDownLatch[TOTAL_LATCH_NUMBER];
+        sLatches = new CountDownLatch[LATCH_MAX];
         for (int i = 0; i < LATCH_MAX; i++) {
             sLatches[i] = new CountDownLatch(1);
+            if (i == LATCH_RADIO_INTERFACES_READY) {
+                int radioInterfaceNumber =
+                        IRADIO_CONFIG_INTERFACE_NUMBER + mNumOfPhone * IRADIO_INTERFACE_NUMBER;
+                sLatches[i] = new CountDownLatch(radioInterfaceNumber);
+            } else {
+                sLatches[i] = new CountDownLatch(1);
+            }
         }
 
-        int radioInterfaceNumber =
-                IRADIO_CONFIG_INTERFACE_NUMBER + mSimNumber * IRADIO_INTERFACE_NUMBER;
-        sLatches[LATCH_RADIO_INTERFACES_READY] = new CountDownLatch(radioInterfaceNumber);
-        sLatches[LATCH_MOCK_MODEM_INITIALIZATION_READY] = new CountDownLatch(1);
+        sMockModemConfigInterfaces = new MockModemConfigBase[mNumOfPhone];
+        for (int i = 0; i < mNumOfPhone; i++) {
+            sMockModemConfigInterfaces[i] =
+                    new MockModemConfigBase(sContext, i, mNumOfSim, mNumOfPhone);
+        }
 
-        sContext = InstrumentationRegistry.getInstrumentation().getContext();
-        sIRadioConfigImpl = new IRadioConfigImpl(this);
-        sIRadioModemImpl = new IRadioModemImpl(this);
-        sIRadioSimImpl = new IRadioSimImpl(this);
+        sIRadioConfigImpl = new IRadioConfigImpl(this, sMockModemConfigInterfaces, DEFAULT_SUB_ID);
+        // TODO: Support DSDS
+        sIRadioModemImpl = new IRadioModemImpl(this, sMockModemConfigInterfaces, DEFAULT_SUB_ID);
+        sIRadioSimImpl = new IRadioSimImpl(this, sMockModemConfigInterfaces, DEFAULT_SUB_ID);
         sIRadioNetworkImpl = new IRadioNetworkImpl(this);
         sIRadioDataImpl = new IRadioDataImpl(this);
         sIRadioMessagingImpl = new IRadioMessagingImpl(this);
@@ -181,14 +191,6 @@
         return mBinder;
     }
 
-    public void resetState() {
-        synchronized (mLock) {
-            for (int i = 0; i < LATCH_MAX; i++) {
-                sLatches[i] = new CountDownLatch(1);
-            }
-        }
-    }
-
     public boolean waitForLatchCountdown(int latchIndex) {
         boolean complete = false;
         try {
@@ -231,7 +233,6 @@
         int numPhysicalSlots =
                 sContext.getResources()
                         .getInteger(com.android.internal.R.integer.config_num_physical_slots);
-        Log.d(TAG, "numPhysicalSlots: " + numPhysicalSlots);
         return numPhysicalSlots;
     }
 
@@ -253,61 +254,51 @@
         return rspInfo;
     }
 
-    private boolean initRadioState() {
-        int waitLatch;
+    public boolean initialize() {
+        Log.d(TAG, "initialize");
+        boolean result = true;
 
-        boolean apm = TelephonyProperties.airplane_mode_on().orElse(false);
-        Log.d(TAG, "APM setting: " + apm);
-
-        if (!apm) {
-            waitLatch = LATCH_MOCK_MODEM_RADIO_POWR_ON;
-        } else {
-            waitLatch = LATCH_MOCK_MODEM_RADIO_POWR_OFF;
+        // Sync mock modem status between modules
+        for (int i = 0; i < mNumOfPhone; i++) {
+            sMockModemConfigInterfaces[i].notifyAllRegistrantNotifications();
         }
 
-        sIRadioModemImpl.radioStateChanged(android.hardware.radio.modem.RadioState.OFF);
-
-        // Radio Power command is expected.
-        return waitForLatchCountdown(waitLatch);
-    }
-
-    private boolean initSimSlotState() {
-
-        // SIM/Slot commands are expected.
-        return waitForLatchCountdown(LATCH_MOCK_MODEM_SIM_READY);
-    }
-
-    public void initialization() {
-        Log.d(TAG, "initialization");
-
-        boolean status = false;
-
+        // Connect to telephony framework
         sIRadioModemImpl.rilConnected();
 
-        status = initRadioState();
-        if (!status) {
-            Log.e(TAG, "radio state initialization fail");
-            return;
-        }
-
-        status = initSimSlotState();
-        if (!status) {
-            Log.e(TAG, "sim/slot state initialization fail");
-            return;
-        }
-
-        countDownLatch(LATCH_MOCK_MODEM_INITIALIZATION_READY);
+        return result;
     }
 
-    public void unsolSimSlotsStatusChanged() {
-        sIRadioConfigImpl.unsolSimSlotsStatusChanged();
-        sIRadioSimImpl.simStatusChanged();
+    public MockModemConfigInterface[] getMockModemConfigInterfaces() {
+        return sMockModemConfigInterfaces;
     }
 
-    public void setSimPresent(int slotId) {
-        Log.d(TAG, "setSimPresent");
+    // TODO: Support DSDS
+    public IRadioConfigImpl getIRadioConfig() {
+        return sIRadioConfigImpl;
+    }
 
-        sIRadioSimImpl.setSimPresent(slotId);
-        sIRadioConfigImpl.setSimPresent(slotId);
+    public IRadioModemImpl getIRadioModem() {
+        return sIRadioModemImpl;
+    }
+
+    public IRadioSimImpl getIRadioSim() {
+        return sIRadioSimImpl;
+    }
+
+    public IRadioNetworkImpl getIRadioNetwork() {
+        return sIRadioNetworkImpl;
+    }
+
+    public IRadioVoiceImpl getIRadioVoice() {
+        return sIRadioVoiceImpl;
+    }
+
+    public IRadioMessagingImpl getIRadioMessaging() {
+        return sIRadioMessagingImpl;
+    }
+
+    public IRadioDataImpl getIRadioData() {
+        return sIRadioDataImpl;
     }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockModemServiceConnector.java b/tests/tests/telephony/current/src/android/telephony/cts/MockModemServiceConnector.java
index ebafc81..beeb720 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MockModemServiceConnector.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MockModemServiceConnector.java
@@ -33,8 +33,7 @@
 
     private static final String TAG = "MockModemServiceConnector";
 
-    private static final String DEFAULT_SERVICE_NAME =
-            MockModemService.class.getClass().getName();
+    private static final String DEFAULT_SERVICE_NAME = MockModemService.class.getClass().getName();
     private static final String COMMAND_BASE = "cmd phone ";
     private static final String COMMAND_SET_MODEM_SERVICE = "radio set-modem-service ";
     private static final String COMMAND_GET_MODEM_SERVICE = "radio get-modem-service ";
@@ -196,10 +195,9 @@
                             MockModemService.LATCH_RADIO_INTERFACES_READY,
                             BIND_RADIO_INTERFACE_READY_TIMEOUT_MS);
 
-            mMockModemService.initialization();
-            isComplete =
-                    mMockModemService.waitForLatchCountdown(
-                            MockModemService.LATCH_MOCK_MODEM_INITIALIZATION_READY);
+            if (isComplete) {
+                isComplete = mMockModemService.initialize();
+            }
         }
 
         return isComplete;
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MockSimCard.java b/tests/tests/telephony/current/src/android/telephony/cts/MockSimCard.java
new file mode 100644
index 0000000..280b8d5
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MockSimCard.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.cts;
+
+import android.util.Log;
+
+public class MockSimCard {
+    private static final String TAG = "MockSimCard";
+
+    /* Support SIM card identify */
+    public static final int MOCK_SIM_PROFILE_ID_DEFAULT = 0; // SIM Absent
+    public static final int MOCK_SIM_PROFILE_ID_MAX = 1;
+
+    /* Support SIM slot */
+    private static final int MOCK_SIM_SLOT_1 = 0;
+    private static final int MOCK_SIM_SLOT_2 = 1;
+    private static final int MOCK_SIM_SLOT_3 = 2;
+    private static final int MOCK_SIM_SLOT_MAX = 3;
+
+    /* Default value definition */
+    private static final int MOCK_SIM_DEFAULT_SLOTID = MOCK_SIM_SLOT_1;
+    private static final int DEFAULT_NUM_OF_SIM_PORT_INfO = 1;
+    private static final int DEFAULT_NUM_OF_SIM_APP = 0;
+    private static final int DEFAULT_GSM_APP_IDX = -1;
+    private static final int DEFAULT_CDMA_APP_IDX = -1;
+    private static final int DEFAULT_IMS_APP_IDX = -1;
+    // SIM1 slot status
+    private static final int DEFAULT_SIM1_PROFILE_ID = MOCK_SIM_PROFILE_ID_DEFAULT;
+    private static final boolean DEFAULT_SIM1_CARD_PRESENT = false;
+    private static final String DEFAULT_SIM1_ATR = "";
+    private static final String DEFAULT_SIM1_EID = "";
+    private static final String DEFAULT_SIM1_ICCID = "";
+    private static final boolean DEFAULT_SIM1_PORT_ACTIVE = true;
+    private static final int DEFAULT_SIM1_PORT_ID = 0;
+    private static final int DEFAULT_SIM1_LOGICAL_SLOT_ID = 0;
+    private static final int DEFAULT_SIM1_PHYSICAL_SLOT_ID = 0;
+    private static final int DEFAULT_SIM1_UNIVERSAL_PIN_STATE = 0;
+    // SIM2 slot status
+    private static final int DEFAULT_SIM2_PROFILE_ID = MOCK_SIM_PROFILE_ID_DEFAULT;
+    private static final boolean DEFAULT_SIM2_CARD_PRESENT = false;
+    private static final String DEFAULT_SIM2_ATR =
+            "3B9F97C00A3FC6828031E073FE211F65D002341512810F51";
+    private static final String DEFAULT_SIM2_EID = "89033023426200000000005430099507";
+    private static final String DEFAULT_SIM2_ICCID = "";
+    private static final boolean DEFAULT_SIM2_PORT_ACTIVE = false;
+    private static final int DEFAULT_SIM2_PORT_ID = 0;
+    private static final int DEFAULT_SIM2_LOGICAL_SLOT_ID = -1;
+    private static final int DEFAULT_SIM2_PHYSICAL_SLOT_ID = 0;
+    private static final int DEFAULT_SIM2_UNIVERSAL_PIN_STATE = 0;
+    // SIM3 slot status
+    private static final int DEFAULT_SIM3_PROFILE_ID = MOCK_SIM_PROFILE_ID_DEFAULT;
+    private static final boolean DEFAULT_SIM3_CARD_PRESENT = false;
+    private static final String DEFAULT_SIM3_ATR = "";
+    private static final String DEFAULT_SIM3_EID = "";
+    private static final String DEFAULT_SIM3_ICCID = "";
+    private static final boolean DEFAULT_SIM3_PORT_ACTIVE = false;
+    private static final int DEFAULT_SIM3_PORT_ID = 0;
+    private static final int DEFAULT_SIM3_LOGICAL_SLOT_ID = -1;
+    private static final int DEFAULT_SIM3_PHYSICAL_SLOT_ID = 0;
+    private static final int DEFAULT_SIM3_UNIVERSAL_PIN_STATE = 0;
+
+    // SIM Slot status
+    private int mPhysicalSlotId;
+    private int mLogicalSlotId;
+    private int mSlotPortId;
+    private boolean mIsSlotPortActive;
+    private boolean mIsCardPresent;
+
+    // SIM card data
+    private int mSimProfileId;
+    private String mICCID;
+    private String mEID;
+    private String mATR;
+    private int mUniversalPinState;
+    private int mGsmAppIndex;
+    private int mCdmaAppIndex;
+    private int mImsAppIndex;
+    private int mNumOfSimApp;
+
+    public MockSimCard(int slotId) {
+        int simprofile = DEFAULT_SIM1_PROFILE_ID;
+
+        if (slotId >= MOCK_SIM_SLOT_MAX) {
+            Log.e(
+                    TAG,
+                    "Invalid slot id("
+                            + slotId
+                            + "). Using default slot id("
+                            + MOCK_SIM_DEFAULT_SLOTID
+                            + ").");
+            slotId = MOCK_SIM_DEFAULT_SLOTID;
+        }
+
+        // Init default SIM profile id
+        switch (slotId) {
+            case MOCK_SIM_SLOT_1:
+                simprofile = DEFAULT_SIM1_PROFILE_ID;
+                break;
+            case MOCK_SIM_SLOT_2:
+                simprofile = DEFAULT_SIM2_PROFILE_ID;
+                break;
+            case MOCK_SIM_SLOT_3:
+                simprofile = DEFAULT_SIM3_PROFILE_ID;
+                break;
+        }
+
+        // Initiate SIM card with default profile
+        initMockSimCard(slotId, simprofile);
+    }
+
+    private void initMockSimCard(int slotId, int simProfileId) {
+        if (slotId > MockModemConfigInterface.MAX_NUM_OF_SIM_SLOT) {
+            Log.e(
+                    TAG,
+                    "Physical slot id("
+                            + slotId
+                            + ") is invalid. Using default slot id("
+                            + MOCK_SIM_DEFAULT_SLOTID
+                            + ").");
+            mPhysicalSlotId = MOCK_SIM_DEFAULT_SLOTID;
+        } else {
+            mPhysicalSlotId = slotId;
+        }
+        if (simProfileId >= 0 && simProfileId < MOCK_SIM_PROFILE_ID_MAX) {
+            mSimProfileId = simProfileId;
+            Log.i(
+                    TAG,
+                    "Load SIM profile ID: "
+                            + mSimProfileId
+                            + " into physical slot["
+                            + mPhysicalSlotId
+                            + "]");
+        } else {
+            mSimProfileId = MOCK_SIM_PROFILE_ID_DEFAULT;
+            Log.e(
+                    TAG,
+                    "SIM Absent on physical slot["
+                            + mPhysicalSlotId
+                            + "]. Not support SIM card ID: "
+                            + mSimProfileId);
+        }
+
+        // Initiate slot status
+        initMockSimSlot();
+
+        // Load SIM profile data
+        loadMockSimCard();
+    }
+
+    private void initMockSimSlot() {
+        switch (mPhysicalSlotId) {
+            case MOCK_SIM_SLOT_1:
+                mLogicalSlotId = DEFAULT_SIM1_LOGICAL_SLOT_ID;
+                mSlotPortId = DEFAULT_SIM1_PORT_ID;
+                mIsSlotPortActive = DEFAULT_SIM1_PORT_ACTIVE;
+                mIsCardPresent = DEFAULT_SIM1_CARD_PRESENT;
+                break;
+            case MOCK_SIM_SLOT_2:
+                mLogicalSlotId = DEFAULT_SIM2_LOGICAL_SLOT_ID;
+                mSlotPortId = DEFAULT_SIM2_PORT_ID;
+                mIsSlotPortActive = DEFAULT_SIM2_PORT_ACTIVE;
+                mIsCardPresent = DEFAULT_SIM2_CARD_PRESENT;
+                break;
+            case MOCK_SIM_SLOT_3:
+                mLogicalSlotId = DEFAULT_SIM3_LOGICAL_SLOT_ID;
+                mSlotPortId = DEFAULT_SIM3_PORT_ID;
+                mIsSlotPortActive = DEFAULT_SIM3_PORT_ACTIVE;
+                mIsCardPresent = DEFAULT_SIM3_CARD_PRESENT;
+                break;
+        }
+    }
+
+    private void loadMockSimCard() {
+        // TODO: Read SIM card data from file
+        switch (mSimProfileId) {
+            default: // SIM absent
+                switch (mPhysicalSlotId) {
+                    case MOCK_SIM_SLOT_1:
+                        mICCID = DEFAULT_SIM1_ICCID;
+                        mATR = DEFAULT_SIM1_ATR;
+                        mEID = DEFAULT_SIM1_EID;
+                        mUniversalPinState = DEFAULT_SIM1_UNIVERSAL_PIN_STATE;
+                        break;
+                    case MOCK_SIM_SLOT_2:
+                        mICCID = DEFAULT_SIM2_ICCID;
+                        mATR = DEFAULT_SIM2_ATR;
+                        mEID = DEFAULT_SIM2_EID;
+                        mUniversalPinState = DEFAULT_SIM2_UNIVERSAL_PIN_STATE;
+                        break;
+                    case MOCK_SIM_SLOT_3:
+                        mICCID = DEFAULT_SIM3_ICCID;
+                        mATR = DEFAULT_SIM3_ATR;
+                        mEID = DEFAULT_SIM3_EID;
+                        mUniversalPinState = DEFAULT_SIM3_UNIVERSAL_PIN_STATE;
+                        break;
+                }
+                mGsmAppIndex = DEFAULT_GSM_APP_IDX;
+                mCdmaAppIndex = DEFAULT_CDMA_APP_IDX;
+                mImsAppIndex = DEFAULT_IMS_APP_IDX;
+                mNumOfSimApp = DEFAULT_NUM_OF_SIM_APP;
+                break;
+        }
+    }
+
+    public boolean isSlotPortActive() {
+        return mIsSlotPortActive;
+    }
+
+    public boolean isCardPresent() {
+        return mIsCardPresent;
+    }
+
+    public int getNumOfSimPortInfo() {
+        return DEFAULT_NUM_OF_SIM_PORT_INfO;
+    }
+
+    public int getPhysicalSlotId() {
+        return mPhysicalSlotId;
+    }
+
+    public int getLogicalSlotId() {
+        return mLogicalSlotId;
+    }
+
+    public int getSlotPortId() {
+        return mSlotPortId;
+    }
+
+    public String getEID() {
+        return mEID;
+    }
+
+    public String getATR() {
+        return mATR;
+    }
+
+    public String getICCID() {
+        return mICCID;
+    }
+
+    public int getUniversalPinState() {
+        return mUniversalPinState;
+    }
+
+    public int getGsmAppIndex() {
+        return mGsmAppIndex;
+    }
+
+    public int getCdmaAppIndex() {
+        return mCdmaAppIndex;
+    }
+
+    public int getImsAppIndex() {
+        return mImsAppIndex;
+    }
+
+    public int getNumOfSimApp() {
+        return mNumOfSimApp;
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
index 1121197..5335f96 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -49,6 +49,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
+import android.telephony.cts.util.CarrierPrivilegeUtils;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsManager;
 import android.telephony.ims.ImsMmTelManager;
@@ -89,6 +90,8 @@
 public class SubscriptionManagerTest {
     private static final String TAG = "SubscriptionManagerTest";
     private static final String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
+    private static final String READ_PRIVILEGED_PHONE_STATE =
+            "android.permission.READ_PRIVILEGED_PHONE_STATE";
     private static final List<Uri> CONTACTS = new ArrayList<>();
     static {
         CONTACTS.add(Uri.fromParts("tel", "+16505551212", null));
@@ -1115,6 +1118,93 @@
         uiAutomation.dropShellPermissionIdentity();
     }
 
+    @Test
+    public void tetsSetAndGetPhoneNumber() throws Exception {
+        if (!isSupported()) return;
+
+        // The phone number may be anything depends on the state of SIM and device.
+        // Simply call the getter and make sure no exception.
+
+        // Getters accessiable with READ_PRIVILEGED_PHONE_STATE
+        try {
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .adoptShellPermissionIdentity(READ_PRIVILEGED_PHONE_STATE);
+
+            mSm.getPhoneNumber(mSubId);
+            mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
+            mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER);
+            mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
+
+        } finally {
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .dropShellPermissionIdentity();
+        }
+
+        // Getters accessiable with READ_PHONE_NUMBERS
+        try {
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .adoptShellPermissionIdentity(android.Manifest.permission.READ_PHONE_NUMBERS);
+
+            mSm.getPhoneNumber(mSubId);
+            mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
+            mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER);
+            mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
+
+        } finally {
+            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .dropShellPermissionIdentity();
+        }
+
+        // Getters and the setter accessiable with carrier privilege
+        final String carrierNumber = "1234567890";
+        CarrierPrivilegeUtils.withCarrierPrivileges(
+                InstrumentationRegistry.getContext(),
+                mSubId,
+                () -> {
+                    mSm.getPhoneNumber(mSubId);
+                    mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
+                    mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
+
+                    mSm.setCarrierPhoneNumber(mSubId, carrierNumber);
+                    assertEquals(
+                            carrierNumber,
+                            mSm.getPhoneNumber(
+                                    mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER));
+                });
+
+        // Otherwise, getter and setter will hit SecurityException
+        try {
+            mSm.getPhoneNumber(mSubId);
+            fail("Expect SecurityException from getPhoneNumber()");
+        } catch (SecurityException e) {
+            // expected
+        }
+        try {
+            mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
+            fail("Expect SecurityException from getPhoneNumber()");
+        } catch (SecurityException e) {
+            // expected
+        }
+        try {
+            mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
+            fail("Expect SecurityException from getPhoneNumber()");
+        } catch (SecurityException e) {
+            // expected
+        }
+        try {
+            mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER);
+            fail("Expect SecurityException from getPhoneNumber()");
+        } catch (SecurityException e) {
+            // expected
+        }
+        try {
+            mSm.setCarrierPhoneNumber(mSubId, "987");
+            fail("Expect SecurityException from setCarrierPhoneNumber()");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
     @Nullable
     private PersistableBundle getBundleFromBackupData(byte[] data) {
         try (ByteArrayInputStream bis = new ByteArrayInputStream(data)) {
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 359b6c3..3802888 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -89,7 +89,9 @@
 import android.telephony.TelephonyManager;
 import android.telephony.ThermalMitigationRequest;
 import android.telephony.UiccCardInfo;
+import android.telephony.UiccPortInfo;
 import android.telephony.UiccSlotInfo;
+import android.telephony.UiccSlotMapping;
 import android.telephony.data.ApnSetting;
 import android.telephony.data.NetworkSlicingConfig;
 import android.telephony.emergency.EmergencyNumber;
@@ -2252,12 +2254,13 @@
         // test that these methods don't crash
         if (infos.size() > 0) {
             UiccCardInfo info = infos.get(0);
-            info.getIccId();
             info.getEid();
             info.isRemovable();
             info.isEuicc();
             info.getCardId();
-            info.getSlotIndex();
+            info.getPorts();
+            info.getPhysicalSlotIndex();
+            info.isRemovable();
         }
     }
 
@@ -2338,6 +2341,44 @@
     }
 
     /**
+     * Tests that the device properly sets the VoNr
+     */
+    @Test
+    public void testIsVoNrEnabled() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            return;
+        }
+
+        try {
+            int result = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                    (tm) -> tm.setVoNrEnabled(true));
+            if (result ==  TelephonyManager.ENABLE_VONR_REQUEST_NOT_SUPPORTED) {
+                return;
+            }
+        } catch (Exception e) {
+        }
+
+        assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
+                (tm) -> tm.isVoNrEnabled()));
+    }
+
+    /**
+     * Tests that a SecurityException is thrown when trying to set VoNR
+     */
+    @Test
+    public void testSetVoNrEnabledException() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
+            return;
+        }
+        try {
+            mTelephonyManager.setVoNrEnabled(true);
+            fail("Expected SecurityException. App does not have carrier privileges.");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    /**
      * Construct a CallAttributes object and test getters.
      */
     @Test
@@ -4447,7 +4488,8 @@
                     List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo();
                     Set<String> presentCards = Arrays.stream(mTelephonyManager.getUiccSlotsInfo())
                             .filter(Objects::nonNull)
-                            .filter(UiccSlotInfo::getIsActive)
+                            .filter(port -> port.getPorts().stream().anyMatch(portInfo ->
+                                    portInfo.isActive()))
                             .map(UiccSlotInfo::getCardId)
                             .filter(Objects::nonNull)
                             // hack around getUiccSlotsInfo not stripping trailing F
@@ -4455,10 +4497,12 @@
                             .collect(Collectors.toSet());
                     int slotIndex = -1;
                     for (UiccCardInfo cardInfo : cardInfos) {
-                        if (presentCards.contains(cardInfo.getIccId())
-                                || presentCards.contains(cardInfo.getEid())) {
-                            slotIndex = cardInfo.getSlotIndex();
-                            break;
+                        for (UiccPortInfo portInfo : cardInfo.getPorts()) {
+                            if (presentCards.contains(portInfo.getIccId())
+                                    || presentCards.contains(cardInfo.getEid())) {
+                                slotIndex = cardInfo.getPhysicalSlotIndex();
+                                break;
+                            }
                         }
                     }
                     if (slotIndex < 0) {
@@ -4777,5 +4821,91 @@
             // expected
         }
     }
+
+    @Test
+    public void testSimSlotMapping() {
+        if (!hasCellular()) return;
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity("android.permission.MODIFY_PHONE_STATE");
+        // passing slotMapping combination
+        UiccSlotMapping slotMapping1 = new UiccSlotMapping(0, 1, 1);
+        UiccSlotMapping slotMapping2 = new UiccSlotMapping(1, 0, 0);
+        List<UiccSlotMapping> slotMappingList = new ArrayList<UiccSlotMapping>();
+        slotMappingList.add(slotMapping1);
+        slotMappingList.add(slotMapping2);
+        try {
+            mTelephonyManager.setSimSlotMapping(slotMappingList);
+        } catch (Exception e) {
+            fail("Not Expected Fail, Error in setSimSlotMapping :" + e);
+        }
+        slotMappingList.clear();
+
+        // Duplicate logicalSlotIndex - Fail
+        UiccSlotMapping slotMapping3 = new UiccSlotMapping(0, 1, 0);
+        UiccSlotMapping slotMapping4 = new UiccSlotMapping(1, 0, 0);
+        slotMappingList.add(slotMapping3);
+        slotMappingList.add(slotMapping4);
+        try {
+            mTelephonyManager.setSimSlotMapping(slotMappingList);
+            fail("Expected IllegalArgumentException, Duplicate UiccSlotMapping data found");
+        } catch (IllegalArgumentException e) {
+            //expected
+        }
+        slotMappingList.clear();
+
+        // Duplicate {portIndex+physicalSlotIndex} - Fail
+        UiccSlotMapping slotMapping5 = new UiccSlotMapping(0, 1, 0);
+        UiccSlotMapping slotMapping6 = new UiccSlotMapping(0, 1, 1);
+        slotMappingList.add(slotMapping5);
+        slotMappingList.add(slotMapping6);
+        try {
+            mTelephonyManager.setSimSlotMapping(slotMappingList);
+            fail("Expected IllegalArgumentException, Duplicate UiccSlotMapping data found");
+        } catch (IllegalArgumentException e) {
+            //expected
+        }
+        slotMappingList.clear();
+
+        // Duplicate {portIndex+physicalSlotIndex+logicalSlotIndex} - Fail
+        UiccSlotMapping slotMapping7 = new UiccSlotMapping(0, 1, 0);
+        UiccSlotMapping slotMapping8 = new UiccSlotMapping(0, 1, 0);
+        slotMappingList.add(slotMapping7);
+        slotMappingList.add(slotMapping8);
+        try {
+            mTelephonyManager.setSimSlotMapping(slotMappingList);
+            fail("Expected IllegalArgumentException, Duplicate UiccSlotMapping data found");
+        } catch (IllegalArgumentException e) {
+            //expected
+        }
+        slotMappingList.clear();
+
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                    .dropShellPermissionIdentity();
+
+    }
+
+    @Test
+    public void getUiccSlotInfoTest() {
+        UiccSlotInfo[] slotInfos = mTelephonyManager.getUiccSlotsInfo();
+
+        if (slotInfos == null) {
+            return;
+        }
+
+        // Call below methods to make sure it doesn't crash.
+        for (UiccSlotInfo slotInfo : slotInfos) {
+            slotInfo.getIsEuicc();
+            slotInfo.getCardId();
+            slotInfo.getCardStateInfo();
+            slotInfo.getIsExtendedApduSupported();
+            slotInfo.isRemovable();
+            for (UiccPortInfo portInfo :slotInfo.getPorts()) {
+                portInfo.isActive();
+                portInfo.getIccId();
+                portInfo.getLogicalSlotIndex();
+                portInfo.getPortIndex();
+            }
+        }
+    }
 }
 
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java
index 8a970e7..6f0e3f5 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java
@@ -15,12 +15,15 @@
  */
 package android.telephony.cts;
 
+import static com.android.internal.telephony.RILConstants.INTERNAL_ERR;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
-import android.sysprop.TelephonyProperties;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
@@ -38,10 +41,8 @@
 /** Test MockModemService interfaces. */
 public class TelephonyManagerTestOnMockModem {
     private static final String TAG = "TelephonyManagerTestOnMockModem";
-    private static MockModemServiceConnector sServiceConnector;
-    private static MockModemService sMockModem = null;
-
-    TelephonyManager mTelephonyManager =
+    private static MockModemManager sMockModemManager;
+    private TelephonyManager mTelephonyManager =
             (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
 
     @BeforeClass
@@ -49,14 +50,9 @@
 
         Log.d(TAG, "TelephonyManagerTestOnMockModem#beforeAllTests()");
 
-        // Override all interfaces to MockModemService
-        sServiceConnector =
-                new MockModemServiceConnector(InstrumentationRegistry.getInstrumentation());
-
-        assertNotNull(sServiceConnector);
-        assertTrue(sServiceConnector.connectMockModemService());
-
-        sMockModem = sServiceConnector.getMockModemService();
+        sMockModemManager = new MockModemManager();
+        assertNotNull(sMockModemManager);
+        assertTrue(sMockModemManager.connectMockModemService());
     }
 
     @AfterClass
@@ -64,10 +60,9 @@
         Log.d(TAG, "TelephonyManagerTestOnMockModem#afterAllTests()");
 
         // Rebind all interfaces which is binding to MockModemService to default.
-        assertNotNull(sServiceConnector);
-        assertTrue(sServiceConnector.disconnectMockModemService());
-        sMockModem = null;
-        sServiceConnector = null;
+        assertNotNull(sMockModemManager);
+        assertTrue(sMockModemManager.disconnectMockModemService());
+        sMockModemManager = null;
     }
 
     private static Context getContext() {
@@ -86,55 +81,76 @@
                         .contains(simCardState));
 
         int slotId = 0;
-        sMockModem.setSimPresent(slotId);
-        sMockModem.resetState();
+        sMockModemManager.setSimPresent(slotId);
 
-        sMockModem.unsolSimSlotsStatusChanged();
-        assertTrue(
-                sMockModem.waitForLatchCountdown(MockModemService.LATCH_MOCK_MODEM_SIM_READY));
-
-        TimeUnit.SECONDS.sleep(1);
         simCardState = mTelephonyManager.getSimCardState();
         Log.d(TAG, "New SIM card state: " + simCardState);
         assertEquals(TelephonyManager.SIM_STATE_PRESENT, simCardState);
     }
 
     @Test
-    public void testRadioPower() throws Throwable {
-        Log.d(TAG, "TelephonyManagerTestOnMockModem#testRadioPower");
+    public void testRadioPowerToggle() throws Throwable {
+        Log.d(TAG, "TelephonyManagerTestOnMockModem#testRadioPowerToggle");
 
-        boolean apm = TelephonyProperties.airplane_mode_on().orElse(false);
-        Log.d(TAG, "APM setting: " + apm);
+        int radioState = mTelephonyManager.getRadioPowerState();
+        Log.d(TAG, "Radio state: " + radioState);
 
-        int expectedState;
-        int waitLatch;
-        if (!apm) {
-            expectedState = TelephonyManager.RADIO_POWER_ON;
-            waitLatch = MockModemService.LATCH_MOCK_MODEM_RADIO_POWR_ON;
-        } else {
-            expectedState = TelephonyManager.RADIO_POWER_OFF;
-            waitLatch = MockModemService.LATCH_MOCK_MODEM_RADIO_POWR_OFF;
+        // Toggle radio power
+        try {
+            ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
+                    mTelephonyManager,
+                    (tm) -> tm.toggleRadioOnOff(),
+                    SecurityException.class,
+                    "android.permission.MODIFY_PHONE_STATE");
+        } catch (SecurityException e) {
+            Log.d(TAG, "TelephonyManager#toggleRadioOnOff should require " + e);
         }
 
-        assertEquals(mTelephonyManager.getRadioPowerState(), expectedState);
+        // Wait the radio state update in Framework
+        TimeUnit.SECONDS.sleep(1);
+        int toggleRadioState =
+                radioState == TelephonyManager.RADIO_POWER_ON
+                        ? TelephonyManager.RADIO_POWER_OFF
+                        : TelephonyManager.RADIO_POWER_ON;
+        assertEquals(mTelephonyManager.getRadioPowerState(), toggleRadioState);
 
-        boolean switchState;
-        if (!apm) {
-            waitLatch = MockModemService.LATCH_MOCK_MODEM_RADIO_POWR_OFF;
-            switchState = false;
-            expectedState = TelephonyManager.RADIO_POWER_OFF;
-        } else {
-            waitLatch = MockModemService.LATCH_MOCK_MODEM_RADIO_POWR_ON;
-            switchState = true;
-            expectedState = TelephonyManager.RADIO_POWER_ON;
+        // Toggle radio power again back to original radio state
+        try {
+            ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
+                    mTelephonyManager,
+                    (tm) -> tm.toggleRadioOnOff(),
+                    SecurityException.class,
+                    "android.permission.MODIFY_PHONE_STATE");
+        } catch (SecurityException e) {
+            Log.d(TAG, "TelephonyManager#toggleRadioOnOff should require " + e);
         }
-        sMockModem.resetState(); // Reset the latch
 
-        Log.d(TAG, "set Radio Power: " + switchState);
+        // Wait the radio state update in Framework
+        TimeUnit.SECONDS.sleep(1);
+        assertEquals(mTelephonyManager.getRadioPowerState(), radioState);
+
+        Log.d(TAG, "Test Done ");
+    }
+
+    @Test
+    public void testRadioPowerWithFailureResults() throws Throwable {
+        Log.d(TAG, "TelephonyManagerTestOnMockModem#testRadioPowerWithFailureResults");
+
+        int radioState = mTelephonyManager.getRadioPowerState();
+        Log.d(TAG, "Radio state: " + radioState);
+
+        int slotId = 0;
+        int toggleRadioState =
+                radioState == TelephonyManager.RADIO_POWER_ON
+                        ? TelephonyManager.RADIO_POWER_OFF
+                        : TelephonyManager.RADIO_POWER_ON;
+
+        // Force the returned response of RIL_REQUEST_RADIO_POWER as INTERNAL_ERR
+        sMockModemManager.forceErrorResponse(slotId, RIL_REQUEST_RADIO_POWER, INTERNAL_ERR);
 
         boolean result = false;
         try {
-            boolean state = switchState;
+            boolean state = (toggleRadioState == TelephonyManager.RADIO_POWER_ON) ? true : false;
             result =
                     ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
                             mTelephonyManager,
@@ -144,29 +160,18 @@
         } catch (SecurityException e) {
             Log.d(TAG, "TelephonyManager#setRadioPower should require " + e);
         }
+
         TimeUnit.SECONDS.sleep(1);
-
         assertTrue(result);
-        assertTrue(sMockModem.waitForLatchCountdown(waitLatch));
-        assertEquals(mTelephonyManager.getRadioPowerState(), expectedState);
+        assertNotEquals(mTelephonyManager.getRadioPowerState(), toggleRadioState);
 
-        // Recovery to APM setting
-        if (apm) {
-            waitLatch = MockModemService.LATCH_MOCK_MODEM_RADIO_POWR_OFF;
-            switchState = false;
-            expectedState = TelephonyManager.RADIO_POWER_OFF;
-        } else {
-            waitLatch = MockModemService.LATCH_MOCK_MODEM_RADIO_POWR_ON;
-            switchState = true;
-            expectedState = TelephonyManager.RADIO_POWER_ON;
-        }
-        sMockModem.resetState(); // Reset the latch
+        // Reset the modified error response of RIL_REQUEST_RADIO_POWER to the original behavior
+        // and -1 means to disable the modifed mechanism in mock modem
+        sMockModemManager.forceErrorResponse(slotId, RIL_REQUEST_RADIO_POWER, -1);
 
-        Log.d(TAG, "Recovery Radio Power: " + switchState);
-
-        result = false;
+        // Recovery the power state back to original radio state
         try {
-            boolean state = switchState;
+            boolean state = (radioState == TelephonyManager.RADIO_POWER_ON) ? true : false;
             result =
                     ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
                             mTelephonyManager,
@@ -177,11 +182,7 @@
             Log.d(TAG, "TelephonyManager#setRadioPower should require " + e);
         }
         TimeUnit.SECONDS.sleep(1);
-
         assertTrue(result);
-        assertTrue(sMockModem.waitForLatchCountdown(waitLatch));
-        assertEquals(mTelephonyManager.getRadioPowerState(), expectedState);
-
-        Log.d(TAG, "Test Done ");
+        assertEquals(mTelephonyManager.getRadioPowerState(), radioState);
     }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/UiccPortInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/UiccPortInfoTest.java
new file mode 100644
index 0000000..c9820c7
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/UiccPortInfoTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.telephony.UiccPortInfo;
+import android.test.AndroidTestCase;
+
+public class UiccPortInfoTest extends AndroidTestCase {
+    /**
+     * Create fake UiccPortInfo objects run basic tests.
+     */
+    public void testFakeUiccSlotInfoObject() {
+        String iccId = "FAKE_ICC_ID";
+        int portIndex = 0;
+        int logicalSlotIndex = 0;
+        boolean isActive = true;
+        UiccPortInfo uiccPortInfo = new UiccPortInfo(
+                iccId,            /* ICCID */
+                portIndex,      /* portIndex */
+                logicalSlotIndex, /* logicalSlotIndex */
+                isActive     /* isActive */
+        );
+
+        //Getters.
+        assertThat(uiccPortInfo.getIccId()).isEqualTo(iccId);
+        assertThat(uiccPortInfo.getPortIndex()).isEqualTo(portIndex);
+        assertThat(uiccPortInfo.getLogicalSlotIndex()).isEqualTo(logicalSlotIndex);
+        assertThat(uiccPortInfo.isActive()).isEqualTo(isActive);
+
+        // Other common methods.
+        assertThat(uiccPortInfo.describeContents()).isEqualTo(0);
+        assertThat(uiccPortInfo.hashCode()).isNotEqualTo(0);
+        assertThat(uiccPortInfo.toString()).isNotEmpty();
+
+        // Parcel read and write.
+        Parcel parcel = Parcel.obtain();
+        uiccPortInfo.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        UiccPortInfo toCompare = uiccPortInfo.CREATOR.createFromParcel(parcel);
+        assertThat(uiccPortInfo.hashCode()).isEqualTo(toCompare.hashCode());
+        assertThat(uiccPortInfo).isEqualTo(toCompare);
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
index 096c1b9..57dbeed 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
@@ -41,6 +41,7 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.cts.AsyncSmsMessageListener;
+import android.telephony.cts.CarrierCapability;
 import android.telephony.cts.SmsReceiverHelper;
 import android.telephony.cts.TelephonyUtils;
 import android.telephony.ims.ImsException;
@@ -1273,6 +1274,23 @@
         assertEquals(RcsUceAdapter.PUBLISH_STATE_OK, waitForIntResult(publishStateQueue));
         publishStateQueue.clear();
 
+        // Setup the operation of the publish request.
+        capExchangeImpl.setPublishOperator((listener, pidfXml, cb) -> {
+            int networkResp = 999;
+            String reason = "";
+            cb.onNetworkResponse(networkResp, reason);
+            listener.onPublish();
+        });
+        eventListener.onRequestPublishCapabilities(
+                RcsUceAdapter.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN);
+
+        assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_UCE_REQUEST_PUBLISH));
+
+        assertEquals(RcsUceAdapter.PUBLISH_STATE_PUBLISHING, waitForIntResult(publishStateQueue));
+        assertEquals(RcsUceAdapter.PUBLISH_STATE_OK, waitForIntResult(publishStateQueue));
+        publishStateQueue.clear();
+
         // Trigger RcsFeature is unavailable
         sServiceConnector.getCarrierService().getRcsFeature()
                 .setFeatureState(ImsFeature.STATE_UNAVAILABLE);
@@ -1443,6 +1461,12 @@
             fail("Cannot find IMS service");
         }
 
+        TelephonyManager tm = (TelephonyManager) getContext()
+                .getSystemService(Context.TELEPHONY_SERVICE);
+
+        String mccmnc = tm.getSimOperator();
+        boolean mTelUriSupported = CarrierCapability.SUPPORT_TEL_URI_PUBLISH.contains(mccmnc);
+
         ImsRcsManager imsRcsManager = imsManager.getImsRcsManager(sTestSub);
         RcsUceAdapter uceAdapter = imsRcsManager.getUceAdapter();
 
@@ -1462,7 +1486,13 @@
             receivedPidfXml.add(pidfXml);
         });
 
-        final Uri imsUri = Uri.fromParts(PhoneAccount.SCHEME_SIP, "test", null);
+        Uri imsUri;
+        if (mTelUriSupported) {
+            imsUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, "0001112222", null);
+        } else {
+            imsUri = Uri.fromParts(PhoneAccount.SCHEME_SIP, "test", null);
+        }
+
         StringBuilder expectedUriBuilder = new StringBuilder();
         expectedUriBuilder.append("<contact>").append(imsUri.toString()).append("</contact>");
 
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
index 196b5c7..6f1b399 100644
--- a/tests/tests/tv/AndroidManifest.xml
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -18,6 +18,10 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="android.tv.cts">
 
+    <permission android:name="android.media.tv.cts.TvInputManagerTest.PERMISSION_GRANTED"/>
+    <permission android:name="android.media.tv.cts.TvInputManagerTest.PERMISSION_UNGRANTED"/>
+    <uses-permission android:name="android.media.tv.cts.TvInputManagerTest.PERMISSION_GRANTED"/>
+
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
     <uses-permission android:name="android.permission.INJECT_EVENTS"/>
 
@@ -82,6 +86,17 @@
                  android:resource="@xml/stub_tv_input_service"/>
         </service>
 
+        <service android:name="android.media.tv.cts.TvInputManagerTest$StubHardwareTvInputService"
+             android:enabled="false"
+             android:permission="android.permission.BIND_TV_INPUT"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.media.tv.TvInputService" />
+            </intent-filter>
+            <meta-data android:name="android.media.tv.input"
+                       android:resource="@xml/stub_tv_input_service" />
+        </service>
+
         <service android:name="android.media.tv.cts.TvInputServiceTest$CountingTvInputService"
              android:permission="android.permission.BIND_TV_INPUT"
              android:exported="true">
diff --git a/tests/tests/tv/OWNERS b/tests/tests/tv/OWNERS
index 215ebb0..e562f11 100644
--- a/tests/tests/tv/OWNERS
+++ b/tests/tests/tv/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 105760
-amyjojo@google.com
-nchalko@google.com
 quxiangfang@google.com
 shubang@google.com
+hgchen@google.com
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
index 87b8913..22b4b1b 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
@@ -20,8 +20,12 @@
 import android.app.Instrumentation;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.database.Cursor;
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.tv.cts.TvViewTest.MockCallback;
 import android.media.tv.TunedInfo;
@@ -36,8 +40,10 @@
 import android.media.tv.TvStreamConfig;
 import android.media.tv.TvView;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.test.ActivityInstrumentationTestCase2;
 import android.tv.cts.R;
 
@@ -47,8 +53,11 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.concurrent.Executor;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -59,12 +68,26 @@
     /** The maximum time to wait for an operation. */
     private static final long TIME_OUT_MS = 15000L;
 
+    private static final int DUMMY_DEVICE_ID = Integer.MAX_VALUE;
     private static final String[] VALID_TV_INPUT_SERVICES = {
         StubTunerTvInputService.class.getName()
     };
     private static final String[] INVALID_TV_INPUT_SERVICES = {
         NoMetadataTvInputService.class.getName(), NoPermissionTvInputService.class.getName()
     };
+    private static final String EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION =
+            "android.media.tv.cts.TvInputManagerTest.EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION";
+    private static final String EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED =
+            "android.media.tv.cts.TvInputManagerTest"
+            + ".EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED";
+    private static final String EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED =
+            "android.media.tv.cts.TvInputManagerTest"
+            + ".EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED";
+    private static final String PERMISSION_GRANTED =
+            "android.media.tv.cts.TvInputManagerTest.PERMISSION_GRANTED";
+    private static final String PERMISSION_UNGRANTED =
+            "android.media.tv.cts.TvInputManagerTest.PERMISSION_UNGRANTED";
+
     private static final TvContentRating DUMMY_RATING = TvContentRating.createRating(
             "com.android.tv", "US_TV", "US_TV_PG", "US_TV_D", "US_TV_L");
 
@@ -78,6 +101,12 @@
             "android.permission.TV_INPUT_HARDWARE";
     private static final String PERMISSION_TUNER_RESOURCE_ACCESS =
             "android.permission.TUNER_RESOURCE_ACCESS";
+    private static final String[] BASE_SHELL_PERMISSIONS = {
+            PERMISSION_ACCESS_WATCHED_PROGRAMS,
+            PERMISSION_WRITE_EPG_DATA,
+            PERMISSION_ACCESS_TUNED_INFO,
+            PERMISSION_TUNER_RESOURCE_ACCESS
+    };
 
     private String mStubId;
     private TvInputManager mManager;
@@ -98,6 +127,68 @@
         return null;
     }
 
+    private static boolean isHardwareDeviceAdded(List<TvInputHardwareInfo> list, int deviceId) {
+        if (list != null) {
+            for (TvInputHardwareInfo info : list) {
+                if (info.getDeviceId() == deviceId) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private String prepareStubHardwareTvInputService() {
+        String[] newPermissions = Arrays.copyOf(
+                BASE_SHELL_PERMISSIONS, BASE_SHELL_PERMISSIONS.length + 1);
+        newPermissions[BASE_SHELL_PERMISSIONS.length] = PERMISSION_TV_INPUT_HARDWARE;
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(newPermissions);
+
+        // Use the test api to add an HDMI hardware device
+        mManager.addHardwareDevice(DUMMY_DEVICE_ID);
+        assertTrue(isHardwareDeviceAdded(mManager.getHardwareList(), DUMMY_DEVICE_ID));
+
+        PackageManager pm = getActivity().getPackageManager();
+        ComponentName component =
+                new ComponentName(getActivity(), StubHardwareTvInputService.class);
+        pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                PackageManager.DONT_KILL_APP);
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            protected boolean check() {
+                return null != getInfoForClassName(
+                        mManager.getTvInputList(), StubHardwareTvInputService.class.getName());
+            }
+        }.run();
+
+        TvInputInfo info = getInfoForClassName(
+                mManager.getTvInputList(), StubHardwareTvInputService.class.getName());
+        assertNotNull(info);
+        return info.getId();
+    }
+
+    private void cleanupStubHardwareTvInputService() {
+        // Restore the base shell permissions
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(BASE_SHELL_PERMISSIONS);
+
+        PackageManager pm = getActivity().getPackageManager();
+        ComponentName component =
+                new ComponentName(getActivity(), StubHardwareTvInputService.class);
+        pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+        new PollingCheck(TIME_OUT_MS) {
+            @Override
+            protected boolean check() {
+                return null == getInfoForClassName(
+                        mManager.getTvInputList(), StubHardwareTvInputService.class.getName());
+            }
+        }.run();
+
+        mManager.removeHardwareDevice(DUMMY_DEVICE_ID);
+    }
+
     public TvInputManagerTest() {
         super(TvViewStubActivity.class);
     }
@@ -110,14 +201,8 @@
             return;
         }
 
-        InstrumentationRegistry
-                .getInstrumentation()
-                .getUiAutomation()
-                .adoptShellPermissionIdentity(
-                        PERMISSION_ACCESS_WATCHED_PROGRAMS,
-                        PERMISSION_WRITE_EPG_DATA,
-                        PERMISSION_ACCESS_TUNED_INFO,
-                        PERMISSION_TUNER_RESOURCE_ACCESS);
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(BASE_SHELL_PERMISSIONS);
 
         mInstrumentation = getInstrumentation();
         mTvView = findTvViewById(R.id.tvview);
@@ -411,16 +496,15 @@
     }
 
     public void testAcquireTvInputHardware() {
-        if (mManager == null) {
+        if (!Utils.hasTvInputFramework(getActivity()) || mManager == null) {
             return;
         }
 
-        InstrumentationRegistry
-                .getInstrumentation()
-                .getUiAutomation()
-                .adoptShellPermissionIdentity(
-                        PERMISSION_WRITE_EPG_DATA,
-                        PERMISSION_TV_INPUT_HARDWARE);
+        String[] newPermissions = Arrays.copyOf(
+                BASE_SHELL_PERMISSIONS, BASE_SHELL_PERMISSIONS.length + 1);
+        newPermissions[BASE_SHELL_PERMISSIONS.length] = PERMISSION_TV_INPUT_HARDWARE;
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(newPermissions);
 
         // Update hardware device list
         int deviceId = 0;
@@ -461,6 +545,120 @@
         if (hardwareDeviceAdded) {
             mManager.removeHardwareDevice(deviceId);
         }
+        // Restore the base shell permissions
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(BASE_SHELL_PERMISSIONS);
+    }
+
+    public void testTvInputHardwareOverrideAudioSink() {
+        if (mManager == null) {
+            return;
+        }
+        // Update hardware device list
+        int deviceId = 0;
+        boolean hardwareDeviceAdded = false;
+        List<TvInputHardwareInfo> hardwareList = mManager.getHardwareList();
+        if (hardwareList == null || hardwareList.isEmpty()) {
+            // Use the test api to add an HDMI hardware device
+            mManager.addHardwareDevice(deviceId);
+            hardwareDeviceAdded = true;
+        } else {
+            deviceId = hardwareList.get(0).getDeviceId();
+        }
+
+        // Acquire Hardware with a record client
+        HardwareCallback callback = new HardwareCallback() {
+            @Override
+            public void onReleased() {
+            }
+
+            @Override
+            public void onStreamConfigChanged(TvStreamConfig[] configs) {
+            }
+        };
+        CallbackExecutor executor = new CallbackExecutor();
+        Hardware hardware = mManager.acquireTvInputHardware(
+                deviceId, mStubTvInputInfo, null /*tvInputSessionId*/,
+                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK,
+                executor, callback);
+        assertNotNull(hardware);
+
+        // Override audio sink
+        try {
+            AudioManager am = mActivity.getSystemService(AudioManager.class);
+            AudioDeviceInfo[] deviceInfos = am.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+            if (deviceInfos.length > 0) {
+                // test available overrideAudioSink APIs
+                hardware.overrideAudioSink(deviceInfos[0], 0,
+                        AudioFormat.CHANNEL_OUT_DEFAULT, AudioFormat.ENCODING_DEFAULT);
+                hardware.overrideAudioSink(deviceInfos[0].getType(), deviceInfos[0].getAddress(), 0,
+                        AudioFormat.CHANNEL_OUT_DEFAULT, AudioFormat.ENCODING_DEFAULT);
+            }
+        } catch (Exception e) {
+            fail();
+        } finally {
+            if (hardwareDeviceAdded) {
+                mManager.removeHardwareDevice(deviceId);
+            }
+        }
+    }
+
+    public void testGetAvailableExtensionInterfaceNames() {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+
+        try {
+            String inputId = prepareStubHardwareTvInputService();
+
+            StubHardwareTvInputService.injectAvailableExtensionInterface(
+                    EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION, null);
+            StubHardwareTvInputService.injectAvailableExtensionInterface(
+                    EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED, PERMISSION_GRANTED);
+            StubHardwareTvInputService.injectAvailableExtensionInterface(
+                    EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED, PERMISSION_UNGRANTED);
+
+            List<String> names = mManager.getAvailableExtensionInterfaceNames(inputId);
+            assertTrue(names != null && !names.isEmpty());
+            assertTrue(names.contains(EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION));
+            assertTrue(names.contains(EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED));
+            assertFalse(names.contains(EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED));
+
+            StubHardwareTvInputService.clearAvailableExtensionInterfaces();
+
+            names = mManager.getAvailableExtensionInterfaceNames(inputId);
+            assertTrue(names != null && names.isEmpty());
+        } finally {
+            StubHardwareTvInputService.clearAvailableExtensionInterfaces();
+            cleanupStubHardwareTvInputService();
+        }
+    }
+
+    public void testGetExtensionInterface() {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+
+        try {
+            String inputId = prepareStubHardwareTvInputService();
+
+            StubHardwareTvInputService.injectAvailableExtensionInterface(
+                    EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION, null);
+            StubHardwareTvInputService.injectAvailableExtensionInterface(
+                    EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED, PERMISSION_GRANTED);
+            StubHardwareTvInputService.injectAvailableExtensionInterface(
+                    EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED, PERMISSION_UNGRANTED);
+
+            assertNotNull(mManager.getExtensionInterface(inputId,
+                    EXTENSION_INTERFACE_NAME_WITHOUT_PERMISSION));
+            assertNotNull(mManager.getExtensionInterface(inputId,
+                    EXTENSION_INTERFACE_NAME_WITH_PERMISSION_GRANTED));
+            assertNull(mManager.getExtensionInterface(inputId,
+                    EXTENSION_INTERFACE_NAME_WITH_PERMISSION_UNGRANTED));
+        } finally {
+            StubHardwareTvInputService.clearAvailableExtensionInterfaces();
+            cleanupStubHardwareTvInputService();
+        }
     }
 
     private static class LoggingCallback extends TvInputManager.TvInputCallback {
@@ -519,6 +717,74 @@
         }
     }
 
+    public static class StubHardwareTvInputService extends TvInputService {
+        private static final Map<String, String> sAvailableExtensionInterfaceMap = new HashMap<>();
+
+        private ResolveInfo mResolveInfo = null;
+        private TvInputInfo mTvInputInfo = null;
+
+        public static void clearAvailableExtensionInterfaces() {
+            sAvailableExtensionInterfaceMap.clear();
+        }
+
+        public static void injectAvailableExtensionInterface(String name, String permission) {
+            sAvailableExtensionInterfaceMap.put(name, permission);
+        }
+
+        @Override
+        public void onCreate() {
+            mResolveInfo = getPackageManager().resolveService(
+                    new Intent(SERVICE_INTERFACE).setClass(this, getClass()),
+                    PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+        }
+
+        @Override
+        public  TvInputInfo onHardwareAdded(TvInputHardwareInfo hardwareInfo) {
+            TvInputInfo info = null;
+            if (hardwareInfo.getDeviceId() == DUMMY_DEVICE_ID) {
+                info = new TvInputInfo.Builder(this, mResolveInfo)
+                        .setTvInputHardwareInfo(hardwareInfo)
+                        .build();
+                mTvInputInfo = info;
+            }
+            return info;
+        }
+
+        @Override
+        public String onHardwareRemoved(TvInputHardwareInfo hardwareInfo) {
+            String inputId = null;
+            if (hardwareInfo.getDeviceId() == DUMMY_DEVICE_ID && mTvInputInfo != null) {
+                inputId = mTvInputInfo.getId();
+                mTvInputInfo = null;
+            }
+            return inputId;
+        }
+
+        @Override
+        public Session onCreateSession(String inputId) {
+            return null;
+        }
+
+        @Override
+        public List<String> getAvailableExtensionInterfaceNames() {
+            return new ArrayList<>(sAvailableExtensionInterfaceMap.keySet());
+        }
+
+        @Override
+        public String getExtensionInterfacePermission(String name) {
+            return sAvailableExtensionInterfaceMap.get(name);
+        }
+
+        @Override
+        public IBinder getExtensionInterface(String name) {
+            if (sAvailableExtensionInterfaceMap.containsKey(name)) {
+                return new Binder();
+            } else {
+                return null;
+            }
+        }
+    }
+
     public class CallbackExecutor implements Executor {
         @Override
         public void execute(Runnable r) {
diff --git a/tests/tests/uiautomation/OWNERS b/tests/tests/uiautomation/OWNERS
index bf9a18d..70a5364 100644
--- a/tests/tests/uiautomation/OWNERS
+++ b/tests/tests/uiautomation/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 44215
-pweaver@google.com
-rhedjao@google.com
-qasid@google.com
 ryanlwlin@google.com
+sallyyuen@google.com
+pweaver@google.com
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
index 99d1d33..341d346 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -39,6 +39,7 @@
 import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.view.FrameStats;
 import android.view.KeyEvent;
 import android.view.WindowAnimationFrameStats;
@@ -189,7 +190,7 @@
             getInstrumentation().waitForIdleSync();
 
             // Find the application window.
-            final int windowId = findAppWindowId(uiAutomation.getWindows());
+            final int windowId = findAppWindowId(uiAutomation.getWindows(), activity);
             assertTrue(windowId >= 0);
 
             // Clear stats to be with a clean slate.
@@ -251,7 +252,7 @@
             getInstrumentation().waitForIdleSync();
 
             // Find the application window.
-            final int windowId = findAppWindowId(uiAutomation.getWindows());
+            final int windowId = findAppWindowId(uiAutomation.getWindows(), activity);
             assertTrue(windowId >= 0);
 
             // Clear stats to be with a clean slate.
@@ -755,11 +756,13 @@
         waitForAccessibilityServiceToStart();
     }
 
-    private int findAppWindowId(List<AccessibilityWindowInfo> windows) {
+    private int findAppWindowId(List<AccessibilityWindowInfo> windows, Activity activity) {
+        final CharSequence activityTitle = getActivityTitle(getInstrumentation(), activity);
         final int windowCount = windows.size();
         for (int i = 0; i < windowCount; i++) {
             AccessibilityWindowInfo window = windows.get(i);
-            if (window.getType() == AccessibilityWindowInfo.TYPE_APPLICATION) {
+            if (window.getType() == AccessibilityWindowInfo.TYPE_APPLICATION
+                    && TextUtils.equals(window.getTitle(), activityTitle)) {
                 return window.getId();
             }
         }
@@ -769,4 +772,11 @@
     private Instrumentation getInstrumentation() {
         return InstrumentationRegistry.getInstrumentation();
     }
+
+    private static CharSequence getActivityTitle(
+            Instrumentation instrumentation, Activity activity) {
+        final StringBuilder titleBuilder = new StringBuilder();
+        instrumentation.runOnMainSync(() -> titleBuilder.append(activity.getTitle()));
+        return titleBuilder;
+    }
 }
diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnCellUnderlyingNetworkTemplateTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnCellUnderlyingNetworkTemplateTest.java
new file mode 100644
index 0000000..e9ecd78
--- /dev/null
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnCellUnderlyingNetworkTemplateTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn.cts;
+
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class VcnCellUnderlyingNetworkTemplateTest {
+    private static final Set<String> ALLOWED_PLMN_IDS = Set.of("123456", "234567");
+    private static final Set<Integer> ALLOWED_CARRIER_IDS = Set.of(100, 101);
+
+    // Package private for use in VcnGatewayConnectionConfigTest
+    static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
+        return new VcnCellUnderlyingNetworkTemplate.Builder()
+                .setMetered(MATCH_REQUIRED)
+                .setRoaming(MATCH_REQUIRED)
+                .setOpportunistic(MATCH_FORBIDDEN)
+                .setOperatorPlmnIds(ALLOWED_PLMN_IDS)
+                .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
+                .build();
+    }
+
+    @Test
+    public void testBuilderAndGetters() {
+        final VcnCellUnderlyingNetworkTemplate networkTemplate = getTestNetworkTemplate();
+        assertEquals(MATCH_REQUIRED, networkTemplate.getMetered());
+        assertEquals(MATCH_REQUIRED, networkTemplate.getRoaming());
+        assertEquals(MATCH_FORBIDDEN, networkTemplate.getOpportunistic());
+        assertEquals(ALLOWED_PLMN_IDS, networkTemplate.getOperatorPlmnIds());
+        assertEquals(ALLOWED_CARRIER_IDS, networkTemplate.getSimSpecificCarrierIds());
+    }
+
+    @Test
+    public void testBuilderAndGettersForDefaultValues() {
+        final VcnCellUnderlyingNetworkTemplate networkTemplate =
+                new VcnCellUnderlyingNetworkTemplate.Builder().build();
+        assertEquals(MATCH_ANY, networkTemplate.getMetered());
+        assertEquals(MATCH_ANY, networkTemplate.getRoaming());
+        assertEquals(MATCH_ANY, networkTemplate.getOpportunistic());
+        assertEquals(new HashSet<String>(), networkTemplate.getOperatorPlmnIds());
+        assertEquals(new HashSet<Integer>(), networkTemplate.getSimSpecificCarrierIds());
+    }
+
+    @Test
+    public void testBuildWithEmptySets() {
+        final VcnCellUnderlyingNetworkTemplate networkTemplate =
+                new VcnCellUnderlyingNetworkTemplate.Builder()
+                        .setOperatorPlmnIds(new HashSet<String>())
+                        .setSimSpecificCarrierIds(new HashSet<Integer>())
+                        .build();
+        assertEquals(new HashSet<String>(), networkTemplate.getOperatorPlmnIds());
+        assertEquals(new HashSet<Integer>(), networkTemplate.getSimSpecificCarrierIds());
+    }
+}
diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java
index 4cc6335..602ad4a 100644
--- a/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java
@@ -27,12 +27,16 @@
 import android.net.ipsec.ike.IkeSessionParams;
 import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
@@ -46,12 +50,22 @@
             };
     private static final int MAX_MTU = 1360;
 
+    private static final List<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_TEMPLATES;
+
+    static {
+        List<VcnUnderlyingNetworkTemplate> nwTemplates = new ArrayList<>();
+        nwTemplates.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+        nwTemplates.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+        UNDERLYING_NETWORK_TEMPLATES = Collections.unmodifiableList(nwTemplates);
+    }
+
     public static VcnGatewayConnectionConfig.Builder buildVcnGatewayConnectionConfigBase() {
         return new VcnGatewayConnectionConfig.Builder(
                         VCN_GATEWAY_CONNECTION_NAME, buildTunnelConnectionParams())
                 .addExposedCapability(NET_CAPABILITY_INTERNET)
                 .setRetryIntervalsMillis(RETRY_INTERNAL_MILLIS)
-                .setMaxMtu(MAX_MTU);
+                .setMaxMtu(MAX_MTU)
+                .setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
     }
 
     private static VcnGatewayConnectionConfig buildVcnGatewayConnectionConfig() {
@@ -66,6 +80,9 @@
         assertEquals(buildTunnelConnectionParams(), gatewayConnConfig.getTunnelConnectionParams());
         assertArrayEquals(
                 new int[] {NET_CAPABILITY_INTERNET}, gatewayConnConfig.getExposedCapabilities());
+        assertEquals(
+                UNDERLYING_NETWORK_TEMPLATES,
+                gatewayConnConfig.getVcnUnderlyingNetworkPriorities());
         assertArrayEquals(RETRY_INTERNAL_MILLIS, gatewayConnConfig.getRetryIntervalsMillis());
     }
 
diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java
index 8376ccb..b785128 100644
--- a/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java
@@ -26,6 +26,7 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -96,8 +97,6 @@
     private final TelephonyManager mTelephonyManager;
     private final ConnectivityManager mConnectivityManager;
 
-    private TestNetworkWrapper mTestNetworkWrapper;
-
     public VcnManagerTest() {
         mContext = InstrumentationRegistry.getContext();
         mVcnManager = mContext.getSystemService(VcnManager.class);
@@ -115,14 +114,7 @@
 
     @After
     public void tearDown() throws Exception {
-        try {
-            if (mTestNetworkWrapper != null) {
-                mTestNetworkWrapper.close();
-                mTestNetworkWrapper = null;
-            }
-        } finally {
-            getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
-        }
+        getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
     }
 
     private VcnConfig.Builder buildVcnConfigBase() {
@@ -353,61 +345,73 @@
         mVcnManager.unregisterVcnStatusCallback(callback);
     }
 
-    @Test
-    public void testVcnManagedNetworkLosesNotVcnManagedCapability() throws Exception {
-        final int subId = verifyAndGetValidDataSubId();
-
-        mTestNetworkWrapper =
+    private TestNetworkWrapper createTestNetworkWrapper(
+            boolean isMetered, int subId, InetAddress localAddress) throws Exception {
+        TestNetworkWrapper testNetworkWrapper =
                 new TestNetworkWrapper(
                         mContext,
                         TEST_NETWORK_MTU,
-                        true /* isMetered */,
+                        isMetered,
                         Collections.singleton(subId),
-                        LOCAL_ADDRESS);
-        assertNotNull("No test network found", mTestNetworkWrapper.tunNetwork);
+                        localAddress);
+        assertNotNull("No test network found", testNetworkWrapper.tunNetwork);
+        return testNetworkWrapper;
+    }
 
-        // Before the VCN starts, the test network should have NOT_VCN_MANAGED
-        waitForExpectedUnderlyingNetworkWithCapabilities(
-                true /* expectNotVcnManaged */,
-                false /* expectNotMetered */,
-                TestNetworkWrapper.NETWORK_CB_TIMEOUT_MS);
+    @Test
+    public void testVcnManagedNetworkLosesNotVcnManagedCapability() throws Exception {
+        final int subId = verifyAndGetValidDataSubId();
+        try (TestNetworkWrapper testNetworkWrapper =
+                createTestNetworkWrapper(true /* isMetered */, subId, LOCAL_ADDRESS)) {
+            // Before the VCN starts, the test network should have NOT_VCN_MANAGED
+            waitForExpectedUnderlyingNetworkWithCapabilities(
+                    testNetworkWrapper,
+                    true /* expectNotVcnManaged */,
+                    false /* expectNotMetered */,
+                    TestNetworkWrapper.NETWORK_CB_TIMEOUT_MS);
 
-        CarrierPrivilegeUtils.withCarrierPrivilegesForShell(mContext, subId, () -> {
-            SubscriptionGroupUtils.withEphemeralSubscriptionGroup(mContext, subId, (subGrp) -> {
-                mVcnManager.setVcnConfig(subGrp, buildVcnConfig());
+            CarrierPrivilegeUtils.withCarrierPrivilegesForShell(mContext, subId, () -> {
+                SubscriptionGroupUtils.withEphemeralSubscriptionGroup(mContext, subId, (subGrp) -> {
+                    mVcnManager.setVcnConfig(subGrp, buildVcnConfig());
 
-                // Once VCN starts, the test network should lose NOT_VCN_MANAGED
-                waitForExpectedUnderlyingNetworkWithCapabilities(
-                        false /* expectNotVcnManaged */,
-                        false /* expectNotMetered */,
-                        TestNetworkWrapper.NETWORK_CB_TIMEOUT_MS);
+                    // Once VCN starts, the test network should lose NOT_VCN_MANAGED
+                    waitForExpectedUnderlyingNetworkWithCapabilities(
+                            testNetworkWrapper,
+                            false /* expectNotVcnManaged */,
+                            false /* expectNotMetered */,
+                            TestNetworkWrapper.NETWORK_CB_TIMEOUT_MS);
 
-                mVcnManager.clearVcnConfig(subGrp);
+                    mVcnManager.clearVcnConfig(subGrp);
 
-                // After the VCN tears down, the test network should have
-                // NOT_VCN_MANAGED again
-                waitForExpectedUnderlyingNetworkWithCapabilities(
-                        true /* expectNotVcnManaged */,
-                        false /* expectNotMetered */,
-                        TestNetworkWrapper.NETWORK_CB_TIMEOUT_MS);
+                    // After the VCN tears down, the test network should have
+                    // NOT_VCN_MANAGED again
+                    waitForExpectedUnderlyingNetworkWithCapabilities(
+                            testNetworkWrapper,
+                            true /* expectNotVcnManaged */,
+                            false /* expectNotMetered */,
+                            TestNetworkWrapper.NETWORK_CB_TIMEOUT_MS);
+                });
             });
-        });
+        }
     }
 
     private void waitForExpectedUnderlyingNetworkWithCapabilities(
-            boolean expectNotVcnManaged, boolean expectNotMetered, long timeoutMillis)
+            TestNetworkWrapper testNetworkWrapper,
+            boolean expectNotVcnManaged,
+            boolean expectNotMetered,
+            long timeoutMillis)
             throws Exception {
         final long start = SystemClock.elapsedRealtime();
 
         // Wait for NetworkCapabilities changes until they match the expected capabilities
         do {
             final CapabilitiesChangedEvent capabilitiesChangedEvent =
-                    mTestNetworkWrapper.vcnNetworkCallback.waitForOnCapabilitiesChanged(
+                    testNetworkWrapper.vcnNetworkCallback.waitForOnCapabilitiesChanged(
                             timeoutMillis);
             assertNotNull("Failed to receive NetworkCapabilities change", capabilitiesChangedEvent);
 
             final NetworkCapabilities nc = capabilitiesChangedEvent.networkCapabilities;
-            if (mTestNetworkWrapper.tunNetwork.equals(capabilitiesChangedEvent.network)
+            if (testNetworkWrapper.tunNetwork.equals(capabilitiesChangedEvent.network)
                     && nc.hasCapability(NET_CAPABILITY_VALIDATED)
                     && expectNotVcnManaged == nc.hasCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                     && expectNotMetered == nc.hasCapability(NET_CAPABILITY_NOT_METERED)) {
@@ -417,53 +421,63 @@
 
         fail(
                 "Expected update for network="
-                        + mTestNetworkWrapper.tunNetwork.getNetId()
+                        + testNetworkWrapper.tunNetwork.getNetId()
                         + ". Wanted NOT_VCN_MANAGED="
                         + expectNotVcnManaged
                         + " NOT_METERED="
                         + expectNotMetered);
     }
 
-    @Test
-    public void testSetVcnConfigOnTestNetwork() throws Exception {
-        final int subId = verifyAndGetValidDataSubId();
+    private interface VcnTestRunnable {
+        void runTest(ParcelUuid subGrp, Network cellNetwork, VcnTestNetworkCallback cellNetworkCb)
+                throws Exception;
+    }
 
-        mTestNetworkWrapper =
-                new TestNetworkWrapper(
-                        mContext,
-                        TEST_NETWORK_MTU,
-                        true /* isMetered */,
-                        Collections.singleton(subId),
-                        LOCAL_ADDRESS);
-        assertNotNull("No test network found", mTestNetworkWrapper.tunNetwork);
-
-        // Get current cell Network then wait for it to drop (due to losing NOT_VCN_MANAGED) before
-        // waiting for VCN Network.
-        final NetworkRequest cellNetworkReq = new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .build();
+    private void verifyUnderlyingCellAndRunTest(int subId, VcnTestRunnable test) throws Exception {
+        // Get current cell Network then wait for it to drop (due to losing NOT_VCN_MANAGED)
+        // before waiting for VCN Network.
+        final NetworkRequest cellNetworkReq =
+                new NetworkRequest.Builder()
+                        .addTransportType(TRANSPORT_CELLULAR)
+                        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                        .build();
         final VcnTestNetworkCallback cellNetworkCb = new VcnTestNetworkCallback();
         mConnectivityManager.requestNetwork(cellNetworkReq, cellNetworkCb);
         final Network cellNetwork = cellNetworkCb.waitForAvailable();
         assertNotNull("No cell network found", cellNetwork);
 
         CarrierPrivilegeUtils.withCarrierPrivilegesForShell(mContext, subId, () -> {
-            SubscriptionGroupUtils.withEphemeralSubscriptionGroup(mContext, subId, (subGrp) -> {
+            SubscriptionGroupUtils.withEphemeralSubscriptionGroup(
+                mContext,
+                subId,
+                (subGrp) -> {
+                    test.runTest(subGrp, cellNetwork, cellNetworkCb);
+                }
+            );
+        });
+        mConnectivityManager.unregisterNetworkCallback(cellNetworkCb);
+    }
+
+    @Test
+    public void testSetVcnConfigOnTestNetwork() throws Exception {
+        final int subId = verifyAndGetValidDataSubId();
+
+        try (TestNetworkWrapper testNetworkWrapper =
+                createTestNetworkWrapper(true /* isMetered */, subId, LOCAL_ADDRESS)) {
+            verifyUnderlyingCellAndRunTest(subId, (subGrp, cellNetwork, cellNetworkCb) -> {
                 final Network vcnNetwork =
-                        setupAndGetVcnNetwork(subGrp, cellNetwork, cellNetworkCb);
+                    setupAndGetVcnNetwork(subGrp, cellNetwork, cellNetworkCb, testNetworkWrapper);
 
                 clearVcnConfigsAndVerifyNetworkTeardown(subGrp, cellNetworkCb, vcnNetwork);
             });
-        });
-
-        mConnectivityManager.unregisterNetworkCallback(cellNetworkCb);
+        }
     }
 
     private Network setupAndGetVcnNetwork(
             @NonNull ParcelUuid subGrp,
             @NonNull Network cellNetwork,
-            @NonNull VcnTestNetworkCallback cellNetworkCb)
+            @NonNull VcnTestNetworkCallback cellNetworkCb,
+            @NonNull TestNetworkWrapper testNetworkWrapper)
             throws Exception {
         cellNetworkCb.waitForAvailable();
         mVcnManager.setVcnConfig(subGrp, buildTestModeVcnConfig());
@@ -473,7 +487,7 @@
         final Network lostCellNetwork = cellNetworkCb.waitForLost();
         assertEquals(cellNetwork, lostCellNetwork);
 
-        injectAndVerifyIkeSessionNegotiationPackets(mTestNetworkWrapper.ikeTunUtils);
+        injectAndVerifyIkeSessionNegotiationPackets(testNetworkWrapper.ikeTunUtils);
 
         final Network vcnNetwork = cellNetworkCb.waitForAvailable();
         assertNotNull("VCN network did not come up", vcnNetwork);
@@ -551,55 +565,27 @@
     public void testVcnMigrationAfterNetworkDies() throws Exception {
         final int subId = verifyAndGetValidDataSubId();
 
-        mTestNetworkWrapper =
-                new TestNetworkWrapper(
-                        mContext,
-                        TEST_NETWORK_MTU,
-                        true /* isMetered */,
-                        Collections.singleton(subId),
-                        LOCAL_ADDRESS);
-        assertNotNull("No test network found", mTestNetworkWrapper.tunNetwork);
-
-        // Get current cell Network then wait for it to drop (due to losing NOT_VCN_MANAGED) before
-        // waiting for VCN Network.
-        final NetworkRequest cellNetworkReq = new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .build();
-        final VcnTestNetworkCallback cellNetworkCb = new VcnTestNetworkCallback();
-        mConnectivityManager.requestNetwork(cellNetworkReq, cellNetworkCb);
-        final Network cellNetwork = cellNetworkCb.waitForAvailable();
-        assertNotNull("No cell network found", cellNetwork);
-
-        CarrierPrivilegeUtils.withCarrierPrivilegesForShell(mContext, subId, () -> {
-            SubscriptionGroupUtils.withEphemeralSubscriptionGroup(mContext, subId, (subGrp) -> {
+        try (TestNetworkWrapper testNetworkWrapper =
+                createTestNetworkWrapper(true /* isMetered */, subId, LOCAL_ADDRESS)) {
+            verifyUnderlyingCellAndRunTest(subId, (subGrp, cellNetwork, cellNetworkCb) -> {
                 final Network vcnNetwork =
-                        setupAndGetVcnNetwork(subGrp, cellNetwork, cellNetworkCb);
+                    setupAndGetVcnNetwork(subGrp, cellNetwork, cellNetworkCb, testNetworkWrapper);
 
-                mTestNetworkWrapper.close();
-                mTestNetworkWrapper.vcnNetworkCallback.waitForLost();
+                testNetworkWrapper.close();
+                testNetworkWrapper.vcnNetworkCallback.waitForLost();
 
-                final TestNetworkWrapper secondaryTestNetworkWrapper =
-                        new TestNetworkWrapper(
-                                mContext,
-                                TEST_NETWORK_MTU,
-                                true /* isMetered */,
-                                Collections.singleton(subId),
-                                SECONDARY_LOCAL_ADDRESS);
-
+            try (TestNetworkWrapper secondaryTestNetworkWrapper =
+                    createTestNetworkWrapper(true /* isMetered */, subId, LOCAL_ADDRESS)) {
                 try {
-                    assertNotNull("No test network found", secondaryTestNetworkWrapper.tunNetwork);
-
                     injectAndVerifyIkeMobikePackets(secondaryTestNetworkWrapper.ikeTunUtils);
 
                     clearVcnConfigsAndVerifyNetworkTeardown(subGrp, cellNetworkCb, vcnNetwork);
                 } finally {
                     secondaryTestNetworkWrapper.close();
                 }
+            }
             });
-        });
-
-        mConnectivityManager.unregisterNetworkCallback(cellNetworkCb);
+        }
     }
 
     private void injectAndVerifyIkeMobikePackets(@NonNull IkeTunUtils ikeTunUtils)
@@ -648,48 +634,31 @@
     public void testVcnSafemodeOnTestNetwork() throws Exception {
         final int subId = verifyAndGetValidDataSubId();
 
-        mTestNetworkWrapper =
-                new TestNetworkWrapper(
-                        mContext,
-                        TEST_NETWORK_MTU,
-                        true /* isMetered */,
-                        Collections.singleton(subId),
-                        LOCAL_ADDRESS);
-        assertNotNull("No test network found", mTestNetworkWrapper.tunNetwork);
-
-        // Before the VCN starts, the test network should have NOT_VCN_MANAGED
-        waitForExpectedUnderlyingNetworkWithCapabilities(
-                true /* expectNotVcnManaged */,
-                false /* expectNotMetered */,
-                TestNetworkWrapper.NETWORK_CB_TIMEOUT_MS);
-
-        // Get current cell Network then wait for it to drop (due to losing NOT_VCN_MANAGED) before
-        // waiting for VCN Network.
-        final NetworkRequest cellNetworkReq = new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .build();
-        final VcnTestNetworkCallback cellNetworkCb = new VcnTestNetworkCallback();
-        mConnectivityManager.requestNetwork(cellNetworkReq, cellNetworkCb);
-        final Network cellNetwork = cellNetworkCb.waitForAvailable();
-        assertNotNull("No cell network found", cellNetwork);
-
-        CarrierPrivilegeUtils.withCarrierPrivilegesForShell(mContext, subId, () -> {
-            SubscriptionGroupUtils.withEphemeralSubscriptionGroup(mContext, subId, (subGrp) -> {
+        try (TestNetworkWrapper testNetworkWrapper =
+                createTestNetworkWrapper(true /* isMetered */, subId, LOCAL_ADDRESS)) {
+            // Before the VCN starts, the test network should have NOT_VCN_MANAGED
+            waitForExpectedUnderlyingNetworkWithCapabilities(
+                    testNetworkWrapper,
+                    true /* expectNotVcnManaged */,
+                    false /* expectNotMetered */,
+                    TestNetworkWrapper.NETWORK_CB_TIMEOUT_MS);
+            verifyUnderlyingCellAndRunTest(subId, (subGrp, cellNetwork, cellNetworkCb) -> {
                 final Network vcnNetwork =
-                        setupAndGetVcnNetwork(subGrp, cellNetwork, cellNetworkCb);
+                    setupAndGetVcnNetwork(subGrp, cellNetwork, cellNetworkCb, testNetworkWrapper);
 
                 // TODO(b/191801185): use VcnStatusCallbacks to verify safemode
 
                 // Once VCN starts, the test network should lose NOT_VCN_MANAGED
                 waitForExpectedUnderlyingNetworkWithCapabilities(
+                        testNetworkWrapper,
                         false /* expectNotVcnManaged */,
                         false /* expectNotMetered */,
                         TestNetworkWrapper.NETWORK_CB_TIMEOUT_MS);
 
-                // After VCN has started up, wait for safemode to kick in and expect the underlying
-                // Test Network to regain NOT_VCN_MANAGED.
+                // After VCN has started up, wait for safemode to kick in and expect the
+                // underlying Test Network to regain NOT_VCN_MANAGED.
                 waitForExpectedUnderlyingNetworkWithCapabilities(
+                        testNetworkWrapper,
                         true /* expectNotVcnManaged */,
                         false /* expectNotMetered */,
                         SAFEMODE_TIMEOUT_MILLIS);
@@ -700,8 +669,6 @@
 
                 mVcnManager.clearVcnConfig(subGrp);
             });
-        });
-
-        mConnectivityManager.unregisterNetworkCallback(cellNetworkCb);
+        }
     }
 }
diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnWifiUnderlyingNetworkTemplateTest.java
new file mode 100644
index 0000000..c4061e2
--- /dev/null
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn.cts;
+
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class VcnWifiUnderlyingNetworkTemplateTest {
+    private static final Set<String> SSIDS = Set.of("TestWifi");
+
+    // Package private for use in VcnGatewayConnectionConfigTest
+    static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() {
+        return new VcnWifiUnderlyingNetworkTemplate.Builder()
+                .setMetered(MATCH_FORBIDDEN)
+                .setSsids(SSIDS)
+                .build();
+    }
+
+    @Test
+    public void testBuilderAndGetters() {
+        final VcnWifiUnderlyingNetworkTemplate networkTemplate = getTestNetworkTemplate();
+        assertEquals(MATCH_FORBIDDEN, networkTemplate.getMetered());
+        assertEquals(SSIDS, networkTemplate.getSsids());
+    }
+
+    @Test
+    public void testBuilderAndGettersForDefaultValues() {
+        final VcnWifiUnderlyingNetworkTemplate networkTemplate =
+                new VcnWifiUnderlyingNetworkTemplate.Builder().build();
+        assertEquals(MATCH_ANY, networkTemplate.getMetered());
+        assertEquals(new HashSet<String>(), networkTemplate.getSsids());
+    }
+
+    @Test
+    public void testBuildWithEmptySets() {
+        final VcnWifiUnderlyingNetworkTemplate networkTemplate =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setSsids(new HashSet<String>())
+                        .build();
+        assertEquals(new HashSet<String>(), networkTemplate.getSsids());
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/TooltipTest.java b/tests/tests/view/src/android/view/cts/TooltipTest.java
index 67f27c4..4ce5eba 100644
--- a/tests/tests/view/src/android/view/cts/TooltipTest.java
+++ b/tests/tests/view/src/android/view/cts/TooltipTest.java
@@ -835,15 +835,18 @@
         waitOut(halfTimeout);
         assertFalse(hasTooltip(mTooltipView));
 
+        injectShortClick(mTooltipView);
         injectHoverMove(source, mTooltipView, 0, 0);
         waitOut(halfTimeout);
         assertFalse(hasTooltip(mTooltipView));
 
+        injectShortClick(mTooltipView);
         injectHoverMove(source, mTooltipView, 0, jitterHigh);
         waitOut(halfTimeout);
         assertFalse(hasTooltip(mTooltipView));
 
         // Jitter below threshold should be ignored and the tooltip should be shown.
+        injectShortClick(mTooltipView);
         injectHoverMove(source, mTooltipView, 0, 0);
         waitOut(halfTimeout);
         assertFalse(hasTooltip(mTooltipView));
@@ -857,6 +860,7 @@
         injectShortClick(mTooltipView);
         assertFalse(hasTooltip(mTooltipView));
 
+        injectShortClick(mTooltipView);
         injectHoverMove(source, mTooltipView, 0, 0);
         waitOut(halfTimeout);
         assertFalse(hasTooltip(mTooltipView));
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
index dbfcfa2..8b04c2d 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
@@ -24,6 +24,7 @@
 import android.util.Base64;
 import android.view.MotionEvent;
 import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.webkit.ConsoleMessage;
 import android.webkit.JsPromptResult;
 import android.webkit.JsResult;
@@ -437,6 +438,7 @@
         private boolean mHadOnCreateWindow;
         private boolean mHadOnRequestFocus;
         private boolean mHadOnReceivedIcon;
+        private WebView mChildWebView;
 
         public MockWebChromeClient() {
             super(mOnUiThread);
@@ -548,6 +550,15 @@
         public void onCloseWindow(WebView window) {
             super.onCloseWindow(window);
             mHadOnCloseWindow = true;
+
+            if (mChildWebView != null) {
+                ViewParent parent =  mChildWebView.getParent();
+                if (parent instanceof ViewGroup) {
+                    ((ViewGroup) parent).removeView(mChildWebView);
+                }
+                mChildWebView.destroy();
+            }
+
         }
 
         @Override
@@ -561,12 +572,12 @@
             if (mBlockWindowCreationAsync) {
                 transport.setWebView(null);
             } else {
-                WebView childView = new WebView(getActivity());
-                final WebSettings settings = childView.getSettings();
+                mChildWebView = new WebView(getActivity());
+                final WebSettings settings = mChildWebView.getSettings();
                 settings.setJavaScriptEnabled(true);
-                childView.setWebChromeClient(this);
-                transport.setWebView(childView);
-                getActivity().addContentView(childView, new ViewGroup.LayoutParams(
+                mChildWebView.setWebChromeClient(this);
+                transport.setWebView(mChildWebView);
+                getActivity().addContentView(mChildWebView, new ViewGroup.LayoutParams(
                         ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
             }
             resultMsg.sendToTarget();
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessClientTest.java
index 39e3eb9..c74dcb9 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessClientTest.java
@@ -23,12 +23,13 @@
 import android.webkit.WebView;
 import android.webkit.WebViewRenderProcess;
 import android.webkit.WebViewRenderProcessClient;
+
 import com.android.compatibility.common.util.NullWebViewUtils;
+
 import com.google.common.util.concurrent.SettableFuture;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
-import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicInteger;
 
 @AppModeFull
@@ -92,17 +93,21 @@
     }
 
     private void blockRenderProcess(final JSBlocker blocker) {
-        WebkitUtils.onMainThreadSync(new Runnable() {
-            @Override
-            public void run() {
-                WebView webView = mOnUiThread.getWebView();
-                webView.evaluateJavascript("blocker.block();", null);
-                blocker.waitForBlocked();
-                // Sending an input event that does not get acknowledged will cause
-                // the unresponsive renderer event to fire.
-                webView.dispatchKeyEvent(
-                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
-            }
+        WebkitUtils.onMainThreadSync(() -> {
+            WebView webView = mOnUiThread.getWebView();
+            webView.evaluateJavascript("blocker.block();", null);
+        });
+        // Wait on the test instrumentation thread not the main thread. Blocking the main thread
+        // may block other async calls such as initializing the GPU service channel that happens on
+        // the UI thread and has to finish before the renderer can execute any javascript,
+        // see https://crbug.com/1269552.
+        blocker.waitForBlocked();
+        WebkitUtils.onMainThreadSync(() -> {
+            WebView webView = mOnUiThread.getWebView();
+            // Sending an input event that does not get acknowledged will cause
+            // the unresponsive renderer event to fire.
+            webView.dispatchKeyEvent(
+                    new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
         });
     }
 
diff --git a/tests/trust/OWNERS b/tests/trust/OWNERS
new file mode 100644
index 0000000..ad48129
--- /dev/null
+++ b/tests/trust/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 36824
+
+include platform/frameworks/base:/core/java/android/service/trust/OWNERS
diff --git a/tests/video/src/android/video/cts/CodecPerformanceTestBase.java b/tests/video/src/android/video/cts/CodecPerformanceTestBase.java
index 2e261cf..d85327c 100644
--- a/tests/video/src/android/video/cts/CodecPerformanceTestBase.java
+++ b/tests/video/src/android/video/cts/CodecPerformanceTestBase.java
@@ -52,6 +52,7 @@
     static final boolean IS_AT_LEAST_VNDK_S;
 
     static final int DEVICE_INITIAL_SDK;
+    static final int VNDK_VERSION;
 
     // Some older devices can not support concurrent instances of both decoder and encoder
     // at max resolution. To handle such cases, this test is limited to test the
@@ -97,17 +98,21 @@
         // will mean that the tests built in Android S can't be run on Android R and below.
         DEVICE_INITIAL_SDK = SystemProperties.getInt("ro.product.first_api_level", 0);
 
-        // fps tolerance factor is kept quite low for devices launched on Android R and lower
-        FPS_TOLERANCE_FACTOR = DEVICE_INITIAL_SDK <= Build.VERSION_CODES.R ? 0.67 : 0.95;
+        VNDK_VERSION = SystemProperties.getInt("ro.vndk.version", 0);
 
-        IS_AT_LEAST_VNDK_S = SystemProperties.getInt("ro.vndk.version", 0) > Build.VERSION_CODES.R;
+        // fps tolerance factor is kept quite low for devices with Android R VNDK or lower
+        FPS_TOLERANCE_FACTOR = VNDK_VERSION <= Build.VERSION_CODES.R ? 0.67 : 0.95;
+
+        IS_AT_LEAST_VNDK_S = VNDK_VERSION > Build.VERSION_CODES.R;
 
         // Encoders on devices launched on Android Q and lower aren't tested at maximum resolution
         EXCLUDE_ENCODER_MAX_RESOLUTION = DEVICE_INITIAL_SDK <= Build.VERSION_CODES.Q;
 
         // Encoders on devices launched on Android R and lower aren't tested when operating rate
-        // that is set is > 0 and < 30 for resolution 4k
-        EXCLUDE_ENCODER_OPRATE_0_TO_30_FOR_4K = DEVICE_INITIAL_SDK <= Build.VERSION_CODES.R;
+        // that is set is > 0 and < 30 for resolution 4k.
+        // This includes devices launched on Android S with R or lower vendor partition.
+        EXCLUDE_ENCODER_OPRATE_0_TO_30_FOR_4K =
+            !IS_AT_LEAST_VNDK_S || (DEVICE_INITIAL_SDK <= Build.VERSION_CODES.R);
     }
 
     @Before
diff --git a/tools/cts-device-info/Android.bp b/tools/cts-device-info/Android.bp
index ff85860..33afc9a 100644
--- a/tools/cts-device-info/Android.bp
+++ b/tools/cts-device-info/Android.bp
@@ -32,6 +32,7 @@
         "sts",
         "mts",
         "vts",
+        "catbox",
     ],
     static_libs: [
         "compatibility-device-info",
diff --git a/tools/cts-tradefed/OWNERS b/tools/cts-tradefed/OWNERS
index 06936014..790c317 100644
--- a/tools/cts-tradefed/OWNERS
+++ b/tools/cts-tradefed/OWNERS
@@ -1,7 +1,7 @@
 #  Android EngProd Approvers
 guangzhu@google.com
 fdeng@google.com
-moonk@google.com
+normancheung@google.com
 jdesprez@google.com
 
 # Android Partner Eng Approvers
diff --git a/tools/cts-tradefed/res/config/cts-common.xml b/tools/cts-tradefed/res/config/cts-common.xml
index c1dffd2..47f3637 100644
--- a/tools/cts-tradefed/res/config/cts-common.xml
+++ b/tools/cts-tradefed/res/config/cts-common.xml
@@ -19,6 +19,9 @@
     <option name="compatibility:run-suite-tag" value="cts" />
     <!-- Enable module parameterization to run instant_app modules in main CTS -->
     <option name="compatibility:enable-parameterized-modules" value="true" />
+    <!-- Main CTS remains single device until decided otherwise -->
+    <option name="multi-devices-modules" value="EXCLUDE_ALL" />
+
     <include name="cts-preconditions" />
     <include name="cts-system-checkers" />
     <include name="cts-known-failures" />
diff --git a/tools/cts-tradefed/res/config/cts-exclude.xml b/tools/cts-tradefed/res/config/cts-exclude.xml
index 88b83ef..77d6038 100644
--- a/tools/cts-tradefed/res/config/cts-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-exclude.xml
@@ -28,9 +28,8 @@
          tests into CTS, but until then, they should be excluded. -->
     <option name="compatibility:exclude-filter" value="CtsTestHarnessModeTestCases" />
 
-    <!-- Exclude multi device test cases - in developing -->
-    <option name="compatibility:exclude-filter" value="CtsSampleMultiDeviceTestCases" />
-    <option name="compatibility:exclude-filter" value="CtsSampleMoblyTestCases" />
+    <!-- Exclude multi device test cases - work in progress -->
+    <option name="compatibility:exclude-filter" value="CtsWifiAwareTestCases" />
 
     <!-- Exclude downstreaming tests from CTS, i.e. tests added after the
          first major release for this API level (They are pulled into GTS
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index 4fbd9cd..a5eb18b 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -245,8 +245,10 @@
     <option name="compatibility:exclude-filter" value="CtsPermission3TestCases android.permission3.cts.PermissionTest23#testNoResidualPermissionsOnUninstall" />
  
     <!-- b/198992105 -->
-    <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases android.cts.statsd.atom.UidAtomTests#testDangerousPermissionState" />
-    <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases android.cts.statsd.atom.UidAtomTests#testDangerousPermissionStateSampled" />
+    <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionState" />
+    <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[instant] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionState" />
+    <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionStateSampled" />
+    <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases[instant] android.cts.statsdatom.permissionstate.DangerousPermissionStateTests#testDangerousPermissionStateSampled" />
 
     <!-- b/202357331 -->
     <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedDeviceOwnerTest#testCreateAdminSupportIntent" />
diff --git a/tools/cts-tradefed/res/config/cts-multidevice.xml b/tools/cts-tradefed/res/config/cts-multidevice.xml
index 612fe9d..74531547 100644
--- a/tools/cts-tradefed/res/config/cts-multidevice.xml
+++ b/tools/cts-tradefed/res/config/cts-multidevice.xml
@@ -20,9 +20,6 @@
     <option name="plan" value="cts-multidevice" />
 
     <!-- CTS multi device test cases only-->
-    <option name="compatibility:include-filter"
-        value="CtsSampleMultiDeviceTestCases"/>
-    <option name="compatibility:include-filter"
-        value="CtsSampleMoblyTestCases"/>
+    <option name="multi-devices-modules" value="ONLY_MULTI_DEVICES" />
 
 </configuration>
diff --git a/tools/cts-tradefed/res/config/cts-system-checkers.xml b/tools/cts-tradefed/res/config/cts-system-checkers.xml
index e15907e..7bad8b4 100644
--- a/tools/cts-tradefed/res/config/cts-system-checkers.xml
+++ b/tools/cts-tradefed/res/config/cts-system-checkers.xml
@@ -28,6 +28,8 @@
     <system_checker class="com.android.tradefed.suite.checker.SystemServerStatusChecker" />
     <system_checker class="com.android.tradefed.suite.checker.SystemServerFileDescriptorChecker" />
     <system_checker class="com.android.tradefed.suite.checker.DeviceBaselineChecker">
-        <option name="enable-device-baseline-settings" value="false" />
+        <option name="enable-device-baseline-settings" value="true" />
+        <option name="enable-experimental-device-baseline-setters" value="keep_screen_on" />
+        <option name="enable-experimental-device-baseline-setters" value="disable_os_auto_update" />
     </system_checker>
 </configuration>
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
index 80f1a44..0a392fd 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
@@ -112,8 +112,6 @@
                             // test/suite_harness/OWNERS.
                             "CtsSliceTestCases.config",
                             "CtsSampleDeviceTestCases.config",
-                            "CtsSampleMultiDeviceTestCases.config",
-                            "CtsSampleMoblyTestCases.config",
                             "CtsUsbTests.config",
                             "CtsGpuToolsHostTestCases.config",
                             "CtsEdiHostTestCases.config",