Merge "Poll for package stopped." into stage-aosp-tm-ts-dev
diff --git a/apps/CameraITS/tests/its_base_test.py b/apps/CameraITS/tests/its_base_test.py
index 9b29ab7..daa55c6 100644
--- a/apps/CameraITS/tests/its_base_test.py
+++ b/apps/CameraITS/tests/its_base_test.py
@@ -52,6 +52,8 @@
     'sensor_fusion': [],
 }
 
+logging.getLogger('matplotlib.font_manager').disabled = True
+
 
 class ItsBaseTest(base_test.BaseTestClass):
   """Base test for CameraITS tests.
diff --git a/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py b/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
index 7d8b9f2..e407fd6 100644
--- a/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
+++ b/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
@@ -71,7 +71,6 @@
       props = cam.get_camera_properties()
       props = cam.override_with_hidden_physical_camera_props(props)
       log_path = self.log_path
-      debug = self.debug_mode
       test_name_w_path = os.path.join(log_path, NAME)
 
       # check SKIP conditions
@@ -113,11 +112,6 @@
         caps = cam.do_capture([req]*THRESH_CONVERGE_FOR_EV, fmt)
         luma_locked = []
         for i, cap in enumerate(caps):
-          if debug:
-            img = image_processing_utils.convert_capture_to_rgb_image(
-                cap, props)
-            image_processing_utils.write_image(
-                img, f'{test_name_w_path}_ev{ev}_frame{i}.jpg')
           if cap['metadata']['android.control.aeState'] == LOCKED:
             ev_meta = cap['metadata']['android.control.aeExposureCompensation']
             logging.debug('cap EV compensation: %d', ev_meta)
@@ -132,7 +126,8 @@
                                   rel_tol=luma_locked_rtol):
                 raise AssertionError(f'AE locked lumas: {luma_locked}, '
                                      f'RTOL: {luma_locked_rtol}')
-      logging.debug('lumas in AE locked captures: %s', str(lumas))
+        logging.debug('lumas per frame ev %d: %s', ev, str(luma_locked))
+      logging.debug('mean lumas in AE locked captures: %s', str(lumas))
       if caps[THRESH_CONVERGE_FOR_EV-1]['metadata'][
           'android.control.aeState'] != LOCKED:
         raise AssertionError(f'No AE lock by {THRESH_CONVERGE_FOR_EV} frame.')
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 658ec8b..cb5be2a 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
@@ -27,7 +27,7 @@
 class CameraLaunchSPerfClassTest(its_base_test.ItsBaseTest):
   """Test camera launch latency for S performance class as specified in CDD.
 
-  [7.5/H-1-6] MUST have camera2 startup latency (open camera to first preview
+  [2.2.7.2/7.5/H-1-6] MUST have camera2 startup latency (open camera to first preview
   frame) < 600ms as measured by the CTS camera PerformanceTest under ITS
   lighting conditions (3000K) for both primary cameras.
   """
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 f56d34c..fb19f2f 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
@@ -27,7 +27,7 @@
 class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest):
   """Test jpeg capture latency for S performance class as specified in CDD.
 
-  [7.5/H-1-5] MUST have camera2 JPEG capture latency < 1000ms for 1080p
+  [2.2.7.2/7.5/H-1-5] MUST have camera2 JPEG capture latency < 1000ms for 1080p
   resolution as measured by the CTS camera PerformanceTest under ITS lighting
   conditions (3000K) for both primary cameras.
   """
diff --git a/apps/CameraITS/tests/scene3/test_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_edge_enhancement.py
index c6a3bcb..c1b63c7 100644
--- a/apps/CameraITS/tests/scene3/test_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_edge_enhancement.py
@@ -67,17 +67,17 @@
   for n in range(NUM_SAMPLES):
     cap = cam.do_capture(req, out_surface, repeat_request=req)
     y, _, _ = image_processing_utils.convert_capture_to_planes(cap)
-    chart.img = image_processing_utils.normalize_img(
-        image_processing_utils.get_image_patch(
-            y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+    chart.img = image_processing_utils.get_image_patch(
+        y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm)
     if n == 0:
       image_processing_utils.write_image(
           chart.img, '%s_edge=%d.jpg' % (
               os.path.join(log_path, NAME), edge_mode))
       edge_mode_res = cap['metadata']['android.edge.mode']
     sharpness_list.append(
-        image_processing_utils.compute_image_sharpness(chart.img))
-
+        image_processing_utils.compute_image_sharpness(chart.img)*255)
+  logging.debug('edge mode: %d, sharpness values: %s',
+                edge_mode_res, sharpness_list)
   return {'edge_mode': edge_mode_res, 'sharpness': np.mean(sharpness_list)}
 
 
@@ -89,7 +89,6 @@
   """
 
   def test_edge_enhancement(self):
-    logging.debug('Starting %s', NAME)
     with its_session_utils.ItsSession(
         device_id=self.dut.serial,
         camera_id=self.camera_id,
diff --git a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
index c5e9b19..d67ebe4 100644
--- a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
@@ -107,16 +107,15 @@
   caps = cam.do_capture([req]*NUM_SAMPLES, [out_surface], reprocess_format)
   for n in range(NUM_SAMPLES):
     y, _, _ = image_processing_utils.convert_capture_to_planes(caps[n])
-    chart.img = image_processing_utils.normalize_img(
-        image_processing_utils.get_image_patch(
-            y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+    chart.img = image_processing_utils.get_image_patch(
+        y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm)
     if n == 0:
       image_processing_utils.write_image(
           chart.img, '%s_reprocess_fmt_%s_edge=%d.jpg' % (
               os.path.join(log_path, NAME), reprocess_format, edge_mode))
       edge_mode_res = caps[n]['metadata']['android.edge.mode']
     sharpness_list.append(
-        image_processing_utils.compute_image_sharpness(chart.img))
+        image_processing_utils.compute_image_sharpness(chart.img)*255)
   logging.debug('Sharpness list for edge mode %d: %s',
                 edge_mode, str(sharpness_list))
   return {'edge_mode': edge_mode_res, 'sharpness': np.mean(sharpness_list)}
@@ -134,7 +133,6 @@
   """
 
   def test_reprocess_edge_enhancement(self):
-    logging.debug('Starting %s', NAME)
     logging.debug('Edge modes: %s', str(EDGE_MODES))
     with its_session_utils.ItsSession(
         device_id=self.dut.serial,
@@ -179,9 +177,10 @@
 
       # Initialize plot
       pylab.figure('reprocess_result')
-      pylab.title(NAME)
-      pylab.xlabel('Edge Enhance Mode')
-      pylab.ylabel('Sharpness')
+      pylab.suptitle(NAME)
+      pylab.title(str(EDGE_MODES))
+      pylab.xlabel('Edge Enhancement Mode')
+      pylab.ylabel('Image Sharpness')
       pylab.xticks(EDGE_MODES_VALUES)
 
       # Get the sharpness for each edge mode for regular requests
@@ -244,6 +243,7 @@
         logging.debug('Check reprocess format: %s', reprocess_format)
         check_edge_modes(sharpnesses_reprocess[reprocess_format])
 
+        # Check reprocessing doesn't make everyting worse
         hq_div_off_reprocess = (
             sharpnesses_reprocess[reprocess_format][EDGE_MODES['HQ']] /
             sharpnesses_reprocess[reprocess_format][EDGE_MODES['OFF']])
@@ -251,11 +251,10 @@
             sharpness_regular[EDGE_MODES['HQ']] /
             sharpness_regular[EDGE_MODES['OFF']])
         logging.debug('Verify reprocess HQ ~= reg HQ relative to OFF')
-        if not math.isclose(hq_div_off_reprocess, hq_div_off_regular,
-                            rel_tol=SHARPNESS_RTOL):
-          raise AssertionError(f'HQ/OFF_reprocess: {hq_div_off_reprocess:.4f}, '
-                               f'HQ/OFF_reg: {hq_div_off_regular:.4f}, '
-                               f'RTOL: {SHARPNESS_RTOL}')
+        if hq_div_off_reprocess < hq_div_off_regular*(1-SHARPNESS_RTOL):
+          raise AssertionError(
+              f'HQ/OFF_{reprocess_format}: {hq_div_off_reprocess:.4f}, '
+              f'HQ/OFF_reg: {hq_div_off_regular:.4f}, RTOL: {SHARPNESS_RTOL}')
 
 
 if __name__ == '__main__':
diff --git a/apps/CtsVerifier/Android.bp b/apps/CtsVerifier/Android.bp
index 9b2c16a..024e038 100644
--- a/apps/CtsVerifier/Android.bp
+++ b/apps/CtsVerifier/Android.bp
@@ -91,6 +91,7 @@
         "CtsForceStopHelper-constants",
         "ctsmediautil",
         "DpmWrapper",
+        "MediaPerformanceClassCommon",
     ],
 
     libs: ["telephony-common"] + ["android.test.runner.stubs"] + ["android.test.base.stubs"] + ["android.test.mock.stubs"] + ["android.car-test-stubs"] + ["voip-common"] + ["truth-prebuilt"],
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 7bffa69..bec0024 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -248,6 +248,7 @@
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
             </intent-filter>
+            <meta-data android:name="CddTest" android:value="3.8.17/C-1-1,C-2-1" />
             <meta-data android:name="test_category" android:value="@string/test_category_features" />
             <meta-data android:name="test_excluded_features"
                        android:value="android.hardware.type.watch:android.software.leanback:android.hardware.type.automotive" />
@@ -2052,6 +2053,9 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.wifi" />
             <meta-data android:name="display_mode"
                        android:value="multi_display_mode" />
+            <meta-data android:name="CddTest" android:value="7.4.5.2" />
+            <meta-data android:name="ApiTest"
+                       android:value="android.net.ConnectivityManager#registerNetworkCallback|android.net.ConnectivityManager#unregisterNetworkCallback|android.net.ConnectivityManager#getLinkProperties" />
         </activity>
 
         <activity android:name=".net.MultiNetworkConnectivityTestActivity"
@@ -2068,6 +2072,8 @@
                        android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
             <meta-data android:name="display_mode"
                        android:value="multi_display_mode" />
+            <meta-data android:name="ApiTest"
+                       android:value="android.net.ConnectivityManager#getNetworkCapabilities|android.net.ConnectivityManager#getAllNetworks|android.net.ConnectivityManager#requestNetwork|android.net.ConnectivityManager#unregisterNetworkCallback|android.net.ConnectivityManager#getActiveNetwork|android.net.ConnectivityManager#getNetworkInfo|android.net.ConnectivityManager#reportNetworkConnectivity" />
         </activity>
 
         <activity android:name=".nfc.NfcTestActivity"
@@ -2827,6 +2833,16 @@
                        android:value="android.hardware.type.automotive"/>
             <meta-data android:name="display_mode"
                        android:value="single_display_mode" />
+            <meta-data android:name="ApiTest"
+                       android:value="android.hardware.Camera#getNumberOfCameras|
+                               android.hardware.Camera#open|
+                               android.hardware.Camera#startPreview|
+                               android.hardware.Camera#stopPreview|
+                               android.hardware.Camera#takePicture|
+                               android.hardware.Camera#setParameters|
+                               android.hardware.Camera#setDisplayOrientation|
+                               android.hardware.Camera.Parameters#setHorizontalViewAngle|
+                               android.hardware.Camera.Parameters#setVerticalViewAngle" />
         </activity>
         <activity
             android:name=".camera.fov.DetermineFovActivity"
@@ -2836,6 +2852,11 @@
                        android:value="android.hardware.type.automotive"/>
             <meta-data android:name="display_mode"
                        android:value="multi_display_mode" />
+            <meta-data android:name="ApiTest"
+                       android:value="android.media.ExifInterface#TAG_ORIENTATION|
+                               android.media.ExifInterface#ORIENTATION_ROTATE_90|
+                               android.media.ExifInterface#ORIENTATION_ROTATE_180|
+                               android.media.ExifInterface#ORIENTATION_ROTATE_270" />
         </activity>
         <activity
             android:name=".camera.fov.CalibrationPreferenceActivity"
@@ -2861,6 +2882,32 @@
                        android:value="android.hardware.type.automotive"/>
             <meta-data android:name="display_mode"
                        android:value="single_display_mode" />
+            <meta-data android:name="ApiTest"
+                       android:value="android.hardware.Camera#getParameters|
+                               android.hardware.Camera#lock|
+                               android.hardware.Camera#setDisplayOrientation|
+                               android.hardware.Camera#setPreviewCallback|
+                               android.hardware.Camera#setParameters|
+                               android.hardware.Camera#setPreviewTexture|
+                               android.hardware.Camera#startPreview|
+                               android.hardware.Camera#stopPreview|
+                               android.hardware.Camera#unlock|
+                               android.media.MediaRecorder#prepare|
+                               android.media.MediaRecorder#release|
+                               android.media.MediaRecorder#reset|
+                               android.media.MediaRecorder#setAudioEncoder|
+                               android.media.MediaRecorder#setAudioSource|
+                               android.media.MediaRecorder#setCamera|
+                               android.media.MediaRecorder#setOnErrorListener|
+                               android.media.MediaRecorder#setOutputFormat|
+                               android.media.MediaRecorder#setOutputFile|
+                               android.media.MediaRecorder#setProfile|
+                               android.media.MediaRecorder#setVideoEncoder|
+                               android.media.MediaRecorder#setVideoEncodingBitRate|
+                               android.media.MediaRecorder#setVideoSize|
+                               android.media.MediaRecorder#setVideoSource|
+                               android.media.MediaRecorder#start|
+                               android.media.MediaRecorder#stop" />
         </activity>
 
         <activity android:name=".camera.its.ItsTestActivity"
@@ -3919,174 +3966,6 @@
                        android:value="single_display_mode" />
         </activity>
 
-        <!--            CTS Verifier Presence Test Top Screen -->
-        <activity
-            android:name=".presence.PresenceTestActivity"
-            android:configChanges="keyboardHidden|orientation|screenSize"
-            android:exported="true"
-            android:label="@string/presence_test" >
-            <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_networking" />
-            <meta-data android:name="display_mode"
-                       android:value="single_display_mode" />
-        </activity>
-
-        <!--
-             CTS Verifier Uwb Precision Test Screen
-                 test category : uwb
-                 test parent : PresenceTestActivity
-        -->
-        <activity
-            android:name=".presence.UwbPrecisionActivity"
-            android:configChanges="keyboardHidden|orientation|screenSize"
-            android:exported="true"
-            android:label="@string/uwb_precision" >
-            <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/uwb" />
-            <meta-data
-                android:name="test_parent"
-                android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
-            <meta-data android:name="display_mode"
-                       android:value="single_display_mode" />
-            <meta-data android:name="CddTest"
-                       android:value="7.4.9/C-1-1" />
-        </activity>
-
-        <!--
-             CTS Verifier Uwb Short Range Test Screen
-                 test category : uwb
-                 test parent : PresenceTestActivity
-        -->
-        <activity
-            android:name=".presence.UwbShortRangeActivity"
-            android:configChanges="keyboardHidden|orientation|screenSize"
-            android:exported="true"
-            android:label="@string/uwb_short_range" >
-            <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/uwb" />
-            <meta-data
-                android:name="test_parent"
-                android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
-            <meta-data
-                android:name="display_mode"
-                android:value="single_display_mode" />
-            <meta-data
-                android:name="CddTest"
-                android:value="7.4.9/C-1-2" />
-        </activity>
-
-        <!--
-            CTS Verifier BLE RSSI Precision Test Screen
-                test category : BLE
-                test parent : PresenceTestActivity
-        -->
-        <activity
-            android:name=".presence.BleRssiPrecisionActivity"
-            android:exported="true"
-            android:label="@string/ble_rssi_precision_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/ble" />
-            <meta-data
-                android:name="test_parent"
-                android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
-            <meta-data
-                android:name="test_required_features"
-                android:value="android.hardware.bluetooth_le" />
-            <meta-data
-                android:name="display_mode"
-                android:value="single_display_mode" />
-            <meta-data
-                android:name="CddText"
-                android:value="7.4.3/C-7-1" />
-        </activity>
-
-        <!--
-            CTS Verifier BLE Rx/Tx Calibration Test Screen
-                test category : BLE
-                test parent : PresenceTestActivity
-        -->
-        <activity
-            android:name=".presence.BleRxTxCalibrationActivity"
-            android:exported="true"
-            android:label="@string/ble_rx_tx_calibration_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/ble" />
-            <meta-data
-                android:name="test_parent"
-                android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
-            <meta-data
-                android:name="test_required_features"
-                android:value="android.hardware.bluetooth_le" />
-            <meta-data
-                android:name="display_mode"
-                android:value="single_display_mode" />
-            <meta-data
-                android:name="CddText"
-                android:value="7.4.3/C-7-2" />
-        </activity>
-
-        <!-- CTS Verifier Nan Precision and Bias Test Screen
-                 test category : wifi_nan
-                 test parent : PresenceTestActivity
-        -->
-        <activity
-            android:name=".presence.NanPrecisionTestActivity"
-            android:configChanges="keyboardHidden|orientation|screenSize"
-            android:exported="true"
-            android:label="@string/nan_precision" >
-            <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/wifi_nan" />
-            <meta-data
-                android:name="test_parent"
-                android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
-            <meta-data android:name="display_mode"
-                       android:value="single_display_mode" />
-            <meta-data android:name="CddTest"
-                       android:value="7.4.2.5/H-1-1|7.4.2.5/H-1-2" />
-        </activity>
-
         <activity-alias
             android:name=".CtsVerifierActivity"
             android:label="@string/app_name"
diff --git a/apps/CtsVerifier/res/layout/ble_rssi_precision.xml b/apps/CtsVerifier/res/layout/ble_rssi_precision.xml
deleted file mode 100644
index 5f01e1f..0000000
--- a/apps/CtsVerifier/res/layout/ble_rssi_precision.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?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.
-  -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    style="@style/RootLayoutPadding"
-    tools:ignore="Autofill">
-
-    <ScrollView
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content">
-
-        <LinearLayout
-            android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <TextView
-                android:text="@string/ble_rssi_precision_test_instructions"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:scrollbars="vertical" />
-
-            <EditText
-                android:id="@+id/report_rssi_range"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:hint="@string/report_ble_rssi_range"
-                android:inputType="number" />
-
-            <EditText
-                android:id="@+id/report_reference_device"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:hint="@string/report_reference_device"
-                android:inputType="text" />
-
-            <include
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                layout="@layout/pass_fail_buttons" />
-        </LinearLayout>
-    </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_rx_tx_calibration.xml b/apps/CtsVerifier/res/layout/ble_rx_tx_calibration.xml
deleted file mode 100644
index 3ca955b..0000000
--- a/apps/CtsVerifier/res/layout/ble_rx_tx_calibration.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?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.
-  -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    style="@style/RootLayoutPadding"
-    tools:ignore="Autofill">
-
-    <ScrollView
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content">
-
-        <LinearLayout
-            android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <TextView
-                android:text="@string/ble_rx_tx_calibration_test_instructions"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:scrollbars="vertical" />
-
-            <EditText
-                android:id="@+id/report_channels_rssi_range"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:hint="@string/report_channels_ble_rssi_range"
-                android:inputType="number" />
-
-            <EditText
-                android:id="@+id/report_cores_rssi_range"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:hint="@string/report_cores_ble_rssi_range"
-                android:inputType="number" />
-
-            <EditText
-                android:id="@+id/report_reference_device"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:hint="@string/report_reference_device"
-                android:inputType="text" />
-
-            <include
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                layout="@layout/pass_fail_buttons" />
-        </LinearLayout>
-    </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/clipboard_preview.xml b/apps/CtsVerifier/res/layout/clipboard_preview.xml
index 85f38c8..efec118 100644
--- a/apps/CtsVerifier/res/layout/clipboard_preview.xml
+++ b/apps/CtsVerifier/res/layout/clipboard_preview.xml
@@ -31,86 +31,6 @@
         android:layout_marginBottom="100dp"
         android:layout_marginTop="30dp"
         android:text="@string/clipboard_preview_test_copy_button"/>
-
-    <TableLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-        <TableRow
-            android:layout_width="match_parent"
-            android:layout_height="50dp">
-            <View android:layout_weight="3"
-                  android:layout_height="50dp"/>
-            <Button android:layout_weight="1"
-                    android:id="@+id/clipboard_preview_test_b1"
-                    android:layout_width="50dp"
-                    android:layout_height="50dp"
-                    android:text="1"/>
-            <Button android:layout_weight="1"
-                    android:id="@+id/clipboard_preview_test_b2"
-                    android:layout_width="50dp"
-                    android:layout_height="50dp"
-                    android:text="2"/>
-            <Button android:layout_weight="1"
-                    android:id="@+id/clipboard_preview_test_b3"
-                    android:layout_width="50dp"
-                    android:layout_height="50dp"
-                    android:text="3"/>
-            <View android:layout_weight="3"
-                  android:layout_height="50dp"/>
-        </TableRow>
-        <TableRow
-            android:layout_width="match_parent"
-            android:layout_height="50dp">
-            <View android:layout_weight="3"
-                  android:layout_height="50dp"/>
-            <Button android:layout_weight="1"
-                    android:id="@+id/clipboard_preview_test_b4"
-                    android:layout_width="50dp"
-                    android:layout_height="50dp"
-                    android:text="4"/>
-            <Button android:layout_weight="1"
-                    android:id="@+id/clipboard_preview_test_b5"
-                    android:layout_width="50dp"
-                    android:layout_height="50dp"
-                    android:text="5"/>
-            <Button android:layout_weight="1"
-                    android:id="@+id/clipboard_preview_test_b6"
-                    android:layout_width="50dp"
-                    android:layout_height="50dp"
-                    android:text="6"/>
-            <View android:layout_weight="3"
-                  android:layout_height="50dp"/>
-        </TableRow>
-        <TableRow
-            android:layout_width="match_parent"
-            android:layout_height="50dp">
-            <View android:layout_weight="3"
-                  android:layout_height="match_parent"/>
-            <Button android:layout_weight="1"
-                    android:id="@+id/clipboard_preview_test_b7"
-                    android:layout_width="50dp"
-                    android:layout_height="match_parent"
-                    android:text="7"/>
-            <Button android:layout_weight="1"
-                    android:id="@+id/clipboard_preview_test_b8"
-                    android:layout_width="50dp"
-                    android:layout_height="match_parent"
-                    android:text="8"/>
-            <Button android:layout_weight="1"
-                    android:id="@+id/clipboard_preview_test_b9"
-                    android:layout_width="50dp"
-                    android:layout_height="match_parent"
-                    android:text="9"/>
-            <View android:layout_weight="3"
-                  android:layout_height="50dp"/>
-        </TableRow>
-    </TableLayout>
-    <Button
-        android:id="@+id/clipboard_preview_test_b0"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:text="0"/>
     <include
         android:id="@+id/clipboard_preview_test_pass_fail"
         android:layout_width="match_parent"
diff --git a/apps/CtsVerifier/res/layout/nan_precision.xml b/apps/CtsVerifier/res/layout/nan_precision.xml
deleted file mode 100644
index c81a9c4..0000000
--- a/apps/CtsVerifier/res/layout/nan_precision.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?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.
-  -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:orientation="vertical"
-                style="@style/RootLayoutPadding">
-    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="fill_parent"
-                android:layout_height="wrap_content">
-        <LinearLayout android:orientation="vertical"
-                      android:layout_width="match_parent"
-                      android:layout_height="wrap_content">
-            <TextView android:text="@string/nan_precision_instruction"
-                      android:id="@+id/nan_precision_instruction"
-                      android:layout_width="wrap_content"
-                      android:layout_height="wrap_content"
-                      android:scrollbars="vertical"/>
-            <LinearLayout android:orientation="vertical"
-                          android:layout_width="match_parent"
-                          android:layout_height="wrap_content">
-                <EditText android:id="@+id/nan_bandwidth"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="number"
-                          android:hint="@string/report_nan_bandwidth_mhz"/>
-                <EditText android:id="@+id/distance_range_10cm_gt_68p"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="numberDecimal"
-                          android:hint="@string/report_distance_range_10cm_gt_68p"/>
-                <EditText android:id="@+id/distance_range_1m_gt_68p"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="numberDecimal"
-                          android:hint="@string/report_distance_range_1m_gt_68p"/>
-                <EditText android:id="@+id/distance_range_3m_gt_68p"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="numberDecimal"
-                          android:hint="@string/report_distance_range_3m_gt_68p"/>
-                <EditText android:id="@+id/distance_range_5m_gt_68p"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="numberDecimal"
-                          android:hint="@string/report_distance_range_5m_gt_68p"/>
-                <EditText android:id="@+id/distance_range_10cm_gt_90p"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="numberDecimal"
-                          android:hint="@string/report_distance_range_10cm_gt_90p"/>
-                <EditText android:id="@+id/distance_range_1m_gt_90p"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="numberDecimal"
-                          android:hint="@string/report_distance_range_1m_gt_90p"/>
-                <EditText android:id="@+id/distance_range_3m_gt_90p"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="numberDecimal"
-                          android:hint="@string/report_distance_range_3m_gt_90p"/>
-                <EditText android:id="@+id/distance_range_5m_gt_90p"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="numberDecimal"
-                          android:hint="@string/report_distance_range_5m_gt_90p"/>
-                <EditText android:id="@+id/reference_device"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:hint="@string/report_reference_device"/>
-            </LinearLayout>
-
-            <include android:layout_width="match_parent"
-                     android:layout_height="wrap_content"
-                     layout="@layout/pass_fail_buttons"/>
-        </LinearLayout>
-    </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/uwb_precision.xml b/apps/CtsVerifier/res/layout/uwb_precision.xml
deleted file mode 100644
index 14e996d..0000000
--- a/apps/CtsVerifier/res/layout/uwb_precision.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?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.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:orientation="vertical"
-                style="@style/RootLayoutPadding">
-    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="fill_parent"
-                android:layout_height="wrap_content">
-        <LinearLayout android:orientation="vertical"
-                      android:layout_width="match_parent"
-                      android:layout_height="wrap_content">
-            <TextView android:text="@string/uwb_precision_instruction"
-                      android:id="@+id/uwb_precision_instruction"
-                      android:layout_width="wrap_content"
-                      android:layout_height="wrap_content"
-                      android:scrollbars="vertical"/>
-            <LinearLayout android:orientation="vertical"
-                          android:layout_width="match_parent"
-                          android:layout_height="wrap_content">
-                <EditText android:id="@+id/distance_range_cm"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:inputType="numberDecimal"
-                          android:hint="@string/report_distance_range_cm"/>
-                <EditText android:id="@+id/reference_device"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:hint="@string/report_reference_device"/>
-            </LinearLayout>
-
-            <include android:layout_width="match_parent"
-                     android:layout_height="wrap_content"
-                     layout="@layout/pass_fail_buttons"/>
-        </LinearLayout>
-    </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/uwb_short_range.xml b/apps/CtsVerifier/res/layout/uwb_short_range.xml
deleted file mode 100644
index 9afc6e5..0000000
--- a/apps/CtsVerifier/res/layout/uwb_short_range.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?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.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical"
-        style="@style/RootLayoutPadding">
-    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content">
-        <LinearLayout android:orientation="vertical"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-            <TextView android:text="@string/uwb_short_range_instruction"
-                    android:id="@+id/uwb_short_range_instruction"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:scrollbars="vertical"/>
-            <LinearLayout android:orientation="vertical"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content">
-                <EditText android:id="@+id/distance_median_meters"
-                        android:layout_width="wrap_content"
-                          android:inputType="numberDecimal"
-                        android:layout_height="wrap_content"
-                        android:hint="@string/report_distance_median_meters"/>
-                <EditText android:id="@+id/reference_device"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:hint="@string/report_reference_device"/>
-            </LinearLayout>
-
-            <include android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    layout="@layout/pass_fail_buttons"/>
-        </LinearLayout>
-    </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 526645e..7d34865 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -32,6 +32,16 @@
     <string name="finish_button_text">Finish</string>
     <string name="fail_and_next_button_text">Fail and Next</string>
 
+    <!-- Strings for CtsReportLog warning -->
+    <string name="reportlog_warning_title">CTS-Verifier Report Log</string>
+    <string name="reportlog_warning_body">Can\'t create folder for CTS-Verifier Report Logs.
+        \n\nPlease enable Report Log creation by exiting CTS Verifier and running the following commands:
+        \n\n<code>adb shell appops set com.android.cts.verifier android:read_device_identifiers allow</code>
+        \n\n<code>adb shell appops set com.android.cts.verifier MANAGE_EXTERNAL_STORAGE 0</code>
+        \n\nTest instructions are found in the \"Using CTS Verifier\" document found at
+        <a href="https://source.android.com/compatibility/cts/verifier">https://source.android.com/compatibility/cts/verifier</a>
+    </string>
+
     <!-- Strings for TestListActivity -->
     <string name="test_category_audio">Audio</string>
     <string name="test_category_camera">Camera</string>
@@ -301,9 +311,11 @@
     </string>
     <string name="clipboard_preview_test_instructions">
         Press the \'Copy\' button to copy the secret code to the clipboard.
-        \n\nUse the clipboard preview UI, or the clipboard editor component to view the secret code.
-        \n\nEnter the secret code using the buttons below.
+        \n\n If nothing happens, press Fail.
+        \n\n If you see the word "FAIL" appear on screen, press Fail.
+        \n\n If you see a confirmation that content has been copied to the clipboard, press Pass.
     </string>
+    <string name="clipboard_preview_test_secret">FAIL</string>
     <string name="clipboard_preview_test_copy_button">Copy</string>
 
 
@@ -5480,6 +5492,7 @@
     <string name="audio_general_test_not_run">Test Not Run</string>
     <string name="audio_general_testnotcompleted">Test not completed.</string>
 
+    <string name="audio_general_reportlogtest">[Can\'t Write ReportLog]</string>
 
     <!-- Audio Loopback Latency Test -->
     <string name="audio_loopback_latency_test">Audio Loopback Latency Test</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
index b013bb7..a93e3b5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
@@ -74,6 +74,10 @@
         }
     }
 
+    public boolean isOpen() {
+        return mStore != null;
+    }
+
     /**
      * Closes report file. Static functions that do not have access to instrumentation can
      * use this to close report logs. Summary, if present, is not reported to instrumentation, hence
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index 0294ff7..36975c2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -32,7 +32,6 @@
 import android.os.Bundle;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
@@ -143,11 +142,13 @@
 
     public static class Activity extends android.app.Activity implements PassFailActivity {
         private WakeLock mWakeLock;
-        private final CtsVerifierReportLog mReportLog;
+        private CtsVerifierReportLog mReportLog;
         private final TestResultHistoryCollection mHistoryCollection;
 
+        protected boolean mRequireReportLogToPass;
+
         public Activity() {
-            this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
+            newReportLog();
             this.mHistoryCollection = new TestResultHistoryCollection();
         }
 
@@ -159,6 +160,10 @@
                         .newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "PassFailButtons");
                 mWakeLock.acquire();
             }
+
+            if (!this.mReportLog.isOpen()) {
+                showReportLogWarningDialog(this);
+            }
         }
 
         @Override
@@ -206,12 +211,25 @@
                     getHistoryCollection());
         }
 
+        protected CtsVerifierReportLog newReportLog() {
+            return mReportLog = new CtsVerifierReportLog(
+                    getReportFileName(), getReportSectionName());
+        }
+
         @Override
         public CtsVerifierReportLog getReportLog() {
             return mReportLog;
         }
 
         /**
+         * A mechanism to block tests from passing if no ReportLog data has been collected.
+         * @return true if the ReportLog is open OR if the test does not require that.
+         */
+        public boolean isReportLogOkToPass() {
+            return !mRequireReportLogToPass || mReportLog.isOpen();
+        }
+
+        /**
          * @return The name of the file to store the (suite of) ReportLog information.
          */
         @Override
@@ -527,6 +545,12 @@
         activity.showDialog(INFO_DIALOG_ID, args);
     }
 
+    protected static void showReportLogWarningDialog(final android.app.Activity activity) {
+        showInfoDialog(activity,
+                R.string.reportlog_warning_title, R.string.reportlog_warning_body, -1);
+    }
+
+
     protected static Dialog createDialog(final android.app.Activity activity, int id, Bundle args) {
         switch (id) {
             case INFO_DIALOG_ID:
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 528d914..83b32de 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
@@ -182,12 +182,15 @@
             mResultsTxt.setText(getResources().getString(R.string.analog_headset_pass_noheadset));
             return true;
         } else {
-            boolean pass = mPlugIntentReceived &&
-                    mHeadsetDeviceInfo != null &&
-                    mPlaybackSuccess &&
-                    (mHasHeadsetHook || mHasPlayPause) && mHasVolUp && mHasVolDown;
+            boolean pass = isReportLogOkToPass()
+                    && mPlugIntentReceived
+                    && mHeadsetDeviceInfo != null
+                    && mPlaybackSuccess
+                    && (mHasHeadsetHook || mHasPlayPause) && mHasVolUp && mHasVolDown;
             if (pass) {
                 mResultsTxt.setText(getResources().getString(R.string.analog_headset_pass));
+            } else if (!isReportLogOkToPass()) {
+                mResultsTxt.setText(getResources().getString(R.string.audio_general_reportlogtest));
             }
             return pass;
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java
index 6b9bf05..3f1266e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java
@@ -496,8 +496,10 @@
             Log.v(TAG, "Test EndedOk. " + testId + " str:"+str);
             showProgressIndicator(false);
             mResultTest.setText("test completed. " + str);
-            if (mTestAECPassed) {
-                getPassButton().setEnabled(true);;
+            if (!isReportLogOkToPass()) {
+                mResultTest.setText(getResources().getString(R.string.audio_general_reportlogtest));
+            } else if (mTestAECPassed) {
+                getPassButton().setEnabled(true);
             }
         }
 
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 a6bd4ad..413734b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
@@ -127,11 +127,11 @@
 
     private TestSpec[] mTestSpecs = new TestSpec[NUM_TEST_ROUTES];
     class TestSpec {
+        private static final String TAG = "AudioLoopbackLatencyActivity.TestSpec";
         // impossibly low latencies (indicating something in the test went wrong).
         protected static final double LOWEST_REASONABLE_LATENCY_MILLIS = 1.0;
 
         final int mRouteId;
-        // final double mMustLatencyMS;
 
         // runtime assigned device ID
         static final int DEVICEID_NONE = -1;
@@ -151,7 +151,6 @@
         boolean mRouteAvailable; // Have we seen this route/device at any time
         boolean mRouteConnected; // is the route available NOW
         boolean mTestRun;
-        // boolean mTestPass;
 
         TestSpec(int routeId, double requiredConfidence) {
             mRouteId = routeId;
@@ -181,14 +180,6 @@
             mMeanConfidence = StatUtils.calculateMean(mConfidence);
         }
 
-        boolean getRouteAvailable() {
-            return mRouteAvailable;
-        }
-
-        boolean getTestRun() {
-            return mTestRun;
-        }
-
         boolean isMeasurementValid() {
             return mTestRun && mMeanLatencyMS > 1.0 && mMeanConfidence >= mRequiredConfidence;
         }
@@ -224,60 +215,42 @@
         }
 
         // ReportLog Schema (per route)
-        private static final String KEY_ROUTEAVAILABLE = "route_available";
-        private static final String KEY_ROUTECONNECTED = "route_connected";
-        private static final String KEY_ROUTERUN = "route_run";
-        private static final String KEY_LATENCY = "route_latency";
-        private static final String KEY_CONFIDENCE = "route_confidence";
-        private static final String KEY_MEANABSDEVIATION = "route_mean_absolute_deviation";
-        private static final String KEY_IS_PERIPHERAL_ATTACHED = "route_is_peripheral_attached";
-        private static final String KEY_INPUT_PERIPHERAL_NAME = "route_input_peripheral";
-        private static final String KEY_OUTPUT_PERIPHERAL_NAME = "route_output_peripheral";
-        private static final String KEY_TEST_PERIPHERAL = "route_test_peripheral";
-
-        String makeSectionKey(String key) {
-            return Integer.toString(mRouteId) + "_" + key;
-        }
+        private static final String KEY_ROUTEINDEX = "route_index";
+        private static final String KEY_LATENCY = "latency";
+        private static final String KEY_CONFIDENCE = "confidence";
+        private static final String KEY_MEANABSDEVIATION = "mean_absolute_deviation";
+        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";
 
         void recordTestResults(CtsVerifierReportLog reportLog) {
             reportLog.addValue(
-                    makeSectionKey(KEY_ROUTEAVAILABLE),
-                    mRouteAvailable ? 1 : 0,
+                    KEY_ROUTEINDEX,
+                    mRouteId,
                     ResultType.NEUTRAL,
                     ResultUnit.NONE);
 
             reportLog.addValue(
-                    makeSectionKey(KEY_ROUTECONNECTED),
-                    mRouteConnected ? 1 : 0,
-                    ResultType.NEUTRAL,
-                    ResultUnit.NONE);
-
-            reportLog.addValue(
-                    makeSectionKey(KEY_ROUTERUN),
-                    mTestRun ? 1 : 0,
-                    ResultType.NEUTRAL,
-                    ResultUnit.NONE);
-
-            reportLog.addValue(
-                    makeSectionKey(KEY_LATENCY),
+                    KEY_LATENCY,
                     mMeanLatencyMS,
                     ResultType.LOWER_BETTER,
                     ResultUnit.MS);
 
             reportLog.addValue(
-                    makeSectionKey(KEY_CONFIDENCE),
+                    KEY_CONFIDENCE,
                     mMeanConfidence,
                     ResultType.HIGHER_BETTER,
                     ResultUnit.NONE);
 
             reportLog.addValue(
-                    makeSectionKey(KEY_MEANABSDEVIATION),
+                    KEY_MEANABSDEVIATION,
                     mMeanAbsoluteDeviation,
                     ResultType.NEUTRAL,
                     ResultUnit.NONE);
 
             reportLog.addValue(
-                    makeSectionKey(KEY_TEST_PERIPHERAL),
+                    KEY_TEST_PERIPHERAL,
                     mDeviceName,
                     ResultType.NEUTRAL,
                     ResultUnit.NONE);
@@ -294,6 +267,8 @@
         getPassButton().setEnabled(false);
         setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1);
 
+        mRequireReportLogToPass = true;
+
         mClaimsOutput = AudioSystemFlags.claimsOutput(this);
         mClaimsInput = AudioSystemFlags.claimsInput(this);
         mClaimsProAudio = AudioSystemFlags.claimsProAudio(this);
@@ -474,7 +449,6 @@
                     mTestSpecs[TESTROUTE_USB].mDeviceName = devInfo.getProductName().toString();
             }
 
-            // setTestButtonsState();
             enableStartButtons(true);
         }
     }
@@ -533,69 +507,66 @@
         return setTestNameSuffix(sCurrentDisplayMode, "audio_loopback_latency_activity");
     }
 
-    // Schema
+    // Test-Schema
     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_TEST_MMAP = "supports_mmap";
     private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive";
     private static final String KEY_LEVEL = "level";
-    //
-    // Subclasses should call this explicitly. SubClasses should call submit() after their logs
-    //
+
+    private void recordRouteResults(int routeIndex) {
+        if (mTestSpecs[routeIndex].mTestRun) {
+            CtsVerifierReportLog reportLog = newReportLog();
+
+            int audioLevel = mAudioLevelSeekbar.getProgress();
+            reportLog.addValue(
+                    KEY_LEVEL,
+                    audioLevel,
+                    ResultType.NEUTRAL,
+                    ResultUnit.NONE);
+
+            reportLog.addValue(
+                    KEY_IS_PRO_AUDIO,
+                    mClaimsProAudio,
+                    ResultType.NEUTRAL,
+                    ResultUnit.NONE);
+
+            reportLog.addValue(
+                    KEY_TEST_MMAP,
+                    mSupportsMMAP,
+                    ResultType.NEUTRAL,
+                    ResultUnit.NONE);
+
+            reportLog.addValue(
+                    KEY_TEST_MMAPEXCLUSIVE,
+                    mSupportsMMAPExclusive,
+                    ResultType.NEUTRAL,
+                    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);
+
+            mTestSpecs[routeIndex].recordTestResults(reportLog);
+
+            reportLog.submit();
+        }
+    }
+
     @Override
     public void recordTestResults() {
-        Log.i(TAG, "recordTestResults() mNativeAnalyzerThread:" + mNativeAnalyzerThread);
-
-        // We need to rework that
-        CtsVerifierReportLog reportLog = getReportLog();
-
-        int audioLevel = mAudioLevelSeekbar.getProgress();
-        reportLog.addValue(
-                KEY_LEVEL,
-                audioLevel,
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        reportLog.addValue(
-                KEY_IS_PRO_AUDIO,
-                mClaimsProAudio,
-                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 (mNativeAnalyzerThread == null) {
-            return; // no test results to report
+        for (int route = 0; route < NUM_TEST_ROUTES; route++) {
+            recordRouteResults(route);
         }
-
-        reportLog.addValue(
-                KEY_SAMPLE_RATE,
-                mNativeAnalyzerThread.getSampleRate(),
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        reportLog.addValue(
-                KEY_IS_LOW_LATENCY,
-                mNativeAnalyzerThread.isLowLatencyStream(),
-                ResultType.NEUTRAL,
-                ResultUnit.NONE);
-
-        for (TestSpec testSpec : mTestSpecs) {
-            testSpec.recordTestResults(reportLog);
-        }
-
-        reportLog.submit();
     }
 
     private void startAudioTest(Handler messageHandler, int testRouteId) {
@@ -658,6 +629,7 @@
                 e.printStackTrace();
             }
 
+
             mTestPhase++;
             if (mTestPhase >= NUM_TEST_PHASES) {
                 handleTestCompletion();
@@ -683,7 +655,8 @@
         mResultsText[mTestRoute].setText(testSpec.getResultString());
 
         LoopbackLatencyRequirements requirements = new LoopbackLatencyRequirements();
-        boolean pass = requirements.evaluate(mClaimsProAudio,
+        boolean pass = isReportLogOkToPass()
+                && requirements.evaluate(mClaimsProAudio,
                 Build.VERSION.MEDIA_PERFORMANCE_CLASS,
                 mTestSpecs[TESTROUTE_DEVICE].isMeasurementValid()
                         ? mTestSpecs[TESTROUTE_DEVICE].mMeanLatencyMS : 0.0,
@@ -694,8 +667,12 @@
 
         getPassButton().setEnabled(pass);
 
-        String resultText = requirements.getResultsString();
-        mTestStatusText.setText(resultText);
+        StringBuilder sb = new StringBuilder();
+        if (!isReportLogOkToPass()) {
+            sb.append(getResources().getString(R.string.audio_general_reportlogtest) + "\n");
+        }
+        sb.append(requirements.getResultsString());
+        mTestStatusText.setText(sb.toString());
 
         showWait(false);
         enableStartButtons(true);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java
index 215d26f..8ff2358 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java
@@ -154,6 +154,8 @@
         String yesString = getResources().getString(R.string.audio_general_yes);
         String noString = getResources().getString(R.string.audio_general_no);
 
+        mRequireReportLogToPass = true;
+
         boolean claimsProAudio = AudioSystemFlags.claimsProAudio(this);
         boolean claimsLowLatencyAudio = AudioSystemFlags.claimsLowLatencyAudio(this);
 
@@ -311,11 +313,14 @@
         }
 
         double averageLatency = mLatencyAve[mActiveTestAPI];
-        boolean pass = averageLatency != 0 && averageLatency <= mMaxRequiredLatency;
+        boolean pass = isReportLogOkToPass()
+                && averageLatency != 0 && averageLatency <= mMaxRequiredLatency;
 
         if (pass) {
             mSpecView.setText("Average: " + averageLatency + " ms <= "
                     + mMaxRequiredLatency + " ms -- PASS");
+        } else if (!isReportLogOkToPass()) {
+            mSpecView.setText(getResources().getString(R.string.audio_general_reportlogtest));
         } else {
             mSpecView.setText("Average: " + averageLatency + " ms > "
                     + mMaxRequiredLatency + " ms -- FAIL");
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 126d15f..e93d2b3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
@@ -158,11 +158,9 @@
         boolean usbOK = mClaimsUSBHostMode && mClaimsUSBPeripheralMode;
         boolean hdmiOK = !mClaimsHDMI || isHDMIValid();
 
-        boolean hasPassed = !mClaimsProAudio ||
-                (mClaimsLowLatencyAudio &&
-                mClaimsMIDI &&
-                usbOK &&
-                hdmiOK);
+        boolean hasPassed = isReportLogOkToPass()
+                && !mClaimsProAudio
+                || (mClaimsLowLatencyAudio && mClaimsMIDI && usbOK && hdmiOK);
 
         getPassButton().setEnabled(hasPassed);
         return hasPassed;
@@ -172,7 +170,9 @@
         boolean hasPassed = calculatePass();
 
         Resources strings = getResources();
-        if (hasPassed) {
+        if (!isReportLogOkToPass()) {
+            mTestStatusLbl.setText(getResources().getString(R.string.audio_general_reportlogtest));
+        } else  if (hasPassed) {
             mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_pass));
         } else if (!mClaimsMIDI) {
             mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_midinotreported));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java
index bc23048..6270a5a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java
@@ -20,23 +20,17 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-
 import android.media.AudioDeviceCallback;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
-
 import android.os.Bundle;
 import android.os.Handler;
-
 import android.util.Log;
-
 import android.widget.TextView;
 
 import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.ReportLog;
 import com.android.compatibility.common.util.ResultType;
 import com.android.compatibility.common.util.ResultUnit;
-
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;  // needed to access resource in CTSVerifier project namespace.
 
@@ -173,9 +167,10 @@
     // Test Status
     //
     private boolean calculatePass() {
-        return mUsbHeadsetInReceived && mUsbHeadsetOutReceived &&
-                mUsbDeviceInReceived && mUsbDeviceOutReceived &&
-                mPlugIntentReceived;
+        return isReportLogOkToPass()
+                && mUsbHeadsetInReceived && mUsbHeadsetOutReceived
+                && mUsbDeviceInReceived && mUsbDeviceOutReceived
+                && mPlugIntentReceived;
     }
 
     //
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
index d83464b..692538a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
@@ -21,9 +21,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
-import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraManager;
-import android.os.Build;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
 import android.os.Bundle;
 import android.text.method.ScrollingMovementMethod;
 import android.util.Log;
@@ -31,6 +30,11 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -39,17 +43,11 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
 import com.android.compatibility.common.util.ResultType;
 import com.android.compatibility.common.util.ResultUnit;
 import com.android.cts.verifier.ArrayTestListAdapter;
@@ -59,6 +57,7 @@
 
 import org.json.JSONArray;
 import org.json.JSONObject;
+import org.junit.rules.TestName;
 
 /**
  * Test for Camera features that require that the camera be aimed at a specific test scene.
@@ -81,12 +80,6 @@
             Arrays.asList(new String[] {RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED}));
     private static final int MAX_SUMMARY_LEN = 200;
 
-    private static final int MPC12_CAMERA_LAUNCH_THRESHOLD = 600; // ms
-    private static final int MPC12_JPEG_CAPTURE_THRESHOLD = 1000; // ms
-
-    private static final String MPC_TESTS_REPORT_LOG_NAME = "MediaPerformanceClassLogs";
-    private static final String MPC_TESTS_REPORT_LOG_SECTION = "CameraIts";
-
     private static final Pattern MPC12_CAMERA_LAUNCH_PATTERN =
             Pattern.compile("camera_launch_time_ms:(\\d+(\\.\\d+)?)");
     private static final Pattern MPC12_JPEG_CAPTURE_PATTERN =
@@ -95,8 +88,12 @@
     private final ResultReceiver mResultsReceiver = new ResultReceiver();
     private boolean mReceiverRegistered = false;
 
+    public final TestName mTestName = new TestName();
+
     // Initialized in onCreate
     List<String> mToBeTestedCameraIds = null;
+    String mPrimaryRearCameraId = null;
+    String mPrimaryFrontCameraId = null;
 
     // Scenes
     private static final ArrayList<String> mSceneIds = new ArrayList<String> () {{
@@ -132,8 +129,15 @@
     private final HashMap<ResultKey, String> mSummaryMap = new HashMap<>();
     // All primary cameras for which MPC level test has run
     private Set<ResultKey> mExecutedMpcTests = null;
-    // Map primary camera id to MPC level
-    private final HashMap<String, Integer> mMpcLevelMap = new HashMap<>();
+    private static final String MPC_LAUNCH_REQ_NUM = "2.2.7.2/7.5/H-1-6";
+    private static final String MPC_JPEG_CAPTURE_REQ_NUM = "2.2.7.2/7.5/H-1-5";
+    // Performance class evaluator used for writing test result
+    PerformanceClassEvaluator mPce = new PerformanceClassEvaluator(mTestName);
+    PerformanceClassEvaluator.CameraLatencyRequirement mJpegLatencyReq =
+            mPce.addR7_5__H_1_5();
+    PerformanceClassEvaluator.CameraLatencyRequirement mLaunchLatencyReq =
+            mPce.addR7_5__H_1_6();
+
 
     final class ResultKey {
         public final String cameraId;
@@ -266,10 +270,7 @@
                         JSONArray metrics = sceneResult.getJSONArray("mpc_metrics");
                         for (int i = 0; i < metrics.length(); i++) {
                             String mpcResult = metrics.getString(i);
-                            if (!matchMpcResult(cameraId, mpcResult, MPC12_CAMERA_LAUNCH_PATTERN,
-                                    "2.2.7.2/7.5/H-1-6", MPC12_CAMERA_LAUNCH_THRESHOLD) &&
-                                    !matchMpcResult(cameraId, mpcResult, MPC12_JPEG_CAPTURE_PATTERN,
-                                    "2.2.7.2/7.5/H-1-5", MPC12_JPEG_CAPTURE_THRESHOLD)) {
+                            if (!matchMpcResult(cameraId, mpcResult)) {
                                 Log.e(TAG, "Error parsing MPC result string:" + mpcResult);
                                 return;
                             }
@@ -294,17 +295,6 @@
                             summary.toString(), 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
                 }
 
-                //  Save MPC info once both front primary and rear primary data are collected.
-                if (mExecutedMpcTests.size() == 4) {
-                    ItsTestActivity.this.getReportLog().addValue(
-                            "Version", "0.0.1", ResultType.NEUTRAL, ResultUnit.NONE);
-                    for (Map.Entry<String, Integer> entry : mMpcLevelMap.entrySet()) {
-                        ItsTestActivity.this.getReportLog().addValue(entry.getKey(),
-                                entry.getValue(), ResultType.NEUTRAL, ResultUnit.NONE);
-                    }
-                    ItsTestActivity.this.getReportLog().submit();
-                }
-
                 // Display current progress
                 StringBuilder progress = new StringBuilder();
                 for (ResultKey k : mAllScenes) {
@@ -367,28 +357,44 @@
             }
         }
 
-        private boolean matchMpcResult(String cameraId, String mpcResult, Pattern pattern,
-                String reqNum, float threshold) {
-            Matcher matcher = pattern.matcher(mpcResult);
-            boolean match = matcher.matches();
-            final int LATEST_MPC_LEVEL = Build.VERSION_CODES.TIRAMISU;
+        private boolean matchMpcResult(String cameraId, String mpcResult) {
+            Matcher launchMatcher = MPC12_CAMERA_LAUNCH_PATTERN.matcher(mpcResult);
+            boolean launchMatches = launchMatcher.matches();
 
-            if (match) {
-                // Store test result
-                ItsTestActivity.this.getReportLog().addValue("Cam" + cameraId,
-                        mpcResult, ResultType.NEUTRAL, ResultUnit.NONE);
+            Matcher jpegMatcher = MPC12_JPEG_CAPTURE_PATTERN.matcher(mpcResult);
+            boolean jpegMatches = jpegMatcher.matches();
 
-                float latency = Float.parseFloat(matcher.group(1));
-                int mpcLevel = latency < threshold ? LATEST_MPC_LEVEL : 0;
-                mExecutedMpcTests.add(new ResultKey(cameraId, reqNum));
-
-                if (mMpcLevelMap.containsKey(reqNum)) {
-                    mpcLevel = Math.min(mpcLevel, mMpcLevelMap.get(reqNum));
-                }
-                mMpcLevelMap.put(reqNum, mpcLevel);
+            if (!launchMatches && !jpegMatches) {
+                return false;
+            }
+            if (!cameraId.equals(mPrimaryRearCameraId) &&
+                    !cameraId.equals(mPrimaryFrontCameraId)) {
+                return false;
             }
 
-            return match;
+            if (launchMatches) {
+                float latency = Float.parseFloat(launchMatcher.group(1));
+                if (cameraId.equals(mPrimaryRearCameraId)) {
+                    mLaunchLatencyReq.setRearCameraLatency(latency);
+                } else {
+                    mLaunchLatencyReq.setFrontCameraLatency(latency);
+                }
+                mExecutedMpcTests.add(new ResultKey(cameraId, MPC_LAUNCH_REQ_NUM));
+            } else {
+                float latency = Float.parseFloat(jpegMatcher.group(1));
+                if (cameraId.equals(mPrimaryRearCameraId)) {
+                    mJpegLatencyReq.setRearCameraLatency(latency);
+                } else {
+                    mJpegLatencyReq.setFrontCameraLatency(latency);
+                }
+                mExecutedMpcTests.add(new ResultKey(cameraId, MPC_JPEG_CAPTURE_REQ_NUM));
+            }
+
+            // Save MPC info once both front primary and rear primary data are collected.
+            if (mExecutedMpcTests.size() == 4) {
+                mPce.submit();
+            }
+            return true;
         }
     }
 
@@ -397,8 +403,11 @@
         // Hide the test if all camera devices are legacy
         CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
         try {
-            ItsUtils.ItsCameraIdList cameraIdList = ItsUtils.getItsCompatibleCameraIds(manager);
+            ItsUtils.ItsCameraIdList cameraIdList =
+                    ItsUtils.getItsCompatibleCameraIds(manager);
             mToBeTestedCameraIds = cameraIdList.mCameraIdCombos;
+            mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId;
+            mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId;
         } catch (ItsException e) {
             Toast.makeText(ItsTestActivity.this,
                     "Received error from camera service while checking device capabilities: "
@@ -499,14 +508,4 @@
         setInfoResources(R.string.camera_its_test, R.string.camera_its_test_info, -1);
         setPassFailButtonClickListeners();
     }
-
-    @Override
-    public String getReportFileName() {
-        return MPC_TESTS_REPORT_LOG_NAME;
-    }
-
-    @Override
-    public String getReportSectionName() {
-        return MPC_TESTS_REPORT_LOG_SECTION;
-    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
index c648e8e..734b4a2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
@@ -16,28 +16,24 @@
 
 package com.android.cts.verifier.camera.its;
 
-import android.content.Context;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
 import android.media.Image.Plane;
-import android.net.Uri;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.util.Log;
 import android.util.Size;
 
 import com.android.ex.camera2.blocking.BlockingCameraManager;
-import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
 
 import org.json.JSONArray;
@@ -322,6 +318,9 @@
         // Camera Id combos (ids from CameraIdList, and hidden physical camera Ids
         // in the form of [logical camera id]:[hidden physical camera id]
         public List<String> mCameraIdCombos;
+        // Primary rear and front camera Ids (as defined in MPC)
+        public String mPrimaryRearCameraId;
+        public String mPrimaryFrontCameraId;
     }
 
     public static ItsCameraIdList getItsCompatibleCameraIds(CameraManager manager)
@@ -345,6 +344,18 @@
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
                 final int LOGICAL_MULTI_CAMERA =
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA;
+
+                final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
+                if (facing != null) {
+                    if (facing == CameraMetadata.LENS_FACING_BACK
+                            && outList.mPrimaryRearCameraId == null) {
+                        outList.mPrimaryRearCameraId = id;
+                    } else if (facing == CameraMetadata.LENS_FACING_FRONT
+                            && outList.mPrimaryFrontCameraId == null) {
+                        outList.mPrimaryFrontCameraId = id;
+                    }
+                }
+
                 for (int capability : actualCapabilities) {
                     if (capability == BACKWARD_COMPAT) {
                         haveBC = true;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java
index 3587e6f..63b8904 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java
@@ -18,49 +18,22 @@
 
 
 import android.content.ClipData;
+import android.content.ClipDescription;
 import android.content.ClipboardManager;
-import android.graphics.Color;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.view.View;
 import android.widget.Button;
 
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
-import java.util.concurrent.ThreadLocalRandom;
-
 
 /**
- * A CTS Verifier test case for validating the user-visible clipboard preview.
- *
- * This test assumes bluetooth is turned on and the device is already paired with a second device.
- * Note: the second device need not be an Android device; it could be a laptop or desktop.
+ * A CTS Verifier test case for validating the user-visible clipboard confirmation.
  */
 public class ClipboardPreviewTestActivity extends PassFailButtons.Activity {
 
-    /**
-     * The content of the test file being transferred.
-     */
-    private static final String TEST_STRING = "Sample Test String";
-    /**
-     * The name of the test file being transferred.
-     */
-    private final int[] mSecretCode = new int[4];
-    private final int[] mSecretGuess = new int[4];
-    private final int[] mButtons = {
-            R.id.clipboard_preview_test_b0,
-            R.id.clipboard_preview_test_b1,
-            R.id.clipboard_preview_test_b2,
-            R.id.clipboard_preview_test_b3,
-            R.id.clipboard_preview_test_b4,
-            R.id.clipboard_preview_test_b5,
-            R.id.clipboard_preview_test_b6,
-            R.id.clipboard_preview_test_b7,
-            R.id.clipboard_preview_test_b8,
-            R.id.clipboard_preview_test_b9
-    };
-    private int mGuessIndex = 0;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -74,87 +47,29 @@
         copyButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                generateAndCopySecret();
+                setClipboardData();
             }
         });
-        disableKeypad();
+        disablePassFail();
     }
 
-    private void generateAndCopySecret() {
-        String s = "";
-        resetState();
-        for (int i = 0; i < mSecretCode.length; ++i) {
-            mSecretCode[i] = ThreadLocalRandom.current().nextInt(0, 10);
-            s += mSecretCode[i];
-        }
+    private void setClipboardData() {
         ClipboardManager cm = this.getSystemService(ClipboardManager.class);
-        cm.setPrimaryClip(ClipData.newPlainText("Secret", s));
-        enableKeypad();
+
+        ClipData cd = ClipData.newPlainText("",
+                getString(R.string.clipboard_preview_test_secret));
+        PersistableBundle pb = new PersistableBundle(1);
+        pb.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true);
+        cd.getDescription().setExtras(pb);
+        cm.setPrimaryClip(cd);
+        enablePassFail();
     }
 
-    private void enableKeypad() {
-        for (int i = 0; i < mButtons.length; ++i) {
-            Button numButton = findViewById(mButtons[i]);
-            numButton.setBackgroundColor(Color.GREEN);
-            int finalI = i;
-            numButton.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    buttonClicked(finalI);
-                }
-            });
-        }
-    }
-
-    private void disableKeypad() {
-        for (int i = 0; i < mButtons.length; ++i) {
-            Button numButton = findViewById(mButtons[i]);
-            numButton.setOnClickListener(null);
-            numButton.setBackgroundColor(Color.LTGRAY);
-        }
-    }
-
-    private void resetState() {
-        for (int i = 0; i < mSecretGuess.length; ++i) {
-            mSecretGuess[i] = -1;
-        }
-        mGuessIndex = 0;
-        View v = findViewById(R.id.clipboard_preview_test_pass_fail);
+    private void disablePassFail() {
         findViewById(R.id.clipboard_preview_test_pass_fail).setVisibility(View.INVISIBLE);
-        findViewById(R.id.fail_button).setVisibility(View.VISIBLE);
-        findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
     }
 
-    private void buttonClicked(int i) {
-        if (mGuessIndex < mSecretGuess.length) {
-            mSecretGuess[mGuessIndex] = i;
-            ++mGuessIndex;
-        }
-        checkSolution();
-    }
-
-    private void checkSolution() {
-        boolean testPassed = true;
-        if (mGuessIndex == mSecretGuess.length) {
-            for (int i = 0; i < mSecretGuess.length && i < mSecretCode.length; ++i) {
-                if (mSecretGuess[i] != mSecretCode[i]) {
-                    testPassed = false;
-                }
-            }
-            markPassed(testPassed);
-            disableKeypad();
-        }
-    }
-
-    private void markPassed(boolean passed) {
+    private void enablePassFail() {
         findViewById(R.id.clipboard_preview_test_pass_fail).setVisibility(View.VISIBLE);
-        if (passed) {
-            findViewById(R.id.fail_button).setVisibility(View.INVISIBLE);
-        } else {
-            findViewById(R.id.pass_button).setVisibility(View.INVISIBLE);
-        }
-
     }
-
-
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRssiPrecisionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRssiPrecisionActivity.java
deleted file mode 100644
index 1548910..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRssiPrecisionActivity.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.presence;
-
-import android.app.AlertDialog;
-import android.bluetooth.BluetoothAdapter;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/** Tests the precision of the device's RSSI measurement wtfdelet */
-public class BleRssiPrecisionActivity extends PassFailButtons.Activity {
-    private static final String TAG = BleRssiPrecisionActivity.class.getName();
-
-    // Report log schema
-    private static final String KEY_RSSI_RANGE_DBM = "rssi_range_dbm";
-    private static final String KEY_REFERENCE_DEVICE = "reference_device";
-
-    // Thresholds
-    private static final int MAX_RSSI_RANGE_DBM = 18;
-
-    private EditText reportRssiRangeEditText;
-    private EditText reportReferenceDeviceEditText;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.ble_rssi_precision);
-        setPassFailButtonClickListeners();
-        getPassButton().setEnabled(false);
-
-        reportRssiRangeEditText = findViewById(R.id.report_rssi_range);
-        reportReferenceDeviceEditText = findViewById(R.id.report_reference_device);
-
-        DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
-                PackageManager.FEATURE_BLUETOOTH_LE);
-
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
-        if (!adapter.isEnabled()) {
-            new AlertDialog.Builder(this)
-                    .setTitle(R.string.ble_bluetooth_disable_title)
-                    .setMessage(R.string.ble_bluetooth_disable_message)
-                    .setOnCancelListener(dialog -> finish())
-                    .create().show();
-        }
-
-        reportRssiRangeEditText.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        reportReferenceDeviceEditText.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-    }
-
-    private void checkTestInputs() {
-        getPassButton().setEnabled(checkDistanceRangeInput() && checkReferenceDeviceInput());
-    }
-
-    private boolean checkDistanceRangeInput() {
-        String rssiRangeInput = reportRssiRangeEditText.getText().toString();
-
-        if (!rssiRangeInput.isEmpty()) {
-            int rssiRange = Integer.parseInt(rssiRangeInput);
-            // RSSI range must be inputted and within acceptable range before test can be passed
-            return rssiRange <= MAX_RSSI_RANGE_DBM;
-        }
-        return false;
-    }
-
-    private boolean checkReferenceDeviceInput() {
-        // Reference device must be inputted before test can be passed
-        return !reportReferenceDeviceEditText.getText().toString().isEmpty();
-    }
-
-    @Override
-    public void recordTestResults() {
-        String rssiRange = reportRssiRangeEditText.getText().toString();
-        String referenceDevice = reportReferenceDeviceEditText.getText().toString();
-
-        if (!rssiRange.isEmpty()) {
-            Log.i(TAG, "BLE RSSI Range (dBm): " + rssiRange);
-            getReportLog().addValue(KEY_RSSI_RANGE_DBM, Integer.parseInt(rssiRange),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!referenceDevice.isEmpty()) {
-            Log.i(TAG, "BLE Reference Device: " + referenceDevice);
-            getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice,
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-        getReportLog().submit();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRxTxCalibrationActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRxTxCalibrationActivity.java
deleted file mode 100644
index 2de7edf..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRxTxCalibrationActivity.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.presence;
-
-import android.app.AlertDialog;
-import android.bluetooth.BluetoothAdapter;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/**
- * Tests that the device's Rx/Tx calibration results in a median range (cm) within the specified
- * bounds
- */
-public class BleRxTxCalibrationActivity extends PassFailButtons.Activity {
-    private static final String TAG = BleRxTxCalibrationActivity.class.getName();
-
-    // Report log schema
-    private static final String KEY_CHANNEL_RSSI_RANGE = "channel_rssi_range";
-    private static final String KEY_CORE_RSSI_RANGE = "core_rssi_range";
-    private static final String KEY_REFERENCE_DEVICE = "reference_device";
-
-    // Thresholds
-    private static final int MAX_RSSI_RANGE = 6;
-
-    private EditText reportChannelsRssiRangeEditText;
-    private EditText reportCoresRssiRangeEditText;
-    private EditText reportReferenceDeviceEditText;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.ble_rx_tx_calibration);
-        setPassFailButtonClickListeners();
-        getPassButton().setEnabled(false);
-
-        reportChannelsRssiRangeEditText = findViewById(R.id.report_channels_rssi_range);
-        reportCoresRssiRangeEditText = findViewById(R.id.report_cores_rssi_range);
-        reportReferenceDeviceEditText = findViewById(R.id.report_reference_device);
-
-        DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
-                PackageManager.FEATURE_BLUETOOTH_LE);
-
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
-        if (!adapter.isEnabled()) {
-            new AlertDialog.Builder(this)
-                    .setTitle(R.string.ble_bluetooth_disable_title)
-                    .setMessage(R.string.ble_bluetooth_disable_message)
-                    .setOnCancelListener(dialog -> finish())
-                    .create().show();
-        }
-
-        reportChannelsRssiRangeEditText.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        reportCoresRssiRangeEditText.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        reportReferenceDeviceEditText.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-    }
-
-    private void checkTestInputs() {
-        getPassButton().setEnabled(
-                checkChannelRssiInput() && checkCoreRssiInput() && checkReferenceDeviceInput());
-    }
-
-    private boolean checkChannelRssiInput() {
-        String channelsRssiRangeInput = reportChannelsRssiRangeEditText.getText().toString();
-        if (!channelsRssiRangeInput.isEmpty()) {
-            int channelsRssiRange = Integer.parseInt(channelsRssiRangeInput);
-            // RSSI range must be inputted and within acceptable range before test can be passed
-            return channelsRssiRange <= MAX_RSSI_RANGE;
-        }
-        return false;
-    }
-
-    private boolean checkCoreRssiInput() {
-        String coresRssiRangeInput = reportCoresRssiRangeEditText.getText().toString();
-        if (!coresRssiRangeInput.isEmpty()) {
-            int coresRssiRange = Integer.parseInt(coresRssiRangeInput);
-            // RSSI range must be inputted and within acceptable range before test can be passed
-            return coresRssiRange <= MAX_RSSI_RANGE;
-        }
-        // This field is optional, so return true even if the user has not inputted anything
-        return true;
-    }
-
-    private boolean checkReferenceDeviceInput() {
-        // Reference device must be inputted before test can be passed
-        return !reportReferenceDeviceEditText.getText().toString().isEmpty();
-    }
-
-    @Override
-    public void recordTestResults() {
-        String channelRssiRange = reportChannelsRssiRangeEditText.getText().toString();
-        String coreRssiRange = reportCoresRssiRangeEditText.getText().toString();
-        String referenceDevice = reportReferenceDeviceEditText.getText().toString();
-
-        if (!channelRssiRange.isEmpty()) {
-            Log.i(TAG, "BLE RSSI Range Across Channels (dBm): " + channelRssiRange);
-            getReportLog().addValue(KEY_CHANNEL_RSSI_RANGE, Integer.parseInt(channelRssiRange),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!coreRssiRange.isEmpty()) {
-            Log.i(TAG, "BLE RSSI Range Across Cores (dBm): " + coreRssiRange);
-            getReportLog().addValue(KEY_CORE_RSSI_RANGE, Integer.parseInt(coreRssiRange),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!referenceDevice.isEmpty()) {
-            Log.i(TAG, "BLE Reference Device: " + referenceDevice);
-            getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice,
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        getReportLog().submit();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/DeviceFeatureChecker.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/DeviceFeatureChecker.java
deleted file mode 100644
index 256fe3a..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/DeviceFeatureChecker.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.verifier.presence;
-import android.app.Activity;
-import android.content.Context;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-/**
- * Checks if a device supports a hardware feature needed for a test, and passes the test
- * automatically otherwise.
- */
-public class DeviceFeatureChecker {
-
-    /** Checks if a feature is supported.
-     *
-     * @param feature must be a string defined in PackageManager
-     */
-    public static void checkFeatureSupported(Context context, View passButton, String feature) {
-        if (!context.getPackageManager().hasSystemFeature(feature)) {
-            String message = String.format("Device does not support %s, automatically passing test",
-                    feature);
-            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
-            Log.e(context.getClass().getName(), message);
-            passButton.performClick();
-            Activity activity = (Activity) (context);
-            activity.finish();
-        }
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/InputTextHandler.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/InputTextHandler.java
deleted file mode 100644
index 2de68a5..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/InputTextHandler.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package com.android.cts.verifier.presence;
-
-import android.text.Editable;
-import android.text.TextWatcher;
-
-/**
- * Handles editable text inputted into test activities.
- */
-public class InputTextHandler {
-
-    /** Callback that is executed when text is changed. Takes modified text as input. */
-    public interface OnTextChanged {
-        void run(Editable s);
-    }
-
-    /**
-     * Generic text changed handler that will execute the provided callback when text is modified.
-     *
-     * @param callback called when text is changed, and passed the modified text
-     */
-    public static TextWatcher getOnTextChangedHandler(OnTextChanged callback) {
-        return new TextWatcher() {
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {}
-
-            @Override
-            public void afterTextChanged(Editable s) {
-                callback.run(s);
-            }
-        };
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/NanPrecisionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/NanPrecisionTestActivity.java
deleted file mode 100644
index 458d192..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/NanPrecisionTestActivity.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.presence;
-
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import com.google.common.collect.ImmutableMap;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Activity for testing that NAN measurements are within the acceptable ranges
- */
-public class NanPrecisionTestActivity extends PassFailButtons.Activity {
-    private static final String TAG = NanPrecisionTestActivity.class.getName();
-
-    // Report log schema
-    private static final String KEY_BANDWIDTH = "nan_bandwidth";
-    private static final String KEY_MEASUREMENT_RANGE_10CM_AT_68P = "measurement_range_10cm_68p";
-    private static final String KEY_MEASUREMENT_RANGE_1M_AT_68P = "measurement_range_1m_68p";
-    private static final String KEY_MEASUREMENT_RANGE_3M_AT_68p = "measurement_range_3m_68p";
-    private static final String KEY_MEASUREMENT_RANGE_5M_AT_68p = "measurement_range_5m_68p";
-    private static final String KEY_MEASUREMENT_RANGE_10CM_AT_90P = "measurement_range_10cm_90p";
-    private static final String KEY_MEASUREMENT_RANGE_1M_AT_90P = "measurement_range_1m_90p";
-    private static final String KEY_MEASUREMENT_RANGE_3M_AT_90p = "measurement_range_3m_90p";
-    private static final String KEY_MEASUREMENT_RANGE_5M_AT_90p = "measurement_range_5m_90p";
-    private static final String KEY_REFERENCE_DEVICE = "reference_device";
-
-    // Thresholds
-    private static final int MAX_DISTANCE_RANGE_METERS_160MHZ = 2;
-    private static final int MAX_DISTANCE_RANGE_METERS_80MHZ = 4;
-    private static final int MAX_DISTANCE_RANGE_METERS_40MHZ = 8;
-    private static final int MAX_DISTANCE_RANGE_METERS_20MHZ = 16;
-
-    // Maps NAN bandwidths to acceptable range thresholds
-    private static final ImmutableMap<Integer, Integer> BANDWIDTH_TO_THRESHOLD_MAP =
-            ImmutableMap.of(160, MAX_DISTANCE_RANGE_METERS_160MHZ, 80,
-                    MAX_DISTANCE_RANGE_METERS_80MHZ, 40, MAX_DISTANCE_RANGE_METERS_40MHZ, 20,
-                    MAX_DISTANCE_RANGE_METERS_20MHZ);
-
-    private EditText mBandwidthMhz;
-    private EditText mMeasurementRange10cmGt68p;
-    private EditText mMeasurementRange1mGt68p;
-    private EditText mMeasurementRange3mGt68p;
-    private EditText mMeasurementRange5mGt68p;
-    private EditText mMeasurementRange10cmGt90p;
-    private EditText mMeasurementRange1mGt90p;
-    private EditText mMeasurementRange3mGt90p;
-    private EditText mMeasurementRange5mGt90p;
-    private EditText mReferenceDeviceInput;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.nan_precision);
-        setPassFailButtonClickListeners();
-        getPassButton().setEnabled(false);
-
-        mBandwidthMhz = (EditText) findViewById(R.id.nan_bandwidth);
-        mMeasurementRange10cmGt68p = (EditText) findViewById(R.id.distance_range_10cm_gt_68p);
-        mMeasurementRange1mGt68p = (EditText) findViewById(R.id.distance_range_1m_gt_68p);
-        mMeasurementRange3mGt68p = (EditText) findViewById(R.id.distance_range_3m_gt_68p);
-        mMeasurementRange5mGt68p = (EditText) findViewById(R.id.distance_range_5m_gt_68p);
-        mMeasurementRange10cmGt90p = (EditText) findViewById(R.id.distance_range_10cm_gt_90p);
-        mMeasurementRange1mGt90p = (EditText) findViewById(R.id.distance_range_1m_gt_90p);
-        mMeasurementRange3mGt90p = (EditText) findViewById(R.id.distance_range_3m_gt_90p);
-        mMeasurementRange5mGt90p = (EditText) findViewById(R.id.distance_range_5m_gt_90p);
-        mReferenceDeviceInput = (EditText) findViewById(R.id.reference_device);
-
-        DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
-                PackageManager.FEATURE_WIFI_AWARE);
-
-        mBandwidthMhz.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mMeasurementRange10cmGt68p.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mMeasurementRange1mGt68p.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mMeasurementRange3mGt68p.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mMeasurementRange5mGt68p.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mMeasurementRange10cmGt90p.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mMeasurementRange1mGt90p.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mMeasurementRange3mGt90p.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mMeasurementRange5mGt90p.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mReferenceDeviceInput.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-    }
-
-    private void checkTestInputs() {
-        getPassButton().setEnabled(checkMeasurementRange68thPercentileInput()
-                && checkMeasurementRange90thPercentileInput()
-                && checkReferenceDeviceInput());
-    }
-
-    private boolean checkMeasurementRange68thPercentileInput() {
-        return checkRequiredMeasurementRangeInput(mMeasurementRange10cmGt68p,
-                mMeasurementRange1mGt68p, mMeasurementRange3mGt68p, mMeasurementRange5mGt68p);
-    }
-
-    private boolean checkMeasurementRange90thPercentileInput() {
-        String measurementRangeInput10cmGt90p = mMeasurementRange10cmGt90p.getText().toString();
-        String measurementRangeInput1mGt90p = mMeasurementRange1mGt90p.getText().toString();
-        String measurementRangeInput3mGt90p = mMeasurementRange3mGt90p.getText().toString();
-        String measurementRangeInput5mGt90p = mMeasurementRange5mGt90p.getText().toString();
-        List<String> optionalMeasurementRangeList = Arrays.asList(measurementRangeInput10cmGt90p,
-                measurementRangeInput1mGt90p,
-                measurementRangeInput3mGt90p, measurementRangeInput5mGt90p);
-
-        boolean inputted = false;
-        for (String input : optionalMeasurementRangeList) {
-            if (!input.isEmpty()) {
-                inputted = true;
-                break;
-            }
-        }
-        // If one of the ranges is inputted for one of the distances, then it becomes required
-        // that the ranges are inputted for all the distances and for tests to pass, must be
-        // acceptable values
-        return !inputted || checkRequiredMeasurementRangeInput(mMeasurementRange10cmGt90p,
-                mMeasurementRange1mGt90p, mMeasurementRange3mGt90p, mMeasurementRange5mGt90p);
-    }
-
-    private boolean checkRequiredMeasurementRangeInput(EditText rangeInput10cm,
-            EditText rangeInput1m, EditText rangeInput3m, EditText rangeInput5m) {
-        String bandwidthInputMhz = mBandwidthMhz.getText().toString();
-        String measurementRangeInput10cmGt = rangeInput10cm.getText().toString();
-        String measurementRangeInput1mGt = rangeInput1m.getText().toString();
-        String measurementRangeInput3mGt = rangeInput3m.getText().toString();
-        String measurementRangeInput5mGt = rangeInput5m.getText().toString();
-        List<String> requiredMeasurementRangeList = Arrays.asList(measurementRangeInput10cmGt,
-                measurementRangeInput1mGt,
-                measurementRangeInput3mGt, measurementRangeInput5mGt);
-
-        for (String input : requiredMeasurementRangeList) {
-            if (bandwidthInputMhz.isEmpty() || input.isEmpty()) {
-                // Distance range must be inputted for all fields so fail early otherwise
-                return false;
-            }
-            if (!BANDWIDTH_TO_THRESHOLD_MAP.containsKey(Integer.parseInt(bandwidthInputMhz))) {
-                // bandwidth must be one of the expected thresholds
-                return false;
-            }
-            double distanceRange = Double.parseDouble(input);
-            int bandwidth = Integer.parseInt(bandwidthInputMhz);
-            if (distanceRange > BANDWIDTH_TO_THRESHOLD_MAP.get(bandwidth)) {
-                // All inputs must be in acceptable range so fail early otherwise
-                return false;
-            }
-        }
-        return true;
-    }
-
-    private boolean checkReferenceDeviceInput() {
-        // Reference device used must be inputted before test can be passed.
-        return !mReferenceDeviceInput.getText().toString().isEmpty();
-    }
-
-    @Override
-    public void recordTestResults() {
-        String nanBandwidthMhz = mBandwidthMhz.getText().toString();
-        String measurementRange10cmGt68p = mMeasurementRange10cmGt68p.getText().toString();
-        String measurementRange1mGt68p = mMeasurementRange1mGt68p.getText().toString();
-        String measurementRange3mGt68p = mMeasurementRange3mGt68p.getText().toString();
-        String measurementRange5mGt68p = mMeasurementRange5mGt68p.getText().toString();
-        String measurementRange10cmGt90p = mMeasurementRange10cmGt90p.getText().toString();
-        String measurementRange1mGt90p = mMeasurementRange1mGt90p.getText().toString();
-        String measurementRange3mGt90p = mMeasurementRange3mGt90p.getText().toString();
-        String measurementRange5mGt90p = mMeasurementRange5mGt90p.getText().toString();
-        String referenceDevice = mReferenceDeviceInput.getText().toString();
-
-        if (!nanBandwidthMhz.isEmpty()) {
-            Log.i(TAG, "NAN Bandwidth at which data was collected: " + nanBandwidthMhz);
-            getReportLog().addValue(KEY_BANDWIDTH,
-                    Integer.parseInt(nanBandwidthMhz),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!measurementRange10cmGt68p.isEmpty()) {
-            Log.i(TAG, "NAN Measurement Range at 10cm: " + measurementRange10cmGt68p);
-            getReportLog().addValue(KEY_MEASUREMENT_RANGE_10CM_AT_68P,
-                    Double.parseDouble(measurementRange10cmGt68p),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!measurementRange1mGt68p.isEmpty()) {
-            Log.i(TAG, "NAN Measurement Range at 1m: " + measurementRange1mGt68p);
-            getReportLog().addValue(KEY_MEASUREMENT_RANGE_1M_AT_68P,
-                    Double.parseDouble(measurementRange1mGt68p),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!measurementRange3mGt68p.isEmpty()) {
-            Log.i(TAG, "NAN Measurement Range at 3m: " + measurementRange3mGt68p);
-            getReportLog().addValue(KEY_MEASUREMENT_RANGE_3M_AT_68p,
-                    Double.parseDouble(measurementRange3mGt68p),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!measurementRange5mGt68p.isEmpty()) {
-            Log.i(TAG, "NAN Measurement Range at 5m: " + measurementRange5mGt68p);
-            getReportLog().addValue(KEY_MEASUREMENT_RANGE_5M_AT_68p,
-                    Double.parseDouble(measurementRange5mGt68p),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!measurementRange10cmGt90p.isEmpty()) {
-            Log.i(TAG, "NAN Measurement Range at 10cm: " + measurementRange10cmGt68p);
-            getReportLog().addValue(KEY_MEASUREMENT_RANGE_10CM_AT_90P,
-                    Double.parseDouble(measurementRange10cmGt90p),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!measurementRange1mGt90p.isEmpty()) {
-            Log.i(TAG, "NAN Measurement Range at 1m: " + measurementRange1mGt90p);
-            getReportLog().addValue(KEY_MEASUREMENT_RANGE_1M_AT_90P,
-                    Double.parseDouble(measurementRange1mGt90p),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!measurementRange3mGt90p.isEmpty()) {
-            Log.i(TAG, "NAN Measurement Range at 3m: " + measurementRange3mGt90p);
-            getReportLog().addValue(KEY_MEASUREMENT_RANGE_3M_AT_90p,
-                    Double.parseDouble(measurementRange3mGt90p),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!measurementRange5mGt90p.isEmpty()) {
-            Log.i(TAG, "NAN Measurement Range at 5m: " + measurementRange5mGt90p);
-            getReportLog().addValue(KEY_MEASUREMENT_RANGE_5M_AT_90p,
-                    Double.parseDouble(measurementRange5mGt90p),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-
-        if (!referenceDevice.isEmpty()) {
-            Log.i(TAG, "NAN Reference Device: " + referenceDevice);
-            getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice,
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-        getReportLog().submit();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/presence/OWNERS
deleted file mode 100644
index e874499..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 1106357
-asalo@google.com
-jbabs@google.com
-christinatao@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/PresenceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/PresenceTestActivity.java
deleted file mode 100644
index 6776693..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/PresenceTestActivity.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.presence;
-
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemProperties;
-
-import com.android.cts.verifier.ManifestTestListAdapter;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PresenceTestActivity extends PassFailButtons.TestListActivity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.pass_fail_list);
-        setPassFailButtonClickListeners();
-
-        List<String> disabledTest = new ArrayList<String>();
-        boolean isTv = getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
-        if (isTv) {
-            setInfoResources(R.string.presence_test, R.string.presence_test_tv_info, -1);
-            int firstSdk = SystemProperties.getInt("ro.product.first_api_level", 0);
-            if (firstSdk < Build.VERSION_CODES.TIRAMISU) {
-                disabledTest.add("com.android.cts.verifier.presence.UwbPrecisionActivity");
-                disabledTest.add("com.android.cts.verifier.presence.UwbShortRangeActivity");
-                disabledTest.add("com.android.cts.verifier.presence.BleRssiPrecisionActivity");
-                disabledTest.add("com.android.cts.verifier.presence.BleRxTxCalibrationActivity");
-                disabledTest.add("com.android.cts.verifier.presence.BleRxOffsetActivity");
-                disabledTest.add("com.android.cts.verifier.presence.BleTxOffsetActivity");
-                disabledTest.add("com.android.cts.verifier.presence.NanPrecisionTestActivity");
-            }
-        } else {
-            setInfoResources(R.string.presence_test, R.string.presence_test_info, -1);
-        }
-
-        setTestListAdapter(new ManifestTestListAdapter(this, PresenceTestActivity.class.getName(),
-                disabledTest.toArray(new String[disabledTest.size()])));
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbPrecisionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbPrecisionActivity.java
deleted file mode 100644
index 5d1ff86..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbPrecisionActivity.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.verifier.presence;
-
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/**
- * Activity for testing that UWB distance and angle of arrival measurements are within the right
- * range.
- */
-public class UwbPrecisionActivity extends PassFailButtons.Activity {
-    private static final String TAG = UwbPrecisionActivity.class.getName();
-    // Report log schema
-    private static final String KEY_DISTANCE_RANGE_CM = "distance_range_cm";
-    private static final String KEY_REFERENCE_DEVICE = "reference_device";
-    // Thresholds
-    private static final int MAX_DISTANCE_RANGE_CM = 30;
-
-    private EditText mDistanceRangeInput;
-    private EditText mReferenceDeviceInput;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.uwb_precision);
-        setPassFailButtonClickListeners();
-        getPassButton().setEnabled(false);
-
-        mDistanceRangeInput = (EditText) findViewById(R.id.distance_range_cm);
-        mReferenceDeviceInput = (EditText) findViewById(R.id.reference_device);
-
-        DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
-                PackageManager.FEATURE_UWB);
-
-        mDistanceRangeInput.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mReferenceDeviceInput.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-    }
-
-    private void checkTestInputs() {
-        getPassButton().setEnabled(
-                checkDistanceRangeInput() && checkReferenceDeviceInput());
-    }
-
-    private boolean checkDistanceRangeInput() {
-        String distanceRangeInput = mDistanceRangeInput.getText().toString();
-        if (!distanceRangeInput.isEmpty()) {
-            double distanceRange = Double.parseDouble(distanceRangeInput);
-            // Distance range must be inputted and within acceptable range before test can be
-            // passed.
-            return distanceRange <= MAX_DISTANCE_RANGE_CM;
-        }
-        return false;
-    }
-
-    private boolean checkReferenceDeviceInput() {
-        // Reference device must be inputted before test can be passed.
-        return !mReferenceDeviceInput.getText().toString().isEmpty();
-    }
-
-    @Override
-    public void recordTestResults() {
-        String distanceRange = mDistanceRangeInput.getText().toString();
-        String referenceDevice = mReferenceDeviceInput.getText().toString();
-        if (!distanceRange.isEmpty()) {
-            Log.i(TAG, "UWB Distance Range: " + distanceRange);
-            getReportLog().addValue(KEY_DISTANCE_RANGE_CM, Double.parseDouble(distanceRange),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-        if (!referenceDevice.isEmpty()) {
-            Log.i(TAG, "UWB Reference Device: " + referenceDevice);
-            getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice,
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-        getReportLog().submit();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbShortRangeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbShortRangeActivity.java
deleted file mode 100644
index 7f14800..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbShortRangeActivity.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.cts.verifier.presence;
-
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/**
- * Activity for testing that UWB distance measurements are within the acceptable median.
- */
-public class UwbShortRangeActivity extends PassFailButtons.Activity {
-    private static final String TAG = UwbShortRangeActivity.class.getName();
-    // Report log schema
-    private static final String KEY_DISTANCE_MEDIAN_CM = "distance_median_cm";
-    private static final String KEY_REFERENCE_DEVICE = "reference_device";
-    // Median Thresholds
-    private static final double MIN_MEDIAN = 0.75;
-    private static final double MAX_MEDIAN = 1.25;
-    private EditText mMedianInput;
-    private EditText mReferenceDeviceInput;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.uwb_short_range);
-        setPassFailButtonClickListeners();
-        getPassButton().setEnabled(false);
-
-        mMedianInput = (EditText) findViewById(R.id.distance_median_meters);
-        mReferenceDeviceInput = (EditText) findViewById(R.id.reference_device);
-
-        DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
-                PackageManager.FEATURE_UWB);
-
-        mMedianInput.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-        mReferenceDeviceInput.addTextChangedListener(
-                InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
-    }
-
-    private void checkTestInputs() {
-        getPassButton().setEnabled(checkMedianInput() && checkReferenceDeviceInput());
-    }
-
-    private boolean checkMedianInput() {
-        String medianInput = mMedianInput.getText().toString();
-        if (!medianInput.isEmpty()) {
-            double median = Double.parseDouble(medianInput);
-            return median >= MIN_MEDIAN && median <= MAX_MEDIAN;
-        }
-        return false;
-    }
-
-    private boolean checkReferenceDeviceInput() {
-        return !mReferenceDeviceInput.getText().toString().isEmpty();
-    }
-
-    @Override
-    public void recordTestResults() {
-        String medianInput = mMedianInput.getText().toString();
-        String referenceDeviceInput = mReferenceDeviceInput.getText().toString();
-        if (!medianInput.isEmpty()) {
-            Log.i(TAG, "UWB Distance Median: " + medianInput);
-            getReportLog().addValue(KEY_DISTANCE_MEDIAN_CM, Double.parseDouble(medianInput),
-                    ResultType.NEUTRAL, ResultUnit.NONE);
-        }
-        if (!referenceDeviceInput.isEmpty()) {
-            Log.i(TAG, "UWB Reference Device: " + referenceDeviceInput);
-            getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDeviceInput, ResultType.NEUTRAL,
-                    ResultUnit.NONE);
-        }
-        getReportLog().submit();
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java
index 055f26f..c0df10c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java
@@ -66,9 +66,9 @@
 
     private void playVideo() {
         try {
-            mPlayer.start();
             mPlayer.prepare();
-            mPlayer.startThread();
+            mPlayer.startCodec();
+            mPlayer.play();
             mHandler.postDelayed(this::pauseStep, 5000);
         } catch(Exception e) {
             Log.d(TAG, "Could not play video", e);
@@ -95,7 +95,7 @@
 
     private void resumeStep() {
         try {
-            mPlayer.start();
+            mPlayer.resume();
             mHandler.postDelayed(this::enablePassButton, 3000);
         } catch(Exception e) {
             Log.d(TAG, "Could not resume video", e);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/OWNERS
new file mode 100644
index 0000000..4744ab8
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/OWNERS
@@ -0,0 +1,3 @@
+# Buganizer component id: 687598
+blindahl@google.com
+narcisaam@google.com
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java
index 0163c62..c446143 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java
@@ -209,10 +209,10 @@
 
     private void playVideo() {
         try {
-            mPlayer.start();
             mPlayer.prepare();
+            mPlayer.startCodec();
             mPlayer.setLoopEnabled(true);
-            mPlayer.startThread();
+            mPlayer.play();
         } catch (Exception e) {
             Log.d(TAG, "Could not play the video.", e);
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java
index 641ab20..94ef536 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java
@@ -221,10 +221,10 @@
 
     private void playVideo() {
         try {
-            mPlayer.start();
             mPlayer.prepare();
+            mPlayer.startCodec();
             mPlayer.setLoopEnabled(true);
-            mPlayer.startThread();
+            mPlayer.play();
         } catch (Exception e) {
             Log.d(TAG, "Could not play video", e);
         }
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireMultiUserSupport.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireMultiUserSupport.java
new file mode 100644
index 0000000..03d21f4
--- /dev/null
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireMultiUserSupport.java
@@ -0,0 +1,33 @@
+/*
+ * 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.bedstead.harrier.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to indicate that a test requires multi-user support.
+ *
+ * <p>This can be enforced by using {@code DeviceState}.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+//@Experimental
+public @interface RequireMultiUserSupport {
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
index b28b815..7bd649d 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
@@ -67,6 +67,7 @@
 import com.android.bedstead.harrier.annotations.RequireFeature;
 import com.android.bedstead.harrier.annotations.RequireHeadlessSystemUserMode;
 import com.android.bedstead.harrier.annotations.RequireLowRamDevice;
+import com.android.bedstead.harrier.annotations.RequireMultiUserSupport;
 import com.android.bedstead.harrier.annotations.RequireNotHeadlessSystemUserMode;
 import com.android.bedstead.harrier.annotations.RequireNotLowRamDevice;
 import com.android.bedstead.harrier.annotations.RequirePackageInstalled;
@@ -786,6 +787,12 @@
                 ensureGlobalSettingSet(
                         ensureGlobalSettingSetAnnotation.key(),
                         ensureGlobalSettingSetAnnotation.value());
+                continue;
+            }
+
+            if (annotation instanceof RequireMultiUserSupport) {
+                requireMultiUserSupport();
+                continue;
             }
         }
 
@@ -2528,4 +2535,9 @@
         }
         TestApis.settings().global().putString(key, value);
     }
+
+    private void requireMultiUserSupport() {
+        assumeTrue("This test is only supported on multi user devices",
+                TestApis.users().supportsMultipleUsers());
+    }
 }
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
index 200feb7..0ce2d7f 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
@@ -40,6 +40,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.nene.annotations.Experimental;
 import com.android.bedstead.nene.exceptions.AdbException;
 import com.android.bedstead.nene.exceptions.AdbParseException;
 import com.android.bedstead.nene.exceptions.NeneException;
@@ -485,6 +486,11 @@
         return mCachedUsers.get(id);
     }
 
+    @Experimental
+    public boolean supportsMultipleUsers() {
+        return UserManager.supportsMultipleUsers();
+    }
+
     static Stream<UserInfo> users() {
         if (Permissions.sIgnorePermissions.get()) {
             return sUserManager.getUsers(
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java
index ff389a1..4e798df 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java
@@ -19,17 +19,15 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.app.Instrumentation;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.SystemClock;
 import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.util.ArrayMap;
+import android.view.WindowInsets;
+import android.view.WindowManager;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -37,33 +35,31 @@
 import org.junit.rules.ExternalResource;
 
 import java.io.IOException;
-import java.util.Map;
 
 /**
  * Test rule to enable gesture navigation on the device. Designed to be a {@link ClassRule}.
  */
 public class GestureNavRule extends ExternalResource {
-    private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
     private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
-    private static final int NAV_BAR_INTERACTION_MODE_GESTURAL = 2;
+    private static final int NAV_BAR_MODE_3BUTTON = 0;
+    private static final int NAV_BAR_MODE_2BUTTON = 1;
+    private static final int NAV_BAR_MODE_GESTURAL = 2;
+
+    private static final String NAV_BAR_MODE_3BUTTON_OVERLAY =
+            "com.android.internal.systemui.navbar.threebutton";
+    private static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
+            "com.android.internal.systemui.navbar.twobutton";
     private static final String GESTURAL_OVERLAY_NAME =
             "com.android.internal.systemui.navbar.gestural";
 
-    /** Most application's res id must be larger than 0x7f000000 */
-    public static final int MIN_APPLICATION_RES_ID = 0x7f000000;
-    public static final String SETTINGS_CLASS =
-            SETTINGS_PACKAGE_NAME + ".Settings$SystemDashboardActivity";
+    private static final int WAIT_OVERLAY_TIMEOUT = 3000;
+    private static final int PEEK_INTERVAL = 200;
 
-    private final Map<String, Boolean> mSystemGestureOptionsMap = new ArrayMap<>();
     private final Context mTargetContext;
     private final UiDevice mDevice;
+    private final WindowManager mWindowManager;
 
-    // Bounds for actions like swipe and click.
-    private String mEdgeToEdgeNavigationTitle;
-    private String mSystemNavigationTitle;
-    private String mGesturePreferenceTitle;
-    private boolean mConfiguredInSettings;
-    private boolean mRevertOverlay;
+    private final String mOriginalOverlayPackage;
 
     @Override
     protected void before() throws Throwable {
@@ -74,7 +70,9 @@
 
     @Override
     protected void after() {
-        disableGestureNav();
+        if (!mOriginalOverlayPackage.equals(GESTURAL_OVERLAY_NAME)) {
+            disableGestureNav();
+        }
     }
 
     /**
@@ -85,39 +83,16 @@
         Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
         mDevice = UiDevice.getInstance(instrumentation);
         mTargetContext = instrumentation.getTargetContext();
-        PackageManager packageManager = mTargetContext.getPackageManager();
-        Resources res;
-        try {
-            res = packageManager.getResourcesForApplication(SETTINGS_PACKAGE_NAME);
-        } catch (PackageManager.NameNotFoundException e) {
-            return;
-        }
-        if (res == null) {
-            return;
-        }
 
-        mEdgeToEdgeNavigationTitle = getSettingsString(res, "edge_to_edge_navigation_title");
-        mGesturePreferenceTitle = getSettingsString(res, "gesture_preference_title");
-        mSystemNavigationTitle = getSettingsString(res, "system_navigation_title");
-
-        String text = getSettingsString(res, "edge_to_edge_navigation_title");
-        if (text != null) {
-            mSystemGestureOptionsMap.put(text, false);
-        }
-        text = getSettingsString(res, "swipe_up_to_switch_apps_title");
-        if (text != null) {
-            mSystemGestureOptionsMap.put(text, false);
-        }
-        text = getSettingsString(res, "legacy_navigation_title");
-        if (text != null) {
-            mSystemGestureOptionsMap.put(text, false);
-        }
-
-        mConfiguredInSettings = false;
+        mOriginalOverlayPackage = getCurrentOverlayPackage();
+        mWindowManager = mTargetContext.getSystemService(WindowManager.class);
     }
 
     @SuppressWarnings("BooleanMethodIsAlwaysInverted")
     private boolean hasSystemGestureFeature() {
+        if (!containsNavigationBar()) {
+            return false;
+        }
         final PackageManager pm = mTargetContext.getPackageManager();
 
         // No bars on embedded devices.
@@ -128,59 +103,21 @@
                 || pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
     }
 
-
-    private UiObject2 findSystemNavigationObject(String text, boolean addCheckSelector) {
-        BySelector widgetFrameSelector = By.res("android", "widget_frame");
-        BySelector checkboxSelector = By.checkable(true);
-        if (addCheckSelector) {
-            checkboxSelector = checkboxSelector.checked(true);
+    private String getCurrentOverlayPackage() {
+        final int currentNavMode = getCurrentNavMode();
+        switch (currentNavMode) {
+            case NAV_BAR_MODE_GESTURAL:
+                return GESTURAL_OVERLAY_NAME;
+            case NAV_BAR_MODE_2BUTTON:
+                return NAV_BAR_MODE_2BUTTON_OVERLAY;
+            case NAV_BAR_MODE_3BUTTON:
+            default:
+                return NAV_BAR_MODE_3BUTTON_OVERLAY;
         }
-        BySelector textSelector = By.text(text);
-        BySelector targetSelector = By.hasChild(widgetFrameSelector).hasDescendant(textSelector)
-                .hasDescendant(checkboxSelector);
-
-        return mDevice.findObject(targetSelector);
     }
 
-    private boolean launchToSettingsSystemGesture() {
-
-        // Open the Settings app as close as possible to the gesture Fragment
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        ComponentName settingComponent = new ComponentName(SETTINGS_PACKAGE_NAME, SETTINGS_CLASS);
-        intent.setComponent(settingComponent);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        mTargetContext.startActivity(intent);
-
-        // Wait for the app to appear
-        mDevice.wait(Until.hasObject(By.pkg("com.android.settings").depth(0)),
-                5000);
-        mDevice.wait(Until.hasObject(By.text(mGesturePreferenceTitle)), 5000);
-        if (mDevice.findObject(By.text(mGesturePreferenceTitle)) == null) {
-            return false;
-        }
-        mDevice.findObject(By.text(mGesturePreferenceTitle)).click();
-        mDevice.wait(Until.hasObject(By.text(mSystemNavigationTitle)), 5000);
-        if (mDevice.findObject(By.text(mSystemNavigationTitle)) == null) {
-            return false;
-        }
-        mDevice.findObject(By.text(mSystemNavigationTitle)).click();
-        mDevice.wait(Until.hasObject(By.text(mEdgeToEdgeNavigationTitle)), 5000);
-
-        return mDevice.hasObject(By.text(mEdgeToEdgeNavigationTitle));
-    }
-
-    private void leaveSettings() {
-        mDevice.pressBack(); /* Back to Gesture */
-        mDevice.waitForIdle();
-        mDevice.pressBack(); /* Back to System */
-        mDevice.waitForIdle();
-        mDevice.pressBack(); /* back to Settings */
-        mDevice.waitForIdle();
-        mDevice.pressBack(); /* Back to Home */
-        mDevice.waitForIdle();
-
-        mDevice.pressHome(); /* double confirm back to home */
-        mDevice.waitForIdle();
+    private void insetsToRect(Insets insets, Rect outRect) {
+        outRect.set(insets.left, insets.top, insets.right, insets.bottom);
     }
 
     private void enableGestureNav() {
@@ -188,71 +125,68 @@
             return;
         }
         try {
-            if (mDevice.executeShellCommand("cmd overlay list").contains(GESTURAL_OVERLAY_NAME)) {
+            if (!mDevice.executeShellCommand("cmd overlay list").contains(GESTURAL_OVERLAY_NAME)) {
+                return;
+            }
+        } catch (IOException ignore) {
+            //
+        }
+        monitorOverlayChange(() -> {
+            try {
                 mDevice.executeShellCommand("cmd overlay enable " + GESTURAL_OVERLAY_NAME);
-                mDevice.waitForIdle();
+            } catch (IOException e) {
+                // Do nothing
             }
-        } catch (IOException e) {
-            // Do nothing
-        }
-
-        if (isGestureMode()) {
-            mRevertOverlay = true;
-            return;
-        }
-
-        // Set up the gesture navigation by enabling it via the Settings app
-        boolean isOperatedSettingsToExpectedOption = launchToSettingsSystemGesture();
-        if (isOperatedSettingsToExpectedOption) {
-            for (Map.Entry<String, Boolean> entry : mSystemGestureOptionsMap.entrySet()) {
-                UiObject2 uiObject2 = findSystemNavigationObject(entry.getKey(), true);
-                entry.setValue(uiObject2 != null);
-            }
-            UiObject2 edgeToEdgeObj = mDevice.findObject(By.text(mEdgeToEdgeNavigationTitle));
-            if (edgeToEdgeObj != null) {
-                edgeToEdgeObj.click();
-                mConfiguredInSettings = true;
-            }
-        }
-        mDevice.waitForIdle();
-        leaveSettings();
-
-        mDevice.pressHome();
-        mDevice.waitForIdle();
-
-        mDevice.waitForIdle();
+        });
     }
 
-    /**
-     * Restore the original configured value for the system gesture by operating Settings.
-     */
     private void disableGestureNav() {
         if (!hasSystemGestureFeature()) {
             return;
         }
-
-        if (mRevertOverlay) {
+        monitorOverlayChange(() -> {
             try {
-                mDevice.executeShellCommand("cmd overlay disable " + GESTURAL_OVERLAY_NAME);
-            } catch (IOException e) {
+                mDevice.executeShellCommand("cmd overlay enable " + mOriginalOverlayPackage);
+            } catch (IOException ignore) {
                 // Do nothing
             }
-            if (!isGestureMode()) {
-                return;
-            }
-        }
+        });
+    }
 
-        if (mConfiguredInSettings) {
-            launchToSettingsSystemGesture();
-            for (Map.Entry<String, Boolean> entry : mSystemGestureOptionsMap.entrySet()) {
-                if (entry.getValue()) {
-                    UiObject2 navigationObject = findSystemNavigationObject(entry.getKey(), false);
-                    if (navigationObject != null) {
-                        navigationObject.click();
-                    }
+    private void getCurrentInsetsSize(Rect outSize) {
+        outSize.setEmpty();
+        if (mWindowManager != null) {
+            WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
+            Insets navInsets = insets.getInsetsIgnoringVisibility(
+                    WindowInsets.Type.navigationBars());
+            insetsToRect(navInsets, outSize);
+        }
+    }
+
+    // Monitoring the navigation bar insets size change as a hint of gesture mode has changed, not
+    // the best option for every kind of devices. We can consider listening OVERLAY_CHANGED
+    // broadcast in U.
+    private void monitorOverlayChange(Runnable overlayChangeCommand) {
+        if (mWindowManager != null) {
+            final Rect initSize = new Rect();
+            getCurrentInsetsSize(initSize);
+
+            overlayChangeCommand.run();
+            // wait for insets size change
+            final Rect peekSize = new Rect();
+            int t = 0;
+            while (t < WAIT_OVERLAY_TIMEOUT) {
+                SystemClock.sleep(PEEK_INTERVAL);
+                t += PEEK_INTERVAL;
+                getCurrentInsetsSize(peekSize);
+                if (!peekSize.equals(initSize)) {
+                    break;
                 }
             }
-            leaveSettings();
+        } else {
+            // shouldn't happen
+            overlayChangeCommand.run();
+            SystemClock.sleep(WAIT_OVERLAY_TIMEOUT);
         }
     }
 
@@ -266,20 +200,23 @@
         assumeTrue("Gesture navigation required", isGestureMode);
     }
 
-    private boolean isGestureMode() {
-        // TODO: b/153032202 consider the CTS on GSI case.
+    private int getCurrentNavMode() {
         Resources res = mTargetContext.getResources();
         int naviModeId = res.getIdentifier(NAV_BAR_INTERACTION_MODE_RES_NAME, "integer", "android");
-        int naviMode = res.getInteger(naviModeId);
-        return naviMode == NAV_BAR_INTERACTION_MODE_GESTURAL;
+        return res.getInteger(naviModeId);
     }
 
-    private static String getSettingsString(Resources res, String strResName) {
-        int resIdString = res.getIdentifier(strResName, "string", SETTINGS_PACKAGE_NAME);
-        if (resIdString <= MIN_APPLICATION_RES_ID) {
-            return null;
-        }
+    private boolean containsNavigationBar() {
+        final Rect peekSize = new Rect();
+        getCurrentInsetsSize(peekSize);
+        return peekSize.height() != 0;
+    }
 
-        return res.getString(resIdString);
+    private boolean isGestureMode() {
+        if (!containsNavigationBar()) {
+            return false;
+        }
+        final int naviMode = getCurrentNavMode();
+        return naviMode == NAV_BAR_MODE_GESTURAL;
     }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
index 4da5fff..9c8a81d 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
@@ -25,6 +25,8 @@
 import android.content.pm.PackageManager;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
 import java.util.ArrayList;
 
 /**
@@ -41,6 +43,7 @@
     private static final String SIMPLE_APP_PKG = "com.android.cts.launcherapps.simpleapp";
     private static final String SIMPLE_APP_ACTIVITY =
             "com.android.cts.launcherapps.simpleapp.SimpleActivityImmediateExit";
+    private static final String ARG_PID_BEFORE_STOP = "pidOfSimpleapp";
 
     public void testSetUserControlDisabledPackages() throws Exception {
         ArrayList<String> protectedPackages = new ArrayList<>();
@@ -86,14 +89,15 @@
         // Check if package is part of UserControlDisabledPackages before checking if
         // package is stopped since it is a necessary condition to prevent stopping of
         // package
-
         assertThat(mDevicePolicyManager.getUserControlDisabledPackages(getWho()))
                 .containsExactly(SIMPLE_APP_PKG);
-        assertPackageRunningState(/* running= */ true);
+        assertPackageRunningState(/* running= */ true,
+                InstrumentationRegistry.getArguments().getString(ARG_PID_BEFORE_STOP, "-1"));
     }
 
     public void testFgsStopWithUserControlEnabled() throws Exception {
-        assertPackageRunningState(/* running= */ false);
+        assertPackageRunningState(/* running= */ false,
+                InstrumentationRegistry.getArguments().getString(ARG_PID_BEFORE_STOP, "-1"));
         assertThat(mDevicePolicyManager.getUserControlDisabledPackages(getWho())).isEmpty();
     }
 
@@ -120,9 +124,15 @@
         return pid.length() > 0;
     }
 
-    private void assertPackageRunningState(boolean shouldBeRunning) throws Exception {
+    private void assertPackageRunningState(boolean shouldBeRunning, String argPid)
+            throws Exception {
+        String pid = executeShellCommand(String.format("pidof %s", SIMPLE_APP_PKG)).trim();
+
+        final boolean samePid = pid.equals(argPid);
+        final boolean stillRunning = samePid && isPackageRunning(SIMPLE_APP_PKG);
+
         assertWithMessage("Package %s running for user %s", SIMPLE_APP_PKG,
                 getCurrentUser().getIdentifier())
-                .that(isPackageRunning(SIMPLE_APP_PKG)).isEqualTo(shouldBeRunning);
+                .that(stillRunning).isEqualTo(shouldBeRunning);
     }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index c87a7f8..d1773d0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -70,6 +70,7 @@
     private static final String TEST_APP_LOCATION = "/data/local/tmp/cts/packageinstaller/";
 
     private static final String ARG_NETWORK_LOGGING_BATCH_COUNT = "batchCount";
+    private static final String ARG_PID_BEFORE_STOP = "pidOfSimpleapp";
 
     private static final String LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK =
             "CtsHasLauncherActivityApp.apk";
@@ -1059,13 +1060,16 @@
      */
     private void tryFgsStoppingProtectedPackage(int userId, boolean canUserStopPackage)
             throws Exception {
+        String pid = executeShellCommand(String.format("pidof %s", SIMPLE_APP_PKG)).trim();
         fgsStopPackageForUser(SIMPLE_APP_PKG, userId);
         if (canUserStopPackage) {
             executeDeviceTestMethod(".UserControlDisabledPackagesTest",
-                    "testFgsStopWithUserControlEnabled");
+                    "testFgsStopWithUserControlEnabled",
+                     Collections.singletonMap(ARG_PID_BEFORE_STOP, pid));
         } else {
             executeDeviceTestMethod(".UserControlDisabledPackagesTest",
-                    "testFgsStopWithUserControlDisabled");
+                    "testFgsStopWithUserControlDisabled",
+                     Collections.singletonMap(ARG_PID_BEFORE_STOP, pid));
         }
     }
 
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 13ade8b..dea2688 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -116,8 +116,6 @@
     private File devicePcFile;
     private File deviceSvcFile;
     private File seappNeverAllowFile;
-    private File libsepolwrap;
-    private File libcpp;
     private File copyLibcpp;
     private File sepolicyTests;
 
@@ -907,29 +905,8 @@
         return (os.startsWith("mac") || os.startsWith("darwin"));
     }
 
-    private void setupLibraries() throws Exception {
-        // The host side binary tests are host OS specific. Use Linux
-        // libraries on Linux and Mac libraries on Mac.
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
-        if (isMac()) {
-            libsepolwrap = buildHelper.getTestFile("libsepolwrap.dylib");
-            libcpp = buildHelper.getTestFile("libc++.dylib");
-            copyLibcpp = new File(System.getProperty("java.io.tmpdir") + "/libc++.dylib");
-            Files.copy(libcpp.toPath(), copyLibcpp.toPath(), StandardCopyOption.REPLACE_EXISTING);
-        } else {
-            libsepolwrap = buildHelper.getTestFile("libsepolwrap.so");
-            libcpp = buildHelper.getTestFile("libc++.so");
-            copyLibcpp = new File(System.getProperty("java.io.tmpdir") + "/libc++.so");
-            Files.copy(libcpp.toPath(), copyLibcpp.toPath(), StandardCopyOption.REPLACE_EXISTING);
-        }
-        libsepolwrap.deleteOnExit();
-        libcpp.deleteOnExit();
-        copyLibcpp.deleteOnExit();
-    }
-
     private void assertSepolicyTests(String test, String testExecutable,
             boolean includeVendorSepolicy) throws Exception {
-        setupLibraries();
         sepolicyTests = copyResourceToTempFile(testExecutable);
         sepolicyTests.setExecutable(true);
 
@@ -951,12 +928,6 @@
         }
 
         ProcessBuilder pb = new ProcessBuilder(args);
-        Map<String, String> env = pb.environment();
-        if (isMac()) {
-            env.put("DYLD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
-        } else {
-            env.put("LD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
-        }
         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
         pb.redirectErrorStream(true);
         Process p = pb.start();
diff --git a/tests/app/app/assets/picture_800_by_600.png b/tests/app/app/assets/picture_800_by_600.png
new file mode 100644
index 0000000..dc8f3d4
--- /dev/null
+++ b/tests/app/app/assets/picture_800_by_600.png
Binary files differ
diff --git a/tests/app/src/android/app/cts/NotificationTemplateTest.kt b/tests/app/src/android/app/cts/NotificationTemplateTest.kt
index f5f0f13..5f3f7e8 100644
--- a/tests/app/src/android/app/cts/NotificationTemplateTest.kt
+++ b/tests/app/src/android/app/cts/NotificationTemplateTest.kt
@@ -31,6 +31,7 @@
 import android.widget.TextView
 import androidx.annotation.ColorInt
 import androidx.test.filters.SmallTest
+import com.android.compatibility.common.util.CddTest;
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assume
 import kotlin.test.assertFailsWith
@@ -282,8 +283,9 @@
         }
     }
 
+    @CddTest(requirement = "3.8.3.1/C-2-1")
     fun testPromoteBigPicture_withBigPictureUriIcon() {
-        val pictureUri = Uri.parse("content://android.app.stubs.assets/picture_400_by_300.png")
+        val pictureUri = Uri.parse("content://android.app.stubs.assets/picture_800_by_600.png")
         val pictureIcon = Icon.createWithContentUri(pictureUri)
         val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
                 .setSmallIcon(R.drawable.ic_media_play)
@@ -384,8 +386,9 @@
                 !!.sameAs(picture)).isTrue()
     }
 
+    @CddTest(requirement = "3.8.3.1/C-2-1")
     fun testBigPicture_withBigLargeIcon_withContentUri() {
-        val iconUri = Uri.parse("content://android.app.stubs.assets/picture_400_by_300.png")
+        val iconUri = Uri.parse("content://android.app.stubs.assets/picture_800_by_600.png")
         val icon = Icon.createWithContentUri(iconUri)
         val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
                 .setSmallIcon(R.drawable.ic_media_play)
diff --git a/tests/camera/Android.bp b/tests/camera/Android.bp
index aae58c6..c2334fc 100644
--- a/tests/camera/Android.bp
+++ b/tests/camera/Android.bp
@@ -65,6 +65,7 @@
         "truth-prebuilt",
         "androidx.heifwriter_heifwriter",
         "androidx.test.rules",
+        "MediaPerformanceClassCommon",
     ],
     jni_libs: [
         "libctscamera2_jni",
diff --git a/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java b/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java
index feb5567..772e7a5 100644
--- a/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java
+++ b/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java
@@ -36,6 +36,7 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.media.Image;
 import android.media.ImageReader;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.test.AndroidTestCase;
@@ -210,8 +211,9 @@
      * Version.MEDIA_PERFORMANCE_CLASS
      */
     public void testSPerfClassJpegSizes() throws Exception {
-        boolean isSPerfClass = CameraTestUtils.isSPerfClass();
-        if (!isSPerfClass) {
+        final boolean isAtLeastSPerfClass =
+                (Build.VERSION.MEDIA_PERFORMANCE_CLASS >= Build.VERSION_CODES.S);
+        if (!isAtLeastSPerfClass) {
             return;
         }
 
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 6af7758..c9d0314 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -55,6 +55,10 @@
 import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.cts.helpers.CameraUtils;
+import android.mediapc.cts.common.Requirement;
+import android.mediapc.cts.common.RequiredMeasurement;
+import android.mediapc.cts.common.RequirementConstants;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
 import android.media.CamcorderProfile;
 import android.media.ImageReader;
 import android.os.Build;
@@ -74,15 +78,11 @@
 
 import androidx.test.rule.ActivityTestRule;
 
-import androidx.test.InstrumentationRegistry;
-
 import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
 
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
@@ -92,12 +92,10 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.BiPredicate;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import static android.hardware.camera2.cts.CameraTestUtils.MPC_REPORT_LOG_NAME;
-import static android.hardware.camera2.cts.CameraTestUtils.MPC_STREAM_NAME;
-
 /**
  * Extended tests for static camera characteristics.
  */
@@ -113,6 +111,9 @@
      */
     private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5
 
+    @Rule
+    public final TestName mTestName = new TestName();
+
     private List<CameraCharacteristics> mCharacteristics;
 
     private static final Size FULLHD = new Size(1920, 1080);
@@ -131,10 +132,6 @@
     private static final long PREVIEW_RUN_MS = 500;
     private static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30;
 
-    private static final long MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION = 12000000;
-    private static final long MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION = 5000000;
-    private static final long MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION = 4000000;
-
     private static final long MIN_UHR_SENSOR_RESOLUTION = 24000000;
     /*
      * HW Levels short hand
@@ -2857,28 +2854,70 @@
     }
 
     /**
-     * Update performance class level based on condition
-     *
-     * @param condition whether the condition is met for passLevel
-     * @param passLevel the highest performance class level when condition is true
-     * @param failLevel the performance class when condition is false
+     * Camera hardware level requirement for Media Performance Class
      */
-    private int updatePerfClassLevel(boolean condition, int passLevel, int failLevel) {
-        return condition ? passLevel : failLevel;
-    }
+    public static class PrimaryCameraHwLevelReq extends Requirement {
+        private static final String TAG = PrimaryCameraHwLevelReq.class.getSimpleName();
 
-    /**
-     * Update perf class level based on meetSPerfClass and meetRPerfClass.
-     */
-    private int updatePerfClassLevelRS(boolean meetSPerfClass, boolean meetRPerfClass,
-            int perfClassLevel) {
-        if (!meetRPerfClass) {
-            return CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
-        } else if (!meetSPerfClass &&
-                perfClassLevel > CameraTestUtils.PERFORMANCE_CLASS_R) {
-            return Math.min(CameraTestUtils.PERFORMANCE_CLASS_R, perfClassLevel);
+        /**
+         * Creates a >= predicate for camera hardware level
+         */
+        private static BiPredicate<Integer, Integer> camHwLevelGte() {
+            return new BiPredicate<Integer, Integer>() {
+                @Override
+                public boolean test(Integer actual, Integer expected) {
+                    return StaticMetadata.hardwareLevelPredicate(actual, expected);
+                }
+
+                @Override
+                public String toString() {
+                    return "Camera Hardware Level Greater than or equal to";
+                }
+            };
         }
-        return perfClassLevel;
+        private static final BiPredicate<Integer, Integer> CAM_HW_LEVEL_GTE = camHwLevelGte();
+        private PrimaryCameraHwLevelReq(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setPrimaryRearCameraHwlLevel(Integer hwLevel) {
+            this.setMeasuredValue(RequirementConstants.REAR_CAMERA_HWL_LEVEL, hwLevel);
+        }
+
+        public void setPrimaryFrontCameraHwlLevel(Integer hwLevel) {
+            this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_HWL_LEVEL, hwLevel);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-3] MUST support android.info.supportedHardwareLevel property as FULL or
+         * better for back primary and LIMITED or better for front primary camera.
+         */
+        public static PrimaryCameraHwLevelReq createPrimaryCameraHwLevelReq() {
+            RequiredMeasurement<Integer> rearCameraHwlLevel = RequiredMeasurement
+                .<Integer>builder()
+                .setId(RequirementConstants.REAR_CAMERA_HWL_LEVEL)
+                .setPredicate(CAM_HW_LEVEL_GTE)
+                .addRequiredValue(Build.VERSION_CODES.R,
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+                .addRequiredValue(Build.VERSION_CODES.S,
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU,
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+                .build();
+            RequiredMeasurement<Integer> frontCameraHwlLevel = RequiredMeasurement
+                .<Integer>builder()
+                .setId(RequirementConstants.FRONT_CAMERA_HWL_LEVEL)
+                .setPredicate(CAM_HW_LEVEL_GTE)
+                .addRequiredValue(Build.VERSION_CODES.R,
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
+                .addRequiredValue(Build.VERSION_CODES.S,
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU,
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+                .build();
+            return new PrimaryCameraHwLevelReq(RequirementConstants.R7_5__H_1_3,
+                    rearCameraHwlLevel, frontCameraHwlLevel);
+        }
     }
 
     /**
@@ -2886,34 +2925,47 @@
      * in CDD camera section 7.5
      */
     @Test
-    @CddTest(requirement = "7.5/H-1-1,H-1-2,H-1-3,H-1-4,H-1-8,H-1-9,H-1-10,H-1-11,H-1-12,H-1-13,H-1-14")
+    @CddTest(requirements = {
+            "2.2.7.2/7.5/H-1-1",
+            "2.2.7.2/7.5/H-1-2",
+            "2.2.7.2/7.5/H-1-3",
+            "2.2.7.2/7.5/H-1-4",
+            "2.2.7.2/7.5/H-1-8",
+            "2.2.7.2/7.5/H-1-9",
+            "2.2.7.2/7.5/H-1-10",
+            "2.2.7.2/7.5/H-1-11",
+            "2.2.7.2/7.5/H-1-12",
+            "2.2.7.2/7.5/H-1-13",
+            "2.2.7.2/7.5/H-1-14"})
     public void testCameraPerfClassCharacteristics() throws Exception {
         if (mAdoptShellPerm) {
             // Skip test for system camera. Performance class is only applicable for public camera
             // ids.
             return;
         }
-        boolean assertRPerfClass = CameraTestUtils.isRPerfClass();
-        boolean assertSPerfClass = CameraTestUtils.isSPerfClass();
-        boolean assertTPerfClass = CameraTestUtils.isTPerfClass();
-        boolean assertPerfClass = (assertRPerfClass || assertSPerfClass || assertTPerfClass);
-
-        // R & S Performance Class
-        int perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-        int perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-        int perfClassLevelH13 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-        int perfClassLevelH14 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-        int perfClassLevelH18 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-
-        // T Performance Class
-        int perfClassLevelH19 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-        int perfClassLevelH110 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-        int perfClassLevelH111 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-        int perfClassLevelH112 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-        int perfClassLevelH113 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-        int perfClassLevelH114 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-
-        DeviceReportLog reportLog = new DeviceReportLog(MPC_REPORT_LOG_NAME, MPC_STREAM_NAME);
+        PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+        PerformanceClassEvaluator.PrimaryCameraRequirement primaryRearReq =
+                pce.addPrimaryRearCameraReq();
+        PerformanceClassEvaluator.PrimaryCameraRequirement primaryFrontReq =
+                pce.addPrimaryFrontCameraReq();
+        PrimaryCameraHwLevelReq hwLevelReq = pce.addRequirement(
+                PrimaryCameraHwLevelReq.createPrimaryCameraHwLevelReq());
+        PerformanceClassEvaluator.CameraTimestampSourceRequirement timestampSourceReq =
+                pce.addR7_5__H_1_4();
+        PerformanceClassEvaluator.CameraRawRequirement rearRawReq =
+                pce.addR7_5__H_1_8();
+        PerformanceClassEvaluator.Camera240FpsRequirement hfrReq =
+                pce.addR7_5__H_1_9();
+        PerformanceClassEvaluator.UltraWideZoomRatioRequirement ultrawideZoomRatioReq =
+                pce.addR7_5__H_1_10();
+        PerformanceClassEvaluator.ConcurrentRearFrontRequirement concurrentRearFrontReq =
+                pce.addR7_5__H_1_11();
+        PerformanceClassEvaluator.PreviewStabilizationRequirement previewStabilizationReq =
+                pce.addR7_5__H_1_12();
+        PerformanceClassEvaluator.LogicalMultiCameraRequirement logicalMultiCameraReq =
+                pce.addR7_5__H_1_13();
+        PerformanceClassEvaluator.StreamUseCaseRequirement streamUseCaseReq =
+                pce.addR7_5__H_1_14();
 
         String primaryRearId = null;
         String primaryFrontId = null;
@@ -2940,42 +2992,29 @@
             List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(cameraId,
                     mCameraManager, null /*bound*/);
 
+            Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
             if (isPrimaryRear) {
                 primaryRearId = cameraId;
-                if (sensorResolution < MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION) {
-                    mCollector.expectTrue("Primary rear camera resolution should be at least " +
-                            MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION + " pixels, is "+
-                            sensorResolution, !assertPerfClass);
-                    perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
-                }
-                reportLog.addValue("rear camera resolution", sensorResolution,
-                        ResultType.NEUTRAL, ResultUnit.NONE);
+                primaryRearReq.setPrimaryCameraSupported(true);
+                primaryRearReq.setResolution(sensorResolution);
+                hwLevelReq.setPrimaryRearCameraHwlLevel(staticInfo.getHardwareLevelChecked());
+                timestampSourceReq.setRearCameraTimestampSource(timestampSource);
 
                 // 4K @ 30fps
                 boolean supportUHD = videoSizes.contains(UHD);
                 boolean supportDC4K = videoSizes.contains(DC4K);
-                reportLog.addValue("rear camera 4k support", supportUHD | supportDC4K,
-                        ResultType.NEUTRAL, ResultUnit.NONE);
-                if (!supportUHD && !supportDC4K) {
-                    mCollector.expectTrue("Primary rear camera should support 4k video recording",
-                            !assertPerfClass);
-                    perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
-                } else {
+                boolean support4K = (supportUHD || supportDC4K);
+                primaryRearReq.setVideoSizeReqSatisfied(support4K);
+                if (support4K) {
                     long minFrameDuration = config.getOutputMinFrameDuration(
                             android.media.MediaRecorder.class, supportDC4K ? DC4K : UHD);
-                    reportLog.addValue("rear camera 4k frame duration", minFrameDuration,
-                        ResultType.NEUTRAL, ResultUnit.NONE);
-                    if (minFrameDuration >= (1e9 / 29.9)) {
-                        mCollector.expectTrue("Primary rear camera should support 4k video @ 30fps",
-                                !assertPerfClass);
-                        perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
-                    }
+                    primaryRearReq.setVideoFps(1e9 / minFrameDuration);
+                } else {
+                    primaryRearReq.setVideoFps(-1);
                 }
 
                 // H-1-9
                 boolean supportHighSpeed = staticInfo.isCapabilitySupported(CONSTRAINED_HIGH_SPEED);
-                mCollector.expectTrue("Primary rear camera should support high speed recording",
-                        !assertTPerfClass || supportHighSpeed);
                 boolean support240Fps = false;
                 if (supportHighSpeed) {
                     Size[] availableHighSpeedSizes = config.getHighSpeedVideoSizes();
@@ -2995,101 +3034,31 @@
                             break;
                         }
                     }
-                    mCollector.expectTrue("Primary rear camera should support HD or FULLHD @ 240",
-                            !assertTPerfClass || support240Fps);
                 }
-                perfClassLevelH19 = updatePerfClassLevel(support240Fps,
-                        perfClassLevelH19, CameraTestUtils.PERFORMANCE_CLASS_S);
-                reportLog.addValue("rear camera 720p/1080p @ 240fps support", support240Fps,
-                        ResultType.NEUTRAL, ResultUnit.NONE);
+                hfrReq.setRear240FpsSupported(support240Fps);
             } else {
                 primaryFrontId = cameraId;
-                if (sensorResolution < MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION) {
-                    mCollector.expectTrue("Primary front camera resolution should be at least "
-                            + MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION + " pixels, is "
-                            + sensorResolution, !(assertSPerfClass || assertTPerfClass));
-                    perfClassLevelH12 = Math.min(
-                            perfClassLevelH12, CameraTestUtils.PERFORMANCE_CLASS_R);
-                }
-                if (sensorResolution < MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION) {
-                    mCollector.expectTrue("Primary front camera resolution should be at least " +
-                            MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION + " pixels, is "+
-                            sensorResolution, !assertRPerfClass);
-                    perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
-                }
-                reportLog.addValue("front camera resolution", sensorResolution,
-                        ResultType.NEUTRAL, ResultUnit.NONE);
+                primaryFrontReq.setPrimaryCameraSupported(true);
+                primaryFrontReq.setResolution(sensorResolution);
+                hwLevelReq.setPrimaryFrontCameraHwlLevel(staticInfo.getHardwareLevelChecked());
+                timestampSourceReq.setFrontCameraTimestampSource(timestampSource);
 
                 // 1080P @ 30fps
                 boolean supportFULLHD = videoSizes.contains(FULLHD);
-                reportLog.addValue("front camera 1080p support", supportFULLHD,
-                        ResultType.NEUTRAL, ResultUnit.NONE);
-                if (!supportFULLHD) {
-                    mCollector.expectTrue(
-                            "Primary front camera should support 1080P video recording",
-                            !assertPerfClass);
-                    perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
-                } else {
+                primaryFrontReq.setVideoSizeReqSatisfied(supportFULLHD);
+                if (supportFULLHD) {
                     long minFrameDuration = config.getOutputMinFrameDuration(
                             android.media.MediaRecorder.class, FULLHD);
-                    if (minFrameDuration >= (1e9 / 29.9)) {
-                        mCollector.expectTrue(
-                                "Primary front camera should support 1080P video @ 30fps",
-                                !assertPerfClass);
-                        perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
-                    }
-                    reportLog.addValue("front camera 1080p frame duration", minFrameDuration,
-                        ResultType.NEUTRAL, ResultUnit.NONE);
+                    primaryFrontReq.setVideoFps(1e9 / minFrameDuration);
+                } else {
+                    primaryFrontReq.setVideoFps(-1);
                 }
             }
 
-            String facingString = isPrimaryRear ? "rear" : "front";
-            // H-1-3
-            if (assertTPerfClass || assertSPerfClass || (assertRPerfClass && isPrimaryRear)) {
-                mCollector.expectTrue("Primary " + facingString +
-                        " camera should be at least FULL, but is " +
-                        toStringHardwareLevel(staticInfo.getHardwareLevelChecked()),
-                        staticInfo.isHardwareLevelAtLeastFull());
-            } else if (assertRPerfClass) {
-                mCollector.expectTrue("Primary " + facingString +
-                        " camera should be at least LIMITED, but is " +
-                        toStringHardwareLevel(staticInfo.getHardwareLevelChecked()),
-                        staticInfo.isHardwareLevelAtLeastLimited());
-            }
-
-            reportLog.addValue(facingString + " camera hardware level",
-                    staticInfo.getHardwareLevelChecked(), ResultType.NEUTRAL, ResultUnit.NONE);
-            if (isPrimaryRear) {
-                perfClassLevelH13 = updatePerfClassLevel(staticInfo.isHardwareLevelAtLeastFull(),
-                        perfClassLevelH13, CameraTestUtils.PERFORMANCE_CLASS_NOT_MET);
-            } else {
-                perfClassLevelH13 = updatePerfClassLevelRS(staticInfo.isHardwareLevelAtLeastFull(),
-                        staticInfo.isHardwareLevelAtLeastLimited(), perfClassLevelH13);
-            }
-
-            // H-1-4
-            Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
-            reportLog.addValue(facingString + " timestampSource",
-                    timestampSource, ResultType.NEUTRAL, ResultUnit.NONE);
-            boolean realtimeTimestamp = (timestampSource != null &&
-                    timestampSource.equals(CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME));
-            mCollector.expectTrue(
-                    "Primary " + facingString + " camera should support real-time timestamp source",
-                    !assertPerfClass || realtimeTimestamp);
-            perfClassLevelH14 = updatePerfClassLevel(realtimeTimestamp, perfClassLevelH14,
-                    CameraTestUtils.PERFORMANCE_CLASS_NOT_MET);
-
             // H-1-8
             if (isPrimaryRear) {
                 boolean supportRaw = staticInfo.isCapabilitySupported(RAW);
-                reportLog.addValue(facingString + " camera raw support",
-                        supportRaw, ResultType.NEUTRAL, ResultUnit.NONE);
-                if (assertSPerfClass || assertTPerfClass) {
-                    mCollector.expectTrue("Primary rear camera should support RAW capability",
-                            supportRaw);
-                }
-                perfClassLevelH18 = updatePerfClassLevel(supportRaw, perfClassLevelH18,
-                        CameraTestUtils.PERFORMANCE_CLASS_R);
+                rearRawReq.setRearRawSupported(supportRaw);
             }
 
             // H-1-10
@@ -3098,96 +3067,76 @@
             Range<Float> zoomRatioRange = staticInfo.getZoomRatioRangeChecked();
             boolean meetH110 = (primaryToMaxFovRatio >= 1.0f - FOV_THRESHOLD)
                     || (zoomRatioRange.getLower() < 1.0f - FOV_THRESHOLD);
-            mCollector.expectTrue("Primary " + facingString + " camera must support zoomRatio < "
-                    + "1.0f if there is an ultrawide lens with the same facing",
-                    !assertTPerfClass || meetH110);
-            perfClassLevelH110 = updatePerfClassLevel(meetH110, perfClassLevelH110,
-                    CameraTestUtils.PERFORMANCE_CLASS_S);
-            reportLog.addValue(facingString + " camera supports maximum FOV using zoom ratio",
-                    meetH110, ResultType.NEUTRAL, ResultUnit.NONE);
+            if (isPrimaryRear) {
+                ultrawideZoomRatioReq.setRearUltraWideZoomRatioReqMet(meetH110);
+            } else {
+                ultrawideZoomRatioReq.setFrontUltraWideZoomRatioReqMet(meetH110);
+            }
 
             // H-1-12
-            boolean meetH112 = staticInfo.isPreviewStabilizationSupported();
-            mCollector.expectTrue("Primary " + facingString + " camera must support preview "
-                    + "stabilization", !assertTPerfClass || meetH112);
-            perfClassLevelH112 = updatePerfClassLevel(meetH112, perfClassLevelH112,
-                    CameraTestUtils.PERFORMANCE_CLASS_S);
-            reportLog.addValue(facingString + " camera preview stabilization", meetH112,
-                    ResultType.NEUTRAL, ResultUnit.NONE);
+            boolean previewStab = staticInfo.isPreviewStabilizationSupported();
+            if (isPrimaryRear) {
+                previewStabilizationReq.setRearPreviewStabilizationSupported(previewStab);
+            } else {
+                previewStabilizationReq.setFrontPreviewStabilizationSupported(previewStab);
+            }
 
             // H-1-13
             int facing = staticInfo.getLensFacingChecked();
             int numOfPhysicalRgbCameras = getNumberOfRgbPhysicalCameras(facing);
-            boolean meetH113 = (numOfPhysicalRgbCameras <= 1) || staticInfo.isLogicalMultiCamera();
-            mCollector.expectTrue("Primary " + facingString + " camera must be LOGICAL_MULTI_CAMERA"
-                    + " in case of multiple RGB cameras with same facing",
-                    !assertTPerfClass || meetH113);
-            perfClassLevelH113 = updatePerfClassLevel(meetH113, perfClassLevelH113,
-                    CameraTestUtils.PERFORMANCE_CLASS_S);
-            reportLog.addValue(facingString + " camera is LOGICAL_MULTI_CAMERA in case of multiple "
-                    + "RGB cameras with same facing", meetH113, ResultType.NEUTRAL,
-                    ResultUnit.NONE);
+            boolean logicalMultiCameraReqMet =
+                    (numOfPhysicalRgbCameras <= 1) || staticInfo.isLogicalMultiCamera();
+            if (isPrimaryRear) {
+                logicalMultiCameraReq.setRearLogicalMultiCameraReqMet(logicalMultiCameraReqMet);
+            } else {
+                logicalMultiCameraReq.setFrontLogicalMultiCameraReqMet(logicalMultiCameraReqMet);
+            }
 
             // H-1-14
-            boolean meetH114 = staticInfo.isStreamUseCaseSupported();
-            mCollector.expectTrue("Primary " + facingString + " camera must support stream "
-                    + "use case", !assertTPerfClass || meetH114);
-            perfClassLevelH114 = updatePerfClassLevel(meetH114, perfClassLevelH114,
-                    CameraTestUtils.PERFORMANCE_CLASS_S);
-            reportLog.addValue(facingString + " camera stream use case", meetH114,
-                    ResultType.NEUTRAL, ResultUnit.NONE);
+            boolean streamUseCaseSupported = staticInfo.isStreamUseCaseSupported();
+            if (isPrimaryRear) {
+                streamUseCaseReq.setRearStreamUseCaseSupported(streamUseCaseSupported);
+            } else {
+                streamUseCaseReq.setFrontStreamUseCaseSupported(streamUseCaseSupported);
+            }
         }
-        HashSet<String> primaryCameras = new HashSet<String>();
+
         if (primaryRearId == null) {
-            mCollector.expectTrue("There must be a primary rear camera for performance class.",
-                    !assertPerfClass);
-            perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
-        } else {
-            primaryCameras.add(primaryRearId);
+            primaryRearReq.setPrimaryCameraSupported(false);
+            primaryRearReq.setResolution(-1);
+            primaryRearReq.setVideoSizeReqSatisfied(false);
+            primaryRearReq.setVideoFps(-1);
+            hwLevelReq.setPrimaryRearCameraHwlLevel(-1);
+            timestampSourceReq.setRearCameraTimestampSource(
+                    CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN);
+            rearRawReq.setRearRawSupported(false);
+            hfrReq.setRear240FpsSupported(false);
+            ultrawideZoomRatioReq.setRearUltraWideZoomRatioReqMet(false);
+            previewStabilizationReq.setRearPreviewStabilizationSupported(false);
+            logicalMultiCameraReq.setRearLogicalMultiCameraReqMet(false);
+            streamUseCaseReq.setRearStreamUseCaseSupported(false);
         }
         if (primaryFrontId == null) {
-            mCollector.expectTrue("There must be a primary front camera for performance class.",
-                    !assertPerfClass);
-            perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
-        } else {
-            primaryCameras.add(primaryFrontId);
+            primaryFrontReq.setPrimaryCameraSupported(false);
+            primaryFrontReq.setResolution(-1);
+            primaryFrontReq.setVideoSizeReqSatisfied(false);
+            primaryFrontReq.setVideoFps(-1);
+            hwLevelReq.setPrimaryFrontCameraHwlLevel(-1);
+            timestampSourceReq.setFrontCameraTimestampSource(
+                    CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN);
+            ultrawideZoomRatioReq.setFrontUltraWideZoomRatioReqMet(false);
+            previewStabilizationReq.setFrontPreviewStabilizationSupported(false);
+            logicalMultiCameraReq.setFrontLogicalMultiCameraReqMet(false);
+            streamUseCaseReq.setFrontStreamUseCaseSupported(false);
         }
 
         // H-1-11
         Set<Set<String>> concurrentCameraIds = mCameraManager.getConcurrentCameraIds();
+        Set<String> primaryCameras = new HashSet<>(Arrays.asList(primaryRearId, primaryFrontId));
         boolean supportPrimaryFrontBack = concurrentCameraIds.contains(primaryCameras);
-        mCollector.expectTrue("Concurrent primary front and primary back streaming must be "
-                + "supported", !assertTPerfClass || supportPrimaryFrontBack);
-        perfClassLevelH111 = updatePerfClassLevel(supportPrimaryFrontBack,
-                perfClassLevelH111, CameraTestUtils.PERFORMANCE_CLASS_S);
-        reportLog.addValue("concurrent front back support", supportPrimaryFrontBack,
-                 ResultType.NEUTRAL, ResultUnit.NONE);
+        concurrentRearFrontReq.setConcurrentRearFrontSupported(supportPrimaryFrontBack);
 
-        reportLog.addValue("Version", "0.0.1", ResultType.NEUTRAL, ResultUnit.NONE);
-        final String PERF_CLASS_REQ_NUM_PREFIX = "2.2.7.2/7.5/";
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-1",
-                perfClassLevelH11, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-2",
-                perfClassLevelH12, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-3",
-                perfClassLevelH13, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-4",
-                perfClassLevelH14, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-8",
-                perfClassLevelH18, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-9",
-                perfClassLevelH19, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-10",
-                perfClassLevelH110, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-11",
-                perfClassLevelH111, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-12",
-                perfClassLevelH112, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-13",
-                perfClassLevelH113, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-14",
-                perfClassLevelH114, ResultType.NEUTRAL, ResultUnit.NONE);
-        reportLog.submit(InstrumentationRegistry.getInstrumentation());
+        pce.submitAndCheck();
     }
 
     /**
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 3c88a3a..c9c28ea 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -1234,8 +1234,13 @@
 
                         mOutMediaFileName = mDebugFileNameBase + "/test_cslowMo_video_" +
                             captureRate + "fps_" + id + "_" + size.toString() + ".mp4";
-                        Log.v(TAG, "previewFrameRate:" + previewFrameRate);
-                        prepareRecording(size, previewFrameRate, captureRate);
+
+                        // b/239101664 It appears that video frame rates higher than 30 fps may not
+                        // trigger slow motion recording consistently.
+                        int videoFrameRate = previewFrameRate > VIDEO_FRAME_RATE ?
+                                VIDEO_FRAME_RATE : previewFrameRate;
+                        Log.v(TAG, "videoFrameRate:" + videoFrameRate);
+                        prepareRecording(size, videoFrameRate, captureRate);
 
                         SystemClock.sleep(PREVIEW_DURATION_MS);
 
@@ -1243,7 +1248,7 @@
 
                         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
                         // Start recording
-                        startSlowMotionRecording(/*useMediaRecorder*/true, previewFrameRate,
+                        startSlowMotionRecording(/*useMediaRecorder*/true, videoFrameRate,
                                 captureRate, fpsRange, resultListener,
                                 /*useHighSpeedSession*/true);
 
@@ -1256,7 +1261,7 @@
                         startConstrainedPreview(fpsRange, previewResultListener);
 
                         // Convert number of frames camera produced into the duration in unit of ms.
-                        float frameDurationMs = 1000.0f / previewFrameRate;
+                        float frameDurationMs = 1000.0f / videoFrameRate;
                         float durationMs = resultListener.getTotalNumFrames() * frameDurationMs;
 
                         // Validation.
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index e8d6168..4a86b49 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -142,8 +142,6 @@
 
     public static final String OFFLINE_CAMERA_ID = "offline_camera_id";
     public static final String REPORT_LOG_NAME = "CtsCameraTestCases";
-    public static final String MPC_REPORT_LOG_NAME = "MediaPerformanceClassLogs";
-    public static final String MPC_STREAM_NAME = "CameraCts";
 
     private static final int EXIF_DATETIME_LENGTH = 19;
     private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60;
@@ -3815,33 +3813,6 @@
         return zoomRatios;
     }
 
-    public static final int PERFORMANCE_CLASS_NOT_MET = 0;
-    public static final int PERFORMANCE_CLASS_R = Build.VERSION_CODES.R;
-    public static final int PERFORMANCE_CLASS_S = Build.VERSION_CODES.R + 1;
-    public static final int PERFORMANCE_CLASS_T = Build.VERSION_CODES.S + 2;
-    public static final int PERFORMANCE_CLASS_CURRENT = PERFORMANCE_CLASS_T;
-
-    /**
-     * Check whether this mobile device is R performance class as defined in CDD
-     */
-    public static boolean isRPerfClass() {
-        return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R;
-    }
-
-    /**
-     * Check whether this mobile device is S performance class as defined in CDD
-     */
-    public static boolean isSPerfClass() {
-        return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S;
-    }
-
-    /**
-     * Check whether this mobile device is T performance class as defined in CDD
-     */
-    public static boolean isTPerfClass() {
-        return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_T;
-    }
-
     /**
      * Check whether a camera Id is a primary rear facing camera
      */
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index fc8c4db..f188685 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -229,6 +229,13 @@
      * at least the desired one (but could be higher)
      */
     public boolean isHardwareLevelAtLeast(int level) {
+        int deviceLevel = getHardwareLevelChecked();
+
+        return hardwareLevelPredicate(deviceLevel, level);
+    }
+
+    // Return true if level1 is at least level2
+    public static boolean hardwareLevelPredicate(int level1, int level2) {
         final int[] sortedHwLevels = {
             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
@@ -236,19 +243,19 @@
             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
         };
-        int deviceLevel = getHardwareLevelChecked();
-        if (level == deviceLevel) {
+
+        if (level1 == level2) {
             return true;
         }
 
         for (int sortedlevel : sortedHwLevels) {
-            if (sortedlevel == level) {
+            if (sortedlevel == level2) {
                 return true;
-            } else if (sortedlevel == deviceLevel) {
+            } else if (sortedlevel == level1) {
                 return false;
             }
         }
-        Assert.fail("Unknown hardwareLevel " + level + " and device hardware level " + deviceLevel);
+        Assert.fail("Unknown hardwareLevel " + level1 + " and device hardware level " + level2);
         return false;
     }
 
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java
index 2c3934c..bdf4bc6 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java
@@ -27,6 +27,7 @@
 import com.android.bedstead.harrier.BedsteadJUnit4;
 import com.android.bedstead.harrier.DeviceState;
 import com.android.bedstead.harrier.annotations.EnsureHasPermission;
+import com.android.bedstead.harrier.annotations.RequireMultiUserSupport;
 import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner;
@@ -52,6 +53,7 @@
     @EnsureHasDeviceOwner
     @EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
     @RequireRunOnPrimaryUser
+    @RequireMultiUserSupport
     public void createCloneProfile_hasDeviceOwner_fails() {
         assertThrows(NeneException.class,
                 () -> TestApis.users().createUser()
@@ -67,6 +69,7 @@
     @EnsureHasNoDeviceOwner
     @EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
     @RequireRunOnPrimaryUser
+    @RequireMultiUserSupport
     public void createCloneProfile_noDeviceOwner_succeeds() {
         UserReference cloneUser = TestApis.users().createUser()
                 .parent(TestApis.users().instrumented())
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
index 286ccae..1c35d48 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
@@ -54,6 +54,7 @@
 import com.android.bedstead.harrier.annotations.EnsureHasPermission;
 import com.android.bedstead.harrier.annotations.Postsubmit;
 import com.android.bedstead.harrier.annotations.RequireFeature;
+import com.android.bedstead.harrier.annotations.RequireMultiUserSupport;
 import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
 import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDpc;
@@ -65,6 +66,7 @@
 import com.android.bedstead.remotedpc.RemoteDpc;
 import com.android.bedstead.testapp.TestApp;
 import com.android.bedstead.testapp.TestAppInstance;
+import com.android.compatibility.common.util.CddTest;
 import com.android.eventlib.truth.EventLogsSubject;
 import com.android.queryable.queries.ActivityQuery;
 
@@ -140,6 +142,7 @@
     @EnsureHasNoDpc
     @EnsureHasNoSecondaryUser
     @Test
+    @CddTest(requirements = {"3.9.4/C-3-1"})
     public void createAndProvisionManagedProfile_roleHolderIsInWorkProfile()
             throws ProvisioningException, InterruptedException {
         UserHandle profile = null;
@@ -172,7 +175,9 @@
     @EnsureHasDeviceOwner
     @RequireRunOnPrimaryUser
     @EnsureHasNoSecondaryUser
+    @RequireMultiUserSupport
     @Test
+    @CddTest(requirements = {"3.9.4/C-3-1"})
     public void createAndManageUser_roleHolderIsInManagedUser() throws InterruptedException {
         UserHandle managedUser = null;
         String roleHolderPackageName = null;
@@ -313,6 +318,7 @@
     @EnsureHasNoWorkProfile
     @RequireRunOnPrimaryUser
     @EnsureHasNoDpc
+    @RequireMultiUserSupport
     public void shouldAllowBypassingDevicePolicyManagementRoleQualification_withUsers_returnsFalse()
             throws Exception {
         resetInternalShouldAllowBypassingState();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java
index b6c43f1..a26d3c2 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java
@@ -23,10 +23,14 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Instrumentation;
+import android.os.SystemClock;
 import android.server.wm.TestJournalProvider.TestJournalContainer;
 import android.server.wm.backlegacyapp.Components;
 import android.support.test.uiautomator.UiDevice;
+import android.view.InputEvent;
+import android.view.MotionEvent;
 
+import androidx.annotation.NonNull;
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.GestureNavRule;
@@ -74,7 +78,45 @@
     private void doBackGesture() {
         int midHeight = mUiDevice.getDisplayHeight() / 2;
         int midWidth = mUiDevice.getDisplayWidth() / 2;
-        mUiDevice.swipe(0, midHeight, midWidth, midHeight, 100);
+        quickSwipe(0, midHeight, midWidth, midHeight, 10);
         mUiDevice.waitForIdle();
     }
+
+    private void injectInputEventUnSynced(@NonNull InputEvent event) {
+        mInstrumentation.getUiAutomation().injectInputEvent(event, false /* sync */,
+                false /* waitForAnimations */);
+    }
+
+    /**
+     * Injecting a sequence of motion event to simulate swipe without waiting for sync transaction.
+     */
+    private void quickSwipe(float startX, float startY, float endX, float endY, int steps) {
+        if (steps <= 0) {
+            steps = 1;
+        }
+        final long startDownTime = SystemClock.uptimeMillis();
+        MotionEvent firstDown = MotionEvent.obtain(startDownTime, startDownTime,
+                MotionEvent.ACTION_DOWN, startX, startY, 0);
+        injectInputEventUnSynced(firstDown);
+
+        // inject in every 5 ms.
+        final int delayMillis = 5;
+        long nextEventTime = startDownTime + delayMillis;
+        final float stepGapX = (endX - startX) / steps;
+        final float stepGapY = (endY - startY) / steps;
+        for (int i = 0; i < steps; i++) {
+            SystemClock.sleep(delayMillis);
+            final float nextX = startX + stepGapX * i;
+            final float nextY = startY + stepGapY * i;
+            MotionEvent move = MotionEvent.obtain(startDownTime, nextEventTime,
+                    MotionEvent.ACTION_MOVE, nextX, nextY, 0);
+            injectInputEventUnSynced(move);
+            nextEventTime += delayMillis;
+        }
+
+        SystemClock.sleep(delayMillis);
+        MotionEvent up = MotionEvent.obtain(startDownTime, nextEventTime,
+                MotionEvent.ACTION_UP, endX, endY, 0);
+        injectInputEventUnSynced(up);
+    }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java
index 318e6f7..530390d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java
@@ -196,14 +196,6 @@
                         null /* activityOptions */);
 
         mTaskFragmentOrganizer.applyTransaction(wct);
-
-        mTaskFragmentOrganizer.waitForTaskFragmentCreated();
-
-        TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken);
-
-        // TaskFragment must remain empty because embedding activities in a new task is not allowed.
-        assertEmptyTaskFragment(info, taskFragToken);
-
         mTaskFragmentOrganizer.waitForTaskFragmentError();
 
         assertThat(mTaskFragmentOrganizer.getThrowable()).isInstanceOf(SecurityException.class);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java
index c13c4ee..9fd1a41 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java
@@ -21,6 +21,7 @@
 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.assumeActivityEmbeddingSupportedDevice;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -33,6 +34,7 @@
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
 import android.server.wm.WindowManagerState.Task;
 import android.window.TaskFragmentCreationParams;
 import android.window.TaskFragmentInfo;
@@ -49,6 +51,7 @@
  * Build/Install/Run:
  *     atest CtsWindowManagerDeviceTestCases:TaskFragmentTrustedModeTest
  */
+@Presubmit
 public class TaskFragmentTrustedModeTest extends TaskFragmentOrganizerTestBase {
 
     private final ComponentName mTranslucentActivity = new ComponentName(mContext,
@@ -248,7 +251,7 @@
      */
     @Test
     public void testUntrustedModeTaskFragment_startActivityInTaskFragmentOutsideOfParentBounds() {
-        final Task parentTask = mWmState.getRootTask(mOwnerTaskId);
+        Task parentTask = mWmState.getRootTask(mOwnerTaskId);
         final Rect parentBounds = new Rect(parentTask.getBounds());
         final IBinder errorCallbackToken = new Binder();
         final WindowContainerTransaction wct = new WindowContainerTransaction()
@@ -263,8 +266,11 @@
         // It is disallowed to start activity to TaskFragment with bounds outside of its parent
         // in untrusted mode.
         assertTaskFragmentError(errorCallbackToken, SecurityException.class);
-        mWmState.waitForAppTransitionIdleOnDisplay(mOwnerActivity.getDisplayId());
-        mWmState.assertNotExist(SECOND_UNTRUSTED_EMBEDDING_ACTIVITY);
+
+        parentTask = mWmState.getRootTask(mOwnerTaskId);
+        assertWithMessage("Activity must be started in parent Task because it's not"
+                + " allowed to be embedded").that(parentTask.mActivities).contains(
+                mWmState.getActivity(SECOND_UNTRUSTED_EMBEDDING_ACTIVITY));
     }
 
     /**
diff --git a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java
index 08ddca1..2baa2f1 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java
@@ -33,10 +33,12 @@
 import android.content.pm.PackageManager.ResolveInfoFlags;
 import android.location.Geocoder;
 import android.location.Geocoder.GeocodeListener;
+import android.platform.test.annotations.AppModeFull;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.compatibility.common.util.ApiTest;
 import com.android.compatibility.common.util.RetryRule;
 
 import org.junit.Before;
@@ -73,6 +75,8 @@
         }
     }
 
+    @ApiTest(apis = "android.location.Geocoder#getFromLocation")
+    @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
     @Test
     public void testGetFromLocation() {
         assumeTrue(Geocoder.isPresent());
@@ -82,6 +86,8 @@
         verify(listener, timeout(10000)).onGeocode(anyList());
     }
 
+    @ApiTest(apis = "android.location.Geocoder#getFromLocation")
+    @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
     @Test
     public void testGetFromLocation_sync() throws Exception {
         assumeTrue(Geocoder.isPresent());
@@ -89,6 +95,8 @@
         mGeocoder.getFromLocation(60, 30, 5);
     }
 
+    @ApiTest(apis = "android.location.Geocoder#getFromLocation")
+    @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
     @Test
     public void testGetFromLocation_badInput() {
         GeocodeListener listener = mock(GeocodeListener.class);
@@ -102,6 +110,8 @@
                 () -> mGeocoder.getFromLocation(10, 181, 5, listener));
     }
 
+    @ApiTest(apis = "android.location.Geocoder#getFromLocationName")
+    @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
     @Test
     public void testGetFromLocationName() {
         assumeTrue(Geocoder.isPresent());
@@ -111,6 +121,8 @@
         verify(listener, timeout(10000)).onGeocode(anyList());
     }
 
+    @ApiTest(apis = "android.location.Geocoder#getFromLocationName")
+    @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
     @Test
     public void testGetFromLocationName_sync() throws Exception {
         assumeTrue(Geocoder.isPresent());
@@ -118,6 +130,8 @@
         mGeocoder.getFromLocationName("Dalvik,Iceland", 5);
     }
 
+    @ApiTest(apis = "android.location.Geocoder#getFromLocationName")
+    @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
     @Test
     public void testGetFromLocationName_badInput() {
         GeocodeListener listener = mock(GeocodeListener.class);
diff --git a/tests/media/src/android/mediav2/cts/CodecInfoTest.java b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
index 39b90ff..14ebd28 100644
--- a/tests/media/src/android/mediav2/cts/CodecInfoTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
@@ -161,7 +161,7 @@
         // For devices launching with Android T, if a codec supports an HDR profile and device
         // supports HDR display, it must advertise P010 support
         int[] HdrProfileArray = mProfileHdrMap.get(mMediaType);
-        if (FIRST_SDK_IS_AT_LEAST_T && HdrProfileArray != null && DISPLAY_HDR_TYPES.length > 0) {
+        if (VNDK_IS_AT_LEAST_T && HdrProfileArray != null && DISPLAY_HDR_TYPES.length > 0) {
             for (CodecProfileLevel pl : caps.profileLevels) {
                 if (IntStream.of(HdrProfileArray).anyMatch(x -> x == pl.profile)) {
                     assertFalse(mCodecInfo.getName() + " supports HDR profile " + pl.profile + "," +
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
index 327fb9c..d3fd292 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assume.assumeTrue;
 
+import android.hardware.camera2.CameraMetadata;
 import android.media.MediaFormat;
 import android.os.Build;
 
@@ -849,7 +850,493 @@
         }
     }
 
-    private <R extends Requirement> R addRequirement(R req) {
+    public static class PrimaryCameraRequirement extends Requirement {
+        private static final long MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION = 12000000;
+        private static final long MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION = 5000000;
+        private static final long MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION = 4000000;
+        private static final String TAG = PrimaryCameraRequirement.class.getSimpleName();
+
+        private PrimaryCameraRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setPrimaryCameraSupported(boolean hasPrimaryCamera) {
+            this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_AVAILABLE,
+                    hasPrimaryCamera);
+        }
+
+        public void setResolution(long resolution) {
+            this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_RESOLUTION,
+                    resolution);
+        }
+
+        public void setVideoSizeReqSatisfied(boolean videoSizeReqSatisfied) {
+            this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED,
+                    videoSizeReqSatisfied);
+        }
+
+        public void setVideoFps(double videoFps) {
+            this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_VIDEO_FPS, videoFps);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-1] MUST have a primary rear facing camera with a resolution of at
+         * least 12 megapixels supporting video capture at 4k@30fps
+         */
+        public static PrimaryCameraRequirement createRearPrimaryCamera() {
+            RequiredMeasurement<Boolean> hasPrimaryCamera = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.PRIMARY_CAMERA_AVAILABLE)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.R, true)
+                .addRequiredValue(Build.VERSION_CODES.S, true)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            RequiredMeasurement<Long> cameraResolution = RequiredMeasurement
+                .<Long>builder()
+                .setId(RequirementConstants.PRIMARY_CAMERA_RESOLUTION)
+                .setPredicate(RequirementConstants.LONG_GTE)
+                .addRequiredValue(Build.VERSION_CODES.R, MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION)
+                .addRequiredValue(Build.VERSION_CODES.S, MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION)
+                .build();
+
+            RequiredMeasurement<Boolean> videoSizeReqSatisfied = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.R, true)
+                .addRequiredValue(Build.VERSION_CODES.S, true)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            RequiredMeasurement<Double> videoFps = RequiredMeasurement
+                .<Double>builder()
+                .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_FPS)
+                .setPredicate(RequirementConstants.DOUBLE_GTE)
+                .addRequiredValue(Build.VERSION_CODES.R, 29.9)
+                .addRequiredValue(Build.VERSION_CODES.S, 29.9)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 29.9)
+                .build();
+
+            return new PrimaryCameraRequirement(RequirementConstants.R7_5__H_1_1,
+                    hasPrimaryCamera, cameraResolution, videoSizeReqSatisfied,
+                    videoFps);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-2] MUST have a primary front facing camera with a resolution of
+         * at least 4 megapixels supporting video capture at 1080p@30fps.
+         */
+        public static PrimaryCameraRequirement createFrontPrimaryCamera() {
+            RequiredMeasurement<Boolean> hasPrimaryCamera = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.PRIMARY_CAMERA_AVAILABLE)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.R, true)
+                .addRequiredValue(Build.VERSION_CODES.S, true)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            RequiredMeasurement<Long> cameraResolution = RequiredMeasurement
+                .<Long>builder()
+                .setId(RequirementConstants.PRIMARY_CAMERA_RESOLUTION)
+                .setPredicate(RequirementConstants.LONG_GTE)
+                .addRequiredValue(Build.VERSION_CODES.R, MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION)
+                .addRequiredValue(Build.VERSION_CODES.S, MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU,
+                        MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION)
+                .build();
+
+            RequiredMeasurement<Boolean> videoSizeReqSatisfied = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.R, true)
+                .addRequiredValue(Build.VERSION_CODES.S, true)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            RequiredMeasurement<Double> videoFps = RequiredMeasurement
+                .<Double>builder()
+                .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_FPS)
+                .setPredicate(RequirementConstants.DOUBLE_GTE)
+                .addRequiredValue(Build.VERSION_CODES.R, 29.9)
+                .addRequiredValue(Build.VERSION_CODES.S, 29.9)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 29.9)
+                .build();
+
+            return new PrimaryCameraRequirement(RequirementConstants.R7_5__H_1_2,
+                    hasPrimaryCamera, cameraResolution, videoSizeReqSatisfied,
+                    videoFps);
+        }
+    }
+
+    public static class CameraTimestampSourceRequirement extends Requirement {
+        private static final String TAG = CameraTimestampSourceRequirement.class.getSimpleName();
+        private static final int TIMESTAMP_REALTIME =
+                CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME;
+
+        private CameraTimestampSourceRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setRearCameraTimestampSource(Integer timestampSource) {
+            this.setMeasuredValue(RequirementConstants.REAR_CAMERA_TIMESTAMP_SOURCE,
+                    timestampSource);
+        }
+
+        public void setFrontCameraTimestampSource(Integer timestampSource) {
+            this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_TIMESTAMP_SOURCE,
+                    timestampSource);
+        }
+        /**
+         * [2.2.7.2/7.5/H-1-4] MUST support CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME
+         * for both primary cameras.
+         */
+        public static CameraTimestampSourceRequirement createTimestampSourceReq() {
+            RequiredMeasurement<Integer> rearTimestampSource = RequiredMeasurement
+                .<Integer>builder()
+                .setId(RequirementConstants.REAR_CAMERA_TIMESTAMP_SOURCE)
+                .setPredicate(RequirementConstants.INTEGER_EQ)
+                .addRequiredValue(Build.VERSION_CODES.R, TIMESTAMP_REALTIME)
+                .addRequiredValue(Build.VERSION_CODES.S, TIMESTAMP_REALTIME)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, TIMESTAMP_REALTIME)
+                .build();
+            RequiredMeasurement<Integer> frontTimestampSource = RequiredMeasurement
+                .<Integer>builder()
+                .setId(RequirementConstants.FRONT_CAMERA_TIMESTAMP_SOURCE)
+                .setPredicate(RequirementConstants.INTEGER_EQ)
+                .addRequiredValue(Build.VERSION_CODES.R, TIMESTAMP_REALTIME)
+                .addRequiredValue(Build.VERSION_CODES.S, TIMESTAMP_REALTIME)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, TIMESTAMP_REALTIME)
+                .build();
+
+            return new CameraTimestampSourceRequirement(RequirementConstants.R7_5__H_1_4,
+                    rearTimestampSource, frontTimestampSource);
+        }
+    }
+
+    public static class CameraLatencyRequirement extends Requirement {
+        private static final String TAG = CameraTimestampSourceRequirement.class.getSimpleName();
+
+        private CameraLatencyRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setRearCameraLatency(float latency) {
+            this.setMeasuredValue(RequirementConstants.REAR_CAMERA_LATENCY, latency);
+        }
+
+        public void setFrontCameraLatency(float latency) {
+            this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_LATENCY, latency);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-5] MUST have camera2 JPEG capture latency < 1000ms for 1080p resolution
+         * as measured by the CTS camera PerformanceTest under ITS lighting conditions
+         * (3000K) for both primary cameras.
+         */
+        public static CameraLatencyRequirement createJpegLatencyReq() {
+            RequiredMeasurement<Float> rearJpegLatency = RequiredMeasurement
+                .<Float>builder()
+                .setId(RequirementConstants.REAR_CAMERA_LATENCY)
+                .setPredicate(RequirementConstants.FLOAT_LTE)
+                .addRequiredValue(Build.VERSION_CODES.R, 1000.0f)
+                .addRequiredValue(Build.VERSION_CODES.S, 1000.0f)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1000.0f)
+                .build();
+            RequiredMeasurement<Float> frontJpegLatency = RequiredMeasurement
+                .<Float>builder()
+                .setId(RequirementConstants.FRONT_CAMERA_LATENCY)
+                .setPredicate(RequirementConstants.FLOAT_LTE)
+                .addRequiredValue(Build.VERSION_CODES.R, 1000.0f)
+                .addRequiredValue(Build.VERSION_CODES.S, 1000.0f)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1000.0f)
+                .build();
+
+            return new CameraLatencyRequirement(RequirementConstants.R7_5__H_1_5,
+                    rearJpegLatency, frontJpegLatency);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-6] MUST have camera2 startup latency (open camera to first
+         * preview frame) < 600ms as measured by the CTS camera PerformanceTest under ITS lighting
+         * conditions (3000K) for both primary cameras.
+         */
+        public static CameraLatencyRequirement createLaunchLatencyReq() {
+            RequiredMeasurement<Float> rearLaunchLatency = RequiredMeasurement
+                .<Float>builder()
+                .setId(RequirementConstants.REAR_CAMERA_LATENCY)
+                .setPredicate(RequirementConstants.FLOAT_LTE)
+                .addRequiredValue(Build.VERSION_CODES.R, 600.0f)
+                .addRequiredValue(Build.VERSION_CODES.S, 600.0f)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 600.0f)
+                .build();
+            RequiredMeasurement<Float> frontLaunchLatency = RequiredMeasurement
+                .<Float>builder()
+                .setId(RequirementConstants.FRONT_CAMERA_LATENCY)
+                .setPredicate(RequirementConstants.FLOAT_LTE)
+                .addRequiredValue(Build.VERSION_CODES.R, 600.0f)
+                .addRequiredValue(Build.VERSION_CODES.S, 600.0f)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 600.0f)
+                .build();
+
+            return new CameraLatencyRequirement(RequirementConstants.R7_5__H_1_6,
+                    rearLaunchLatency, frontLaunchLatency);
+        }
+    }
+
+    public static class CameraRawRequirement extends Requirement {
+        private static final String TAG = CameraRawRequirement.class.getSimpleName();
+
+        private CameraRawRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setRearRawSupported(boolean rearRawSupported) {
+            this.setMeasuredValue(RequirementConstants.REAR_CAMERA_RAW_SUPPORTED,
+                    rearRawSupported);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-8] MUST support CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW and
+         * android.graphics.ImageFormat.RAW_SENSOR for the primary back camera.
+         */
+        public static CameraRawRequirement createRawReq() {
+            RequiredMeasurement<Boolean> requirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.REAR_CAMERA_RAW_SUPPORTED)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.S, true)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            return new CameraRawRequirement(RequirementConstants.R7_5__H_1_8, requirement);
+        }
+    }
+
+    public static class Camera240FpsRequirement extends Requirement {
+        private static final String TAG = Camera240FpsRequirement.class.getSimpleName();
+
+        private Camera240FpsRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setRear240FpsSupported(boolean rear240FpsSupported) {
+            this.setMeasuredValue(RequirementConstants.REAR_CAMERA_240FPS_SUPPORTED,
+                    rear240FpsSupported);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-9] MUST have a rear-facing primary camera supporting 720p or 1080p @ 240fps.
+         */
+        public static Camera240FpsRequirement create240FpsReq() {
+            RequiredMeasurement<Boolean> requirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.REAR_CAMERA_240FPS_SUPPORTED)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            return new Camera240FpsRequirement(RequirementConstants.R7_5__H_1_9, requirement);
+        }
+    }
+
+    public static class UltraWideZoomRatioRequirement extends Requirement {
+        private static final String TAG =
+                UltraWideZoomRatioRequirement.class.getSimpleName();
+
+        private UltraWideZoomRatioRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setRearUltraWideZoomRatioReqMet(boolean ultrawideZoomRatioReqMet) {
+            this.setMeasuredValue(RequirementConstants.REAR_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET,
+                    ultrawideZoomRatioReqMet);
+        }
+
+        public void setFrontUltraWideZoomRatioReqMet(boolean ultrawideZoomRatioReqMet) {
+            this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET,
+                    ultrawideZoomRatioReqMet);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-10] MUST have min ZOOM_RATIO < 1.0 for the primary cameras if
+         * there is an ultrawide RGB camera facing the same direction.
+         */
+        public static UltraWideZoomRatioRequirement createUltrawideZoomRatioReq() {
+            RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.REAR_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+            RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.FRONT_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            return new UltraWideZoomRatioRequirement(RequirementConstants.R7_5__H_1_10,
+                    rearRequirement, frontRequirement);
+        }
+    }
+
+    public static class ConcurrentRearFrontRequirement extends Requirement {
+        private static final String TAG = ConcurrentRearFrontRequirement.class.getSimpleName();
+
+        private ConcurrentRearFrontRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setConcurrentRearFrontSupported(boolean concurrentRearFrontSupported) {
+            this.setMeasuredValue(RequirementConstants.CONCURRENT_REAR_FRONT_SUPPORTED,
+                    concurrentRearFrontSupported);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-11] MUST implement concurrent front-back streaming on primary cameras.
+         */
+        public static ConcurrentRearFrontRequirement createConcurrentRearFrontReq() {
+            RequiredMeasurement<Boolean> requirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.CONCURRENT_REAR_FRONT_SUPPORTED)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            return new ConcurrentRearFrontRequirement(RequirementConstants.R7_5__H_1_11,
+                    requirement);
+        }
+    }
+
+    public static class PreviewStabilizationRequirement extends Requirement {
+        private static final String TAG =
+                PreviewStabilizationRequirement.class.getSimpleName();
+
+        private PreviewStabilizationRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setRearPreviewStabilizationSupported(boolean supported) {
+            this.setMeasuredValue(RequirementConstants.REAR_CAMERA_PREVIEW_STABILIZATION_SUPPORTED,
+                    supported);
+        }
+
+        public void setFrontPreviewStabilizationSupported(boolean supported) {
+            this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_PREVIEW_STABILIZATION_SUPPORTED,
+                    supported);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-12] MUST support CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
+         * for both primary front and primary back camera.
+         */
+        public static PreviewStabilizationRequirement createPreviewStabilizationReq() {
+            RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.REAR_CAMERA_PREVIEW_STABILIZATION_SUPPORTED)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+            RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.FRONT_CAMERA_PREVIEW_STABILIZATION_SUPPORTED)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            return new PreviewStabilizationRequirement(RequirementConstants.R7_5__H_1_12,
+                    rearRequirement, frontRequirement);
+        }
+    }
+
+    public static class LogicalMultiCameraRequirement extends Requirement {
+        private static final String TAG =
+                LogicalMultiCameraRequirement.class.getSimpleName();
+
+        private LogicalMultiCameraRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setRearLogicalMultiCameraReqMet(boolean reqMet) {
+            this.setMeasuredValue(RequirementConstants.REAR_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET,
+                    reqMet);
+        }
+
+        public void setFrontLogicalMultiCameraReqMet(boolean reqMet) {
+            this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET,
+                    reqMet);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-13] MUST support LOGICAL_MULTI_CAMERA capability for the primary
+         * cameras if there are greater than 1 RGB cameras facing the same direction.
+         */
+        public static LogicalMultiCameraRequirement createLogicalMultiCameraReq() {
+            RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.REAR_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+            RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.FRONT_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            return new LogicalMultiCameraRequirement(RequirementConstants.R7_5__H_1_13,
+                    rearRequirement, frontRequirement);
+        }
+    }
+
+    public static class StreamUseCaseRequirement extends Requirement {
+        private static final String TAG =
+                StreamUseCaseRequirement.class.getSimpleName();
+
+        private StreamUseCaseRequirement(String id, RequiredMeasurement<?> ... reqs) {
+            super(id, reqs);
+        }
+
+        public void setRearStreamUseCaseSupported(boolean supported) {
+            this.setMeasuredValue(RequirementConstants.REAR_CAMERA_STREAM_USECASE_SUPPORTED,
+                    supported);
+        }
+
+        public void setFrontStreamUseCaseSupported(boolean supported) {
+            this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_STREAM_USECASE_SUPPORTED,
+                    supported);
+        }
+
+        /**
+         * [2.2.7.2/7.5/H-1-14] MUST support STREAM_USE_CASE capability for both primary
+         * front and primary back camera.
+         */
+        public static StreamUseCaseRequirement createStreamUseCaseReq() {
+            RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.REAR_CAMERA_STREAM_USECASE_SUPPORTED)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+            RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement
+                .<Boolean>builder()
+                .setId(RequirementConstants.FRONT_CAMERA_STREAM_USECASE_SUPPORTED)
+                .setPredicate(RequirementConstants.BOOLEAN_EQ)
+                .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+                .build();
+
+            return new StreamUseCaseRequirement(RequirementConstants.R7_5__H_1_14,
+                    rearRequirement, frontRequirement);
+        }
+    }
+
+    public <R extends Requirement> R addRequirement(R req) {
         if (!this.mRequirements.add(req)) {
             throw new IllegalStateException("Requirement " + req.id() + " already added");
         }
@@ -1000,16 +1487,69 @@
         return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_10());
     }
 
+    public PrimaryCameraRequirement addPrimaryRearCameraReq() {
+        return this.addRequirement(PrimaryCameraRequirement.createRearPrimaryCamera());
+    }
+
+    public PrimaryCameraRequirement addPrimaryFrontCameraReq() {
+        return this.addRequirement(PrimaryCameraRequirement.createFrontPrimaryCamera());
+    }
+
+    public CameraTimestampSourceRequirement addR7_5__H_1_4() {
+        return this.addRequirement(CameraTimestampSourceRequirement.createTimestampSourceReq());
+    }
+
+    public CameraLatencyRequirement addR7_5__H_1_5() {
+        return this.addRequirement(CameraLatencyRequirement.createJpegLatencyReq());
+    }
+
+    public CameraLatencyRequirement addR7_5__H_1_6() {
+        return this.addRequirement(CameraLatencyRequirement.createLaunchLatencyReq());
+    }
+
+    public CameraRawRequirement addR7_5__H_1_8() {
+        return this.addRequirement(CameraRawRequirement.createRawReq());
+    }
+
+    public Camera240FpsRequirement addR7_5__H_1_9() {
+        return this.addRequirement(Camera240FpsRequirement.create240FpsReq());
+    }
+
+    public UltraWideZoomRatioRequirement addR7_5__H_1_10() {
+        return this.addRequirement(UltraWideZoomRatioRequirement.createUltrawideZoomRatioReq());
+    }
+
+    public ConcurrentRearFrontRequirement addR7_5__H_1_11() {
+        return this.addRequirement(ConcurrentRearFrontRequirement.createConcurrentRearFrontReq());
+    }
+
+    public PreviewStabilizationRequirement addR7_5__H_1_12() {
+        return this.addRequirement(PreviewStabilizationRequirement.createPreviewStabilizationReq());
+    }
+
+    public LogicalMultiCameraRequirement addR7_5__H_1_13() {
+        return this.addRequirement(LogicalMultiCameraRequirement.createLogicalMultiCameraReq());
+    }
+
+    public StreamUseCaseRequirement addR7_5__H_1_14() {
+        return this.addRequirement(StreamUseCaseRequirement.createStreamUseCaseReq());
+    }
+
     public void submitAndCheck() {
-        boolean perfClassMet = true;
-        for (Requirement req: this.mRequirements) {
-            perfClassMet &= req.writeLogAndCheck(this.mTestName);
-        }
+        boolean perfClassMet = submit();
 
         // check performance class
         assumeTrue("Build.VERSION.MEDIA_PERFORMANCE_CLASS is not declared", Utils.isPerfClass());
         assertThat(perfClassMet).isTrue();
-
-        this.mRequirements.clear(); // makes sure report isn't submitted twice
     }
+
+    public boolean submit() {
+        boolean perfClassMet = true;
+        for (Requirement req: this.mRequirements) {
+            perfClassMet &= req.writeLogAndCheck(this.mTestName);
+        }
+        this.mRequirements.clear(); // makes sure report isn't submitted twice
+        return perfClassMet;
+    }
+
 }
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
index d93cb2e..ad0d0d5 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
@@ -16,8 +16,6 @@
 
 package android.mediapc.cts.common;
 
-import android.os.Build;
-
 import java.util.function.BiPredicate;
 
 public class RequirementConstants {
@@ -54,8 +52,13 @@
     public static final String R7_5__H_1_4 = "r7_5__h_1_4"; // 7.5/H-1-4
     public static final String R7_5__H_1_5 = "r7_5__h_1_5"; // 7.5/H-1-5
     public static final String R7_5__H_1_6 = "r7_5__h_1_6"; // 7.5/H-1-6
-    public static final String R7_5__H_1_7 = "r7_5__h_1_7"; // 7.5/H-1-7
     public static final String R7_5__H_1_8 = "r7_5__h_1_8"; // 7.5/H-1-8
+    public static final String R7_5__H_1_9 = "r7_5__h_1_9"; // 7.5/H-1-9
+    public static final String R7_5__H_1_10 = "r7_5__h_1_10"; // 7.5/H-1-10
+    public static final String R7_5__H_1_11 = "r7_5__h_1_11"; // 7.5/H-1-11
+    public static final String R7_5__H_1_12 = "r7_5__h_1_12"; // 7.5/H-1-12
+    public static final String R7_5__H_1_13 = "r7_5__h_1_13"; // 7.5/H-1-13
+    public static final String R7_5__H_1_14 = "r7_5__h_1_14"; // 7.5/H-1-14
     public static final String R7_1_1_1__H_1_1 = "r7_1_1_1__h_1_1"; // 7.1.1.1/H-1-1
     public static final String R7_1_1_3__H_1_1 = "r7_1_1_3__h_1_1"; // 7.1.1.3/H-1-1
     public static final String R7_6_1__H_1_1 = "r7_6_1__h_1_1"; // 7.6.1/H-1-1
@@ -90,14 +93,50 @@
     public static final String NUM_CRYPTO_HW_SECURE_ALL_SUPPORT =
         "number_crypto_hw_secure_all_support";
 
+    public static final String PRIMARY_CAMERA_AVAILABLE = "primary_camera_available";
+    public static final String PRIMARY_CAMERA_RESOLUTION = "primary_camera_resolution";
+    public static final String PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED =
+            "primary_camera_video_size_req_satisfied";
+    public static final String PRIMARY_CAMERA_VIDEO_FPS =
+            "primary_camera_video_fps";
+    public static final String REAR_CAMERA_HWL_LEVEL = "rear_primary_camera_hwl_level";
+    public static final String FRONT_CAMERA_HWL_LEVEL = "front_primary_camera_hwl_level";
+    public static final String REAR_CAMERA_TIMESTAMP_SOURCE =
+            "rear_primary_camera_timestamp_source";
+    public static final String FRONT_CAMERA_TIMESTAMP_SOURCE =
+            "front_primary_camera_timestamp_source";
+    public static final String REAR_CAMERA_LATENCY = "rear_camera_latency";
+    public static final String FRONT_CAMERA_LATENCY = "front_camera_latency";
+    public static final String REAR_CAMERA_RAW_SUPPORTED = "rear_camera_raw_supported";
+    public static final String REAR_CAMERA_240FPS_SUPPORTED = "rear_camera_240fps_supported";
+    public static final String REAR_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET =
+            "rear_camera_ultrawide_zoom_req_met";
+    public static final String FRONT_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET =
+            "front_camera_ultrawide_zoom_req_met";
+    public static final String CONCURRENT_REAR_FRONT_SUPPORTED = "rear_front_concurrent_camera";
+    public static final String REAR_CAMERA_PREVIEW_STABILIZATION_SUPPORTED =
+            "rear_camera_preview_stabilization_supported";
+    public static final String FRONT_CAMERA_PREVIEW_STABILIZATION_SUPPORTED =
+            "front_camera_preview_stabilization_supported";
+    public static final String REAR_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET =
+            "rear_camera_logical_multi_camera_req_met";
+    public static final String FRONT_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET =
+            "front_camera_logical_multi_camera_req_met";
+    public static final String REAR_CAMERA_STREAM_USECASE_SUPPORTED =
+            "rear_camera_stream_usecase_supported";
+    public static final String FRONT_CAMERA_STREAM_USECASE_SUPPORTED =
+            "front_camera_stream_usecase_supported";
+
     public enum Result {
         NA, MET, UNMET
     }
 
     public static final BiPredicate<Long, Long> LONG_GTE = RequirementConstants.gte();
     public static final BiPredicate<Long, Long> LONG_LTE = RequirementConstants.lte();
+    public static final BiPredicate<Float, Float> FLOAT_LTE = RequirementConstants.lte();
     public static final BiPredicate<Integer, Integer> INTEGER_GTE = RequirementConstants.gte();
     public static final BiPredicate<Integer, Integer> INTEGER_LTE = RequirementConstants.lte();
+    public static final BiPredicate<Integer, Integer> INTEGER_EQ = RequirementConstants.eq();
     public static final BiPredicate<Double, Double> DOUBLE_EQ = RequirementConstants.eq();
     public static final BiPredicate<Boolean, Boolean> BOOLEAN_EQ = RequirementConstants.eq();
     public static final BiPredicate<Double, Double> DOUBLE_GTE = RequirementConstants.gte();
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
index ac03705..28a122b 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
@@ -73,16 +73,24 @@
 
         Context context = InstrumentationRegistry.getInstrumentation().getContext();
         DisplayMetrics metrics = new DisplayMetrics();
-        WindowManager windowManager = context.getSystemService(WindowManager.class);
-        windowManager.getDefaultDisplay().getMetrics(metrics);
-        DISPLAY_DPI = metrics.densityDpi;
-        DISPLAY_LONG_PIXELS = Math.max(metrics.widthPixels, metrics.heightPixels);
-        DISPLAY_SHORT_PIXELS = Math.min(metrics.widthPixels, metrics.heightPixels);
+        // When used from ItsService, context will be null
+        if (context != null) {
+            WindowManager windowManager = context.getSystemService(WindowManager.class);
+            windowManager.getDefaultDisplay().getMetrics(metrics);
+            DISPLAY_DPI = metrics.densityDpi;
+            DISPLAY_LONG_PIXELS = Math.max(metrics.widthPixels, metrics.heightPixels);
+            DISPLAY_SHORT_PIXELS = Math.min(metrics.widthPixels, metrics.heightPixels);
 
-        ActivityManager activityManager = context.getSystemService(ActivityManager.class);
-        ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
-        activityManager.getMemoryInfo(memoryInfo);
-        TOTAL_MEMORY_MB = memoryInfo.totalMem / 1024 / 1024;
+            ActivityManager activityManager = context.getSystemService(ActivityManager.class);
+            ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
+            activityManager.getMemoryInfo(memoryInfo);
+            TOTAL_MEMORY_MB = memoryInfo.totalMem / 1024 / 1024;
+        } else {
+            DISPLAY_DPI = 0;
+            DISPLAY_LONG_PIXELS = 0;
+            DISPLAY_SHORT_PIXELS = 0;
+            TOTAL_MEMORY_MB = 0;
+        }
     }
 
     /**
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
index 4766268..ef44528 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
@@ -41,6 +41,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.SystemProperties;
 import android.platform.test.annotations.LargeTest;
 import android.platform.test.annotations.RequiresDevice;
 import android.system.ErrnoException;
@@ -1012,6 +1013,9 @@
     public void testDecode10BitHEIFTo10BitBitmap() {
         assumeTrue(
             "Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU));
+        assumeTrue(
+            "Test needs VNDK at least T.",
+            SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU);
         assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder());
 
         BitmapFactory.Options opt = new BitmapFactory.Options();
@@ -1028,6 +1032,9 @@
     public void testDecode10BitHEIFTo8BitBitmap() {
         assumeTrue(
             "Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU));
+        assumeTrue(
+            "Test needs VNDK at least T.",
+            SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU);
         assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder());
 
         BitmapFactory.Options opt = new BitmapFactory.Options();
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index 6741c07..b6689d8 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -49,6 +49,7 @@
 import android.media.MediaFormat;
 import android.net.Uri;
 import android.os.Build;
+import android.os.SystemProperties;
 import android.util.DisplayMetrics;
 import android.util.Size;
 import android.util.TypedValue;
@@ -246,6 +247,9 @@
     public void testDecode10BitHeif() {
         assumeTrue(
             "Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU));
+        assumeTrue(
+            "Test needs VNDK at least T.",
+            SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU);
         assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder());
 
         try {
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 3565fc8..4aa9db4 100644
--- a/tests/tests/media/common/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/common/src/android/media/cts/CodecState.java
@@ -169,13 +169,15 @@
         }
     }
 
-    public void start() {
+    public void startCodec() {
         mCodec.start();
         mCodecInputBuffers = mCodec.getInputBuffers();
         if (!mIsTunneled || mIsAudio) {
             mCodecOutputBuffers = mCodec.getOutputBuffers();
         }
+    }
 
+    public void play() {
         if (mAudioTrack != null) {
             mAudioTrack.play();
         }
diff --git a/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java b/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java
index 5e4df7f..888cf23 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java
@@ -487,11 +487,13 @@
         }
 
         for (CodecState state : mVideoCodecStates.values()) {
-            state.start();
+            state.startCodec();
+            state.play();
         }
 
         for (CodecState state : mAudioCodecStates.values()) {
-            state.start();
+            state.startCodec();
+            state.play();
         }
 
         mDeltaTimeUs = -1;
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 0b495dd..879f561 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
@@ -44,11 +44,13 @@
     /** 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;
+    private static final int STATE_PREPARED = 2;
+    /** State of the player after starting the codecs */
+    private static final int STATE_STARTED = 3;
     /** 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 static final int STATE_PLAYING = 4;
+    /** State of the player when playback is paused. */
+    private static final int STATE_PAUSED = 5;
 
     private Boolean mThreadStarted = false;
     private byte[] mSessionId;
@@ -194,7 +196,12 @@
         return true;
     }
 
+    // Creates the extractors, identifies tracks and formats, and then calls MediaCodec.configure
     public boolean prepare() throws IOException {
+        if (mState != STATE_IDLE) {
+            throw new IllegalStateException("Expected STATE_IDLE, got " + mState);
+        }
+
         if (null == mAudioExtractor) {
             mAudioExtractor = new MediaExtractor();
             if (null == mAudioExtractor) {
@@ -237,9 +244,7 @@
             return false;
         }
 
-        synchronized (mState) {
-            mState = STATE_PAUSED;
-        }
+        mState = STATE_PREPARED;
         return true;
     }
 
@@ -306,70 +311,56 @@
         return format.containsKey(key) ? format.getInteger(key) : 0;
     }
 
-    public boolean start() {
+    // Calls MediaCodec.start
+    public void startCodec() {
         Log.d(TAG, "start");
 
-        synchronized (mState) {
-            if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
-                return true;
-            } else if (mState == STATE_IDLE) {
-                mState = STATE_PREPARING;
-                return true;
-            } else if (mState != STATE_PAUSED) {
-                throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
-            }
-
-            for (CodecState state : mVideoCodecStates.values()) {
-                state.start();
-            }
-
-            for (CodecState state : mAudioCodecStates.values()) {
-                state.start();
-            }
-
-            mDeltaTimeUs = -1;
-            mState = STATE_PLAYING;
+        if (mState != STATE_PREPARED) {
+            throw new IllegalStateException("Expected STATE_PREAPRED, got " + mState);
         }
-        return false;
+
+        for (CodecState state : mVideoCodecStates.values()) {
+            state.startCodec();
+        }
+
+        for (CodecState state : mAudioCodecStates.values()) {
+            state.startCodec();
+        }
+
+        mDeltaTimeUs = -1;
+        mState = STATE_STARTED;
     }
 
-    public void startWork() throws IOException, Exception {
-        try {
-            // Just change state from STATE_IDLE to STATE_PREPARING.
-            start();
-            // Extract media information from uri asset, and change state to STATE_PAUSED.
-            prepare();
-            // Start CodecState, and change from STATE_PAUSED to STATE_PLAYING.
-            start();
-        } catch (IOException e) {
-            throw e;
+    // Starts the decoding threads and then starts AudioTrack playback
+    public void play() {
+        if (mState != STATE_STARTED) {
+            throw new IllegalStateException("Expected STATE_STARTED, got " + mState);
         }
+        mState = STATE_PLAYING;
 
         synchronized (mThreadStarted) {
             mThreadStarted = true;
             mThread.start();
         }
-    }
 
-    public void startThread() {
-        start();
-        synchronized (mThreadStarted) {
-            mThreadStarted = true;
-            mThread.start();
+        for (CodecState state : mVideoCodecStates.values()) {
+            state.play();
+        }
+
+        for (CodecState state : mAudioCodecStates.values()) {
+            state.play();
         }
     }
 
-    // Pauses the audio track
+    // Pauses playback by pausing the AudioTrack
     public void pause() {
         Log.d(TAG, "pause");
 
-        synchronized (mState) {
-            if (mState == STATE_PAUSED) {
-                return;
-            } else if (mState != STATE_PLAYING) {
-                throw new IllegalStateException();
-            }
+        if (mState != STATE_PLAYING) {
+            throw new IllegalStateException("Expected STATE_PLAYING, got " + mState);
+        }
 
+        synchronized (mState) {
             for (CodecState state : mVideoCodecStates.values()) {
                 state.pause();
             }
@@ -382,43 +373,60 @@
         }
     }
 
-    public void flush() {
-        Log.d(TAG, "flush");
+    // Resume playback when paused
+    public void resume() {
+        Log.d(TAG, "resume");
+
+        if (mState != STATE_PAUSED) {
+            throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
+        }
 
         synchronized (mState) {
-            if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
-                return;
+            for (CodecState state : mVideoCodecStates.values()) {
+                state.play();
             }
 
             for (CodecState state : mAudioCodecStates.values()) {
-                state.flush();
+                state.play();
             }
 
-            for (CodecState state : mVideoCodecStates.values()) {
-                state.flush();
-            }
+            mState = STATE_PLAYING;
         }
     }
 
-    /** Seek all tracks to their very beginning.
+    public void flush() {
+        Log.d(TAG, "flush");
+
+        if (mState != STATE_PAUSED) {
+            throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
+        }
+
+        for (CodecState state : mAudioCodecStates.values()) {
+            state.flush();
+        }
+
+        for (CodecState state : mVideoCodecStates.values()) {
+            state.flush();
+        }
+    }
+
+    /** Seek all tracks to the first sample time.
      *
      * @param  presentationTimeOffsetUs The offset for the presentation time to start at.
      * @throws IllegalStateException  if the player is not paused
      */
     public void seekToBeginning(long presentationTimeOffsetUs) {
         Log.d(TAG, "seekToBeginning");
-        synchronized (mState) {
-            if (mState != STATE_PAUSED) {
-                throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
-            }
+        if (mState != STATE_PAUSED) {
+            throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
+        }
 
-            for (CodecState state : mVideoCodecStates.values()) {
-                state.seekToBeginning(presentationTimeOffsetUs);
-            }
+        for (CodecState state : mVideoCodecStates.values()) {
+            state.seekToBeginning(presentationTimeOffsetUs);
+        }
 
-            for (CodecState state : mAudioCodecStates.values()) {
-                state.seekToBeginning(presentationTimeOffsetUs);
-            }
+        for (CodecState state : mAudioCodecStates.values()) {
+            state.seekToBeginning(presentationTimeOffsetUs);
         }
     }
 
@@ -426,53 +434,50 @@
      * Enables or disables looping. Should be called after {@link #prepare()}.
      */
     public void setLoopEnabled(boolean enabled) {
-        synchronized (mState) {
-            if (mVideoCodecStates != null) {
-                for (CodecState state : mVideoCodecStates.values()) {
-                    state.setLoopEnabled(enabled);
-                }
-            }
+        if (mState != STATE_PREPARED) {
+            throw new IllegalStateException("Expected STATE_PREPARED, got " + mState);
+        }
 
-            if (mAudioCodecStates != null) {
-                for (CodecState state : mAudioCodecStates.values()) {
-                    state.setLoopEnabled(enabled);
-                }
-            }
+        for (CodecState state : mVideoCodecStates.values()) {
+            state.setLoopEnabled(enabled);
+        }
+
+        for (CodecState state : mAudioCodecStates.values()) {
+            state.setLoopEnabled(enabled);
         }
     }
 
     public void reset() {
-        synchronized (mState) {
-            if (mState == STATE_PLAYING) {
-                pause();
-            }
-            if (mVideoCodecStates != null) {
-                for (CodecState state : mVideoCodecStates.values()) {
-                    state.release();
-                }
-                mVideoCodecStates = null;
-            }
-
-            if (mAudioCodecStates != null) {
-                for (CodecState state : mAudioCodecStates.values()) {
-                    state.release();
-                }
-                mAudioCodecStates = null;
-            }
-
-            if (mAudioExtractor != null) {
-                mAudioExtractor.release();
-                mAudioExtractor = null;
-            }
-
-            if (mVideoExtractor != null) {
-                mVideoExtractor.release();
-                mVideoExtractor = null;
-            }
-
-            mDurationUs = -1;
-            mState = STATE_IDLE;
+        if (mState == STATE_PLAYING) {
+            pause();
         }
+        if (mVideoCodecStates != null) {
+            for (CodecState state : mVideoCodecStates.values()) {
+                state.release();
+            }
+            mVideoCodecStates = null;
+        }
+
+        if (mAudioCodecStates != null) {
+            for (CodecState state : mAudioCodecStates.values()) {
+                state.release();
+            }
+            mAudioCodecStates = null;
+        }
+
+        if (mAudioExtractor != null) {
+            mAudioExtractor.release();
+            mAudioExtractor = null;
+        }
+
+        if (mVideoExtractor != null) {
+            mVideoExtractor.release();
+            mVideoExtractor = null;
+        }
+
+        mDurationUs = -1;
+        mState = STATE_IDLE;
+
         synchronized (mThreadStarted) {
             mThreadStarted = false;
         }
@@ -607,6 +612,14 @@
         return mVideoCodecStates.get(0).getVideoTimeUs();
     }
 
+    public long getVideoSystemTimeNs() {
+        if (mVideoCodecStates == null || mVideoCodecStates.get(0) == null) {
+            return -1;
+        }
+        return mVideoCodecStates.get(0).getVideoTimeUs();
+
+    }
+
     /**
      * Returns the ordered list of video frame timestamps rendered in tunnel mode.
      *
@@ -643,39 +656,23 @@
     public Long queueOneVideoFrame() {
         Log.d(TAG, "queueOneVideoFrame");
 
-        if (mVideoCodecStates == null || !(mState == STATE_PLAYING || mState == STATE_PAUSED)) {
-            return null;
+        if (mState != STATE_STARTED && mState != STATE_PAUSED) {
+            throw new IllegalStateException("Expected STARTED or PAUSED, got " + mState);
         }
 
         Long result = null;
-        for (CodecState state : mVideoCodecStates.values()) {
-            Long timestamp = state.doSomeWork(true /* mustWait */);
-            if (timestamp != null) {
-                result = timestamp;
+        if (mVideoCodecStates != null) {
+            for (CodecState state : mVideoCodecStates.values()) {
+                Long timestamp = state.doSomeWork(true /* mustWait */);
+                if (timestamp != null) {
+                    result = timestamp;
+                }
             }
         }
         return result;
     }
 
     /**
-     * Resume playback when paused.
-     *
-     * @throws IllegalStateException if playback is not paused or if there is no configured audio
-     *                               track.
-     */
-    public void resume() {
-        Log.d(TAG, "resume");
-        if (mAudioTrackState == null) {
-            throw new IllegalStateException("Resuming playback with no audio track");
-        }
-        if (mState != STATE_PAUSED) {
-            throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
-        }
-        mAudioTrackState.playAudioTrack();
-        mState = STATE_PLAYING;
-    }
-
-    /**
      * Configure video peek for the video codecs attached to the player.
      */
     public void setVideoPeek(boolean enable) {
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
index 145cfaf..c982376 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
@@ -25,7 +25,6 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.media.MediaFormat;
-import android.media.cts.MediaCodecTunneledPlayer;
 import android.media.cts.MediaHeavyPresubmitTest;
 import android.media.cts.TestArgs;
 import android.os.Environment;
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
index a424edb..9926f04 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
@@ -73,6 +73,7 @@
 import androidx.test.filters.SdkSuppress;
 
 import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.compatibility.common.util.ApiTest;
 import com.android.compatibility.common.util.CddTest;
 import com.android.compatibility.common.util.DeviceReportLog;
 import com.android.compatibility.common.util.DynamicConfigDeviceSide;
@@ -124,10 +125,10 @@
     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
+    public static final int CODEC_ALL = 0; // All codecs must support
+    public static final int CODEC_ANY = 1; // At least one codec must support
+    public static final int CODEC_DEFAULT = 2; // Default codec must support
+    public static final int CODEC_OPTIONAL = 3; // Codec support is optional
 
     short[] mMasterBuffer;
     static final String mInpPrefix = WorkDir.getMediaDirString();
@@ -141,8 +142,6 @@
     private DisplayManager mDisplayManager;
     static final Map<String, String> sDefaultDecoders = new HashMap<>();
 
-    private static boolean mIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
-
     protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
             throws FileNotFoundException {
         File inpFile = new File(mInpPrefix + res);
@@ -3884,11 +3883,10 @@
         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());
+        mMediaCodecPlayer.startCodec();
 
-        // starts video playback
-        mMediaCodecPlayer.startThread();
+        mMediaCodecPlayer.play();
         sleepUntil(() ->
                 mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
                 && mMediaCodecPlayer.getTimestamp() != null
@@ -3921,8 +3919,8 @@
     /**
      * Test tunneled video playback mode with HEVC if supported
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledVideoPlaybackHevc() throws Exception {
         tunneledVideoPlayback(MediaFormat.MIMETYPE_VIDEO_HEVC,
                     "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
@@ -3931,8 +3929,8 @@
     /**
      * Test tunneled video playback mode with AVC if supported
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledVideoPlaybackAvc() throws Exception {
         tunneledVideoPlayback(MediaFormat.MIMETYPE_VIDEO_AVC,
                 "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
@@ -3941,8 +3939,8 @@
     /**
      * Test tunneled video playback mode with VP9 if supported
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledVideoPlaybackVp9() throws Exception {
         tunneledVideoPlayback(MediaFormat.MIMETYPE_VIDEO_VP9,
                     "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
@@ -3966,11 +3964,10 @@
         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());
+        mMediaCodecPlayer.startCodec();
 
-        // starts video playback
-        mMediaCodecPlayer.startThread();
+        mMediaCodecPlayer.play();
         sleepUntil(() ->
                 mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
                 && mMediaCodecPlayer.getTimestamp() != null
@@ -3990,8 +3987,8 @@
     /**
      * Test tunneled video playback flush with HEVC if supported
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledVideoFlushHevc() throws Exception {
         testTunneledVideoFlush(MediaFormat.MIMETYPE_VIDEO_HEVC,
                 "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
@@ -4000,8 +3997,8 @@
     /**
      * Test tunneled video playback flush with AVC if supported
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledVideoFlushAvc() throws Exception {
         testTunneledVideoFlush(MediaFormat.MIMETYPE_VIDEO_AVC,
                 "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
@@ -4010,23 +4007,19 @@
     /**
      * Test tunneled video playback flush with VP9 if supported
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledVideoFlushVp9() throws Exception {
         testTunneledVideoFlush(MediaFormat.MIMETYPE_VIDEO_VP9,
                 "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
     }
 
     /**
-     * Test tunneled video peek renders the first frame when on
+     * Test that the first frame is rendered when video peek is on in tunneled mode.
      *
      * TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content
      */
     private void testTunneledVideoPeekOn(String mimeType, String videoName) throws Exception {
-        if (!MediaUtils.check(mIsAtLeastS, "testTunneledVideoPeekOn requires Android 12")) {
-            return;
-        }
-
         if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
                     "No tunneled video playback codec found for MIME " + mimeType)) {
             return;
@@ -4040,9 +4033,8 @@
         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());
-        mMediaCodecPlayer.start();
+        mMediaCodecPlayer.startCodec();
         mMediaCodecPlayer.setVideoPeek(true); // Enable video peek
 
         // Assert that onFirstTunnelFrameReady is called
@@ -4061,30 +4053,30 @@
     }
 
     /**
-     * Test tunneled video peek with HEVC renders the first frame when on
+     * Test that the first frame is rendered when video peek is on for HEVC in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
     public void testTunneledVideoPeekOnHevc() throws Exception {
         testTunneledVideoPeekOn(MediaFormat.MIMETYPE_VIDEO_HEVC,
                 "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
     }
 
     /**
-     * Test tunneled video peek with AVC renders the first frame when on
+     * Test that the first frame is rendered when video peek is on for AVC in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
     public void testTunneledVideoPeekOnAvc() throws Exception {
         testTunneledVideoPeekOn(MediaFormat.MIMETYPE_VIDEO_AVC,
                 "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
     }
 
     /**
-     * Test tunneled video peek with VP9 renders the first frame when on
+     * Test that the first frame is rendered when video peek is on for VP9 in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
     public void testTunneledVideoPeekOnVp9() throws Exception {
         testTunneledVideoPeekOn(MediaFormat.MIMETYPE_VIDEO_VP9,
                 "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
@@ -4092,15 +4084,11 @@
 
 
     /**
-     * Test tunneled video peek doesn't render the first frame when off and then turned on
+     * Test that peek off doesn't render the first frame until turned on in tunneled mode.
      *
      * TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content
      */
     private void testTunneledVideoPeekOff(String mimeType, String videoName) throws Exception {
-        if (!MediaUtils.check(mIsAtLeastS, "testTunneledVideoPeekOff requires Android 12")) {
-            return;
-        }
-
         if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
                     "No tunneled video playback codec found for MIME " + mimeType)) {
             return;
@@ -4114,9 +4102,8 @@
         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());
-        mMediaCodecPlayer.start();
+        mMediaCodecPlayer.startCodec();
         mMediaCodecPlayer.setVideoPeek(false); // Disable video peek
 
         // Assert that onFirstTunnelFrameReady is called
@@ -4142,75 +4129,40 @@
     }
 
     /**
-     * Test tunneled video peek with HEVC doesn't render the first frame when off and then turned on
+     * Test that peek off doesn't render the first frame until turned on for HEC in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
     public void testTunneledVideoPeekOffHevc() throws Exception {
         testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_HEVC,
                 "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
     }
 
     /**
-     * Test tunneled video peek with AVC doesn't render the first frame when off and then turned on
+     * Test that peek off doesn't render the first frame until turned on for AVC in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
     public void testTunneledVideoPeekOffAvc() throws Exception {
         testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_AVC,
                 "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
     }
 
     /**
-     * Test tunneled video peek with VP9 doesn't render the first frame when off and then turned on
+     * Test that peek off doesn't render the first frame until turned on for VP9 in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
     public void testTunneledVideoPeekOffVp9() throws Exception {
         testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_VP9,
                 "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
     }
 
-    /**
-     * 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 {
+   /**
+    * Test that audio timestamps don't progress during audio PTS gaps in tunneled mode.
+    */
+   private void testTunneledAudioProgressWithPtsGaps(String mimeType, String fileName)
+            throws Exception {
         if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
                     "No tunneled video playback codec found for MIME " + mimeType)) {
             return;
@@ -4224,11 +4176,10 @@
         final Uri mediaUri = Uri.fromFile(new File(mInpPrefix, fileName));
         mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
         mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
-        assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
         assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+        mMediaCodecPlayer.startCodec();
 
-        // starts video playback
-        mMediaCodecPlayer.startThread();
+        mMediaCodecPlayer.play();
         sleepUntil(() ->
                 mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
                 && mMediaCodecPlayer.getTimestamp() != null
@@ -4291,37 +4242,40 @@
     }
 
     /**
-     * Test tunneled audioTimestamp progress with underrun, with HEVC if supported
+     * Test that audio timestamps don't progress during audio PTS gaps for HEVC in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
-    public void testTunneledAudioTimestampProgressWithUnderrunHevc() throws Exception {
-        testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC,
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+    public void testTunneledAudioProgressWithPtsGapsHevc() throws Exception {
+        testTunneledAudioProgressWithPtsGaps(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.
+     * Test that audio timestamps don't progress during audio PTS gaps for AVC in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
-    public void testTunneledAudioTimestampProgressWithUnderrunAvc() throws Exception {
-        testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC,
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+    public void testTunneledAudioProgressWithPtsGapsAvc() throws Exception {
+        testTunneledAudioProgressWithPtsGaps(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.
+     * Test that audio timestamps don't progress during audio PTS gaps for VP9 in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
-    public void testTunneledAudioTimestampProgressWithUnderrunVp9() throws Exception {
-        testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9,
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+    public void testTunneledAudioProgressWithPtsGapsVp9() throws Exception {
+        testTunneledAudioProgressWithPtsGaps(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 {
+    /**
+     * Test that audio timestamps stop progressing during underrun in tunneled mode.
+     */
+    private void testTunneledAudioProgressWithUnderrun(String mimeType, String fileName)
+            throws Exception {
         if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
                 "No tunneled video playback codec found for MIME " + mimeType)) {
             return;
@@ -4335,11 +4289,10 @@
         final Uri mediaUri = Uri.fromFile(new File(mInpPrefix, fileName));
         mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
         mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
-        assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
         assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+        mMediaCodecPlayer.startCodec();
 
-        // starts video playback
-        mMediaCodecPlayer.startThread();
+        mMediaCodecPlayer.play();
         sleepUntil(() ->
                 mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
                 && mMediaCodecPlayer.getTimestamp() != null
@@ -4380,9 +4333,39 @@
     }
 
     /**
-     * Test accurate video rendering after a video MediaCodec flush.
+     * Test that audio timestamps stop progressing during underrun for HEVC in tunneled mode.
+     */
+    @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+    public void testTunneledAudioProgressWithUnderrunHevc() throws Exception {
+        testTunneledAudioProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC,
+                "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
+    }
+
+    /**
+     * Test that audio timestamps stop progressing during underrun for AVC in tunneled mode.
+     */
+    @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+    public void testTunneledAudioProgressWithUnderrunAvc() throws Exception {
+        testTunneledAudioProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC,
+                "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
+    }
+
+    /**
+     * Test that audio timestamps stop progressing during underrun for VP9 in tunneled mode.
+     */
+    @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+    public void testTunneledAudioProgressWithUnderrunVp9() throws Exception {
+        testTunneledAudioProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9,
+                "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
+    }
+
+    /**
+     * Test accurate video rendering after a flush in tunneled mode.
      *
-     * On some devices, queuing content when the player is paused, then triggering a flush, then
+     * Test On some devices, queuing content when the player is paused, then triggering a flush, then
      * queuing more content does not behave as expected. The queued content gets lost and the flush
      * is really only applied once playback has resumed.
      *
@@ -4390,10 +4373,6 @@
      */
     private void testTunneledAccurateVideoFlush(String mimeType, String videoName)
             throws Exception {
-        if (!MediaUtils.check(mIsAtLeastS, "testTunneledAccurateVideoFlush requires Android 12")) {
-            return;
-        }
-
         if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
                     "No tunneled video playback codec found for MIME " + mimeType)) {
             return;
@@ -4414,15 +4393,14 @@
         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());
+        mMediaCodecPlayer.startCodec();
         // Video peek might interfere with the test: we want to ensure that queuing more data during
         // a pause does not cause displaying more video frames, which is precisely what video peek
         // does.
         mMediaCodecPlayer.setVideoPeek(false);
 
-        // starts video playback
-        mMediaCodecPlayer.startThread();
+        mMediaCodecPlayer.play();
         sleepUntil(() ->
                 mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
                 && mMediaCodecPlayer.getTimestamp() != null
@@ -4434,22 +4412,72 @@
         assertNotEquals("Audio timestamp has a zero frame position",
                 mMediaCodecPlayer.getTimestamp().framePosition, 0);
 
+        // Allow some time for playback to commence
+        Thread.sleep(500);
+
         // Pause playback
         mMediaCodecPlayer.pause();
-        // Allow some time for playback to pause
-        Thread.sleep(maxDrainTimeMs);
 
-        // Verify that playback has paused
-        long pauseAudioFramePositionUs = mMediaCodecPlayer.getTimestamp().framePosition;
-        long pauseVideoPositionUs = mMediaCodecPlayer.getVideoTimeUs();
-        Thread.sleep(maxDrainTimeMs);
-        assertEquals(mMediaCodecPlayer.getTimestamp().framePosition, pauseAudioFramePositionUs);
+        // Wait for audio to pause
+        AudioTimestamp pauseAudioTimestamp;
+        {
+            AudioTimestamp currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+            long startTimeMs = System.currentTimeMillis();
+            do {
+                // If it takes longer to pause, the UX won't feel responsive to the user
+                int audioPauseTimeoutMs = 250;
+                assertTrue(String.format("No audio pause after %d milliseconds",
+                                audioPauseTimeoutMs),
+                        System.currentTimeMillis() - startTimeMs < audioPauseTimeoutMs);
+                pauseAudioTimestamp = currentAudioTimestamp;
+                Thread.sleep(50);
+                currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+            } while (currentAudioTimestamp.framePosition != pauseAudioTimestamp.framePosition);
+        }
+        long pauseAudioSystemTimeMs = pauseAudioTimestamp.nanoTime / 1000 / 1000;
+
+        // Wait for video to pause
+        long pauseVideoSystemTimeNs;
+        long pauseVideoPositionUs;
+        {
+            long currentVideoSystemTimeNs = mMediaCodecPlayer.getCurrentRenderedSystemTimeNano();
+            long startTimeMs = System.currentTimeMillis();
+            do {
+                int videoUnderrunTimeoutMs = 2000;
+                assertTrue(String.format("No video pause after %d milliseconds",
+                                videoUnderrunTimeoutMs),
+                        System.currentTimeMillis() - startTimeMs < videoUnderrunTimeoutMs);
+                pauseVideoSystemTimeNs = currentVideoSystemTimeNs;
+                Thread.sleep(250); // onFrameRendered can get delayed in the Framework
+                currentVideoSystemTimeNs = mMediaCodecPlayer.getCurrentRenderedSystemTimeNano();
+            } while (currentVideoSystemTimeNs != pauseVideoSystemTimeNs);
+            pauseVideoPositionUs = mMediaCodecPlayer.getVideoTimeUs();
+        }
+        long pauseVideoSystemTimeMs = pauseVideoSystemTimeNs / 1000 / 1000;
+
+        // Video should not continue running for a long period of time after audio pauses
+        long pauseVideoToleranceMs = 500;
+        assertTrue(String.format(
+                        "Video ran %d milliseconds longer than audio (video:%d audio:%d)",
+                        pauseVideoToleranceMs, pauseVideoSystemTimeMs, pauseAudioSystemTimeMs),
+                pauseVideoSystemTimeMs - pauseAudioSystemTimeMs < pauseVideoToleranceMs);
+
+        // Verify that playback stays paused
+        Thread.sleep(500);
+        assertEquals(mMediaCodecPlayer.getTimestamp().framePosition, pauseAudioTimestamp.framePosition);
+        assertEquals(mMediaCodecPlayer.getCurrentRenderedSystemTimeNano(), pauseVideoSystemTimeNs);
         assertEquals(mMediaCodecPlayer.getVideoTimeUs(), pauseVideoPositionUs);
 
-        // Verify audio and video are in sync
-        assertTrue(String.format("Video pts (%d) is ahead of audio pts (%d)",
-                        pauseVideoPositionUs, pauseAudioFramePositionUs),
-                pauseVideoPositionUs <= pauseAudioFramePositionUs);
+        // Verify audio and video are roughly in sync when paused
+        long framePosition = mMediaCodecPlayer.getTimestamp().framePosition;
+        long playbackRateFps = mMediaCodecPlayer.getAudioTrack().getPlaybackRate();
+        long pauseAudioPositionMs = pauseAudioTimestamp.framePosition * 1000 / playbackRateFps;
+        long pauseVideoPositionMs = pauseVideoPositionUs / 1000;
+        long deltaMs = pauseVideoPositionMs - pauseAudioPositionMs;
+        assertTrue(String.format(
+                        "Video is %d milliseconds out of sync from audio (video:%d audio:%d)",
+                        deltaMs, pauseVideoPositionMs, pauseAudioPositionMs),
+                deltaMs > -80 && deltaMs < pauseVideoToleranceMs);
 
         // Flush both audio and video pipelines
         mMediaCodecPlayer.flush();
@@ -4493,8 +4521,8 @@
     /**
      * Test accurate video rendering after a video MediaCodec flush with HEVC if supported
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledAccurateVideoFlushHevc() throws Exception {
         testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_HEVC,
                 "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
@@ -4503,8 +4531,8 @@
     /**
      * Test accurate video rendering after a video MediaCodec flush with AVC if supported
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledAccurateVideoFlushAvc() throws Exception {
         testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_AVC,
                 "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
@@ -4513,49 +4541,18 @@
     /**
      * Test accurate video rendering after a video MediaCodec flush with VP9 if supported
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledAccurateVideoFlushVp9() throws Exception {
         testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_VP9,
                 "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
     }
 
     /**
-     * Test tunneled audioTimestamp progress with HEVC if supported
+     * Test that audio timestamps stop progressing during pause in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
-    @Test
-    public void testTunneledAudioTimestampProgressHevc() throws Exception {
-        testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_HEVC,
-                "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
-    }
-
-    /**
-     * Test tunneled audioTimestamp progress with AVC if supported
-     */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
-    @Test
-    public void testTunneledAudioTimestampProgressAvc() throws Exception {
-        testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_AVC,
-                "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
-    }
-
-    /**
-     * Test tunneled audioTimestamp progress with VP9 if supported
-     */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
-    @Test
-    public void testTunneledAudioTimestampProgressVp9() throws Exception {
-        testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_VP9,
-                "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
-    }
-
-    /**
-     * Test that AudioTrack timestamps don't advance after pause.
-     */
-    private void
-    testTunneledAudioTimestampProgress(String mimeType, String videoName) throws Exception
-    {
+    private void testTunneledAudioProgressWithPause(String mimeType, String videoName)
+            throws Exception {
         if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
                     "No tunneled video playback codec found for MIME " + mimeType)) {
             return;
@@ -4568,11 +4565,10 @@
         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());
+        mMediaCodecPlayer.startCodec();
 
-        // starts video playback
-        mMediaCodecPlayer.startThread();
+        mMediaCodecPlayer.play();
         sleepUntil(() ->
                 mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
                 && mMediaCodecPlayer.getTimestamp() != null
@@ -4604,14 +4600,43 @@
         assertEquals(audioTimestampAfterPause.nanoTime, mMediaCodecPlayer.getTimestamp().nanoTime);
     }
 
+
     /**
-     * Test tunneled audio underrun, if supported.
-     *
-     * Underrun test with lower pts after underrun.
+     * Test that audio timestamps stop progressing during pause for HEVC in tunneled mode.
+     */
+    @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+    public void testTunneledAudioProgressWithPauseHevc() throws Exception {
+        testTunneledAudioProgressWithPause(MediaFormat.MIMETYPE_VIDEO_HEVC,
+                "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
+    }
+
+    /**
+     * Test that audio timestamps stop progressing during pause for AVC in tunneled mode.
+     */
+    @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+    public void testTunneledAudioProgressWithPauseAvc() throws Exception {
+        testTunneledAudioProgressWithPause(MediaFormat.MIMETYPE_VIDEO_AVC,
+                "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
+    }
+
+    /**
+     * Test that audio timestamps stop progressing during pause for VP9 in tunneled mode.
+     */
+    @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+    public void testTunneledAudioProgressWithPauseVp9() throws Exception {
+        testTunneledAudioProgressWithPause(MediaFormat.MIMETYPE_VIDEO_VP9,
+                "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
+    }
+
+    /**
+     * Test that audio underrun pauses video and resumes in-sync in tunneled mode.
      *
      * 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)
+    private void tunneledAudioUnderrun(String mimeType, String videoName)
             throws Exception {
         if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
                 "No tunneled video playback codec found for MIME " + mimeType)) {
@@ -4625,11 +4650,10 @@
         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());
+        mMediaCodecPlayer.startCodec();
 
-        // Starts video playback
-        mMediaCodecPlayer.startThread();
+        mMediaCodecPlayer.play();
         sleepUntil(() ->
                 mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
                 && mMediaCodecPlayer.getTimestamp() != null
@@ -4645,36 +4669,39 @@
         mMediaCodecPlayer.simulateAudioUnderrun(true);
 
         // Wait for audio underrun
-        final int audioUnderrunTimeoutMs = 1000; // Arbitrary upper time limit on loop time duration
-        long startTimeMs = System.currentTimeMillis();
-        AudioTimestamp currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
         AudioTimestamp underrunAudioTimestamp;
-        do {
-            assertTrue(String.format("No audio underrun after %d milliseconds",
-                            System.currentTimeMillis() - startTimeMs),
-                    System.currentTimeMillis() - startTimeMs < audioUnderrunTimeoutMs);
-            underrunAudioTimestamp = currentAudioTimestamp;
-            Thread.sleep(50);
-            currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
-        } while (currentAudioTimestamp.framePosition != underrunAudioTimestamp.framePosition);
+        {
+            AudioTimestamp currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+            long startTimeMs = System.currentTimeMillis();
+            do {
+                int audioUnderrunTimeoutMs = 1000;
+                assertTrue(String.format("No audio underrun after %d milliseconds",
+                                System.currentTimeMillis() - startTimeMs),
+                        System.currentTimeMillis() - startTimeMs < audioUnderrunTimeoutMs);
+                underrunAudioTimestamp = currentAudioTimestamp;
+                Thread.sleep(50);
+                currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+            } while (currentAudioTimestamp.framePosition != underrunAudioTimestamp.framePosition);
+        }
 
+        // Wait until video playback pauses due to underrunning audio
+        long pausedVideoTimeUs = -1;
+        {
+            long currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
+            long startTimeMs = System.currentTimeMillis();
+            do {
+                int videoPauseTimeoutMs = 2000;
+                assertTrue(String.format("No video pause after %d milliseconds",
+                                videoPauseTimeoutMs),
+                        System.currentTimeMillis() - startTimeMs < videoPauseTimeoutMs);
+                pausedVideoTimeUs = currentVideoTimeUs;
+                Thread.sleep(250); // onFrameRendered messages can get delayed in the Framework
+                currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
+            } while (currentVideoTimeUs != pausedVideoTimeUs);
+        }
 
-        // Wait until video playback stalls
-        final int videoUnderrunTimeoutMs = 1000;
-        startTimeMs = System.currentTimeMillis();
-        long currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
-        long underrunVideoTimeUs = -1;
-        do {
-            assertTrue(String.format("No video underrun after %d milliseconds",
-                            videoUnderrunTimeoutMs),
-                    System.currentTimeMillis() - startTimeMs < videoUnderrunTimeoutMs);
-            underrunVideoTimeUs = currentVideoTimeUs;
-            Thread.sleep(50);
-            currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
-        } while (currentVideoTimeUs != underrunVideoTimeUs);
-
-        // Retrieve index for the video rendered frame at the time of underrun
-        int underrunVideoRenderedTimestampIndex =
+        // Retrieve index for the video rendered frame at the time of video pausing
+        int pausedVideoRenderedTimestampIndex =
                 mMediaCodecPlayer.getRenderedVideoFrameTimestampList().size() - 1;
 
         // Resume audio buffering with a negative offset, in order to simulate a desynchronisation.
@@ -4683,35 +4710,38 @@
         mMediaCodecPlayer.simulateAudioUnderrun(false);
 
         // Wait until audio playback resumes
-        final int audioResumeTimeoutMs = 1000;
-        startTimeMs = System.currentTimeMillis();
-        currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
         AudioTimestamp postResumeAudioTimestamp;
-        do {
-            assertTrue(String.format("Audio has not resumed after %d milliseconds",
-                            audioResumeTimeoutMs),
-                    System.currentTimeMillis() - startTimeMs < audioResumeTimeoutMs);
-            postResumeAudioTimestamp = currentAudioTimestamp;
-            Thread.sleep(50);
-            currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
-        } while(currentAudioTimestamp.framePosition == postResumeAudioTimestamp.framePosition);
+        {
+            AudioTimestamp previousAudioTimestamp;
+            long startTimeMs = System.currentTimeMillis();
+            do {
+                int audioResumeTimeoutMs = 1000;
+                assertTrue(String.format("Audio has not resumed after %d milliseconds",
+                                audioResumeTimeoutMs),
+                        System.currentTimeMillis() - startTimeMs < audioResumeTimeoutMs);
+                previousAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+                Thread.sleep(50);
+                postResumeAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+            } while (postResumeAudioTimestamp.framePosition == previousAudioTimestamp.framePosition);
+        }
 
         // Now that audio playback has resumed, 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.
-        final int videoResumeTimeoutMs = 1000;
-        startTimeMs = System.currentTimeMillis();
-        currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
-        long resumeVideoTimeUs = -1;
-        do {
-            assertTrue(String.format("Video has not resumed after %d milliseconds",
-                            videoResumeTimeoutMs),
-                    System.currentTimeMillis() - startTimeMs < videoResumeTimeoutMs);
-            resumeVideoTimeUs = currentVideoTimeUs;
-            Thread.sleep(50);
-            currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
-        } while (currentVideoTimeUs == resumeVideoTimeUs);
+        {
+            // We actually don't care about trying to capture the exact time video resumed, because
+            // we can just look at the historical list of rendered video timestamps
+            long postResumeVideoTimeUs;
+            long previousVideoTimeUs;
+            long startTimeMs = System.currentTimeMillis();
+            do {
+                int videoResumeTimeoutMs = 2000;
+                assertTrue(String.format("Video has not resumed after %d milliseconds",
+                                videoResumeTimeoutMs),
+                        System.currentTimeMillis() - startTimeMs < videoResumeTimeoutMs);
+                previousVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
+                Thread.sleep(50);
+                postResumeVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
+            } while (postResumeVideoTimeUs == previousVideoTimeUs);
+        }
 
         // The system time when rendering the first audio frame after the resume
         long playbackRateFps = mMediaCodecPlayer.getAudioTrack().getPlaybackRate();
@@ -4721,52 +4751,74 @@
         long resumeAudioSystemTimeNs = postResumeAudioTimestamp.nanoTime - (long) elapsedTimeNs;
         long resumeAudioSystemTimeMs = resumeAudioSystemTimeNs / 1000 / 1000;
 
-        // The system time when rendering the first video frame after the resume
+        // The system time when rendering the first video frame after video playback resumes
         long resumeVideoSystemTimeMs = mMediaCodecPlayer.getRenderedVideoFrameSystemTimeList()
-                .get(underrunVideoRenderedTimestampIndex + 1) / 1000 / 1000;
+                .get(pausedVideoRenderedTimestampIndex + 1) / 1000 / 1000;
 
-        // Verify that audio and video are in-sync after resume time
+        // Verify that video resumes in a reasonable amount of time after audio resumes
         // Note: Because a -100ms PTS gap is introduced, the video should resume 100ms later
         resumeAudioSystemTimeMs += 100;
-        long vsyncMs = 1000 / frameRate;
-        long avSyncOffsetMs = resumeAudioSystemTimeMs - resumeVideoSystemTimeMs;
+        long resumeDeltaMs = resumeVideoSystemTimeMs - resumeAudioSystemTimeMs;
+        assertTrue(String.format("Video started %s milliseconds before audio resumed "
+                        + "(video:%d audio:%d)", resumeDeltaMs * -1, resumeVideoSystemTimeMs,
+                        resumeAudioSystemTimeMs),
+                resumeDeltaMs > 0); // video is expected to start after audio resumes
         assertTrue(String.format(
-                        "Audio is %d milliseconds out of sync of video (audio:%d video:%d)",
-                        avSyncOffsetMs, resumeAudioSystemTimeMs, resumeVideoSystemTimeMs),
-                Math.abs(avSyncOffsetMs) <= vsyncMs);
+                        "Video started %d milliseconds after audio resumed (video:%d audio:%d)",
+                        resumeDeltaMs, resumeVideoSystemTimeMs, resumeAudioSystemTimeMs),
+                resumeDeltaMs <= 600); // video starting 300ms after audio is barely noticeable
+
+        // Determine the system time of the audio frame that matches the presentation timestamp of
+        // the resumed video frame
+        long resumeVideoPresentationTimeUs = mMediaCodecPlayer.getRenderedVideoFrameTimestampList()
+                .get(pausedVideoRenderedTimestampIndex + 1);
+        long matchingAudioFramePosition = resumeVideoPresentationTimeUs * playbackRateFps / 1000 / 1000;
+        playedFrames = matchingAudioFramePosition - postResumeAudioTimestamp.framePosition;
+        elapsedTimeNs = playedFrames * (1000.0 * 1000.0 * 1000.0 / playbackRateFps);
+        long matchingAudioSystemTimeNs = postResumeAudioTimestamp.nanoTime + (long) elapsedTimeNs;
+        long matchingAudioSystemTimeMs = matchingAudioSystemTimeNs / 1000 / 1000;
+
+        // Verify that video and audio are in sync at the time when video resumes
+        // Note: Because a -100ms PTS gap is introduced, the video should resume 100ms later
+        matchingAudioSystemTimeMs += 100;
+        long avSyncOffsetMs =  resumeVideoSystemTimeMs - matchingAudioSystemTimeMs;
+        assertTrue(String.format("Video is %d milliseconds out of sync of audio after resuming "
+                        + "(video:%d, audio:%d)", avSyncOffsetMs, resumeVideoSystemTimeMs,
+                        matchingAudioSystemTimeMs),
+                // some leniency in AV sync is required because Android TV STB/OTT OEMs often have
+                // to tune for imperfect downstream TVs (that have processing delays on the video)
+                // by knowingly producing HDMI output that has audio and video mildly out of sync
+                Math.abs(avSyncOffsetMs) <= 80);
     }
 
     /**
-     * Test tunneled audio underrun with HEVC if supported
+     * Test that audio underrun pauses video and resumes in-sync for HEVC in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledAudioUnderrunHevc() throws Exception {
         tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC,
-                "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv",
-                25);
+                "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
     }
 
     /**
-     * Test tunneled audio underrun with AVC if supported
+     * Test that audio underrun pauses video and resumes in-sync for AVC in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledAudioUnderrunAvc() throws Exception {
         tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC,
-                "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4",
-                25);
+                "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
     }
 
     /**
-     * Test tunneled audio underrun with VP9 if supported
+     * Test that audio underrun pauses video and resumes in-sync for VP9 in tunneled mode.
      */
-    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
     @Test
+    @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
     public void testTunneledAudioUnderrunVp9() throws Exception {
         tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9,
-                "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm",
-                30);
+                "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
     }
 
     private void sleepUntil(Supplier<Boolean> supplier, Duration maxWait) throws Exception {
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java
index f34f9e2..0857809 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java
@@ -81,9 +81,9 @@
                         AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER},
                 {"noise_6ch_44khz_aot5_dr_sbr_sig2_mp4.m4a", 6, AudioFormat.CHANNEL_OUT_5POINT1},
         };
-
-        for (Object [] sample: samples) {
-            for (String codecName : DecoderTest.codecsFor((String)sample[0] /* resource */)) {
+        for (Object[] sample: samples) {
+            for (String codecName : DecoderTest.codecsFor((String)sample[0] /* resource */,
+                    DecoderTest.CODEC_DEFAULT)) {
                 // verify correct number of channels is observed without downmixing
                 AudioParameter chanParams = new AudioParameter();
                 decodeUpdateFormat(codecName, (String) sample[0] /*resource*/, chanParams,
diff --git a/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java b/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java
index 1a1a46f..2c1d9c5 100644
--- a/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java
+++ b/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java
@@ -26,7 +26,6 @@
 import android.media.UnsupportedSchemeException;
 import android.media.cts.AudioManagerStub;
 import android.media.cts.AudioManagerStubHelper;
-import android.media.cts.CodecState;
 import android.media.cts.ConnectionStatus;
 import android.media.cts.IConnectionStatus;
 import android.media.cts.InputSurface;
diff --git a/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt
index 6693aa3..8bd15373 100644
--- a/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt
@@ -20,6 +20,7 @@
 import android.os.Build
 import androidx.test.filters.SdkSuppress
 import com.android.compatibility.common.util.SystemUtil
+import org.junit.Assume
 import org.junit.Test
 
 /**
@@ -96,6 +97,8 @@
 
     @Test
     fun testWhenVisualIsDeniedManuallyThenShouldDenyAllPermissions() {
+        // TODO: Re-enable after b/239249703 is fixed
+        Assume.assumeFalse("skip on TV due to flaky", isTv)
         installPackage(APP_APK_PATH_23)
         grantAppPermissions(android.Manifest.permission.READ_MEDIA_VIDEO, targetSdk = 23)
         revokeAppPermissions(android.Manifest.permission.READ_MEDIA_VIDEO, targetSdk = 23)
diff --git a/tests/tests/systemui/Android.bp b/tests/tests/systemui/Android.bp
index e33d8fc..3a68e4d 100644
--- a/tests/tests/systemui/Android.bp
+++ b/tests/tests/systemui/Android.bp
@@ -39,6 +39,7 @@
         "androidx.test.ext.junit",
         "androidx.test.uiautomator",
         "cts-wm-util",
+        "permission-test-util-lib",
         "ub-uiautomator",
     ],
     srcs: [
diff --git a/tests/tests/systemui/AndroidManifest.xml b/tests/tests/systemui/AndroidManifest.xml
index f55ed3f..d4ba3b3 100644
--- a/tests/tests/systemui/AndroidManifest.xml
+++ b/tests/tests/systemui/AndroidManifest.xml
@@ -26,6 +26,7 @@
     <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
     <!-- Required by flickerlib to dump window states -->
     <uses-permission android:name="android.permission.DUMP"/>
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
 
     <application android:requestLegacyExternalStorage="true">
         <activity android:name=".LightBarActivity"
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
index ffa58ba..5079217 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
@@ -16,6 +16,9 @@
 
 package android.systemui.cts;
 
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL;
+import static android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS;
 import static android.server.wm.BarTestUtils.assumeHasColoredNavigationBar;
 import static android.server.wm.BarTestUtils.assumeHasColoredStatusBar;
 import static android.server.wm.BarTestUtils.assumeStatusBarContainsCutout;
@@ -33,7 +36,10 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Insets;
+import android.os.Process;
 import android.os.SystemClock;
+import android.permission.PermissionManager;
+import android.permission.cts.PermissionUtils;
 import android.platform.test.annotations.AppModeFull;
 import android.view.Gravity;
 import android.view.InputDevice;
@@ -45,6 +51,7 @@
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.compatibility.common.util.SystemUtil;
 import com.android.compatibility.common.util.ThrowingRunnable;
 
 import org.junit.Rule;
@@ -244,22 +251,23 @@
     }
 
     private void runInNotificationSession(ThrowingRunnable task) throws Exception {
+        Context context = getInstrumentation().getContext();
+        String packageName = getInstrumentation().getTargetContext().getPackageName();
         try {
-            mNm = (NotificationManager) getInstrumentation().getContext()
-                    .getSystemService(Context.NOTIFICATION_SERVICE);
+            PermissionUtils.grantPermission(packageName, POST_NOTIFICATIONS);
+            mNm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
             NotificationChannel channel1 = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
                     NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW);
             mNm.createNotificationChannel(channel1);
 
             // post 10 notifications to ensure enough icons in the status bar
             for (int i = 0; i < 10; i++) {
-                Notification.Builder noti1 = new Notification.Builder(
-                        getInstrumentation().getContext(),
-                        NOTIFICATION_CHANNEL_ID)
-                        .setSmallIcon(R.drawable.ic_save)
-                        .setChannelId(NOTIFICATION_CHANNEL_ID)
-                        .setPriority(Notification.PRIORITY_LOW)
-                        .setGroup(NOTIFICATION_GROUP_KEY);
+                Notification.Builder noti1 =
+                        new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
+                                .setSmallIcon(R.drawable.ic_save)
+                                .setChannelId(NOTIFICATION_CHANNEL_ID)
+                                .setPriority(Notification.PRIORITY_LOW)
+                                .setGroup(NOTIFICATION_GROUP_KEY);
                 mNm.notify(NOTIFICATION_TAG, i, noti1.build());
             }
 
@@ -267,6 +275,16 @@
         } finally {
             mNm.cancelAll();
             mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
+
+            // Use test API to prevent PermissionManager from killing the test process when revoking
+            // permission.
+            SystemUtil.runWithShellPermissionIdentity(
+                    () -> context.getSystemService(PermissionManager.class)
+                            .revokePostNotificationPermissionWithoutKillForTest(
+                                    packageName,
+                                    Process.myUserHandle().getIdentifier()),
+                    REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL,
+                    REVOKE_RUNTIME_PERMISSIONS);
         }
     }
 
diff --git a/tests/tests/systemui/src/android/systemui/cts/MediaOutputDialogTest.java b/tests/tests/systemui/src/android/systemui/cts/MediaOutputDialogTest.java
deleted file mode 100644
index b74678b..0000000
--- a/tests/tests/systemui/src/android/systemui/cts/MediaOutputDialogTest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.systemui.cts;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.server.wm.WindowManagerStateHelper;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.Until;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests related MediaOutputDialog:
- *
- * atest MediaDialogTest
- */
-@RunWith(AndroidJUnit4.class)
-public class MediaOutputDialogTest {
-
-    private static final int TIMEOUT = 5000;
-    private static final String ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG =
-            "com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG";
-    private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
-    public static final String EXTRA_PACKAGE_NAME = "package_name";
-    public static final String TEST_PACKAGE_NAME = "com.android.package.test";
-    private static final BySelector MEDIA_DIALOG_SELECTOR = By.res(SYSTEMUI_PACKAGE_NAME,
-            "media_output_dialog");
-    private WindowManagerStateHelper mWmState = new WindowManagerStateHelper();
-
-    private Context mContext;
-    private UiDevice mDevice;
-    private String mLauncherPackage;
-    private boolean mHasTouchScreen;
-    private ActivityManager mActivityManager;
-    private static final int DURATION_SCREEN_TOGGLE = 2000;
-
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
-        final PackageManager packageManager = mContext.getPackageManager();
-
-        mHasTouchScreen = packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
-                || packageManager.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
-
-        Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
-        launcherIntent.addCategory(Intent.CATEGORY_HOME);
-        ResolveInfo resolveInfo = packageManager.resolveActivity(launcherIntent,
-                PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY));
-        assumeFalse("Skipping test: can't get resolve info", resolveInfo == null);
-        assumeFalse("Skipping test: not supported on automotive yet",
-                packageManager.hasSystemFeature(
-                        PackageManager.FEATURE_AUTOMOTIVE));
-        mLauncherPackage = resolveInfo.activityInfo.packageName;
-        mActivityManager = mContext.getSystemService(ActivityManager.class);
-    }
-
-    @Test
-    public void mediaOutputDialog_correctDialog() throws Exception {
-        prepareDevice();
-        assumeTrue(mHasTouchScreen);
-        try {
-            if (mActivityManager.isLowRamDevice()) {
-                SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                        "appops set " + SYSTEMUI_PACKAGE_NAME + " SYSTEM_ALERT_WINDOW allow");
-            }
-
-            launchMediaOutputDialog();
-
-            assertThat(mDevice.wait(Until.hasObject(MEDIA_DIALOG_SELECTOR), TIMEOUT)).isTrue();
-        } finally {
-            if (mActivityManager.isLowRamDevice()) {
-                SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
-                        "appops set " + SYSTEMUI_PACKAGE_NAME + " SYSTEM_ALERT_WINDOW ignore");
-            }
-        }
-    }
-
-    private void prepareDevice() throws Exception {
-        mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
-        mDevice.executeShellCommand("wm dismiss-keyguard");
-        // Since the screen on/off intent is ordered, they will not be sent right now.
-        mWmState.waitForKeyguardGone();
-    }
-
-    private void launchMediaOutputDialog() {
-        mDevice.pressHome();
-        mDevice.wait(Until.hasObject(By.pkg(mLauncherPackage).depth(0)), TIMEOUT);
-
-        Intent intent = new Intent();
-        intent.setPackage(SYSTEMUI_PACKAGE_NAME)
-                .setAction(ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG)
-                .putExtra(EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
-
-        mContext.sendBroadcast(intent);
-    }
-
-}
diff --git a/tests/tests/telephony/OWNERS b/tests/tests/telephony/OWNERS
index 02848c8..89e4f4d 100644
--- a/tests/tests/telephony/OWNERS
+++ b/tests/tests/telephony/OWNERS
@@ -1,2 +1,17 @@
-file:platform/frameworks/opt/telephony:/OWNERS
-
+# Bug component: 20868
+amagup@google.com
+amallampati@google.com
+amruthr@google.com
+breadley@google.com
+chinmayd@google.com
+fionaxu@google.com
+huiwang@google.com
+jackyu@google.com
+jayachandranc@google.com
+linggm@google.com
+rgreenwalt@google.com
+sarahchin@google.com
+sasindran@google.com
+tgunn@google.com
+tjstuart@google.com
+xiaotonj@google.com
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 3757b63..d7bc9b1 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -109,6 +109,7 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.ApiTest;
 import com.android.compatibility.common.util.CarrierPrivilegeUtils;
 import com.android.compatibility.common.util.CddTest;
 import com.android.compatibility.common.util.ShellIdentityUtils;
@@ -283,6 +284,7 @@
     private static final int RADIO_HAL_VERSION_1_3 = makeRadioVersion(1, 3);
     private static final int RADIO_HAL_VERSION_1_5 = makeRadioVersion(1, 5);
     private static final int RADIO_HAL_VERSION_1_6 = makeRadioVersion(1, 6);
+    private static final int RADIO_HAL_VERSION_2_0 = makeRadioVersion(2, 0);
 
     static {
         EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
@@ -1250,23 +1252,49 @@
     private static final String ISO_COUNTRY_CODE_PATTERN = "[a-z]{2}";
 
     @Test
+    @ApiTest(apis = "android.telephony.TelephonyManager#getNetworkCountryIso")
     public void testGetNetworkCountryIso() {
         assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS));
 
         String countryCode = mTelephonyManager.getNetworkCountryIso();
-        assertTrue("Country code '" + countryCode + "' did not match "
-                + ISO_COUNTRY_CODE_PATTERN,
-                Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+        ServiceState serviceState = mTelephonyManager.getServiceState();
+        if (serviceState != null && (serviceState.getState()
+                == ServiceState.STATE_IN_SERVICE || serviceState.getState()
+                == ServiceState.STATE_EMERGENCY_ONLY)) {
+            assertTrue("Country code '" + countryCode + "' did not match "
+                    + ISO_COUNTRY_CODE_PATTERN,
+                    Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+        } else {
+            assertTrue("Country code could be empty when out of service",
+                    Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)
+                    || TextUtils.isEmpty(countryCode));
+        }
+
+        int[] allSubs = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mSubscriptionManager, (sm) -> sm.getActiveSubscriptionIdList());
+        for (int i : allSubs) {
+            countryCode = mTelephonyManager.getNetworkCountryIso(
+                    SubscriptionManager.getSlotIndex(i));
+            serviceState = mTelephonyManager.createForSubscriptionId(i).getServiceState();
+
+            if (serviceState != null && (serviceState.getState()
+                    == ServiceState.STATE_IN_SERVICE || serviceState.getState()
+                    == ServiceState.STATE_EMERGENCY_ONLY)) {
+                assertTrue("Country code '" + countryCode + "' did not match "
+                        + ISO_COUNTRY_CODE_PATTERN + " for slot " + i,
+                        Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+            } else {
+                assertTrue("Country code could be empty when out of service",
+                        Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)
+                        || TextUtils.isEmpty(countryCode));
+            }
+        }
 
         for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) {
-            SubscriptionInfo subscriptionInfo =
-                    mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i);
-            if (subscriptionInfo != null) {
-                countryCode = mTelephonyManager.getNetworkCountryIso(i);
-                assertTrue("Country code '" + countryCode + "' did not match "
-                                + ISO_COUNTRY_CODE_PATTERN + " for slot " + i,
-                        Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
-            }
+            countryCode = mTelephonyManager.getNetworkCountryIso(i);
+            assertTrue("Country code must match " + ISO_COUNTRY_CODE_PATTERN + "or empty",
+                    Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)
+                    || TextUtils.isEmpty(countryCode));
         }
     }
 
@@ -1274,19 +1302,20 @@
     public void testSetSystemSelectionChannels() {
         assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS));
 
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
         List<RadioAccessSpecifier> channels;
         try {
-            channels = ShellIdentityUtils.invokeMethodWithShellPermissions(
-                    mTelephonyManager, TelephonyManager::getSystemSelectionChannels);
+            uiAutomation.adoptShellPermissionIdentity();
+            channels = mTelephonyManager.getSystemSelectionChannels();
         } catch (IllegalStateException e) {
             // TODO (b/189255895): Allow ISE once API is enforced in IRadio 2.1.
             Log.d(TAG, "Skipping test since system selection channels are not available.");
             return;
+        } finally {
+            uiAutomation.dropShellPermissionIdentity();
         }
 
         LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(1);
-        final UiAutomation uiAutomation =
-                InstrumentationRegistry.getInstrumentation().getUiAutomation();
         try {
             uiAutomation.adoptShellPermissionIdentity();
             // This is a oneway binder call, meaning we may return before the permission check
@@ -1303,20 +1332,29 @@
             uiAutomation.dropShellPermissionIdentity();
         }
 
+        uiAutomation.adoptShellPermissionIdentity();
+
         // Try calling the API that doesn't provide feedback. We have no way of knowing if it
         // succeeds, so just make sure nothing crashes.
-        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
-                tp -> tp.setSystemSelectionChannels(Collections.emptyList()));
+        mTelephonyManager.setSystemSelectionChannels(Collections.emptyList());
 
         // Assert that we get back the value we set.
-        assertEquals(Collections.emptyList(),
-                ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
-                TelephonyManager::getSystemSelectionChannels));
+        assertEquals(Collections.emptyList(), mTelephonyManager.getSystemSelectionChannels());
 
-        // Reset the values back to the original.
-        List<RadioAccessSpecifier> finalChannels = channels;
-        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
-                tp -> tp.setSystemSelectionChannels(finalChannels));
+        try {
+            // Reset the values back to the original. Use callback to ensure we don't drop
+            // the shell permission until the original state is restored.
+            mTelephonyManager.setSystemSelectionChannels(channels,
+                    getContext().getMainExecutor(), queue::offer);
+            Boolean result = queue.poll(1000, TimeUnit.MILLISECONDS);
+            if (result == null || !result) {
+                Log.e(TAG, "Invalid response when resetting initial system selection channels.");
+            }
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Interrupted while resetting initial system selection channels.");
+        } finally {
+            uiAutomation.dropShellPermissionIdentity();
+        }
     }
 
     @Test
@@ -1729,6 +1767,10 @@
     @Test
     public void testRebootRadio() throws Throwable {
         assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS));
+        if (mRadioVersion <= RADIO_HAL_VERSION_2_0) {
+            Log.d(TAG, "Skipping test since rebootModem is not supported.");
+            return;
+        }
 
         TestThread t = new TestThread(() -> {
             Looper.prepare();
diff --git a/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java b/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java
index a4ef244..e0c172a 100644
--- a/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java
@@ -21,12 +21,17 @@
 
 import android.app.Instrumentation;
 import android.graphics.Color;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
 import android.support.test.uiautomator.UiDevice;
 import android.view.Gravity;
+import android.view.InputEvent;
+import android.view.MotionEvent;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.PopupWindow;
 
+import androidx.annotation.NonNull;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.filters.MediumTest;
@@ -40,7 +45,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+// @AppModeFull because GestureNavRule does not work for
+// instant mode tests (b/238975931)
 @MediumTest
+@AppModeFull
 @RunWith(AndroidJUnit4.class)
 public class BackInvokedOnWidgetsTest {
 
@@ -93,7 +101,45 @@
     private void doBackGesture() {
         int midHeight = mUiDevice.getDisplayHeight() / 2;
         int midWidth = mUiDevice.getDisplayWidth() / 2;
-        mUiDevice.swipe(0, midHeight, midWidth, midHeight, 100);
+        quickSwipe(0, midHeight, midWidth, midHeight, 10);
         mUiDevice.waitForIdle();
     }
+
+    private void injectInputEventUnSynced(@NonNull InputEvent event) {
+        mInstrumentation.getUiAutomation().injectInputEvent(event, false /* sync */,
+                false /* waitForAnimations */);
+    }
+
+    /**
+     * Injecting a sequence of motion event to simulate swipe without waiting for sync transaction.
+     */
+    private void quickSwipe(float startX, float startY, float endX, float endY, int steps) {
+        if (steps <= 0) {
+            steps = 1;
+        }
+        final long startDownTime = SystemClock.uptimeMillis();
+        MotionEvent firstDown = MotionEvent.obtain(startDownTime, startDownTime,
+                MotionEvent.ACTION_DOWN, startX, startY, 0);
+        injectInputEventUnSynced(firstDown);
+
+        // inject in every 5 ms.
+        final int delayMillis = 5;
+        long nextEventTime = startDownTime + delayMillis;
+        final float stepGapX = (endX - startX) / steps;
+        final float stepGapY = (endY - startY) / steps;
+        for (int i = 0; i < steps; i++) {
+            SystemClock.sleep(delayMillis);
+            final float nextX = startX + stepGapX * i;
+            final float nextY = startY + stepGapY * i;
+            MotionEvent move = MotionEvent.obtain(startDownTime, nextEventTime,
+                    MotionEvent.ACTION_MOVE, nextX, nextY, 0);
+            injectInputEventUnSynced(move);
+            nextEventTime += delayMillis;
+        }
+
+        SystemClock.sleep(delayMillis);
+        MotionEvent up = MotionEvent.obtain(startDownTime, nextEventTime,
+                MotionEvent.ACTION_UP, endX, endY, 0);
+        injectInputEventUnSynced(up);
+    }
 }