Merge "update getDPI with DisplayMetrics.DENSITY_DEVICE_STABLE"
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__arm_CtsShimPrivUpgradeWrongSHA_apk.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__arm_CtsShimPrivUpgradeWrongSHA_apk.asciipb
index cc4eb05..51c4d4d 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__arm_CtsShimPrivUpgradeWrongSHA_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__arm_CtsShimPrivUpgradeWrongSHA_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShimPrivUpgradeWrongSHA.apk"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__arm_CtsShimPrivUpgrade_apk.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__arm_CtsShimPrivUpgrade_apk.asciipb
index 2c1b30b..c4296fb 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__arm_CtsShimPrivUpgrade_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__arm_CtsShimPrivUpgrade_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShimPrivUpgrade.apk"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__x86_CtsShimPrivUpgradeWrongSHA_apk.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__x86_CtsShimPrivUpgradeWrongSHA_apk.asciipb
index 8bb986a..51eca9c 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__x86_CtsShimPrivUpgradeWrongSHA_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__x86_CtsShimPrivUpgradeWrongSHA_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShimPrivUpgradeWrongSHA.apk"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__x86_CtsShimPrivUpgrade_apk.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__x86_CtsShimPrivUpgrade_apk.asciipb
index e28a099..396adb4 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__x86_CtsShimPrivUpgrade_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_appsecurity_test-apps_PrivilegedUpdateApp_apk__x86_CtsShimPrivUpgrade_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShimPrivUpgrade.apk"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_not_pre_installed_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_not_pre_installed_apex.asciipb
new file mode 100644
index 0000000..4bea676
--- /dev/null
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_not_pre_installed_apex.asciipb
@@ -0,0 +1,13 @@
+drops {
+  android_build_drop {
+    build_id: "7396576"
+    target: "CtsShim"
+    source_file: "aosp_arm64/com.android.apex.cts.shim_not_pre_installed.apex"
+  }
+  dest_file: "hostsidetests/stagedinstall/testdata/apex//arm/com.android.apex.cts.shim_not_pre_installed.apex"
+  version: ""
+  version_group: ""
+  git_project: "platform/cts"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
+}
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v1_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v1_apex.asciipb
index 4c34b9d..fbdcf59 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v1_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v1_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v1.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_additional_file_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_additional_file_apex.asciipb
index a40d92f..ccbcfcb 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_additional_file_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_additional_file_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_additional_file.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb
index b16eebb..f9ba098 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_additional_folder.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_apex.asciipb
index 6761a86..9c4ac9f 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb
index 3e4b7fd..271f7d2 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb
index cc692d2..96d48cc 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_different_certificate.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb
index 85724c3..730b9cb 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_different_package_name.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb
index adae35d..53a16396 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_no_hashtree.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb
index 926c55f..fda2db4 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_sdk_target_p.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb
new file mode 100644
index 0000000..2bcaf9b
--- /dev/null
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb
@@ -0,0 +1,13 @@
+drops {
+  android_build_drop {
+    build_id: "7396576"
+    target: "CtsShim"
+    source_file: "aosp_arm64/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex"
+  }
+  dest_file: "hostsidetests/stagedinstall/testdata/apex//arm/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex"
+  version: ""
+  version_group: ""
+  git_project: "platform/cts"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
+}
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb
index 800edaa..6825a8c 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_signed_bob.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb
index 8df4e67..5c8c69e 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_signed_bob_rot.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb
index 3cf7696..9e43307 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb
index 3a0383e..9299024 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_unsigned_payload.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb
index 2c3cb4a..a4bd6f6 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_with_post_install_hook.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb
index f4326da..e3dcbc9 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_with_pre_install_hook.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb
index 0d527d1..c727e61 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_without_apk_in_apex.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb
index 183db3f..e769a1e 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v2_wrong_sha.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_apex.asciipb
index 4cfd096..05c7fc8 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v3.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb
index d699a7b..e21f3b5 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v3_signed_bob.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb
index 4db830f..4707975 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__arm_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/com.android.apex.cts.shim.v3_signed_bob_rot.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_not_pre_installed_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_not_pre_installed_apex.asciipb
new file mode 100644
index 0000000..4b428fb
--- /dev/null
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_not_pre_installed_apex.asciipb
@@ -0,0 +1,13 @@
+drops {
+  android_build_drop {
+    build_id: "7396576"
+    target: "CtsShim"
+    source_file: "aosp_x86_64/com.android.apex.cts.shim_not_pre_installed.apex"
+  }
+  dest_file: "hostsidetests/stagedinstall/testdata/apex//x86/com.android.apex.cts.shim_not_pre_installed.apex"
+  version: ""
+  version_group: ""
+  git_project: "platform/cts"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
+}
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v1_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v1_apex.asciipb
index 5c4f161..4e5585d 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v1_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v1_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v1.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_additional_file_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_additional_file_apex.asciipb
index 58378c9..73bed47 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_additional_file_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_additional_file_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_additional_file.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb
index d88ead5..3b71d7f 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_additional_folder.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_apex.asciipb
index f86bd15..ec1090a 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb
index 3b040f2..df97a67 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb
index 31c07e1..67abdb1 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_different_certificate.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb
index b19bcbb..02d684c 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_different_package_name.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb
index 5344843..c5d24308 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_no_hashtree.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb
index 8d97977..56444c5 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_sdk_target_p.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb
new file mode 100644
index 0000000..d08793b
--- /dev/null
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb
@@ -0,0 +1,13 @@
+drops {
+  android_build_drop {
+    build_id: "7396576"
+    target: "CtsShim"
+    source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex"
+  }
+  dest_file: "hostsidetests/stagedinstall/testdata/apex//x86/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex"
+  version: ""
+  version_group: ""
+  git_project: "platform/cts"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
+}
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb
index 979d5ec..7478c60 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_signed_bob.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb
index 88c6a14..2134eaa 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_signed_bob_rot.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb
index 19da60b..202a61c 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb
index e511c01..9e08ac9 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_unsigned_payload.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb
index 8266f21..4b400bc 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_with_post_install_hook.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb
index 737ee4c..859dbeb 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_with_pre_install_hook.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb
index df032f0..51bcfad 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_without_apk_in_apex.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb
index 8700d88..b6177df 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_wrong_sha.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_apex.asciipb
index 400f61c..bfaece0 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v3.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb
index 17ae337..7fba9ed 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v3_signed_bob.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb
index a825aba..62fffc2 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apex__x86_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/com.android.apex.cts.shim.v3_signed_bob_rot.apex"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apk_CtsShimTargetPSdk__arm_CtsShimTargetPSdk_apk.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apk_CtsShimTargetPSdk__arm_CtsShimTargetPSdk_apk.asciipb
index f83e3be..1f4126b 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apk_CtsShimTargetPSdk__arm_CtsShimTargetPSdk_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apk_CtsShimTargetPSdk__arm_CtsShimTargetPSdk_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShimTargetPSdk.apk"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apk_CtsShimTargetPSdk__x86_CtsShimTargetPSdk_apk.asciipb b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apk_CtsShimTargetPSdk__x86_CtsShimTargetPSdk_apk.asciipb
index 89ebb7c..7fceac9 100644
--- a/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apk_CtsShimTargetPSdk__x86_CtsShimTargetPSdk_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_hostsidetests_stagedinstall_testdata_apk_CtsShimTargetPSdk__x86_CtsShimTargetPSdk_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7396576"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShimTargetPSdk.apk"
   }
@@ -8,5 +8,6 @@
   version: ""
   version_group: ""
   git_project: "platform/cts"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
+  transform: TRANSFORM_NONE
 }
diff --git a/apps/CameraITS/pymodules/its/cv2image.py b/apps/CameraITS/pymodules/its/cv2image.py
index 23c8734..09265e9 100644
--- a/apps/CameraITS/pymodules/its/cv2image.py
+++ b/apps/CameraITS/pymodules/its/cv2image.py
@@ -38,6 +38,7 @@
 SCALE_RFOV_IN_WFOV_BOX = 0.67
 SCALE_TELE_IN_RFOV_BOX = 0.67
 SCALE_TELE_IN_WFOV_BOX = 0.5
+SCALE_SUPER_TELE_IN_RFOV_BOX = 0.5
 
 VGA_HEIGHT = 480
 VGA_WIDTH = 640
@@ -52,6 +53,9 @@
     elif (camera_fov <= FOV_THRESH_TELE and
           numpy.isclose(chart_distance, CHART_DISTANCE_WFOV, rtol=0.1)):
         chart_scaling = SCALE_TELE_IN_WFOV_BOX
+    elif (camera_fov <= FOV_THRESH_SUPER_TELE and
+          numpy.isclose(chart_distance, CHART_DISTANCE_RFOV, rtol=0.1)):
+        chart_scaling = SCALE_SUPER_TELE_IN_RFOV_BOX
     elif (camera_fov <= FOV_THRESH_TELE and
           numpy.isclose(chart_distance, CHART_DISTANCE_RFOV, rtol=0.1)):
         chart_scaling = SCALE_TELE_IN_RFOV_BOX
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 2e2c775..ad97d48 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -228,7 +228,6 @@
         self._camera_id = camera_id
         self._hidden_physical_id = hidden_physical_id
 
-    def __enter__(self):
         # Initialize device id and adb command.
         self.device_id = get_device_id()
         self.adb = "adb -s " + self.device_id
@@ -236,6 +235,7 @@
         self.__wait_for_service()
         self.__init_socket_port()
 
+    def __enter__(self):
         self.__close_camera()
         self.__open_camera()
         return self
@@ -961,6 +961,60 @@
         else:
             return rets[0]
 
+    def is_s_performance_class_primary_camera(self):
+      """Query whether the camera device is an S performance class primary camera.
+
+      A primary rear/front facing camera is a camera device with the lowest
+      camera Id for that facing.
+
+      Returns:
+        Boolean
+      """
+      cmd = {}
+      cmd['cmdName'] = 'isSPerformanceClassPrimaryCamera'
+      cmd['cameraId'] = self._camera_id
+      self.sock.send(json.dumps(cmd) + '\n')
+
+      data, _ = self.__read_response_from_socket()
+      if data['tag'] != 'sPerformanceClassPrimaryCamera':
+        raise error_util.CameraItsError('Failed to query S performance class '
+                                        'primary camera')
+      return data['strValue'] == 'true'
+
+    def measure_camera_launch_ms(self):
+      """Measure camera launch latency in millisecond, from open to first frame.
+
+      Returns:
+        Camera launch latency from camera open to receipt of first frame
+      """
+      cmd = {}
+      cmd['cmdName'] = 'measureCameraLaunchMs'
+      cmd['cameraId'] = self._camera_id
+      self.sock.send(json.dumps(cmd) + '\n')
+
+      data, _ = self.__read_response_from_socket()
+      if data['tag'] != 'cameraLaunchMs':
+        raise error_util.CameraItsError('Failed to measure camera launch latency')
+      return float(data['strValue'])
+
+    def measure_camera_1080p_jpeg_capture_ms(self):
+      """Measure camera 1080P jpeg capture latency in milliseconds.
+
+      Returns:
+        Camera jpeg capture latency in milliseconds
+      """
+      cmd = {}
+      cmd['cmdName'] = 'measureCamera1080pJpegCaptureMs'
+      cmd['cameraId'] = self._camera_id
+      self.sock.send(json.dumps(cmd) + '\n')
+
+      data, _ = self.__read_response_from_socket()
+      if data['tag'] != 'camera1080pJpegCaptureMs':
+        raise error_util.CameraItsError(
+            'Failed to measure camera 1080p jpeg capture latency')
+      return float(data['strValue'])
+
+
 def do_capture_with_latency(cam, req, sync_latency, fmt=None):
     """Helper function to take enough frames with do_capture to allow sync latency.
 
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
new file mode 100644
index 0000000..af24dea
--- /dev/null
+++ b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
@@ -0,0 +1,51 @@
+# Copyright 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Verify camera startup is < 500ms for both front and back primary cameras.
+"""
+
+import its.caps
+import its.device
+
+CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 500  # ms
+
+
+def main():
+    """Test camera launch latency for S performance class as specified in CDD.
+
+    [7.5/H-1-7] MUST have camera2 startup latency (open camera to first preview
+    frame) < 500ms as measured by the CTS camera PerformanceTest under ITS
+    lighting conditions (3000K) for both primary cameras.
+    """
+
+    # Open camera with "with" semantics to check skip condition and save camera
+    # id
+    cam_id = ''
+    with its.device.ItsSession() as cam:
+        its.caps.skip_unless(
+            cam.is_s_performance_class_primary_camera())
+        cam_id = cam._camera_id
+
+    # Create an its session without opening the camera to test camera launch
+    # latency
+    session = its.device.ItsSession(camera_id=cam_id)
+
+    launch_ms = session.measure_camera_launch_ms()
+    print 'camera launch time: %.1f ms' % launch_ms
+
+    msg = 'camera launch time: %.1f ms, THRESH: %.1f ms' \
+        % (launch_ms, CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD)
+    assert launch_ms < CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD, msg
+
+if __name__ == '__main__':
+    main()
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
new file mode 100644
index 0000000..2abeec2
--- /dev/null
+++ b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
@@ -0,0 +1,51 @@
+# Copyright 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Verify jpeg capture latency for both front and back primary cameras.
+"""
+
+import its.caps
+import its.device
+
+JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD = 1000  # ms
+
+
+def main():
+    """Test jpeg capture latency for S performance class as specified in CDD.
+
+    [7.5/H-1-6] 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.
+    """
+
+    # Open camera with "with" semantics to check skip condition and save camera
+    # id
+    cam_id = ''
+    with its.device.ItsSession() as cam:
+        its.caps.skip_unless(
+            cam.is_s_performance_class_primary_camera())
+        cam_id = cam._camera_id
+
+    # Create an Its session without opening the camera to test camera jpeg
+    # capture latency because the test opens camera internally
+    session = its.device.ItsSession(camera_id=cam_id)
+
+    jpeg_capture_ms = session.measure_camera_1080p_jpeg_capture_ms()
+    print '1080p jpeg capture time: %.1f ms' % jpeg_capture_ms
+
+    msg = '1080p jpeg capture time: %.1f ms, THRESH: %.1f ms' \
+        % (jpeg_capture_ms, JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD)
+    assert jpeg_capture_ms < JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD, msg
+
+if __name__ == '__main__':
+    main()
diff --git a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
index 0862b3b..a229fdfd 100644
--- a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
+++ b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
@@ -55,8 +55,7 @@
     data_set = {}
     white_level = int(props['android.sensor.info.whiteLevel'])
     min_fd = props['android.lens.info.minimumFocusDistance']
-    fds = [af_fd, min_fd]
-    fds = sorted(fds * NUM_IMGS)
+    fds = [af_fd] * NUM_IMGS + [min_fd] * NUM_IMGS
     reqs = []
     for i, fd in enumerate(fds):
         reqs.append(its.objects.manual_capture_request(gain, exp))
@@ -170,8 +169,9 @@
         assert np.isclose(min_sharp, max_sharp, rtol=SHARPNESS_TOL)
         # assert reported location is close to assign location for min_fd
         print 'Asserting lens location close to assigned fd for min_fd data'
-        assert np.isclose(d_min_fd[NUM_IMGS*2-1]['loc'],
-                          d_min_fd[NUM_IMGS*2-1]['fd'], rtol=POSITION_TOL)
+        last_key = max(d_min_fd.keys())  # find last frame
+        assert np.isclose(d_min_fd[last_key]['loc'], d_min_fd[last_key]['fd'],
+                          rtol=POSITION_TOL)
 
 
 if __name__ == '__main__':
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index 0caf148..c0f5667 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -359,7 +359,7 @@
         if i == 1:
             # Save a debug visualization of the features that are being
             # tracked in the first frame.
-            frame = frames[i]
+            frame = frames[i-1]
             for x, y in p0_filtered[st == 1]:
                 cv2.circle(frame, (x, y), 3, (100, 100, 255), -1)
             its.image.write_image(frame, "%s_features.png" % NAME)
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 47dd4f5..92b4fc6 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -179,7 +179,7 @@
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_other" />
-            <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive" />
+            <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive:android.hardware.type.watch" />
             <meta-data android:name="display_mode"
                        android:value="multi_display_mode" />
         </activity>
@@ -1755,6 +1755,22 @@
                        android:value="multi_display_mode" />
         </activity>
 
+        <activity android:name=".security.UnlockedDeviceRequiredTest"
+                android:label="@string/sec_unlocked_device_required_test"
+                android:configChanges="keyboardHidden|orientation|screenSize" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_security" />
+            <meta-data android:name="test_excluded_features"
+                       android:value="android.software.lockscreen_disabled" />
+            <meta-data android:name="test_required_features"
+                    android:value="android.software.device_admin:android.software.secure_lock_screen" />
+            <meta-data android:name="display_mode"
+                       android:value="multi_display_mode" />
+        </activity>
+
         <activity android:name=".security.LockConfirmBypassTest"
                 android:label="@string/lock_confirm_test_title"
                 android:configChanges="keyboardHidden|orientation|screenSize" >
@@ -5104,7 +5120,7 @@
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_instant_apps" />
-            <meta-data android:name="test_excluded_features" android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive" />
+            <meta-data android:name="test_excluded_features" android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive:android.hardware.type.watch" />
             <meta-data android:name="display_mode"
                        android:value="multi_display_mode" />
         </activity>
@@ -5115,7 +5131,7 @@
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_instant_apps" />
-            <meta-data android:name="test_excluded_features" android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive" />
+            <meta-data android:name="test_excluded_features" android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive:android.hardware.type.watch" />
             <meta-data android:name="display_mode"
                        android:value="multi_display_mode" />
         </activity>
@@ -5127,7 +5143,7 @@
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_instant_apps" />
             <meta-data android:name="test_excluded_features"
-                android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive" />
+                android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive:android.hardware.type.watch" />
             <meta-data android:name="display_mode"
                        android:value="multi_display_mode" />
         </activity>
diff --git a/apps/CtsVerifier/res/layout-small/positive_device_owner.xml b/apps/CtsVerifier/res/layout-small/positive_device_owner.xml
index 82f11a2..81f97f6 100644
--- a/apps/CtsVerifier/res/layout-small/positive_device_owner.xml
+++ b/apps/CtsVerifier/res/layout-small/positive_device_owner.xml
@@ -18,13 +18,16 @@
     android:layout_height="match_parent"
     android:orientation="vertical">
 
-    <ScrollView
+
+    <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
-        <LinearLayout
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <ScrollView
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="vertical">
+            android:layout_height="0px"
+            android:layout_weight="1">
 
             <TextView
                 android:id="@+id/positive_device_owner_instructions"
@@ -32,19 +35,22 @@
                 android:layout_height="wrap_content"
                 android:text="@string/device_owner_positive_tests_instructions"
                 android:textSize="16dip" />
+        </ScrollView>
 
-            <Button
-                android:id="@+id/set_device_owner_button"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/set_device_owner_button_label" />
+        <Button
+            android:id="@+id/set_device_owner_button"
+            android:layout_width="match_parent"
+            android:layout_height="0px"
+            android:layout_weight="1"
+            android:text="@string/set_device_owner_button_label" />
 
-            <ListView
-                android:id="@+id/android:list"
-                android:layout_width="match_parent"
-                android:layout_height="800dip" />
+        <ListView
+            android:id="@+id/android:list"
+            android:layout_width="match_parent"
+            android:layout_height="0px"
+            android:layout_weight="1" />
 
-            <include layout="@layout/pass_fail_buttons" />
-        </LinearLayout>
-    </ScrollView>
+        <include layout="@layout/pass_fail_buttons" />
+    </LinearLayout>
+
 </LinearLayout>
diff --git a/apps/CtsVerifier/res/layout-small/sensor_test.xml b/apps/CtsVerifier/res/layout-small/sensor_test.xml
index 96cf30a..348e321 100644
--- a/apps/CtsVerifier/res/layout-small/sensor_test.xml
+++ b/apps/CtsVerifier/res/layout-small/sensor_test.xml
@@ -16,7 +16,10 @@
 <com.android.cts.verifier.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:paddingTop="20dp"
+    android:paddingLeft="15dp"
+    android:paddingRight="15dp">
     <ScrollView android:id="@+id/log_scroll_view"
             app:ctsv_layout_box="all"
             android:fillViewport="true"
diff --git a/apps/CtsVerifier/res/layout-small/wifi_lockdown.xml b/apps/CtsVerifier/res/layout-small/wifi_lockdown.xml
index f158976..59dd730 100644
--- a/apps/CtsVerifier/res/layout-small/wifi_lockdown.xml
+++ b/apps/CtsVerifier/res/layout-small/wifi_lockdown.xml
@@ -73,7 +73,7 @@
     <ListView
         android:id="@+id/android:list"
         android:layout_width="match_parent"
-        android:layout_height="200dip"/>
+        android:layout_height="230dip"/>
 
     <include layout="@layout/pass_fail_buttons" />
 </LinearLayout>
diff --git a/apps/CtsVerifier/res/layout-watch/display_cutout.xml b/apps/CtsVerifier/res/layout-watch/display_cutout.xml
new file mode 100644
index 0000000..b1cfa01
--- /dev/null
+++ b/apps/CtsVerifier/res/layout-watch/display_cutout.xml
@@ -0,0 +1,289 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:padding="20dp">
+
+        <TextView
+            android:id="@+id/enable_buttons_desc"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/top_buttons"
+            android:layout_marginLeft="30dp"
+            android:layout_marginRight="30dp"
+            android:text="@string/display_cutout_test_instruction"
+            android:textSize="12dp" />
+
+        <include
+            android:id="@+id/pass_fail_buttons"
+            layout="@layout/pass_fail_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/enable_buttons_desc"
+            android:layout_marginLeft="30dp"
+            android:layout_marginRight="30dp" />
+
+        <LinearLayout
+            android:id="@+id/top_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/display_cutout_test_button_size"
+            android:layout_alignParentTop="true"
+            android:orientation="horizontal"
+            android:visibility="gone">
+
+            <Button
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="0"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="1"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="2"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="3"
+                android:textSize="10dp" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/bottom_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/display_cutout_test_button_size"
+            android:layout_alignParentBottom="true"
+            android:orientation="horizontal"
+            android:visibility="gone">
+
+            <Button
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="11"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="10"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="9"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="8"
+                android:textSize="10dp" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/left_buttons"
+            android:layout_width="@dimen/display_cutout_test_button_size"
+            android:layout_height="match_parent"
+            android:layout_alignParentLeft="true"
+            android:layout_marginTop="@dimen/display_cutout_test_button_size"
+            android:layout_marginBottom="@dimen/display_cutout_test_button_size"
+            android:orientation="vertical"
+            android:visibility="gone">
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="15"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="14"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="13"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="12"
+                android:textSize="10dp" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/right_buttons"
+            android:layout_width="@dimen/display_cutout_test_button_size"
+            android:layout_height="match_parent"
+            android:layout_alignParentRight="true"
+            android:layout_marginTop="@dimen/display_cutout_test_button_size"
+            android:layout_marginBottom="@dimen/display_cutout_test_button_size"
+            android:orientation="vertical"
+            android:visibility="gone">
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="4"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="5"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="6"
+                android:textSize="10dp" />
+
+            <Space
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:background="@drawable/display_cutout_test_button"
+                android:onClick="onButtonClicked"
+                android:text="7"
+                android:textSize="10dp" />
+        </LinearLayout>
+
+    </RelativeLayout>
+</ScrollView>
diff --git a/apps/CtsVerifier/res/layout-watch/pass_fail_set_password_complexity.xml b/apps/CtsVerifier/res/layout-watch/pass_fail_set_password_complexity.xml
new file mode 100644
index 0000000..9ac5661
--- /dev/null
+++ b/apps/CtsVerifier/res/layout-watch/pass_fail_set_password_complexity.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <LinearLayout
+            android:id="@+id/linear_layout"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentStart="true"
+            android:layout_alignParentTop="true"
+            android:divider="@android:color/white"
+            android:orientation="vertical"
+            android:showDividers="middle">
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+
+                <Button
+                    android:id="@+id/set_complexity_high_btn"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/set_complexity_high_txt" />
+
+                <TextView
+                    android:id="@+id/set_complexity_high_desc"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="none"
+                    android:text="@string/set_complexity_high_desc" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+
+                <Button
+                    android:id="@+id/set_complexity_medium_btn"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/set_complexity_medium_txt" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="none"
+                    android:text="@string/set_complexity_medium_desc" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+
+                <Button
+                    android:id="@+id/set_complexity_low_btn"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/set_complexity_low_txt" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="none"
+                    android:text="@string/set_complexity_low_desc" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="center_vertical"
+                android:orientation="horizontal">
+
+                <Button
+                    android:id="@+id/set_complexity_none_btn"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/set_complexity_none_txt" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="none"
+                    android:text="@string/set_complexity_none_desc" />
+            </LinearLayout>
+
+        </LinearLayout>
+
+        <include
+            layout="@layout/pass_fail_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/linear_layout" />
+    </RelativeLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout-watch/requesting_bugreport_device_owner.xml b/apps/CtsVerifier/res/layout-watch/requesting_bugreport_device_owner.xml
index 8aebbe6..ae7111a 100644
--- a/apps/CtsVerifier/res/layout-watch/requesting_bugreport_device_owner.xml
+++ b/apps/CtsVerifier/res/layout-watch/requesting_bugreport_device_owner.xml
@@ -39,7 +39,7 @@
         <ListView
             android:id="@+id/android:list"
             android:layout_width="match_parent"
-            android:layout_height="500dp"/>
+            android:layout_height="560dp"/>
 
         <include layout="@layout/pass_fail_buttons"/>
     </LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/wifi_main.xml b/apps/CtsVerifier/res/layout/wifi_main.xml
index 67ecb9a..ae4179e 100644
--- a/apps/CtsVerifier/res/layout/wifi_main.xml
+++ b/apps/CtsVerifier/res/layout/wifi_main.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2017 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,69 +12,83 @@
      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"
-                style="@style/RootLayoutPadding">
 
-        <TextView android:id="@+id/wifi_ssid_label"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:text="@string/wifi_ssid_label"
-              style="@style/InstructionsSmallFont"
-        />
-        <EditText android:id="@+id/wifi_ssid"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:layout_below="@id/wifi_ssid_label"
-              android:inputType="text"
-              style="@style/InstructionsSmallFont"
-        />
-        <TextView android:id="@+id/wifi_psk_label"
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:layout_below="@id/wifi_ssid"
-              android:text="@string/wifi_psk_label"
-              style="@style/InstructionsSmallFont"
-        />
-        <EditText android:id="@+id/wifi_psk"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:layout_below="@id/wifi_psk_label"
-              android:inputType="textPassword"
-              style="@style/InstructionsSmallFont"
-        />
-        <Button android:id="@+id/wifi_start_test_btn"
-            android:text="@string/wifi_start_test_label"
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/wifi_info_scroll_view"
+    style="@style/RootLayoutPadding"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+
+    <RelativeLayout
+        android:id="@+id/wifi_ssid_label"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <TextView
+            android:id="@+id/wifi_ssid_label"
+            style="@style/InstructionsSmallFont"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/wifi_ssid_label" />
+
+        <EditText
+            android:id="@+id/wifi_ssid"
+            style="@style/InstructionsSmallFont"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/wifi_ssid_label"
+            android:inputType="text" />
+
+        <TextView
+            android:id="@+id/wifi_psk_label"
+            style="@style/InstructionsSmallFont"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/wifi_ssid"
+            android:text="@string/wifi_psk_label" />
+
+        <EditText
+            android:id="@+id/wifi_psk"
+            style="@style/InstructionsSmallFont"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/wifi_psk_label"
+            android:inputType="textPassword" />
+
+        <Button
+            android:id="@+id/wifi_start_test_btn"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_below="@id/wifi_psk"
             android:layout_centerHorizontal="true"
-        />
+            android:text="@string/wifi_start_test_label" />
 
-        <ScrollView android:id="@+id/wifi_info_scroll_view"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:layout_below="@id/wifi_start_test_btn"
-        >
-        <TextView android:id="@+id/wifi_info"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+
+        <TextView
+            android:id="@+id/wifi_info"
             style="@style/InstructionsSmallFont"
-        />
-        </ScrollView>
-        <ProgressBar android:id="@+id/wifi_progress"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_below="@id/wifi_info_scroll_view"
-            android:indeterminate="true"
+            android:layout_below="@id/wifi_start_test_btn" />
+
+
+        <ProgressBar
+            android:id="@+id/wifi_progress"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/wifi_info"
             android:layout_gravity="center"
-            android:visibility="gone"
-        />
-        <include android:id="@+id/pass_fail_buttons"
+            android:indeterminate="true"
+            android:visibility="gone" />
+
+
+        <include
+            android:id="@+id/pass_fail_buttons"
+            layout="@layout/pass_fail_buttons"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_alignParentBottom="true"
-            layout="@layout/pass_fail_buttons"/>
-</RelativeLayout>
+            android:layout_below="@id/wifi_progress" />
+    </RelativeLayout>
+</ScrollView>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index bbc3ae3..e27d3e2e 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -395,6 +395,12 @@
         authentication with timeout 0 can only be accessed once per reader session.
     </string>
 
+    <string name="sec_unlocked_device_required_test">Unlocked Device Required</string>
+    <string name="sec_unlocked_device_required_test_info">
+        This test ensures that keys created with setUnlockedDeviceRequired are usable when the
+        screen is unlocked but not when it is locked.
+    </string>
+
     <!-- Strings for BluetoothActivity -->
     <string name="bluetooth_test">Bluetooth Test</string>
     <string name="bluetooth_test_info">
@@ -5462,6 +5468,29 @@
         \nAlphanumeric, length at least 4</string>
     <string name="set_complexity_low_desc">Any pattern, PIN or password</string>
     <string name="set_complexity_none_desc">No restrictions</string>
+    <!-- Strings for UnlockedDeviceRequiredTest -->
+    <string name="unlock_req_config_lock_screen">Secure lock screen has not been set up. Go to
+        Settings -> Security to set up a lock screen.</string>
+    <string name="unlock_req_config_lock_screen_and_biometrics">Secure lock screen has not been set
+        up. Go to Settings -> Security to set up a lock screen and biometric unlock.</string>
+    <string name="unlock_req_config_biometrics">Biometric unlock has not been set up. Go to Settings
+        -> Security to set up biometric unlock.</string>
+    <string name="unlock_req_biometric_lock">Lock the screen, wait 5 seconds, then unlock with
+        biometrics.</string>
+    <!-- This message should only be displayed briefly immediately after the user has unlocked
+        the device; as soon as key access is verified this message will be updated. -->
+    <string name="unlock_req_unlocked">Verifying the key is accessible after the unlock...</string>
+    <string name="unlock_req_credential_lock">Lock the screen, wait 5 seconds, then unlock with
+        credentials.</string>
+    <string name="unlock_req_successful">Test completed successfully.</string>
+    <string name="unlock_req_failed_key_available_when_locked">Test failed; the key was available
+        when the device was locked.</string>
+    <string name="unlock_req_failed_key_unavailable_when_unlocked">Test failed; the key was not
+        available when the device was unlocked.</string>
+    <!-- While this state should never be reached since all possible states are accounted for
+         it is included just in case. -->
+    <string name="unlock_req_unknown_state">The test is in an unexpected state; please dismiss
+        this dialog and restart the test.</string>
 
     <!-- Notification Bubble Tests-->
     <string name="bubbles_notification_title">Bubble Notification Tests</string>
@@ -5618,11 +5647,9 @@
 
     <string name="display_cutout_test">DisplayCutout Test</string>
     <string name="display_cutout_test_instruction">\n
-    This test is to make sure that the area inside the safe insets from the DisplayCutout should be
-    fully visible and touchable. \n\n
-    Please check the below items: \n
-    1. All buttons are fully visible \n
-    2. All buttons are clickable. \n
+    This test is to make sure that the area inside the safe insets from the DisplayCutout should be:\n
+    1. Visible\n
+    2. Clickable.\n
     </string>
 
     <string name="loopback_test_question">Does this device allow for the connection of a loopback audio peripheral?</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
index f9601de..39e8301 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
@@ -409,7 +409,7 @@
                                 .loadClass("android.os.SystemProperties")
                                 .getMethod("get", String.class);
                             String emulatorKernel = (String) getStringMethod.invoke("0",
-                                    "ro.kernel.qemu");
+                                    "ro.boot.qemu");
                             if (emulatorKernel.equals("1")) {
                                 return false;
                             }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
index d3fa699..9e27cb3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
@@ -75,7 +75,9 @@
     private boolean mUseFixedVolume;
     private boolean mIsTelevision;
     private boolean mIsSingleVolume;
+    private boolean mIsWatch;
     private boolean mSkipRingerTests;
+    private boolean mSkipTouchSoundTests;
 
     @Override
     protected int getTitleResource() {
@@ -101,6 +103,8 @@
         mIsSingleVolume = mContext.getResources().getBoolean(
                 Resources.getSystem().getIdentifier("config_single_volume", "bool", "android"));
         mSkipRingerTests = mUseFixedVolume || mIsTelevision || mIsSingleVolume;
+        mIsWatch = packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH);
+        mSkipTouchSoundTests = mIsWatch;
     }
 
     // Test Setup
@@ -246,6 +250,12 @@
 
         @Override
         protected void test() {
+
+            if (mSkipTouchSoundTests) {
+                status = PASS;
+                return;
+            }
+
             boolean touchSoundEnabled =
                 Settings.System.getInt(mContext.getContentResolver(),
                     Settings.System.SOUND_EFFECTS_ENABLED, 1) == 1;
@@ -276,6 +286,12 @@
 
         @Override
         protected void test() {
+
+            if (mSkipTouchSoundTests) {
+                status = PASS;
+                return;
+            }
+
             // should hear sound after loadSoundEffects() called.
             mAudioManager.loadSoundEffects();
             try {
@@ -1101,7 +1117,7 @@
                     if (stream == AudioManager.STREAM_VOICE_CALL) {
                         // Voice call requires MODIFY_PHONE_STATE, so we should not be able to mute
                         mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
-                        assertTrue("Voice call stream (" + stream + ") should require MODIFY_PHONE_STATE "
+                        assertFalse("Voice call stream (" + stream + ") should require MODIFY_PHONE_STATE "
                                 + "to mute.", mAudioManager.isStreamMute(stream));
                     } else {
                         mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
index b776143..5a60ad0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
@@ -126,13 +126,11 @@
         mCallback = new BLEAdvertiseCallback();
         mScannableCallback = new BLEAdvertiseCallback();
         mUnscannableCallback = new BLEAdvertiseCallback();
-        // Medium is last. Android TV has a medium advertiser running already, and if only four
-        // are available, that's the one we want to fail
         mPowerLevel = new int[]{
             AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
             AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
-            AdvertiseSettings.ADVERTISE_TX_POWER_HIGH,
-            AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM};
+            AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM,
+            AdvertiseSettings.ADVERTISE_TX_POWER_HIGH};
         mPowerCallback = new HashMap<Integer, AdvertiseCallback>();
         for (int x : mPowerLevel) {
             mPowerCallback.put(x, new BLEAdvertiseCallback());
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
index 8496905..2bcd86a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
@@ -37,7 +37,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 
 public class BleScannerService extends Service {
 
@@ -75,9 +74,6 @@
             "com.google.cts.verifier.bluetooth.EXTRA_DATA";
 
     private static final byte MANUFACTURER_TEST_ID = (byte)0x07;
-    public static final UUID ATV_REMOTE_UUID =
-            UUID.fromString("cbbfe0e1-f7f3-4206-84e0-84cbb3d09dfc");
-    public static final int MEDIUM_POWER_DBM = -7;
 
     private BluetoothManager mBluetoothManager;
     private BluetoothAdapter mAdapter;
@@ -85,7 +81,6 @@
     private ScanCallback mCallback;
     private Handler mHandler;
     private String mOldMac;
-    private boolean mMediumRcvd;
 
     @Override
     public void onCreate() {
@@ -94,7 +89,6 @@
         mCallback = new BLEScanCallback();
         mHandler = new Handler();
         mOldMac = null;
-        mMediumRcvd = false;
 
         mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
         mAdapter = mBluetoothManager.getAdapter();
@@ -117,9 +111,6 @@
                             BleAdvertiserService.POWER_LEVEL_DATA,
                             BleAdvertiserService.POWER_LEVEL_MASK)
                         .build());
-                    filters.add(new ScanFilter.Builder()
-                            .setServiceUuid(new ParcelUuid(ATV_REMOTE_UUID))
-                            .build());
                     settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
                     break;
                 case COMMAND_SCAN_WITH_FILTER:
@@ -175,7 +166,6 @@
             ScanRecord record = result.getScanRecord();
             String mac = result.getDevice().getAddress();
             Map<ParcelUuid, byte[]> serviceData = record.getServiceData();
-            List<ParcelUuid> serviceUuids = record.getServiceUuids();
 
             if (serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID)) != null) {
                 byte[] data =
@@ -199,24 +189,6 @@
                             sendBroadcast(newIntent);
                         }
                     }
-                    if (data[2] == AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) {
-                        mMediumRcvd = true;
-                    }
-                }
-            } else if (!mMediumRcvd && serviceUuids != null
-                    && serviceUuids.contains(new ParcelUuid(ATV_REMOTE_UUID))) {
-                // If we're not receiving medium power advertising, allow Android TV Remote Service
-                // advertising packets, which are medium power, to be counted
-                String deviceMac = result.getDevice().getAddress();
-                if (deviceMac != null && mOldMac != null && deviceMac.equals(mOldMac)) {
-                    Intent powerIntent = new Intent(BLE_POWER_LEVEL);
-                    powerIntent.putExtra(EXTRA_MAC_ADDRESS, deviceMac);
-                    // These packets don't include TxPower, assume a valid power level
-                    powerIntent.putExtra(EXTRA_POWER_LEVEL, MEDIUM_POWER_DBM);
-                    powerIntent.putExtra(EXTRA_RSSI, new Integer(result.getRssi()).toString());
-                    powerIntent.putExtra(EXTRA_POWER_LEVEL_BIT,
-                            AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM);
-                    sendBroadcast(powerIntent);
                 }
             }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS
index 8ebd7b8..d1d18b4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/OWNERS
@@ -1,2 +1,2 @@
 # Bug component: 41727
-include platform/frameworks/av:/camera/OWNER
\ No newline at end of file
+include platform/frameworks/av:/camera/OWNERS
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 7ce97fe..3fe4e72 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -35,6 +35,7 @@
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.DngCreator;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.cts.PerformanceTest;
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.params.OutputConfiguration;
@@ -49,12 +50,15 @@
 import android.media.ImageWriter;
 import android.media.Image.Plane;
 import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.Vibrator;
 import android.util.Log;
 import android.util.Rational;
@@ -62,16 +66,24 @@
 import android.util.SparseArray;
 import android.view.Surface;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.ex.camera2.blocking.BlockingCameraManager;
 import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
 import com.android.ex.camera2.blocking.BlockingStateCallback;
 import com.android.ex.camera2.blocking.BlockingSessionCallback;
 
+import com.android.compatibility.common.util.ReportLog.Metric;
 import com.android.cts.verifier.camera.its.StatsImage;
+import com.android.cts.verifier.camera.performance.CameraTestInstrumentation;
+import com.android.cts.verifier.camera.performance.CameraTestInstrumentation.MetricListener;
 import com.android.cts.verifier.R;
 
 import org.json.JSONArray;
 import org.json.JSONObject;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Request;
+import org.junit.runner.Result;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -136,6 +148,11 @@
     // Supports at most RAW+YUV+JPEG, one surface each, plus optional background stream
     private static final int MAX_NUM_OUTPUT_SURFACES = 4;
 
+    // Performance class S version number
+    private static final int PERFORMANCE_CLASS_S = Build.VERSION_CODES.R + 1;
+    private static final int PERFORMANCE_CLASS_LEVEL = SystemProperties.getInt(
+            "ro.odm.build.media_performance_class", 0);
+
     public static final int SERVERPORT = 6000;
 
     public static final String REGION_KEY = "regions";
@@ -231,6 +248,11 @@
     private HandlerThread mSensorThread = null;
     private Handler mSensorHandler = null;
 
+    // Camera test instrumentation
+    private CameraTestInstrumentation mCameraInstrumentation;
+    // Camera PerformanceTest metric
+    private final ArrayList<Metric> mResults = new ArrayList<Metric>();
+
     private static final int SERIALIZER_SURFACES_ID = 2;
     private static final int SERIALIZER_PHYSICAL_METADATA_ID = 3;
 
@@ -612,7 +634,7 @@
                         }
                         String line = input.readLine();
                         if (line == null) {
-                            Logt.i(TAG, "Socket readline retuned null (host disconnected)");
+                            Logt.i(TAG, "Socket readline returned null (host disconnected)");
                             break;
                         }
                         processSocketCommand(line);
@@ -709,6 +731,15 @@
                     mSocketRunnableObj.sendResponse("ItsVersion", ITS_SERVICE_VERSION);
                 } else if ("isStreamCombinationSupported".equals(cmdObj.getString("cmdName"))) {
                     doCheckStreamCombination(cmdObj);
+                } else if ("isSPerformanceClassPrimaryCamera".equals(cmdObj.getString("cmdName"))) {
+                    String cameraId = cmdObj.getString("cameraId");
+                    doCheckSPerformanceClassPrimaryCamera(cameraId);
+                } else if ("measureCameraLaunchMs".equals(cmdObj.getString("cmdName"))) {
+                    String cameraId = cmdObj.getString("cameraId");
+                    doMeasureCameraLaunchMs(cameraId);
+                } else if ("measureCamera1080pJpegCaptureMs".equals(cmdObj.getString("cmdName"))) {
+                    String cameraId = cmdObj.getString("cameraId");
+                    doMeasureCamera1080pJpegCaptureMs(cameraId);
                 } else {
                     throw new ItsException("Unknown command: " + cmd);
                 }
@@ -1038,6 +1069,93 @@
         }
     }
 
+    private void doCheckSPerformanceClassPrimaryCamera(String cameraId) throws ItsException {
+        boolean isSPerfClass = (PERFORMANCE_CLASS_LEVEL == PERFORMANCE_CLASS_S);
+
+        if (mItsCameraIdList == null) {
+            mItsCameraIdList = ItsUtils.getItsCompatibleCameraIds(mCameraManager);
+        }
+        if (mItsCameraIdList.mCameraIds.size() == 0) {
+            throw new ItsException("No camera devices");
+        }
+        if (!mItsCameraIdList.mCameraIds.contains(cameraId)) {
+            throw new ItsException("Invalid cameraId " + cameraId);
+        }
+
+        boolean isPrimaryCamera = false;
+        try {
+            CameraCharacteristics c = mCameraManager.getCameraCharacteristics(cameraId);
+            Integer cameraFacing = c.get(CameraCharacteristics.LENS_FACING);
+            for (String id : mItsCameraIdList.mCameraIds) {
+                c = mCameraManager.getCameraCharacteristics(id);
+                Integer facing = c.get(CameraCharacteristics.LENS_FACING);
+                if (cameraFacing.equals(facing)) {
+                    if (cameraId.equals(id)) {
+                        isPrimaryCamera = true;
+                    } else {
+                        isPrimaryCamera = false;
+                    }
+                    break;
+                }
+            }
+        } catch (CameraAccessException e) {
+            throw new ItsException("Failed to get camera characteristics", e);
+        }
+
+        mSocketRunnableObj.sendResponse("sPerformanceClassPrimaryCamera",
+                (isSPerfClass && isPrimaryCamera) ? "true" : "false");
+    }
+
+    private double invokeCameraPerformanceTest(Class testClass, String testName,
+            String cameraId, String metricName) throws ItsException {
+        mResults.clear();
+        mCameraInstrumentation = new CameraTestInstrumentation();
+        MetricListener metricListener = new MetricListener() {
+            @Override
+            public void onResultMetric(Metric metric) {
+                mResults.add(metric);
+            }
+        };
+        mCameraInstrumentation.initialize(this, metricListener);
+
+        Bundle bundle = new Bundle();
+        bundle.putString("camera-id", cameraId);
+        bundle.putString("perf-measure", "on");
+        bundle.putString("perf-class-test", "on");
+        InstrumentationRegistry.registerInstance(mCameraInstrumentation, bundle);
+
+        JUnitCore testRunner = new JUnitCore();
+        Log.v(TAG, String.format("Execute Test: %s#%s", testClass.getSimpleName(), testName));
+        Request request = Request.method(testClass, testName);
+        Result runResult = testRunner.run(request);
+        if (!runResult.wasSuccessful()) {
+            throw new ItsException("Camera PerformanceTest " + testClass.getSimpleName() +
+                    "#" + testName + " failed");
+        }
+
+        for (Metric m : mResults) {
+            if (m.getMessage().equals(metricName) && m.getValues().length == 1) {
+                return m.getValues()[0];
+            }
+        }
+
+        throw new ItsException("Failed to look up " + metricName +
+                " in Camera PerformanceTest result!");
+    }
+
+    private void doMeasureCameraLaunchMs(String cameraId) throws ItsException {
+        double launchMs = invokeCameraPerformanceTest(PerformanceTest.class,
+                "testCameraLaunch", cameraId, "camera_launch_average_time_for_all_cameras");
+        mSocketRunnableObj.sendResponse("cameraLaunchMs", Double.toString(launchMs));
+    }
+
+    private void doMeasureCamera1080pJpegCaptureMs(String cameraId) throws ItsException {
+        double jpegCaptureMs = invokeCameraPerformanceTest(PerformanceTest.class,
+                "testSingleCapture", cameraId,
+                "camera_capture_average_latency_for_all_cameras_jpeg");
+        mSocketRunnableObj.sendResponse("camera1080pJpegCaptureMs", Double.toString(jpegCaptureMs));
+    }
+
     private void prepareImageReaders(Size[] outputSizes, int[] outputFormats, Size inputSize,
             int inputFormat, int maxInputBuffers) {
         closeImageReaders();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index 5f1243f..233e1bd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -23,6 +23,7 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.provider.Telephony;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 
@@ -237,6 +238,11 @@
             case UserManager.DISALLOW_ADJUST_VOLUME:
                 return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
             case UserManager.DISALLOW_CONFIG_CELL_BROADCASTS:
+                final TelephonyManager tm =
+                    context.getSystemService(TelephonyManager.class);
+                if (!tm.isSmsCapable()) {
+                    return false;
+                }
                 // Get com.android.internal.R.bool.config_cellBroadcastAppLinks
                 final int resId = context.getResources().getIdentifier(
                         "config_cellBroadcastAppLinks", "bool", "android");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java
index d6ab01f..f1deb8d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/net/MultiNetworkConnectivityTestActivity.java
@@ -554,7 +554,7 @@
         final NetworkCallback mWifiNetworkCallback = new NetworkCallback() {
             @Override
             public void onAvailable(Network network) {
-                Log.i(TAG, "Wifi network available " + network.netId);
+                Log.i(TAG, "Wifi network available " + network);
                 stopTimerDisplayIfRequested();
                 mMultiNetworkValidator.onWifiNetworkConnected(network);
             }
@@ -569,7 +569,7 @@
         final NetworkCallback mCellularNetworkCallback = new NetworkCallback() {
             @Override
             public void onAvailable(Network network) {
-                Log.i(TAG, "Cellular network available " + network.netId);
+                Log.i(TAG, "Cellular network available " + network);
                 stopTimerDisplayIfRequested();
                 mMultiNetworkValidator.onCellularNetworkConnected(network);
             }
@@ -761,7 +761,7 @@
             Network activeNetwork = mConnectivityManager.getActiveNetwork();
             NetworkCapabilities activeNetworkCapabilities =
                     mConnectivityManager.getNetworkCapabilities(activeNetwork);
-            Log.i(TAG, "Network capabilities for " + activeNetwork.netId + " "
+            Log.i(TAG, "Network capabilities for " + activeNetwork + " "
                     + activeNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
             return activeNetworkCapabilities.hasTransport(transport)
                     && activeNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET);
@@ -774,7 +774,7 @@
         boolean isNetworkConnected(Network network) {
             NetworkInfo networkInfo = mConnectivityManager.getNetworkInfo(network);
             boolean status = networkInfo != null && networkInfo.isConnectedOrConnecting();
-            Log.i(TAG, "Network connection status " + network.netId + " " + status);
+            Log.i(TAG, "Network connection status " + network + " " + status);
             return status;
         }
 
@@ -786,7 +786,7 @@
 
         /** Called when a wifi network is connected and available */
         void onWifiNetworkConnected(Network network) {
-            Log.i(TAG, "Wifi network connected " + network.netId);
+            Log.i(TAG, "Wifi network connected " + network);
         }
 
         void onWifiNetworkUnavailable() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientTestSuite.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientTestSuite.java
index a9fd27a..91ef081 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientTestSuite.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/P2pClientTestSuite.java
@@ -19,6 +19,7 @@
 import java.util.ArrayList;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 
 /**
  * Test suite to join a p2p group.
@@ -62,6 +63,10 @@
 
         sTestSuite = new ArrayList<ReqTestCase>();
         sTestSuite.add(new P2pClientPbcTestCase(context));
-        sTestSuite.add(new P2pClientPinTestCase(context));
+
+        // Remove pin based client for automotive until b/184183917 is resolved.
+        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            sTestSuite.add(new P2pClientPinTestCase(context));
+        }
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/security/OWNERS
index 4241b61..ce787714 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/OWNERS
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/OWNERS
@@ -1,6 +1,6 @@
-# Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204 = per-file: CA*.java, Ca*.java, KeyChainTest.java
-# Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204 = per-file: LockConfirmBypassTest.java, SetNewPasswordComplexityTest.java
-# Bug component: 189335 = per-file: FingerprintBoundKeysTest.java, IdentityCredentialAuthentication.java, ProtectedConfirmationTest.java, ScreenLockBoundKeysTest.java
+# Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204 = per-file CA*.java, Ca*.java, KeyChainTest.java
+# Bug template url: https://b.corp.google.com/issues/new?component=100560&template=63204 = per-file LockConfirmBypassTest.java, SetNewPasswordComplexityTest.java
+# Bug component: 189335 = per-file FingerprintBoundKeysTest.java, IdentityCredentialAuthentication.java, ProtectedConfirmationTest.java, ScreenLockBoundKeysTest.java
 per-file CA*.java, Ca*.java, KeyChainTest.java = alexkershaw@google.com, eranm@google.com, rubinxu@google.com, sandness@google.com, pgrafov@google.com
 per-file LockConfirmBypassTest.java, SetNewPasswordComplexityTest.java = alexkershaw@google.com, eranm@google.com, rubinxu@google.com, sandness@google.com, pgrafov@google.com
 per-file FingerprintBoundKeysTest.java, IdentityCredentialAuthentication.java, ProtectedConfirmationTest.java, ScreenLockBoundKeysTest.java = swillden@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/UnlockedDeviceRequiredTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/UnlockedDeviceRequiredTest.java
new file mode 100644
index 0000000..cdaee66
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/UnlockedDeviceRequiredTest.java
@@ -0,0 +1,625 @@
+/*
+ * 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.security;
+
+import android.annotation.IntDef;
+import android.annotation.StringRes;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.hardware.biometrics.BiometricManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.util.Log;
+import android.widget.Button;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateException;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/**
+ * This test verifies a key created with #setUnlockedDeviceRequired is not accessible when the
+ * device is locked.
+ * Requirements:
+ *   Pin / pattern / password must be set, and if the device supports biometric unlock this must be
+ *   set as well.
+ * Test flow:
+ *   1. Verify a pin / pattern / password has been configured; if the device supports biometric
+ *   unlock verify this has been configured as well. If not direct the user to Settings -> Security.
+ *   2. Prompt the user to lock the device and unlock with biometrics after 5 seconds.
+ *   3. Once notification of the SCREEN_OFF has been received verify the device is locked, then
+ *   verify the key cannot be accessed.
+ *   4. When the device is unlocked verify the key is now accessible after the biometric unlock.
+ *   5. Repeat steps 2-4, this time prompting the user to unlock using the device credentials.
+ */
+public class UnlockedDeviceRequiredTest extends PassFailButtons.Activity {
+    private static final String TAG = "UnlockedDeviceRequiredTest";
+
+    /**
+     * This tag is used to display and, when necessary, remove the dialog to display the current
+     * test status to the user.
+     */
+    private static final String FRAGMENT_TAG = "test_dialog";
+
+    /** Alias for our key in the Android Key Store. */
+    private static final String KEY_NAME = "my_lock_key";
+    private static final byte[] SECRET_BYTE_ARRAY = new byte[]{1, 2, 3, 4, 5, 6};
+
+    private Resources mResources;
+    private HandlerThread mHandlerThread;
+    private ScreenStateChangeReceiver mReceiver;
+
+    private TestController mController;
+    private TestDialogFragment mDialogFragment;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.sec_screen_lock_keys_main);
+        getPassButton().setEnabled(false);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.sec_unlocked_device_required_test,
+                R.string.sec_unlocked_device_required_test_info, -1);
+        mResources = getApplicationContext().getResources();
+
+        // There are no broadcasts / notifications when a device state changes between locked and
+        // unlocked, but these two actions are most closely related to when the device should
+        // transition to a new lock state. Since the lock state may not immediately change when
+        // one of these broadcasts is sent use a HandlerThread to run off the UI thread to wait for
+        // the device to complete the transition to the new lock state.
+        mController = new TestController(this);
+        mReceiver = new ScreenStateChangeReceiver(mController);
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        intentFilter.addAction(Intent.ACTION_USER_PRESENT);
+        mHandlerThread = new HandlerThread("receiver_thread");
+        mHandlerThread.start();
+        Handler handler = new Handler(mHandlerThread.getLooper());
+        registerReceiver(mReceiver, intentFilter, null, handler);
+
+        // The test button should only be available when the DialogFragment is not currently
+        // displayed. When the button is clicked a new test is started (or the previous test
+        // resumed if it did not run through to completion).
+        mDialogFragment = TestDialogFragment.createDialogFragment(mController);
+        Button startTestButton = findViewById(R.id.sec_start_test_button);
+        startTestButton.setOnClickListener(view -> {
+            mController.updateTestState(true);
+            showDialog();
+        });
+    }
+
+    /**
+     * Shows the dialog with the next steps required by the user, or a completion status if the
+     * test has finished.
+     */
+    public void showDialog() {
+        // Remove any previously displayed fragments.
+        FragmentTransaction transaction = getFragmentManager().beginTransaction();
+        Fragment fragment = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
+        if (fragment != null) {
+            transaction.remove(fragment);
+        }
+        transaction.addToBackStack(null);
+
+        mDialogFragment = TestDialogFragment.createDialogFragment(mController);
+        mDialogFragment.show(transaction, FRAGMENT_TAG);
+    }
+
+    /**
+     * Updates the text within the {@link DialogFragment}'s {@link AlertDialog} with the current
+     * state of the test and any required user actions.
+     */
+    private void updateDialogText() {
+        Dialog dialog = mDialogFragment.getDialog();
+        if (dialog instanceof AlertDialog) {
+            ((AlertDialog) dialog).setMessage(mResources.getString(mController.getDialogMessage()));
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        // When the app is resumed update the dialog text to ensure the user is directed to the
+        // next required action.
+        updateDialogText();
+    }
+
+    /**
+     * Creates a symmetric key in the Android Key Store which can only be used after the user has
+     * unlocked the device.
+     */
+    private static void createKey() {
+        // Generate a key to decrypt payment credentials, tokens, etc.
+        try {
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            KeyGenerator keyGenerator = KeyGenerator.getInstance(
+                    KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
+
+            // Set the alias of the entry in Android KeyStore where the key will appear
+            // and the constraints (purposes) in the constructor of the Builder
+            keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
+                    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+                    .setUnlockedDeviceRequired(true)
+                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+                    .build());
+            keyGenerator.generateKey();
+        } catch (NoSuchAlgorithmException | NoSuchProviderException
+                | InvalidAlgorithmParameterException | KeyStoreException
+                | CertificateException | IOException e) {
+            throw new RuntimeException("Failed to create a symmetric key", e);
+        }
+    }
+
+    /**
+     * Tries to encrypt some data with the generated key in {@link #createKey} which
+     * only works if the user has unlocked the device.
+     *
+     * @param shouldFail boolean indicating whether an exception is expected; this is intended to
+     *                   prevent extra Logcat entries when the encrypt fails as expected
+     */
+    private static boolean tryEncrypt(boolean shouldFail) {
+        try {
+            KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);
+            Cipher cipher = Cipher.getInstance(
+                    KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/"
+                            + KeyProperties.ENCRYPTION_PADDING_PKCS7);
+
+            // Try encrypting something, it will only work if the device is unlocked.
+            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+            cipher.doFinal(SECRET_BYTE_ARRAY);
+            return true;
+        } catch (Exception e) {
+            if (!shouldFail) {
+                Log.w(TAG, "", e);
+            }
+            return false;
+        }
+    }
+
+    /**
+     * {@link BroadcastReceiver} intended to receive notification when the device's lock state
+     * should be changing.
+     *
+     * <p>{@link Intent#ACTION_SCREEN_OFF} should be sent when the device's screen is shut off;
+     * shortly after this event the device should be locked. Similarly {@link
+     * Intent#ACTION_USER_PRESENT} should be sent when the device's screen is on, the device is
+     * unlocked, and the user should be present at the device. This receiver forwards the
+     * expected lock state change to the {@link TestController} to verify the device behaves as
+     * expected depending on the current state of the test.
+     */
+    private static class ScreenStateChangeReceiver extends BroadcastReceiver {
+        private TestController mController;
+
+        /**
+         * Private constructor that accepts the {@code controller} that will be used to drive the
+         * test when lock state changes occur.
+         */
+        private ScreenStateChangeReceiver(TestController controller) {
+            mController = controller;
+        }
+
+        /**
+         * Receives one of the registered broadcasts and sends the expected device state to the
+         * {@link TestController}.
+         */
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_SCREEN_OFF:
+                    mController.deviceStateChanged(true);
+                    break;
+                case Intent.ACTION_USER_PRESENT:
+                    mController.deviceStateChanged(false);
+                    break;
+                default:
+                    Log.w(TAG, "Ignoring unexpected broadcast: " + intent.getAction());
+            }
+        }
+    }
+
+    /**
+     * Controls the flow of the test, verifying the prereqs are met and the device behaves as
+     * expected based on the current state of the test.
+     */
+    private static class TestController {
+        /**
+         * Number of times to retry the lock state query after a device has started the transition
+         * to a new lock state. This is intended to allow time for the device to enter the new state
+         * as returned by {@link KeyguardManager#isDeviceLocked()}.
+         */
+        private static final int MAX_DEVICE_STATE_RETRIES = 20;
+        /**
+         * Time to sleep between lock state queries; this will allow the device up to one second to
+         * reach the new lock state before timing out.
+         */
+        private static final long DEVICE_STATE_SLEEP_TIME = 50;
+
+        /**
+         * The test has been initialized and is waiting to verify that the device has met the
+         * requirements for the test.
+         */
+        private static final int STATE_INITIALIZED = 0;
+        /**
+         * The test is waiting for the user to configure a pin / pattern / password and a biometric
+         * unlock (where applicable).
+         */
+        private static final int STATE_AWAITING_LOCK_SCREEN_CONFIG = 1;
+        /**
+         * The test is waiting for the user to configure a biometric unlock, but a pin / pattern /
+         * password is configured on the device.
+         */
+        private static final int STATE_AWAITING_BIOMETRIC_CONFIG = 2;
+        /**
+         * The test is waiting for the user to lock the device; after the lock the device should be
+         * unlocked via biometrics.
+         */
+        private static final int STATE_AWAITING_BIOMETRIC_LOCK = 3;
+        /**
+         * The test successfully verified the key was not available in the lock state; waiting for
+         * the user to unlock the device with biometrics to verify the key is available after the
+         * unlock.
+         */
+        private static final int STATE_BIOMETRIC_UNLOCK_COMPLETE = 4;
+        /**
+         * The test is waiting for the user to lock the device; after the lock the device should be
+         * unlocked via pin / pattern / password.
+         */
+        private static final int STATE_AWAITING_CREDENTIAL_LOCK = 5;
+        /**
+         * The test successfully verified the key was not available in the lock state; waiting for
+         * the user to unlock the device with the pin / pattern / password to verify the key is
+         * available after the unlock.
+         */
+        private static final int STATE_CREDENTIAL_UNLOCK_COMPLETE = 6;
+        /**
+         * The test failed since the key was available when the device was in the lock state.
+         */
+        private static final int STATE_FAILED_KEY_AVAILABLE_IN_LOCK_STATE = 7;
+        /**
+         * The test failed since the key was not available when the device was unlocked.
+         */
+        private static final int STATE_FAILED_KEY_NOT_AVAILABLE_IN_UNLOCKED_STATE = 8;
+        /**
+         * The test completed successfully.
+         */
+        private static final int STATE_TEST_SUCCESSFUL = 9;
+
+        @IntDef(value = {
+                STATE_INITIALIZED,
+                STATE_AWAITING_LOCK_SCREEN_CONFIG,
+                STATE_AWAITING_BIOMETRIC_CONFIG,
+                STATE_AWAITING_BIOMETRIC_LOCK,
+                STATE_BIOMETRIC_UNLOCK_COMPLETE,
+                STATE_AWAITING_CREDENTIAL_LOCK,
+                STATE_CREDENTIAL_UNLOCK_COMPLETE,
+                STATE_FAILED_KEY_AVAILABLE_IN_LOCK_STATE,
+                STATE_FAILED_KEY_NOT_AVAILABLE_IN_UNLOCKED_STATE,
+                STATE_TEST_SUCCESSFUL,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface TestState {
+        }
+
+        private BiometricManager mBiometricManager;
+        private KeyguardManager mKeyguardManager;
+        private @TestState int mTestState;
+        private boolean mBiometricsSupported;
+        private UnlockedDeviceRequiredTest mActivity;
+
+        private TestController(UnlockedDeviceRequiredTest activity) {
+            mBiometricManager = activity.getSystemService(BiometricManager.class);
+            mKeyguardManager = activity.getSystemService(KeyguardManager.class);
+            // Initially assume biometrics are supported; when checking if test requirements are
+            // satisfied this can be set to false if the hardware is not available.
+            mBiometricsSupported = true;
+            mActivity = activity;
+        }
+
+        /**
+         * Updates the current state of the test based on whether the test's requirements are met;
+         * if {@code startNewTest} is true this will also start a new test if the previous test
+         * reached a terminal state.
+         */
+        private void updateTestState(boolean startNewTest) {
+            // If the test requirements are not met then return now as the verification process
+            // will set the appropriate test state based on what needs to be configured.
+            if (!verifyTestRequirements()) {
+                return;
+            }
+            // If the test was just initialized, requirements just satisfied, or a terminal state
+            // was reached then update the state to the first applicable test to be performed.
+            @TestState int initialTestState = STATE_AWAITING_BIOMETRIC_LOCK;
+            if (!mBiometricsSupported) {
+                initialTestState = STATE_AWAITING_CREDENTIAL_LOCK;
+            }
+            switch (mTestState) {
+                case STATE_INITIALIZED:
+                case STATE_AWAITING_LOCK_SCREEN_CONFIG:
+                case STATE_AWAITING_BIOMETRIC_CONFIG:
+                    // When starting a new test recreate the key in case there are any problems
+                    // accessing the key on previous attempts.
+                    createKey();
+                    mTestState = initialTestState;
+                    break;
+                case STATE_FAILED_KEY_AVAILABLE_IN_LOCK_STATE:
+                case STATE_FAILED_KEY_NOT_AVAILABLE_IN_UNLOCKED_STATE:
+                case STATE_TEST_SUCCESSFUL: {
+                    if (startNewTest) {
+                        mTestState = initialTestState;
+                    }
+                    break;
+                }
+            }
+        }
+
+        /**
+         * Called when the device should be entering a new lock state; verifies if the test is in
+         * a state where the new lock state is expected and if so runs the next portion of the test.
+         *
+         * @param enteringLockState boolean indicating whether the device should be entering a
+         *                          locked state
+         */
+        private void deviceStateChanged(boolean enteringLockState) {
+            // The tests should only be run once the device meets the requirements.
+            if (verifyTestRequirements()) {
+                // If the device is entering a lock state then run any of the awaiting lock tests.
+                if (enteringLockState) {
+                    if (mTestState == STATE_AWAITING_BIOMETRIC_LOCK
+                            || mTestState == STATE_AWAITING_CREDENTIAL_LOCK) {
+                        runDeviceTest(enteringLockState);
+                    }
+                } else {
+                    // else the device is entering an unlocked state; if a previous lock state was
+                    // verified then run the unlock test now.
+                    if (mTestState == STATE_BIOMETRIC_UNLOCK_COMPLETE ||
+                            mTestState == STATE_CREDENTIAL_UNLOCK_COMPLETE) {
+                        runDeviceTest(enteringLockState);
+                    }
+                }
+            }
+            // Once the test has completed update the dialog's text to prompt the user for the next
+            // required action or to show the completion of the test.
+            mActivity.runOnUiThread(() -> mActivity.updateDialogText());
+            // Once the test completes successfully enable the pass button.
+            if (mTestState == STATE_TEST_SUCCESSFUL) {
+                mActivity.runOnUiThread(() -> mActivity.getPassButton().setEnabled(true));
+            }
+        }
+
+        /**
+         * Runs the next portion of the test based on the device's lock state.
+         *
+         * <p>This method will first wait for the device to reach the expected lock state since it
+         * can take some time from a lock state change event before the device is actually locked.
+         * If the test fails the lock state is verified again in case the user modified the lock
+         * state after the previous lock state was verified.
+         *
+         * @param enteringLockState boolean indicating whether the device should be entering a
+         *                          locked state.
+         */
+        private void runDeviceTest(boolean enteringLockState) {
+            // Wait for the device to reach the expected lock state before attempting the test.
+            if (waitForDeviceState(enteringLockState)) {
+                boolean encryptSuccessful = tryEncrypt(enteringLockState);
+                // The test has failed if the encryption success is the same as the lock state; if
+                // the device is being locked then the encryption should fail, and if the device is
+                // being unlocked the encryption should be successful.
+                if (encryptSuccessful == enteringLockState) {
+                    // The test was expected to fail; run one more check to ensure the device
+                    // is still in the expected lock state since it's possible the user locked /
+                    // unlocked the device after its state was previously verified.
+                    if (mKeyguardManager.isDeviceLocked() == enteringLockState) {
+                        // The test failed; set the appropriate failed state.
+                        if (enteringLockState) {
+                            mTestState = STATE_FAILED_KEY_AVAILABLE_IN_LOCK_STATE;
+                        } else {
+                            mTestState = STATE_FAILED_KEY_NOT_AVAILABLE_IN_UNLOCKED_STATE;
+                        }
+                    } else {
+                        Log.d(TAG, "Device state was changed while running test; need to"
+                                + " retry the current test");
+                    }
+                } else {
+                    // The current test passed; update the state to prompt the user for the next
+                    // event.
+                    if (enteringLockState) {
+                        if (mTestState == STATE_AWAITING_BIOMETRIC_LOCK) {
+                            mTestState = STATE_BIOMETRIC_UNLOCK_COMPLETE;
+                        } else {
+                            mTestState = STATE_CREDENTIAL_UNLOCK_COMPLETE;
+                        }
+                    } else {
+                        if (mTestState == STATE_BIOMETRIC_UNLOCK_COMPLETE) {
+                            mTestState = STATE_AWAITING_CREDENTIAL_LOCK;
+                        } else {
+                            mTestState = STATE_TEST_SUCCESSFUL;
+                        }
+                    }
+                }
+            } else {
+                Log.w(TAG, "The device did not reach the "
+                        + (enteringLockState ? "locked" : "unlocked")
+                        + " state within the timeout period; this may need to be increased for"
+                        + " future tests");
+            }
+        }
+
+        /**
+         * Waits for the lock state of the device as returned by {@link
+         * KeyguardManager#isDeviceLocked()} to match the expected state, returning {@code true} if
+         * the device reached the expected state within the timeout period.
+         */
+        private boolean waitForDeviceState(boolean enteringLockState) {
+            int numRetries = 0;
+            while (numRetries < MAX_DEVICE_STATE_RETRIES) {
+                numRetries++;
+                boolean lockState = mKeyguardManager.isDeviceLocked();
+                if (lockState == enteringLockState) {
+                    return true;
+                }
+                try {
+                    Thread.sleep(DEVICE_STATE_SLEEP_TIME);
+                } catch (InterruptedException e) {
+                    Log.w(TAG, "Caught an Exception while sleeping: ", e);
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Verifies the device meets the requirements that a pin / pattern / password is set and
+         * that a biometric unlock is configured where supported.
+         *
+         * <p>If the device does not meet the requirements for the test then the test state is
+         * updated to indicate the unmet requirements to ensure the user is prompted to resolve
+         * this.
+         *
+         * @return {@code true} if the device meets the requirements for the test
+         */
+        private boolean verifyTestRequirements() {
+            if (!mKeyguardManager.isDeviceSecure()) {
+                mTestState = STATE_AWAITING_LOCK_SCREEN_CONFIG;
+                return false;
+            }
+            boolean requirementsMet = true;
+            // If the device was previously verified to not support biometric unlock then do not run
+            // this verification again.
+            if (mBiometricsSupported) {
+                int biometricResponse = mBiometricManager.canAuthenticate(
+                        BiometricManager.Authenticators.BIOMETRIC_STRONG);
+                switch (biometricResponse) {
+                    // If the device does not have the hardware to support biometrics then set the
+                    // boolean to indicate the lack of support to prevent this check on future
+                    // invocations.
+                    case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:
+                        mBiometricsSupported = false;
+                        requirementsMet = true;
+                        break;
+                    // A success response indicates at least one biometric is registered for device
+                    // unlock.
+                    case BiometricManager.BIOMETRIC_SUCCESS:
+                        requirementsMet = true;
+                        break;
+                    case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
+                        mTestState = STATE_AWAITING_BIOMETRIC_CONFIG;
+                        requirementsMet = false;
+                        break;
+                    // Treat any other response as a biometric unlock still needs to be configured.
+                    default:
+                        mTestState = STATE_AWAITING_BIOMETRIC_CONFIG;
+                        Log.w(TAG,
+                                "An unexpected response was received when querying "
+                                        + "BiometricManager#canAuthenticate: " + biometricResponse);
+                        requirementsMet = false;
+                        break;
+                }
+            }
+            return requirementsMet;
+        }
+
+        /**
+         * Returns the dialog message that should be displayed to the user based on the current
+         * state of the test.
+         */
+        private @StringRes int getDialogMessage() {
+            // Update the test state in case it was recently initialized to ensure the next
+            // required user action is displayed.
+            updateTestState(false);
+            switch (mTestState) {
+                case STATE_AWAITING_LOCK_SCREEN_CONFIG:
+                    if (mBiometricsSupported) {
+                        return R.string.unlock_req_config_lock_screen_and_biometrics;
+                    }
+                    return R.string.unlock_req_config_lock_screen;
+                case STATE_AWAITING_BIOMETRIC_CONFIG:
+                    return R.string.unlock_req_config_biometrics;
+                case STATE_AWAITING_BIOMETRIC_LOCK:
+                    return R.string.unlock_req_biometric_lock;
+                case STATE_BIOMETRIC_UNLOCK_COMPLETE:
+                case STATE_CREDENTIAL_UNLOCK_COMPLETE:
+                    return R.string.unlock_req_unlocked;
+                case STATE_AWAITING_CREDENTIAL_LOCK:
+                    return R.string.unlock_req_credential_lock;
+                case STATE_TEST_SUCCESSFUL:
+                    return R.string.unlock_req_successful;
+                case STATE_FAILED_KEY_AVAILABLE_IN_LOCK_STATE:
+                    return R.string.unlock_req_failed_key_available_when_locked;
+                case STATE_FAILED_KEY_NOT_AVAILABLE_IN_UNLOCKED_STATE:
+                    return R.string.unlock_req_failed_key_unavailable_when_unlocked;
+                // While all states are accounted for a default return is required; report an
+                // unknown state if this is reached to ensure the test is retried from a clean
+                // state.
+                default:
+                    return R.string.unlock_req_unknown_state;
+            }
+        }
+    }
+
+    /**
+     * {@link DialogFragment} used to display an AlertDialog to guide the user through the steps
+     * required for the test. A {@code DialogFragment} is used since it automatically handles
+     * configuration changes.
+     */
+    public static class TestDialogFragment extends DialogFragment {
+        private TestController mController;
+
+        /**
+         * Creates a new {@link DialogFragment} that can obtain its text from the provided {@code
+         * controller}.
+         */
+        private static TestDialogFragment createDialogFragment(TestController controller) {
+            TestDialogFragment fragment = new TestDialogFragment();
+            fragment.mController = controller;
+            return fragment;
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            return new AlertDialog.Builder(getContext()).setMessage(
+                    mController.getDialogMessage()).create();
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/CallbackUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/CallbackUtils.java
index 38c2f11..d432ef9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/CallbackUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/CallbackUtils.java
@@ -17,7 +17,6 @@
 package com.android.cts.verifier.wifi;
 
 import android.net.ConnectivityManager;
-import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.util.Log;
@@ -59,11 +58,9 @@
         }
 
         @Override
-        public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
-                LinkProperties linkProperties, boolean isBlocked) {
+        public void onAvailable(Network network) {
             if (DBG) Log.v(TAG, "onAvailable");
             mNetwork = network;
-            mNetworkCapabilities = networkCapabilities;
             mOnAvailableBlocker.countDown();
         }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkRequestTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkRequestTestCase.java
index 75e5a42..a19edf3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkRequestTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkRequestTestCase.java
@@ -248,7 +248,7 @@
     @Override
     protected void setUp() {
         super.setUp();
-        mConnectivityManager = ConnectivityManager.from(mContext);
+        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
     }
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
index 1a829f2..49d5d80 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifi/testcase/NetworkSuggestionTestCase.java
@@ -407,7 +407,7 @@
     @Override
     protected void setUp() {
         super.setUp();
-        mConnectivityManager = ConnectivityManager.from(mContext);
+        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
     }
 
     @Override
diff --git a/apps/MainlineModuleDetector/Android.mk b/apps/MainlineModuleDetector/Android.mk
index 6202ac7..2d84f02 100644
--- a/apps/MainlineModuleDetector/Android.mk
+++ b/apps/MainlineModuleDetector/Android.mk
@@ -26,6 +26,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := MainlineModuleDetector
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 LOCAL_SDK_VERSION := current
 
diff --git a/apps/hotspot/Android.bp b/apps/hotspot/Android.bp
new file mode 100644
index 0000000..6fe89c3
--- /dev/null
+++ b/apps/hotspot/Android.bp
@@ -0,0 +1,17 @@
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "hotspot",
+    defaults: ["cts_defaults"],
+    srcs: ["src/**/*.java"],
+    sdk_version: "current",
+    static_libs: ["android-support-v4"],
+    test_suites: [
+        "cts",
+        "general-tests",
+        "sts",
+    ],
+}
diff --git a/apps/hotspot/Android.mk b/apps/hotspot/Android.mk
deleted file mode 100644
index ce3b1ca1..0000000
--- a/apps/hotspot/Android.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := hotspot
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
-
-LOCAL_COMPATIBILITY_SUITE := cts general-tests sts
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index a79e848..2f5b095 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -37,7 +37,6 @@
   $(DEVICE_INFO_PACKAGE).MediaDeviceInfo \
   $(DEVICE_INFO_PACKAGE).MemoryDeviceInfo \
   $(DEVICE_INFO_PACKAGE).PackageDeviceInfo \
-  $(DEVICE_INFO_PACKAGE).PropertyDeviceInfo \
   $(DEVICE_INFO_PACKAGE).ScreenDeviceInfo \
   $(DEVICE_INFO_PACKAGE).StorageDeviceInfo \
   $(DEVICE_INFO_PACKAGE).UserDeviceInfo \
diff --git a/build/test_executable.mk b/build/test_executable.mk
index 1ff9a51..8dca32c 100644
--- a/build/test_executable.mk
+++ b/build/test_executable.mk
@@ -18,4 +18,11 @@
 # Replace "include $(BUILD_EXECUTABLE)" with "include $(BUILD_CTS_EXECUTABLE)"
 #
 
+# Implicitly run this test under MTE SYNC for aarch64 binaries. This is a no-op
+# on non-MTE hardware.
+my_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)
+ifneq (,$(filter arm64,$(my_arch)))
+	LOCAL_WHOLE_STATIC_LIBRARIES += note_memtag_heap_sync
+endif
+
 include $(BUILD_EXECUTABLE)
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PropertyDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PropertyDeviceInfo.java
deleted file mode 100644
index bb7f1eb..0000000
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PropertyDeviceInfo.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.deviceinfo;
-
-import android.util.Log;
-
-import com.android.compatibility.common.util.DeviceInfoStore;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Scanner;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * System property info collector.
- */
-public final class PropertyDeviceInfo extends DeviceInfo {
-
-    private static final String LOG_TAG = "PropertyDeviceInfo";
-
-    @Override
-    protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
-        try {
-            collectRoProperties(store);
-        } catch (IOException e) {
-            Log.w(LOG_TAG, "Failed to collect properties", e);
-        }
-    }
-
-    private void collectRoProperties(DeviceInfoStore store) throws IOException {
-        store.startArray("ro_property");
-        Pattern pattern = Pattern.compile("\\[(ro.+)\\]: \\[(.+)\\]");
-        Scanner scanner = null;
-        try {
-            Process getprop = new ProcessBuilder("getprop").start();
-            scanner = new Scanner(getprop.getInputStream());
-            while (scanner.hasNextLine()) {
-                String line = scanner.nextLine();
-                Matcher matcher = pattern.matcher(line);
-                if (matcher.matches()) {
-                    String name = matcher.group(1);
-                    String value = matcher.group(2);
-
-                    store.startGroup();
-                    store.addResult("name", name);
-                    store.addResult("value", value);
-                    store.endGroup();
-                }
-            }
-        } finally {
-            store.endArray();
-            if (scanner != null) {
-                scanner.close();
-            }
-        }
-    }
-}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/VintfDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/VintfDeviceInfo.java
index 1fb9049..8869c87 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/VintfDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/VintfDeviceInfo.java
@@ -47,6 +47,7 @@
         store.addResult("hardware_id", VintfRuntimeInfo.getHardwareId());
         store.addResult("kernel_version", VintfRuntimeInfo.getKernelVersion());
         store.addResult("sepolicy_version", VintfObject.getSepolicyVersion());
+        store.addResult("platform_sepolicy_version", VintfObject.getPlatformSepolicyVersion());
 
         String[] hals = VintfObject.getHalNamesAndVersions();
         store.addListResult("hals", hals == null
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/PropertyUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/PropertyUtil.java
index c5933a7..e7ebfba 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/PropertyUtil.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/PropertyUtil.java
@@ -16,10 +16,7 @@
 
 package com.android.compatibility.common.util;
 
-import static org.junit.Assert.assertNotEquals;
-
 import android.os.Build;
-import android.os.SystemProperties;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -44,7 +41,8 @@
     private static final String BUILD_TYPE_PROPERTY = "ro.build.type";
     private static final String MANUFACTURER_PROPERTY = "ro.product.manufacturer";
     private static final String TAG_DEV_KEYS = "dev-keys";
-    private static final String VENDOR_SDK_VERSION = "ro.vendor.build.version.sdk";
+    private static final String VENDOR_API_LEVEL = "ro.board.api_level";
+    private static final String VENDOR_FIRST_API_LEVEL = "ro.board.first_api_level";
     private static final String VNDK_VERSION = "ro.vndk.version";
 
     public static final String GOOGLE_SETTINGS_QUERY =
@@ -53,6 +51,9 @@
     /** Value to be returned by getPropertyInt() if property is not found */
     public static int INT_VALUE_IF_UNSET = -1;
 
+    /** API level for current in development */
+    public static final int API_LEVEL_CURRENT = 10000;
+
     /** Returns whether the device build is a user build */
     public static boolean isUserBuild() {
         return propertyEquals(BUILD_TYPE_PROPERTY, "user");
@@ -79,7 +80,48 @@
     }
 
     /**
-     * Return whether the VNDK version of the vendor partiton is newer than the given API level.
+     * Return the API level of the vendor partition. It will read the following properties in order
+     * and returns the value of the first defined property. If none of them are defined, or the
+     * value is a VERSION CODENAME, returns the current API level which is defined in
+     * API_LEVEL_CURRENT.
+     *
+     * <ul>
+     *   <li> ro.board.api_level
+     *   <li> ro.board.first_api_level
+     *   <li> ro.vndk.version
+     * </ul>
+     */
+    public static int getVendorApiLevel() {
+        String[] vendorApiLevelProps = {
+            // Use the properties in order.
+            VENDOR_API_LEVEL, VENDOR_FIRST_API_LEVEL, VNDK_VERSION,
+        };
+        for (String prop : vendorApiLevelProps) {
+            int apiLevel = getPropertyInt(prop);
+            if (apiLevel != INT_VALUE_IF_UNSET) {
+                return apiLevel;
+            }
+        }
+        return API_LEVEL_CURRENT;
+    }
+
+    /**
+     * Return whether the API level of the vendor partition is newer than the given API level.
+     */
+    public static boolean isVendorApiLevelNewerThan(int apiLevel) {
+        return getVendorApiLevel() > apiLevel;
+    }
+
+    /**
+     * Return whether the API level of the vendor partition is same or newer than the
+     * given API level.
+     */
+    public static boolean isVendorApiLevelAtLeast(int apiLevel) {
+        return getVendorApiLevel() >= apiLevel;
+    }
+
+    /**
+     * Return whether the VNDK version of the vendor partition is newer than the given API level.
      * If the property is set to non-integer value, this means the vendor partition is using
      * current API level and true is returned.
      */
@@ -92,7 +134,7 @@
     }
 
     /**
-     * Return whether the VNDK version of the vendor partiton is same or newer than the
+     * Return whether the VNDK version of the vendor partition is same or newer than the
      * given API level.
      * If the property is set to non-integer value, this means the vendor partition is using
      * current API level and true is returned.
@@ -106,33 +148,6 @@
     }
 
     /**
-     * Return whether the SDK version of the vendor partiton is newer than the given API level.
-     */
-    public static boolean isVendorApiLevelNewerThan(int apiLevel) {
-        int vendorSdkVersion = SystemProperties.getInt(VENDOR_SDK_VERSION, 0);
-        // Run previous action when failed to get ro.vendor.build.version.sdk
-        // b/166800127 for details
-        if (vendorSdkVersion == 0) {
-            return isVndkApiLevelNewerThan(apiLevel);
-        }
-        return vendorSdkVersion > apiLevel;
-    }
-
-    /**
-     * Return whether the SDK version of the vendor partiton is same or newer than the
-     * given API level.
-     */
-    public static boolean isVendorApiLevelAtLeast(int apiLevel) {
-        int vendorSdkVersion = SystemProperties.getInt(VENDOR_SDK_VERSION, 0);
-        // Run previous action when failed to get ro.vendor.build.version.sdk
-        // b/166800127 for details
-        if (vendorSdkVersion == 0) {
-            return isVndkApiLevelAtLeast(apiLevel);
-        }
-        return vendorSdkVersion >= apiLevel;
-    }
-
-    /**
      * Return the manufacturer of this product. If unset, return null.
      */
     public static String getManufacturer() {
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
index c2e4224..6bf9cf3 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java
@@ -66,7 +66,9 @@
 
             if (view == null) {
                 UiScrollable scrollable = new UiScrollable(new UiSelector().scrollable(true));
-                scrollable.setSwipeDeadZonePercentage(0.25);
+                if (!FeatureUtil.isWatch()) {
+                    scrollable.setSwipeDeadZonePercentage(0.25);
+                }
                 if (scrollable.exists()) {
                     if (isAtEnd) {
                         if (wasScrolledUpAlready) {
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiccUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiccUtil.java
new file mode 100644
index 0000000..69b59e8
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiccUtil.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util;
+
+import android.Manifest;
+import android.telephony.TelephonyManager;
+
+import androidx.annotation.StringDef;
+import androidx.test.InstrumentationRegistry;
+
+import java.util.List;
+
+/** Utility class for common UICC- and SIM-related operations. */
+public final class UiccUtil {
+    /** The hashes of all supported CTS UICC test keys and their corresponding specification. */
+    @StringDef({UiccCertificate.CTS_UICC_LEGACY, UiccCertificate.CTS_UICC_2021})
+    public @interface UiccCertificate {
+
+        /**
+         * Indicates compliance with the "legacy" CTS UICC specification (prior to 2021).
+         *
+         * <p>Deprecated as of 2021, support to be removed in 2022.
+         *
+         * <p>Corresponding certificate: {@code aosp-testkey}.
+         */
+        String CTS_UICC_LEGACY = "61ED377E85D386A8DFEE6B864BD85B0BFAA5AF81";
+
+        /**
+         * Indicates compliance with the 2021 CTS UICC specification.
+         *
+         * <p>Strongly recommended as of 2021, required as of 2022.
+         *
+         * <p>Corresponding certificate: {@code cts-uicc-2021-testkey}.
+         */
+        String CTS_UICC_2021 = "CE7B2B47AE2B7552C8F92CC29124279883041FB623A5F194A82C9BF15D492AA0";
+    }
+
+    /**
+     * A simple check for use with {@link org.junit.Assume#assumeTrue}. Checks the carrier privilege
+     * certificates stored on the SIM and returns {@code true} if {@code requiredCert} is present.
+     *
+     * <p>Can be used either in the {@code #setUp} method if an entire class requires a particular
+     * UICC, or at the top of a specific {@code @Test} method.
+     *
+     * <p>If we had JUnit 5, we could create a much cooler {@code @RequiresUiccCertificate}
+     * annotation using {@code ExtendWith} and {@code ExecutionCondition}, but that isn't available
+     * to us yet.
+     */
+    public static boolean uiccHasCertificate(@UiccCertificate String requiredCert) {
+        TelephonyManager tm =
+                InstrumentationRegistry.getInstrumentation()
+                        .getTargetContext()
+                        .getSystemService(TelephonyManager.class);
+        List<String> uiccCerts =
+                ShellIdentityUtils.invokeMethodWithShellPermissions(
+                        tm,
+                        TelephonyManager::getCertsFromCarrierPrivilegeAccessRules,
+                        Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+        return uiccCerts == null ? false : uiccCerts.contains(requiredCert);
+    }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/WindowUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/WindowUtil.java
new file mode 100644
index 0000000..4093c84
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/WindowUtil.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util;
+
+import android.app.Activity;
+import android.view.Window;
+
+public class WindowUtil {
+    /**
+     * The timeout in milliseconds to wait for window focus in
+     * {@link #waitForFocus(Window)} and {@link #waitForFocus(Activity)}.
+     */
+    public static final long WINDOW_FOCUS_TIMEOUT_MILLIS = 10_000L;
+
+    /**
+     * Waits until {@code activity}'s main window has focus, timing out after
+     * {@link #WINDOW_FOCUS_TIMEOUT_MILLIS}.
+     * @param activity The Activity whose main window should gain focus.
+     */
+    public static void waitForFocus(Activity activity) {
+        PollingCheck.waitFor(WINDOW_FOCUS_TIMEOUT_MILLIS,
+                activity::hasWindowFocus);
+    }
+
+    /**
+     * Waits until {@code window} has focus, timing out after {@link #WINDOW_FOCUS_TIMEOUT_MILLIS}.
+     * @param window The Window that should gain focus.
+     */
+    public static void waitForFocus(Window window) {
+        PollingCheck.waitFor(WINDOW_FOCUS_TIMEOUT_MILLIS,
+                window.getDecorView()::hasWindowFocus);
+    }
+}
diff --git a/hostsidetests/apex/src/android/apex/cts/ApexTest.java b/hostsidetests/apex/src/android/apex/cts/ApexTest.java
index 6652ef02..91920ad 100644
--- a/hostsidetests/apex/src/android/apex/cts/ApexTest.java
+++ b/hostsidetests/apex/src/android/apex/cts/ApexTest.java
@@ -37,14 +37,14 @@
   private boolean isGSI() throws Exception {
     String systemProduct = getDevice().getProperty("ro.product.system_ext.name");
     return (null != systemProduct)
-        && (systemProduct.equals("aosp_arm")
+        && (systemProduct.equals("gsi_arm")
+            || systemProduct.equals("gsi_arm64")
+            || systemProduct.equals("gsi_x86")
+            || systemProduct.equals("gsi_x86_64")
+            || systemProduct.equals("aosp_arm")
             || systemProduct.equals("aosp_arm64")
             || systemProduct.equals("aosp_x86")
             || systemProduct.equals("aosp_x86_64")
-            || systemProduct.equals("aosp_arm_ab") // _ab for Legacy GSI
-            || systemProduct.equals("aosp_arm64_ab")
-            || systemProduct.equals("aosp_x86_ab")
-            || systemProduct.equals("aosp_x86_64_ab")
             || systemProduct.equals("aosp_tv_arm")
             || systemProduct.equals("aosp_tv_arm64"));
   }
diff --git a/hostsidetests/appcompat/compatchanges/app/Android.bp b/hostsidetests/appcompat/compatchanges/app/Android.bp
index 9dde881..a54e141 100644
--- a/hostsidetests/appcompat/compatchanges/app/Android.bp
+++ b/hostsidetests/appcompat/compatchanges/app/Android.bp
@@ -28,6 +28,7 @@
     ],
     static_libs: [
         "ctstestrunner-axt",
+        "testng",
         "truth-prebuilt",
     ],
 
diff --git a/hostsidetests/appcompat/compatchanges/app/src/com/android/cts/appcompat/compatchanges/CompatChangesTest.java b/hostsidetests/appcompat/compatchanges/app/src/com/android/cts/appcompat/compatchanges/CompatChangesTest.java
index 27cef40..16214ce 100644
--- a/hostsidetests/appcompat/compatchanges/app/src/com/android/cts/appcompat/compatchanges/CompatChangesTest.java
+++ b/hostsidetests/appcompat/compatchanges/app/src/com/android/cts/appcompat/compatchanges/CompatChangesTest.java
@@ -17,9 +17,11 @@
 package com.android.cts.appcompat.compatchanges;
 
 import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
 
 import android.Manifest;
 import android.app.compat.CompatChanges;
+import android.app.compat.PackageOverride;
 import android.content.Context;
 import android.os.Process;
 
@@ -30,7 +32,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+
+import java.util.Collections;
 
 /**
  * Tests for the {@link android.app.compat.CompatChanges} SystemApi.
@@ -44,11 +47,16 @@
 @RunWith(AndroidJUnit4.class)
 public final class CompatChangesTest {
   private static final long CTS_SYSTEM_API_CHANGEID = 149391281;
+  private static final long CTS_SYSTEM_API_OVERRIDABLE_CHANGEID = 174043039;
+
+  private static final String OVERRIDE_PACKAGE = "com.android.cts.appcompat.preinstalloverride";
+
   @Before
   public void setUp() {
     InstrumentationRegistry.getInstrumentation().getUiAutomation()
       .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
-                                    Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+                                    Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+                                    Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD);
   }
 
   @After
@@ -95,4 +103,88 @@
   public void isChangeEnabledUid_changeDisabled() {
     assertThat(CompatChanges.isChangeEnabled(CTS_SYSTEM_API_CHANGEID, Process.myUid())).isFalse();
   }
+
+  @Test
+  public void putPackageOverrides_securityExceptionForNonOverridableChangeId() {
+    SecurityException e = assertThrows(SecurityException.class,
+            () -> CompatChanges.putPackageOverrides(OVERRIDE_PACKAGE,
+                    Collections.singletonMap(CTS_SYSTEM_API_CHANGEID,
+                    new PackageOverride.Builder().setEnabled(true).build())));
+    assertThat(e).hasMessageThat().contains("marked as Overridable");
+  }
+
+  @Test
+  public void putPackageOverrides_success() {
+    CompatChanges.putPackageOverrides(OVERRIDE_PACKAGE,
+            Collections.singletonMap(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID,
+                    new PackageOverride.Builder().setEnabled(true).build()));
+  }
+
+  @Test
+  public void putPackageOverrides_fromVersion2() {
+    CompatChanges.putPackageOverrides(OVERRIDE_PACKAGE,
+            Collections.singletonMap(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID,
+                    new PackageOverride.Builder().setMinVersionCode(2).setEnabled(true).build()));
+  }
+
+  @Test
+  public void putPackageOverrides_untilVersion1() {
+    CompatChanges.putPackageOverrides(OVERRIDE_PACKAGE,
+            Collections.singletonMap(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID,
+                    new PackageOverride.Builder().setMaxVersionCode(1).setEnabled(true).build()));
+  }
+
+  @Test
+  public void putPackageOverrides_securityExceptionForNotHoldingPermission() {
+    // Adopt the normal override permission that doesn't allow to clear overrides on release builds
+    InstrumentationRegistry.getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+    InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+            Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG);
+
+    SecurityException e = assertThrows(SecurityException.class,
+            () -> CompatChanges.putPackageOverrides(OVERRIDE_PACKAGE,
+                    Collections.singletonMap(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID,
+                            new PackageOverride.Builder().setEnabled(true).build())));
+    assertThat(e).hasMessageThat().contains("Cannot override compat change");
+  }
+
+  @Test
+  public void removePackageOverrides_securityExceptionForNonOverridableChangeId() {
+    SecurityException e = assertThrows(SecurityException.class,
+            () -> CompatChanges.removePackageOverrides(OVERRIDE_PACKAGE,
+                    Collections.singleton(CTS_SYSTEM_API_CHANGEID)));
+    assertThat(e).hasMessageThat().contains("marked as Overridable");
+  }
+
+  @Test
+  public void removePackageOverrides_doesNothingIfOverrideNotPresent() {
+    CompatChanges.removePackageOverrides(OVERRIDE_PACKAGE,
+            Collections.singleton(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID));
+  }
+
+  @Test
+  public void removePackageOverrides_overridePresentSuccess() {
+    CompatChanges.putPackageOverrides(OVERRIDE_PACKAGE,
+            Collections.singletonMap(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID,
+                    new PackageOverride.Builder().setEnabled(true).build()));
+    CompatChanges.removePackageOverrides(OVERRIDE_PACKAGE,
+            Collections.singleton(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID));
+  }
+
+  @Test
+  public void removePackageOverrides_securityExceptionForNotHoldingPermission() {
+    CompatChanges.putPackageOverrides(OVERRIDE_PACKAGE,
+            Collections.singletonMap(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID,
+                    new PackageOverride.Builder().setEnabled(true).build()));
+
+    // Adopt the normal override permission that doesn't allow to clear overrides on release builds
+    InstrumentationRegistry.getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+    InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+            Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG);
+
+    SecurityException e = assertThrows(SecurityException.class,
+            () -> CompatChanges.removePackageOverrides(OVERRIDE_PACKAGE,
+                    Collections.singleton(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID)));
+    assertThat(e).hasMessageThat().contains("Cannot override compat change");
+  }
 }
diff --git a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesOverrideOnReleaseBuildTest.java b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesOverrideOnReleaseBuildTest.java
new file mode 100644
index 0000000..70ba008
--- /dev/null
+++ b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesOverrideOnReleaseBuildTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.appcompat;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.compat.cts.Change;
+import android.compat.cts.CompatChangeGatingTestCase;
+
+import com.google.common.collect.ImmutableSet;
+
+public class CompatChangesOverrideOnReleaseBuildTest extends CompatChangeGatingTestCase {
+    private static final String TEST_APK = "CtsHostsideCompatChangeTestsApp.apk";
+    private static final String TEST_PKG = "com.android.cts.appcompat.compatchanges";
+
+    private static final String OVERRIDE_PKG = "com.android.cts.appcompat.preinstalloverride";
+
+    private static final long CTS_CHANGE_ID = 149391281L;
+    private static final long CTS_OVERRIDABLE_CHANGE_ID = 174043039L;
+
+    @Override
+    protected void setUp() throws Exception {
+        installPackage(TEST_APK, true);
+        runCommand("am compat reset-all " + OVERRIDE_PKG);
+        runCommand("settings put global force_non_debuggable_final_build_for_compat 1");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        runCommand("am compat reset-all " + OVERRIDE_PKG);
+        uninstallPackage(TEST_PKG, true);
+        uninstallPackage(OVERRIDE_PKG, false);
+        runCommand("settings put global force_non_debuggable_final_build_for_compat 0");
+    }
+
+    public void testPutPackageOverridesSecurityExceptionNonOverridableChangeId() throws Exception {
+        installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
+
+        runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
+                "putPackageOverrides_securityExceptionForNonOverridableChangeId",
+                /*enabledChanges*/ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+
+        Change ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isFalse();
+        assertThat(ctsChange.hasOverrides).isFalse();
+    }
+
+    public void testPutPackageOverridesSecurityExceptionMissingPermission() throws Exception {
+        installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
+
+        runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
+                "putPackageOverrides_securityExceptionForNotHoldingPermission",
+                /*enabledChanges*/ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+
+        Change ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isFalse();
+        assertThat(ctsChange.hasOverrides).isFalse();
+    }
+
+    public void testPutPackageOverridesForAllVersions() throws Exception {
+        installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
+
+        runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
+                "putPackageOverrides_success",
+                /*enabledChanges*/ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+
+        Change ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isTrue();
+        assertThat(ctsChange.rawOverrideStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
+        assertThat(ctsChange.hasOverrides).isTrue();
+        assertThat(ctsChange.overridesStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
+
+        // Now update to newer app version and the override should be applied
+        installPackage("appcompat_preinstall_override_versioncode2_release.apk", false);
+
+        ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isTrue();
+        assertThat(ctsChange.rawOverrideStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
+        assertThat(ctsChange.hasOverrides).isTrue();
+        assertThat(ctsChange.overridesStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
+    }
+
+    public void testPutPackageOverridesForNewerVersion() throws Exception {
+        installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
+
+        runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
+                "putPackageOverrides_fromVersion2",
+                /*enabledChanges*/ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+
+        Change ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isTrue();
+        assertThat(ctsChange.rawOverrideStr).isEqualTo("{" + OVERRIDE_PKG
+                + "=[2,9223372036854775807,true]}");
+        assertThat(ctsChange.hasOverrides).isFalse();
+
+        // Now update to newer app version and the override should be applied
+        installPackage("appcompat_preinstall_override_versioncode2_release.apk", false);
+
+        ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isTrue();
+        assertThat(ctsChange.rawOverrideStr).isEqualTo("{" + OVERRIDE_PKG
+                + "=[2,9223372036854775807,true]}");
+        assertThat(ctsChange.hasOverrides).isTrue();
+        assertThat(ctsChange.overridesStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
+    }
+
+    public void testPutPackageOverridesForOlderVersion() throws Exception {
+        installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
+
+        runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
+                "putPackageOverrides_untilVersion1",
+                /*enabledChanges*/ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+
+        Change ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isTrue();
+        assertThat(ctsChange.rawOverrideStr).isEqualTo("{" + OVERRIDE_PKG
+                + "=[-9223372036854775808,1,true]}");
+        assertThat(ctsChange.hasOverrides).isTrue();
+        assertThat(ctsChange.overridesStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
+
+        // Now update to newer app version and the override should no longer be applied
+        installPackage("appcompat_preinstall_override_versioncode2_release.apk", false);
+        ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isTrue();
+        assertThat(ctsChange.rawOverrideStr).isEqualTo("{" + OVERRIDE_PKG
+                + "=[-9223372036854775808,1,true]}");
+        assertThat(ctsChange.hasOverrides).isFalse();
+    }
+
+    public void testRemovePackageOverridesSecurityExceptionNonOverridableChangeId()
+            throws Exception {
+        installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
+
+        runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
+                "removePackageOverrides_securityExceptionForNonOverridableChangeId",
+                /*enabledChanges*/ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+    }
+
+    public void testRemovePackageOverridesSecurityExceptionMissingPermission() throws Exception {
+        installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
+
+        runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
+                "removePackageOverrides_securityExceptionForNotHoldingPermission",
+                /*enabledChanges*/ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+
+        Change ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isTrue();
+        assertThat(ctsChange.rawOverrideStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
+    }
+
+    public void testRemovePackageOverridesWhenOverrideNotPresent() throws Exception {
+        installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
+
+        runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
+                "removePackageOverrides_doesNothingIfOverrideNotPresent",
+                /*enabledChanges*/ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+
+        Change ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isFalse();
+        assertThat(ctsChange.hasOverrides).isFalse();
+    }
+
+    public void testRemovePackageOverridesWhenOverridePresent() throws Exception {
+        installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
+
+        runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
+                "removePackageOverrides_overridePresentSuccess",
+                /*enabledChanges*/ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+
+        Change ctsChange = getOnDeviceChangeIdConfig(CTS_OVERRIDABLE_CHANGE_ID);
+        assertWithMessage("CTS specific change %s not found on device", CTS_OVERRIDABLE_CHANGE_ID)
+                .that(ctsChange).isNotNull();
+        assertThat(ctsChange.hasRawOverrides).isFalse();
+        assertThat(ctsChange.hasOverrides).isFalse();
+    }
+}
diff --git a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesPreInstallOverrideTest.java b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesPreInstallOverrideTest.java
index bf5e4d5..e3d3d80 100644
--- a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesPreInstallOverrideTest.java
+++ b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesPreInstallOverrideTest.java
@@ -130,12 +130,6 @@
     }
 
     private Change getCtsChange() throws Exception {
-        List<Change> allChanges = getOnDeviceCompatConfig();
-        for (Change change : allChanges) {
-            if (change.changeId == CTS_CHANGE_ID) {
-                return change;
-            }
-        }
-        return null;
+        return getOnDeviceChangeIdConfig(CTS_CHANGE_ID);
     }
 }
diff --git a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesValidConfigTest.java b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesValidConfigTest.java
index ef53750..5d4b2a1 100644
--- a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesValidConfigTest.java
+++ b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesValidConfigTest.java
@@ -17,6 +17,7 @@
 package com.android.cts.appcompat;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.compat.cts.Change;
 import android.compat.cts.CompatChangeGatingTestCase;
@@ -32,8 +33,6 @@
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
 public final class CompatChangesValidConfigTest extends CompatChangeGatingTestCase {
@@ -43,13 +42,30 @@
         "ALLOW_TEST_API_ACCESS"
     );
 
+    private static final Set<String> OVERRIDABLE_CHANGES = ImmutableSet.of(
+            "CTS_SYSTEM_API_OVERRIDABLE_CHANGEID"
+    );
+
     /**
      * Check that there are no overrides.
      */
     public void testNoOverrides() throws Exception {
         for (Change c : getOnDeviceCompatConfig()) {
-            if (!OVERRIDES_ALLOWLIST.contains(c.changeName)) {
-                assertThat(c.hasOverrides).isFalse();
+            if (!OVERRIDES_ALLOWLIST.contains(c.changeName) && !c.overridable) {
+                assertWithMessage("Change should not have overrides: " + c)
+                        .that(c.hasOverrides).isFalse();
+            }
+        }
+    }
+
+    /**
+     * Check that only approved changes are overridable.
+     */
+    public void testOnlyAllowedlistedChangesAreOverridable() throws Exception {
+        for (Change c : getOnDeviceCompatConfig()) {
+            if (c.overridable) {
+                assertWithMessage("Please contact platform-compat-eng@google.com for approval")
+                        .that(OVERRIDABLE_CHANGES).contains(c.changeName);
             }
         }
     }
diff --git a/hostsidetests/appcompat/host/lib/src/android/compat/cts/Change.java b/hostsidetests/appcompat/host/lib/src/android/compat/cts/Change.java
index 8e6c8e7..105e6ca 100644
--- a/hostsidetests/appcompat/host/lib/src/android/compat/cts/Change.java
+++ b/hostsidetests/appcompat/host/lib/src/android/compat/cts/Change.java
@@ -30,28 +30,30 @@
                                             + "(; (?<disabled>disabled))?"
                                             + "(; (?<loggingOnly>loggingOnly))?"
                                             + "(; packageOverrides=(?<overrides>[^\\);]+))?"
-                                            + "(; rawOverrides=(?<rawOverrides>[^\\)]+))?"
+                                            + "(; rawOverrides=(?<rawOverrides>[^\\);]+))?"
+                                            + "(; (?<overridable>overridable))?"
                                             + "\\)");
     public long changeId;
     public String changeName;
     public int sinceSdk;
     public boolean disabled;
     public boolean loggingOnly;
+    public boolean overridable;
     public boolean hasRawOverrides;
     public boolean hasOverrides;
-
     public String rawOverrideStr;
     public String overridesStr;
 
     private Change(long changeId, String changeName, int sinceSdk,
-            boolean disabled, boolean loggingOnly, boolean hasRawOverrides,
-            boolean hasOverrides, String rawOverrideStr,
-            String overridesStr) {
+            boolean disabled, boolean loggingOnly, boolean overridable,
+            boolean hasRawOverrides, boolean hasOverrides,
+            String rawOverrideStr, String overridesStr) {
         this.changeId = changeId;
         this.changeName = changeName;
         this.sinceSdk = sinceSdk;
         this.disabled = disabled;
         this.loggingOnly = loggingOnly;
+        this.overridable = overridable;
         this.hasRawOverrides = hasRawOverrides;
         this.hasOverrides = hasOverrides;
         this.rawOverrideStr = rawOverrideStr;
@@ -64,9 +66,9 @@
         int sinceSdk = -1;
         boolean disabled = false;
         boolean loggingOnly = false;
+        boolean overridable = false;
         boolean hasRawOverrides = false;
         boolean hasOverrides = false;
-
         String rawOverridesStr = null;
         String overridesStr = null;
 
@@ -103,7 +105,10 @@
             hasRawOverrides = true;
             rawOverridesStr = matcher.group("rawOverrides");
         }
-        return new Change(changeId, changeName, sinceSdk, disabled, loggingOnly,
+        if (matcher.group("overridable") != null) {
+            overridable = true;
+        }
+        return new Change(changeId, changeName, sinceSdk, disabled, loggingOnly, overridable,
                           hasRawOverrides, hasOverrides, rawOverridesStr,
                           overridesStr);
     }
@@ -132,8 +137,13 @@
         if (element.hasAttribute("loggingOnly")) {
             loggingOnly = true;
         }
-        return new Change(changeId, changeName, sinceSdk, disabled, loggingOnly, false, false,
-                          null, null);
+        boolean overridable = false;
+        if (element.hasAttribute("overridable")) {
+            overridable = true;
+        }
+        return new Change(changeId, changeName, sinceSdk, disabled, loggingOnly, overridable,
+                /* hasRawOverrides= */ false, /* hasOverrides= */ false,
+                /* rawOverridesStr= */ null, /* overridesStr= */null);
     }
 
     @Override
@@ -155,8 +165,7 @@
             && this.sinceSdk == that.sinceSdk
             && this.disabled == that.disabled
             && this.loggingOnly == that.loggingOnly
-            && this.hasRawOverrides == that.hasRawOverrides
-            && this.hasOverrides == that.hasOverrides;
+            && this.overridable == that.overridable;
     }
 
     @Override
@@ -172,17 +181,20 @@
         if (disabled) {
             sb.append("; disabled");
         }
-        if (hasRawOverrides) {
-            sb.append("; rawOverrides={");
-            sb.append(rawOverrideStr);
-            sb.append("}");
-        }
         if (hasOverrides) {
             sb.append("; packageOverrides={");
             sb.append(overridesStr);
             sb.append("}");
         }
+        if (hasRawOverrides) {
+            sb.append("; rawOverrides={");
+            sb.append(rawOverrideStr);
+            sb.append("}");
+        }
+        if (overridable) {
+            sb.append("; overridable");
+        }
         sb.append(")");
         return sb.toString();
     }
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java b/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java
index 84300a2..10803dd 100644
--- a/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java
+++ b/hostsidetests/appcompat/host/lib/src/android/compat/cts/CompatChangeGatingTestCase.java
@@ -367,4 +367,13 @@
                 .collect(Collectors.toList());
     }
 
+    protected Change getOnDeviceChangeIdConfig(long changeId) throws Exception {
+        List<Change> changes = getOnDeviceCompatConfig();
+        for (Change change : changes) {
+            if (change.changeId == changeId) {
+                return change;
+            }
+        }
+        return null;
+    }
 }
diff --git a/hostsidetests/appcompat/strictjavapackages/Android.bp b/hostsidetests/appcompat/strictjavapackages/Android.bp
index 0f922102..fcb20e8 100644
--- a/hostsidetests/appcompat/strictjavapackages/Android.bp
+++ b/hostsidetests/appcompat/strictjavapackages/Android.bp
@@ -26,6 +26,7 @@
         "compatibility-host-util",
     ],
     static_libs: [
+        "compat-classpaths-testing",
         "dexlib2-no-guava-no-cli",
     ],
     // tag this module as a cts test artifact
diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
index e73f31a..f170cab 100644
--- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
+++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
@@ -16,200 +16,184 @@
 
 package android.compat.sjp.cts;
 
+import static android.compat.testing.Classpaths.ClasspathType.BOOTCLASSPATH;
+import static android.compat.testing.Classpaths.ClasspathType.SYSTEMSERVERCLASSPATH;
+
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
+
 import static org.junit.Assume.assumeTrue;
-import static java.util.stream.Collectors.toSet;
+
+import android.compat.testing.Classpaths;
 
 import com.android.compatibility.common.util.ApiLevelUtil;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
+import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
+import com.google.common.collect.Multimaps;
 
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Set;
-
-import org.jf.dexlib2.DexFileFactory;
-import org.jf.dexlib2.Opcodes;
-import org.jf.dexlib2.dexbacked.DexBackedDexFile;
-import org.jf.dexlib2.iface.DexFile;
 import org.jf.dexlib2.iface.ClassDef;
-import org.jf.dexlib2.iface.MultiDexContainer;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests for detecting no duplicate class files are present on BOOTCLASSPATH and
+ * SYSTEMSERVERCLASSPATH.
+ *
+ * <p>Duplicate class files are not safe as some of the jars on *CLASSPATH are updated outside of
+ * the main dessert release cycle; they also contribute to unnecessary disk space usage.
+ */
 @RunWith(DeviceJUnit4ClassRunner.class)
-public class StrictJavaPackagesTest extends BaseHostJUnit4Test  {
-    private static final long ADB_TIMEOUT_MILLIS = 10000L;
+public class StrictJavaPackagesTest extends BaseHostJUnit4Test {
+
     /**
      * This is the list of classes that are currently duplicated and should be addressed.
      *
      * <p> DO NOT ADD CLASSES TO THIS LIST!
      */
     private static final Set<String> BCP_AND_SSCP_OVERLAP_BURNDOWN_LIST =
-        ImmutableSet.of(
-            "Landroid/annotation/CallbackExecutor;",
-            "Landroid/annotation/CheckResult;",
-            "Landroid/annotation/CurrentTimeMillisLong;",
-            "Landroid/annotation/Hide;",
-            "Landroid/annotation/IntDef;",
-            "Landroid/annotation/IntRange;",
-            "Landroid/annotation/LongDef;",
-            "Landroid/annotation/NonNull;",
-            "Landroid/annotation/Nullable;",
-            "Landroid/annotation/RequiresPermission;",
-            "Landroid/annotation/RequiresPermission$Read;",
-            "Landroid/annotation/RequiresPermission$Write;",
-            "Landroid/annotation/SdkConstant;",
-            "Landroid/annotation/SdkConstant$SdkConstantType;",
-            "Landroid/annotation/StringDef;",
-            "Landroid/annotation/SystemApi;",
-            "Landroid/annotation/SystemApi$Client;",
-            "Landroid/annotation/SystemApi$Container;",
-            "Landroid/annotation/SystemService;",
-            "Landroid/annotation/TestApi;",
-            "Landroid/annotation/WorkerThread;",
-            "Landroid/gsi/AvbPublicKey;",
-            "Landroid/gsi/AvbPublicKey$1;",
-            "Landroid/gsi/GsiProgress;",
-            "Landroid/gsi/GsiProgress$1;",
-            "Landroid/gsi/IGsiService;",
-            "Landroid/gsi/IGsiService$Default;",
-            "Landroid/gsi/IGsiService$Stub;",
-            "Landroid/gsi/IGsiService$Stub$Proxy;",
-            "Landroid/gsi/IGsiServiceCallback;",
-            "Landroid/gsi/IGsiServiceCallback$Default;",
-            "Landroid/gsi/IGsiServiceCallback$Stub;",
-            "Landroid/gsi/IGsiServiceCallback$Stub$Proxy;",
-            "Landroid/gsi/IImageService;",
-            "Landroid/gsi/IImageService$Default;",
-            "Landroid/gsi/IImageService$Stub;",
-            "Landroid/gsi/IImageService$Stub$Proxy;",
-            "Landroid/gsi/IProgressCallback;",
-            "Landroid/gsi/IProgressCallback$Default;",
-            "Landroid/gsi/IProgressCallback$Stub;",
-            "Landroid/gsi/IProgressCallback$Stub$Proxy;",
-            "Landroid/gsi/MappedImage;",
-            "Landroid/gsi/MappedImage$1;",
-            "Landroid/hardware/contexthub/V1_0/AsyncEventType;",
-            "Landroid/hardware/contexthub/V1_0/ContextHub;",
-            "Landroid/hardware/contexthub/V1_0/ContextHubMsg;",
-            "Landroid/hardware/contexthub/V1_0/HostEndPoint;",
-            "Landroid/hardware/contexthub/V1_0/HubAppInfo;",
-            "Landroid/hardware/contexthub/V1_0/HubMemoryFlag;",
-            "Landroid/hardware/contexthub/V1_0/HubMemoryType;",
-            "Landroid/hardware/contexthub/V1_0/IContexthub;",
-            "Landroid/hardware/contexthub/V1_0/IContexthub$Proxy;",
-            "Landroid/hardware/contexthub/V1_0/IContexthub$Stub;",
-            "Landroid/hardware/contexthub/V1_0/IContexthubCallback;",
-            "Landroid/hardware/contexthub/V1_0/IContexthubCallback$Proxy;",
-            "Landroid/hardware/contexthub/V1_0/IContexthubCallback$Stub;",
-            "Landroid/hardware/contexthub/V1_0/MemRange;",
-            "Landroid/hardware/contexthub/V1_0/NanoAppBinary;",
-            "Landroid/hardware/contexthub/V1_0/NanoAppFlags;",
-            "Landroid/hardware/contexthub/V1_0/PhysicalSensor;",
-            "Landroid/hardware/contexthub/V1_0/Result;",
-            "Landroid/hardware/contexthub/V1_0/SensorType;",
-            "Landroid/hardware/contexthub/V1_0/TransactionResult;",
-            "Landroid/hardware/usb/gadget/V1_0/GadgetFunction;",
-            "Landroid/hardware/usb/gadget/V1_0/IUsbGadget;",
-            "Landroid/hardware/usb/gadget/V1_0/IUsbGadget$Proxy;",
-            "Landroid/hardware/usb/gadget/V1_0/IUsbGadget$Stub;",
-            "Landroid/hardware/usb/gadget/V1_0/IUsbGadgetCallback;",
-            "Landroid/hardware/usb/gadget/V1_0/IUsbGadgetCallback$Proxy;",
-            "Landroid/hardware/usb/gadget/V1_0/IUsbGadgetCallback$Stub;",
-            "Landroid/hardware/usb/gadget/V1_0/Status;",
-            "Landroid/os/IDumpstate;",
-            "Landroid/os/IDumpstate$Default;",
-            "Landroid/os/IDumpstate$Stub;",
-            "Landroid/os/IDumpstate$Stub$Proxy;",
-            "Landroid/os/IDumpstateListener;",
-            "Landroid/os/IDumpstateListener$Default;",
-            "Landroid/os/IDumpstateListener$Stub;",
-            "Landroid/os/IDumpstateListener$Stub$Proxy;",
-            "Landroid/os/IInstalld;",
-            "Landroid/os/IInstalld$Default;",
-            "Landroid/os/IInstalld$Stub;",
-            "Landroid/os/IInstalld$Stub$Proxy;",
-            "Landroid/os/IStoraged;",
-            "Landroid/os/IStoraged$Default;",
-            "Landroid/os/IStoraged$Stub;",
-            "Landroid/os/IStoraged$Stub$Proxy;",
-            "Landroid/os/IVold;",
-            "Landroid/os/IVold$Default;",
-            "Landroid/os/IVold$Stub;",
-            "Landroid/os/IVold$Stub$Proxy;",
-            "Landroid/os/IVoldListener;",
-            "Landroid/os/IVoldListener$Default;",
-            "Landroid/os/IVoldListener$Stub;",
-            "Landroid/os/IVoldListener$Stub$Proxy;",
-            "Landroid/os/IVoldMountCallback;",
-            "Landroid/os/IVoldMountCallback$Default;",
-            "Landroid/os/IVoldMountCallback$Stub;",
-            "Landroid/os/IVoldMountCallback$Stub$Proxy;",
-            "Landroid/os/IVoldTaskListener;",
-            "Landroid/os/IVoldTaskListener$Default;",
-            "Landroid/os/IVoldTaskListener$Stub;",
-            "Landroid/os/IVoldTaskListener$Stub$Proxy;",
-            "Landroid/os/storage/CrateMetadata;",
-            "Landroid/os/storage/CrateMetadata$1;",
-            "Landroid/view/LayerMetadataKey;",
-            "Lcom/android/internal/annotations/GuardedBy;",
-            "Lcom/android/internal/annotations/Immutable;",
-            "Lcom/android/internal/annotations/VisibleForTesting;",
-            "Lcom/android/internal/annotations/VisibleForTesting$Visibility;",
-            // TODO(b/173649240): due to an oversight, some new overlaps slipped through in S.
-            "Landroid/hardware/usb/gadget/V1_1/IUsbGadget;",
-            "Landroid/hardware/usb/gadget/V1_1/IUsbGadget$Proxy;",
-            "Landroid/hardware/usb/gadget/V1_1/IUsbGadget$Stub;",
-            "Landroid/hardware/usb/gadget/V1_2/GadgetFunction;",
-            "Landroid/hardware/usb/gadget/V1_2/IUsbGadget;",
-            "Landroid/hardware/usb/gadget/V1_2/IUsbGadget$Proxy;",
-            "Landroid/hardware/usb/gadget/V1_2/IUsbGadget$Stub;",
-            "Landroid/hardware/usb/gadget/V1_2/IUsbGadgetCallback;",
-            "Landroid/hardware/usb/gadget/V1_2/IUsbGadgetCallback$Proxy;",
-            "Landroid/hardware/usb/gadget/V1_2/IUsbGadgetCallback$Stub;",
-            "Landroid/hardware/usb/gadget/V1_2/UsbSpeed;",
-            "Landroid/os/BlockUntrustedTouchesMode;",
-            "Landroid/os/CreateAppDataArgs;",
-            "Landroid/os/CreateAppDataArgs$1;",
-            "Landroid/os/CreateAppDataResult;",
-            "Landroid/os/CreateAppDataResult$1;",
-            "Landroid/os/IInputConstants;",
-            "Landroid/os/IInputConstants$Default;",
-            "Landroid/os/IInputConstants$Stub;",
-            "Landroid/os/IInputConstants$Stub$Proxy;",
-            "Landroid/os/InputEventInjectionResult;",
-            "Landroid/os/InputEventInjectionSync;",
-            "Landroid/os/TouchOcclusionMode;",
-            "Lcom/android/internal/protolog/common/BitmaskConversionException;",
-            "Lcom/android/internal/protolog/common/InvalidFormatStringException;",
-            "Lcom/android/internal/protolog/common/IProtoLogGroup;",
-            "Lcom/android/internal/protolog/common/LogDataType;",
-            "Lcom/android/internal/protolog/common/ProtoLog;",
-            "Lcom/android/internal/protolog/ProtoLogImpl;",
-            "Lcom/android/internal/protolog/ProtoLogViewerConfigReader;",
-            "Lcom/android/internal/util/FrameworkStatsLog;"
-        );
+            ImmutableSet.of(
+                    "Landroid/annotation/AnimatorRes;",
+                    "Landroid/annotation/AnimRes;",
+                    "Landroid/annotation/AnyRes;",
+                    "Landroid/annotation/AnyThread;",
+                    "Landroid/annotation/AppIdInt;",
+                    "Landroid/annotation/ArrayRes;",
+                    "Landroid/annotation/AttrRes;",
+                    "Landroid/annotation/BinderThread;",
+                    "Landroid/annotation/BoolRes;",
+                    "Landroid/annotation/BroadcastBehavior;",
+                    "Landroid/annotation/BytesLong;",
+                    "Landroid/annotation/CallbackExecutor;",
+                    "Landroid/annotation/CallSuper;",
+                    "Landroid/annotation/CheckResult;",
+                    "Landroid/annotation/ColorInt;",
+                    "Landroid/annotation/ColorLong;",
+                    "Landroid/annotation/ColorRes;",
+                    "Landroid/annotation/Condemned;",
+                    "Landroid/annotation/CurrentTimeMillisLong;",
+                    "Landroid/annotation/CurrentTimeSecondsLong;",
+                    "Landroid/annotation/DimenRes;",
+                    "Landroid/annotation/Dimension;",
+                    "Landroid/annotation/DrawableRes;",
+                    "Landroid/annotation/DurationMillisLong;",
+                    "Landroid/annotation/ElapsedRealtimeLong;",
+                    "Landroid/annotation/FloatRange;",
+                    "Landroid/annotation/FontRes;",
+                    "Landroid/annotation/FractionRes;",
+                    "Landroid/annotation/HalfFloat;",
+                    "Landroid/annotation/Hide;",
+                    "Landroid/annotation/IdRes;",
+                    "Landroid/annotation/IntDef;",
+                    "Landroid/annotation/IntegerRes;",
+                    "Landroid/annotation/InterpolatorRes;",
+                    "Landroid/annotation/IntRange;",
+                    "Landroid/annotation/LayoutRes;",
+                    "Landroid/annotation/LongDef;",
+                    "Landroid/annotation/MainThread;",
+                    "Landroid/annotation/MenuRes;",
+                    "Landroid/annotation/NavigationRes;",
+                    "Landroid/annotation/NonNull;",
+                    "Landroid/annotation/Nullable;",
+                    "Landroid/annotation/PluralsRes;",
+                    "Landroid/annotation/Px;",
+                    "Landroid/annotation/RawRes;",
+                    "Landroid/annotation/RequiresFeature;",
+                    "Landroid/annotation/RequiresPermission;",
+                    "Landroid/annotation/SdkConstant;",
+                    "Landroid/annotation/Size;",
+                    "Landroid/annotation/StringDef;",
+                    "Landroid/annotation/StringRes;",
+                    "Landroid/annotation/StyleableRes;",
+                    "Landroid/annotation/StyleRes;",
+                    "Landroid/annotation/SuppressAutoDoc;",
+                    "Landroid/annotation/SuppressLint;",
+                    "Landroid/annotation/SystemApi;",
+                    "Landroid/annotation/SystemService;",
+                    "Landroid/annotation/TargetApi;",
+                    "Landroid/annotation/TestApi;",
+                    "Landroid/annotation/TransitionRes;",
+                    "Landroid/annotation/UiThread;",
+                    "Landroid/annotation/UserHandleAware;",
+                    "Landroid/annotation/UserIdInt;",
+                    "Landroid/annotation/Widget;",
+                    "Landroid/annotation/WorkerThread;",
+                    "Landroid/annotation/XmlRes;",
+                    "Landroid/gsi/AvbPublicKey;",
+                    "Landroid/gsi/GsiProgress;",
+                    "Landroid/gsi/IGsiService;",
+                    "Landroid/gsi/IGsiServiceCallback;",
+                    "Landroid/gsi/IImageService;",
+                    "Landroid/gsi/IProgressCallback;",
+                    "Landroid/gsi/MappedImage;",
+                    "Landroid/hardware/contexthub/V1_0/AsyncEventType;",
+                    "Landroid/hardware/contexthub/V1_0/ContextHub;",
+                    "Landroid/hardware/contexthub/V1_0/ContextHubMsg;",
+                    "Landroid/hardware/contexthub/V1_0/HostEndPoint;",
+                    "Landroid/hardware/contexthub/V1_0/HubAppInfo;",
+                    "Landroid/hardware/contexthub/V1_0/HubMemoryFlag;",
+                    "Landroid/hardware/contexthub/V1_0/HubMemoryType;",
+                    "Landroid/hardware/contexthub/V1_0/IContexthub;",
+                    "Landroid/hardware/contexthub/V1_0/IContexthubCallback;",
+                    "Landroid/hardware/contexthub/V1_0/MemRange;",
+                    "Landroid/hardware/contexthub/V1_0/NanoAppBinary;",
+                    "Landroid/hardware/contexthub/V1_0/NanoAppFlags;",
+                    "Landroid/hardware/contexthub/V1_0/PhysicalSensor;",
+                    "Landroid/hardware/contexthub/V1_0/Result;",
+                    "Landroid/hardware/contexthub/V1_0/SensorType;",
+                    "Landroid/hardware/contexthub/V1_0/TransactionResult;",
+                    "Landroid/hardware/usb/gadget/V1_0/GadgetFunction;",
+                    "Landroid/hardware/usb/gadget/V1_0/IUsbGadget;",
+                    "Landroid/hardware/usb/gadget/V1_0/IUsbGadgetCallback;",
+                    "Landroid/hardware/usb/gadget/V1_0/Status;",
+                    "Landroid/os/IDumpstate;",
+                    "Landroid/os/IDumpstateListener;",
+                    "Landroid/os/IInstalld;",
+                    "Landroid/os/IStoraged;",
+                    "Landroid/os/IVold;",
+                    "Landroid/os/IVoldListener;",
+                    "Landroid/os/IVoldMountCallback;",
+                    "Landroid/os/IVoldTaskListener;",
+                    "Landroid/os/storage/CrateMetadata;",
+                    "Landroid/view/LayerMetadataKey;",
+                    "Lcom/android/internal/annotations/GuardedBy;",
+                    "Lcom/android/internal/annotations/Immutable;",
+                    "Lcom/android/internal/annotations/VisibleForNative;",
+                    "Lcom/android/internal/annotations/VisibleForTesting;",
+                    // TODO(b/173649240): due to an oversight, some new overlaps slipped through
+                    // in S.
+                    "Landroid/hardware/usb/gadget/V1_1/IUsbGadget;",
+                    "Landroid/hardware/usb/gadget/V1_2/GadgetFunction;",
+                    "Landroid/hardware/usb/gadget/V1_2/IUsbGadget;",
+                    "Landroid/hardware/usb/gadget/V1_2/IUsbGadgetCallback;",
+                    "Landroid/hardware/usb/gadget/V1_2/UsbSpeed;",
+                    "Landroid/os/BlockUntrustedTouchesMode;",
+                    "Landroid/os/CreateAppDataArgs;",
+                    "Landroid/os/CreateAppDataResult;",
+                    "Landroid/os/IInputConstants;",
+                    "Landroid/os/InputEventInjectionResult;",
+                    "Landroid/os/InputEventInjectionSync;",
+                    "Landroid/os/TouchOcclusionMode;",
+                    "Lcom/android/internal/protolog/common/BitmaskConversionException;",
+                    "Lcom/android/internal/protolog/common/InvalidFormatStringException;",
+                    "Lcom/android/internal/protolog/common/IProtoLogGroup;",
+                    "Lcom/android/internal/protolog/common/LogDataType;",
+                    "Lcom/android/internal/protolog/common/ProtoLog;",
+                    "Lcom/android/internal/protolog/ProtoLogImpl;",
+                    "Lcom/android/internal/protolog/ProtoLogViewerConfigReader;",
+                    "Lcom/android/internal/util/FrameworkStatsLog;"
+            );
 
     /**
      * Ensure that there are no duplicate classes among jars listed in BOOTCLASSPATH.
@@ -217,10 +201,9 @@
     @Test
     public void testBootclasspath_nonDuplicateClasses() throws Exception {
         assumeTrue(ApiLevelUtil.isAfter(getDevice(), 29));
-        runWithTempDir(tmpDir -> {
-            final Set<DeviceFile> bcpJarFiles = pullJarsFromEnvVariable(tmpDir, "BOOTCLASSPATH");
-            checkClassDuplicatesMatchAllowlist(bcpJarFiles, ImmutableSet.of());
-        });
+        ImmutableList<String> jars =
+                Classpaths.getJarsOnClasspath(getDevice(), BOOTCLASSPATH);
+        assertThat(getDuplicateClasses(jars)).isEmpty();
     }
 
     /**
@@ -229,11 +212,9 @@
     @Test
     public void testSystemServerClasspath_nonDuplicateClasses() throws Exception {
         assumeTrue(ApiLevelUtil.isAfter(getDevice(), 29));
-        runWithTempDir(tmpDir -> {
-            final Set<DeviceFile> sscpJarFiles =
-                pullJarsFromEnvVariable(tmpDir, "SYSTEMSERVERCLASSPATH");
-            checkClassDuplicatesMatchAllowlist(sscpJarFiles, ImmutableSet.of());
-        });
+        ImmutableList<String> jars =
+                Classpaths.getJarsOnClasspath(getDevice(), SYSTEMSERVERCLASSPATH);
+        assertThat(getDuplicateClasses(jars)).isEmpty();
     }
 
     /**
@@ -241,26 +222,32 @@
      * SYSTEMSERVERCLASSPATH.
      */
     @Test
-    public void testBootClassPathAndSystemServerClasspath_nonDuplicateClasses() throws Exception {
+    public void testBootClasspathAndSystemServerClasspath_nonDuplicateClasses() throws Exception {
         assumeTrue(ApiLevelUtil.isAfter(getDevice(), 29));
-        runWithTempDir(tmpDir -> {
-            final Set<DeviceFile> allJarFiles = Sets.union(
-                pullJarsFromEnvVariable(tmpDir, "BOOTCLASSPATH"),
-                pullJarsFromEnvVariable(tmpDir, "SYSTEMSERVERCLASSPATH")
-            );
-            checkClassDuplicatesMatchAllowlist(allJarFiles, BCP_AND_SSCP_OVERLAP_BURNDOWN_LIST);
-        });
+        ImmutableList.Builder<String> jars = ImmutableList.builder();
+        jars.addAll(Classpaths.getJarsOnClasspath(getDevice(), BOOTCLASSPATH));
+        jars.addAll(Classpaths.getJarsOnClasspath(getDevice(), SYSTEMSERVERCLASSPATH));
+
+        Multimap<String, String> duplicates = getDuplicateClasses(jars.build());
+        Multimap<String, String> filtered = Multimaps.filterKeys(duplicates,
+                duplicate -> !BCP_AND_SSCP_OVERLAP_BURNDOWN_LIST.contains(duplicate));
+
+        assertThat(filtered).isEmpty();
     }
 
     /**
      * Ensure that there are no duplicate classes among APEX jars listed in BOOTCLASSPATH.
      */
     @Test
-    public void testBootclasspath_nonDuplicateApexJarClasses() throws Exception {
-        runWithTempDir(tmpDir -> {
-            final Set<DeviceFile> bcpJarFiles = pullJarsFromEnvVariable(tmpDir, "BOOTCLASSPATH");
-            checkClassDuplicatesNotInApexJars(bcpJarFiles);
-        });
+    public void testBootClasspath_nonDuplicateApexJarClasses() throws Exception {
+        ImmutableList<String> jars =
+                Classpaths.getJarsOnClasspath(getDevice(), BOOTCLASSPATH);
+
+        Multimap<String, String> duplicates = getDuplicateClasses(jars);
+        Multimap<String, String> filtered =
+                Multimaps.filterValues(duplicates, jar -> jar.startsWith("/apex/"));
+
+        assertThat(filtered).isEmpty();
     }
 
     /**
@@ -268,11 +255,14 @@
      */
     @Test
     public void testSystemServerClasspath_nonDuplicateApexJarClasses() throws Exception {
-        runWithTempDir(tmpDir -> {
-            final Set<DeviceFile> sscpJarFiles =
-                pullJarsFromEnvVariable(tmpDir, "SYSTEMSERVERCLASSPATH");
-            checkClassDuplicatesNotInApexJars(sscpJarFiles);
-        });
+        ImmutableList<String> jars =
+                Classpaths.getJarsOnClasspath(getDevice(), SYSTEMSERVERCLASSPATH);
+
+        Multimap<String, String> duplicates = getDuplicateClasses(jars);
+        Multimap<String, String> filtered =
+                Multimaps.filterValues(duplicates, jar -> jar.startsWith("/apex/"));
+
+        assertThat(filtered).isEmpty();
     }
 
     /**
@@ -280,168 +270,48 @@
      * SYSTEMSERVERCLASSPATH.
      */
     @Test
-    public void testBootClassPathAndSystemServerClasspath_nonApexDuplicateClasses()
+    public void testBootClasspathAndSystemServerClasspath_nonApexDuplicateClasses()
             throws Exception {
-        runWithTempDir(tmpDir -> {
-            final Set<DeviceFile> allJarFiles = Sets.union(
-                pullJarsFromEnvVariable(tmpDir, "BOOTCLASSPATH"),
-                pullJarsFromEnvVariable(tmpDir, "SYSTEMSERVERCLASSPATH")
-            );
-            checkClassDuplicatesNotInApexJars(allJarFiles);
-        });
-    }
+        ImmutableList.Builder<String> jars = ImmutableList.builder();
+        jars.addAll(Classpaths.getJarsOnClasspath(getDevice(), BOOTCLASSPATH));
+        jars.addAll(Classpaths.getJarsOnClasspath(getDevice(), SYSTEMSERVERCLASSPATH));
 
-    private String getEnvVariable(String var) {
-        try {
-            return getDevice().executeShellCommand("echo $" + var).trim();
-        } catch(DeviceNotAvailableException e) {
-            throw new RuntimeException(e);
-        }
-    }
+        Multimap<String, String> duplicates = getDuplicateClasses(jars.build());
+        Multimap<String, String> filtered = Multimaps.filterKeys(duplicates,
+                duplicate -> !BCP_AND_SSCP_OVERLAP_BURNDOWN_LIST.contains(duplicate));
+        filtered = Multimaps.filterValues(filtered, jar -> jar.startsWith("/apex/"));
 
-    private DeviceFile pullFromDevice(String devicePath, File tmpDir) {
-        try {
-            final File hostFile = Paths.get(tmpDir.getAbsolutePath(), devicePath).toFile();
-            // Ensure the destination directory structure exists.
-            hostFile.getParentFile().mkdirs();
-            final String hostPath = hostFile.getAbsolutePath();
-            getDevice().executeAdbCommand(ADB_TIMEOUT_MILLIS, "pull", devicePath, hostPath);
-            return new DeviceFile(devicePath, hostPath);
-        } catch (Exception e) {
-            throw new RuntimeException("Failed to pull " + devicePath, e);
-        }
+        assertThat(filtered).isEmpty();
     }
 
     /**
      * Gets the duplicate classes within a list of jar files.
-     * @param jars  A list of jar files.
-     * @return  A multimap with the class name as a key and the jar files as a value.
+     *
+     * @param jars a list of jar files.
+     * @return a multimap with the class name as a key and the jar files as a value.
      */
-    private Multimap<String, DeviceFile> getDuplicateClasses(Set<DeviceFile> jars)
-                throws Exception {
-        final Multimap<String, DeviceFile> allClasses = HashMultimap.create();
-        final Multimap<String, DeviceFile> duplicateClasses = HashMultimap.create();
-        for (DeviceFile deviceFile : jars) {
-            final File jarFile = new File(deviceFile.hostPath);
-            final MultiDexContainer<? extends DexBackedDexFile> container =
-                    DexFileFactory.loadDexContainer(jarFile, Opcodes.getDefault());
-            final List<String> entryNames = container.getDexEntryNames();
-            for (String entryName : entryNames) {
-                final DexFile dexFile = container.getEntry(entryName);
-                for (ClassDef classDef : dexFile.getClasses()) {
-                    allClasses.put(classDef.getType(), deviceFile);
+    private Multimap<String, String> getDuplicateClasses(ImmutableCollection<String> jars)
+            throws Exception {
+        final Multimap<String, String> allClasses = HashMultimap.create();
+        for (String jar : jars) {
+            ImmutableSet<ClassDef> classes = Classpaths.getClassDefsFromJar(getDevice(), jar);
+            for (ClassDef classDef : classes) {
+                // No need to worry about inner classes, as they always go with their parent.
+                if (!classDef.getType().contains("$")) {
+                    allClasses.put(classDef.getType(), jar);
                 }
             }
         }
-        for (Entry<String, Collection<DeviceFile>> entry : allClasses.asMap().entrySet()) {
-            if (entry.getValue().size() > 1) {
-                CLog.i("Class %s is duplicated in %s", entry.getKey(),
-                    entry.getValue().stream().map(x -> x.getJarName()).collect(toSet()));
 
-                duplicateClasses.putAll(entry.getKey(), entry.getValue());
+        final Multimap<String, String> duplicates = HashMultimap.create();
+        for (String clazz : allClasses.keySet()) {
+            Collection<String> jarsWithClazz = allClasses.get(clazz);
+            if (jarsWithClazz.size() > 1) {
+                CLog.i("Class %s is duplicated in %s", clazz, jarsWithClazz);
+                duplicates.putAll(clazz, jarsWithClazz);
             }
         }
-        return duplicateClasses;
-    }
 
-    /**
-     * Checks that the duplicate classes in a set of jars exactly match a given allowlist.
-     */
-    private void checkClassDuplicatesMatchAllowlist(Set<DeviceFile> jars, Set<String> allowlist)
-            throws Exception {
-        // Collect classes which appear in at least two distinct jar files.
-        Multimap<String, DeviceFile> duplicateClasses = getDuplicateClasses(jars);
-        Set<String> deniedClasses = new HashSet<>();
-        deniedClasses.addAll(duplicateClasses.keySet());
-        deniedClasses.removeAll(allowlist);
-        assertThat(deniedClasses).isEmpty();
-    }
-
-    /**
-     * Checks that the duplicate classes are not in APEX jars.
-     */
-    private void checkClassDuplicatesNotInApexJars(Set<DeviceFile> jars)
-            throws Exception {
-        final Multimap<String, DeviceFile> jarClasses = getDuplicateClasses(jars);
-        for (Entry<String, Collection<DeviceFile>> entry : jarClasses.asMap().entrySet()) {
-            final String className = entry.getKey();
-            final Collection<DeviceFile> filesWithClass = entry.getValue();
-            // Check that jars that define the same class are not part of apexes.
-            for (DeviceFile jarFile : filesWithClass) {
-                assertWithMessage("%s is available in: %s, of which %s is an APEX jar",
-                                    className, filesWithClass, jarFile.devicePath)
-                .that(jarFile.devicePath.startsWith("/apex/"))
-                .isFalse();
-            }
-        }
-    }
-
-    /**
-     * Retrieve jar files from the device, based on an env variable.
-     * @param tmpDir    The temporary directory where the file will be dumped.
-     * @param variable  The environment variable containing the colon separated jar files.
-     * @return  A {@link java.util.Set} with the pulled {@link DeviceFile} instances.
-    */
-    private Set<DeviceFile> pullJarsFromEnvVariable(File tmpDir, String variable) {
-        return Arrays.asList(getEnvVariable(variable).split(":")).stream()
-            .map(fileName -> pullFromDevice(fileName, tmpDir))
-            .collect(toSet());
-    }
-
-    private void runWithTempDir(TempDirRunnable runnable) throws Exception {
-        final File tmpDir = Files.createTempDirectory("strictjavapackages").toFile();
-        try {
-            runnable.runWithTempDir(tmpDir);
-        } finally {
-            tmpDir.delete();
-        }
-    }
-
-    private interface TempDirRunnable {
-        public void runWithTempDir(File tempDir) throws Exception;
-    }
-
-    /**
-     * Class representing a device artifact that was pulled for a test method.
-     *
-     * <p> Contains the local and on-device paths.
-     */
-    private static final class DeviceFile {
-        public final String devicePath;
-        public final String hostPath;
-        public DeviceFile(String devicePath, String hostPath) {
-            this.devicePath = devicePath;
-            this.hostPath = hostPath;
-        }
-
-        public String getJarName() {
-            return devicePath.substring(devicePath.lastIndexOf('/') + 1);
-        }
-
-        @Override
-        public boolean equals(Object other) {
-            if (this == other) {
-                return true;
-            }
-            if (other == null) {
-                return false;
-            }
-            if (!(other instanceof DeviceFile)) {
-                return false;
-            }
-            DeviceFile that = (DeviceFile) other;
-            return Objects.equals(this.devicePath, that.devicePath)
-            && Objects.equals(this.hostPath, that.hostPath);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(devicePath, hostPath);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("DeviceFile(devicePath=%s,hostPath=%s)", devicePath, hostPath);
-        }
+        return duplicates;
     }
 }
diff --git a/hostsidetests/appsecurity/OWNERS b/hostsidetests/appsecurity/OWNERS
index d561b80..e4d9d04 100644
--- a/hostsidetests/appsecurity/OWNERS
+++ b/hostsidetests/appsecurity/OWNERS
@@ -1,32 +1,35 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
 per-file AccessSerialNumberTest.java = moltmann@google.com
 per-file ApexSignatureVerificationTest.java = dariofreni@google.com
-per-file ApplicationVisibilityTest.java = toddke@google.com
-per-file AppDataIsolationTests.java = rickywai@google.com
+per-file ApplicationVisibilityTest.java = toddke@google.com,patb@google.com
+per-file AppDataIsolationTests.java = rickywai@google.com,alanstokes@google.com
 per-file AppOpsTest.java = moltmann@google.com
 per-file AppSecurityTests.java = cbrubaker@google.com
-per-file AuthBoundKeyTest.java = cbrubaker@google.com
-per-file BaseInstallMultiple.java = toddke@google.com
+per-file AuthBoundKeyTest.java = file:test-apps/AuthBoundKeyApp/OWNERS
+per-file BaseInstallMultiple.java = toddke@google.com,patb@google.com
 per-file CorruptApkTests.java = rtmitchell@google.com
 per-file DeviceIdentifierTest.java = cbrubaker@google.com
-per-file EphemeralTest.java = toddke@google.com
-per-file InstantAppUserTest.java = toddke@google.com
-per-file InstantCookieHostTest.java = toddke@google.com
+per-file EphemeralTest.java = toddke@google.com,patb@google.com
+per-file ExternalStorageHostTest.java = nandana@google.com
+per-file ExternalStorageHostTest.java = zezeozue@google.com
+per-file InstantAppUserTest.java = toddke@google.com,patb@google.com
+per-file InstantCookieHostTest.java = toddke@google.com,patb@google.com
 per-file IsolatedSplitsTests.java = patb@google.com,toddke@google.com
 per-file KeySetHostTest.java = cbrubaker@google.com
 per-file ListeningPortsTest.java = cbrubaker@google.com
-per-file MajorVersionTest.java = toddke@google.com
+per-file MajorVersionTest.java = toddke@google.com,patb@google.com
 per-file OverlayHostTest.java = rtmitchell@google.com
 per-file Package* = chiuwinson@google.com,patb@google.com,toddke@google.com
 per-file PermissionsHostTest.java = moltmann@google.com
 per-file Pkg* = chiuwinson@google.com,patb@google.com,toddke@google.com
 per-file PkgInstallSignatureVerificationTest.java = cbrubaker@google.com
-per-file PrivilegedUpdateTests.java = toddke@google.com
+per-file PrivilegedUpdateTests.java = toddke@google.com,patb@google.com
 per-file ReviewPermissionHelper = moltmann@google.com
 per-file RequestsOnlyCalendarApp22.java = moltmann@google.com
-per-file SharedUserIdTest.java = toddke@google.com
-per-file SplitTests.java = patb@google.com,toddke@google.com
+per-file SharedUserIdTest.java = toddke@google.com,patb@google.com
+per-file SplitTests.java = patb@google.com,toddke@google.com,patb@google.com
 per-file UseEmbeddedDexTest.java = victorhsieh@google.com
 # test apps
 per-file BasePermissionsTest.java = moltmann@google.com
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java
index 3023cd9..cbe07f0 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ResumeOnRebootHostTest.java
@@ -66,6 +66,7 @@
     private static final long SHUTDOWN_TIME_MS = TimeUnit.SECONDS.toMicros(30);
     private static final int USER_SYSTEM = 0;
 
+    private static final int USER_SWITCH_TIMEOUT_SECONDS = 10;
     private static final long USER_SWITCH_WAIT = TimeUnit.SECONDS.toMillis(10);
 
     private boolean mSupportsMultiUser;
@@ -98,6 +99,9 @@
         int[] users = Utils.prepareSingleUser(getDevice());
         int initialUser = users[0];
 
+        // Clean up the server based parameters for HAL based test.
+        deviceCleanupServerBasedParameter();
+
         try {
             installTestPackages();
 
@@ -140,6 +144,8 @@
 
         int managedUserId = createManagedProfile(initialUser);
 
+        deviceCleanupServerBasedParameter();
+
         try {
             // Set up test app and secure lock screens
             installTestPackages();
@@ -186,6 +192,8 @@
         int initialUser = users[0];
         int secondaryUser = users[1];
 
+        deviceCleanupServerBasedParameter();
+
         try {
             // Set up test app and secure lock screens
             installTestPackages();
@@ -244,6 +252,8 @@
         int initialUser = users[0];
         int secondaryUser = users[1];
 
+        deviceCleanupServerBasedParameter();
+
         try {
             installTestPackages();
 
@@ -419,12 +429,24 @@
 
     private void deviceSetupServerBasedParameter() throws Exception {
         getDevice().executeShellCommand("device_config put ota server_based_ror_enabled true");
+        String res = getDevice().executeShellCommand(
+                "device_config get ota server_based_ror_enabled");
+        if (res == null || !res.contains("true")) {
+            fail("could not set up server based ror");
+        }
+
         getDevice().executeShellCommand(
                 "cmd lock_settings set-resume-on-reboot-provider-package " + PKG);
     }
 
     private void deviceCleanupServerBasedParameter() throws Exception {
         getDevice().executeShellCommand("device_config put ota server_based_ror_enabled false");
+        String res = getDevice().executeShellCommand(
+                "device_config get ota server_based_ror_enabled");
+        if (res == null || !res.contains("false")) {
+            fail("could not clean up server based ror");
+        }
+
         getDevice().executeShellCommand(
                 "cmd lock_settings set-resume-on-reboot-provider-package ");
     }
@@ -526,7 +548,7 @@
      */
     private void switchUser(int userId) throws Exception {
         getDevice().switchUser(userId);
-        HostSideTestUtils.waitUntil("Could not switch users", 5,
+        HostSideTestUtils.waitUntil("Could not switch users", USER_SWITCH_TIMEOUT_SECONDS,
                 () -> getDevice().getCurrentUser() == userId);
         Thread.sleep(USER_SWITCH_WAIT);
     }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/UseEmbeddedDexTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/UseEmbeddedDexTest.java
index 9db3ac2..0d34c46 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/UseEmbeddedDexTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/UseEmbeddedDexTest.java
@@ -47,6 +47,13 @@
     }
 
     @Test
+    public void testIsolatedService() throws Exception {
+        new InstallMultiple().addFile(APK_CANONICAL).run();
+        getDevice().executeShellCommand("cmd package compile -f -m speed " + PACKAGE_NAME);
+        runDeviceTests(PACKAGE_NAME, PACKAGE_NAME + ".EmbeddedDexTest", "testIsolatedService");
+    }
+
+    @Test
     public void testBadInstallWithCompressedDex() throws Exception {
         new InstallMultiple().addFile(APK_DEX_COMPRESSED).runExpectingFailure();
     }
diff --git a/hostsidetests/appsecurity/test-apps/AuthBoundKeyApp/OWNERS b/hostsidetests/appsecurity/test-apps/AuthBoundKeyApp/OWNERS
new file mode 100644
index 0000000..4e06da3
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/AuthBoundKeyApp/OWNERS
@@ -0,0 +1,8 @@
+# Bug component: 533114
+hasinitg@google.com
+jbires@google.com
+jdanis@google.com
+swillden@google.com
+toddke@google.com
+patb@google.com
+zeuthen@google.com
diff --git a/hostsidetests/appsecurity/test-apps/AuthBoundKeyApp/src/com/android/cts/authboundkey/AuthBoundKeyAppTest.java b/hostsidetests/appsecurity/test-apps/AuthBoundKeyApp/src/com/android/cts/authboundkey/AuthBoundKeyAppTest.java
index 1f2adfd..d3960d3 100644
--- a/hostsidetests/appsecurity/test-apps/AuthBoundKeyApp/src/com/android/cts/authboundkey/AuthBoundKeyAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/AuthBoundKeyApp/src/com/android/cts/authboundkey/AuthBoundKeyAppTest.java
@@ -74,11 +74,16 @@
         keyStore.load(null);
         try {
             SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);
+	    // This test verifies that the given key is no longer usable after removing the LSKF.
+            // In Keystore 2.0 we don't keep invalidated keys around. So we cannot diagnose that
+            // a key was invalidated. It simply does not exist. Thus we expect null.
+	    // An UnrecoverableKeyException is also acceptable (see below) and was the typical
+	    // behavior for Keystore 1.0.
+            if (secretKey == null) return;
         } catch (UnrecoverableKeyException e) {
-            // This is correct behavior
             return;
         }
-        fail("Expected an UnrecoverableKeyException");
+        fail("Expected an UnrecoverableKeyException or null");
     }
 
 }
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/OWNERS b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/InstantCookieApp/OWNERS b/hostsidetests/appsecurity/test-apps/InstantCookieApp/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/InstantCookieApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/InstantCookieApp/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/InstantCookieApp2/OWNERS b/hostsidetests/appsecurity/test-apps/InstantCookieApp2/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/InstantCookieApp2/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/InstantCookieApp2/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/InstantUpgradeApp/OWNERS b/hostsidetests/appsecurity/test-apps/InstantUpgradeApp/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/InstantUpgradeApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/InstantUpgradeApp/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/KeyRotationTest/Android.mk b/hostsidetests/appsecurity/test-apps/KeyRotationTest/Android.mk
index 43acdfe..c453d7a 100644
--- a/hostsidetests/appsecurity/test-apps/KeyRotationTest/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/KeyRotationTest/Android.mk
@@ -36,6 +36,8 @@
 LOCAL_STATIC_JAVA_LIBRARIES := cts_signature_query_service
 LOCAL_COMPATIBILITY_SUITE := cts general-tests
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 
 # This is the second version of the test app signed with the rotated signing
@@ -50,6 +52,8 @@
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
 LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256
 LOCAL_CERTIFICATE_LINEAGE := $(cert_dir)/ec-p256-por_1_2-default-caps
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 
 # This is the third version of the test app signed with the same rotated
@@ -64,6 +68,8 @@
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
 LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256
 LOCAL_CERTIFICATE_LINEAGE := $(cert_dir)/ec-p256-por_1_2-default-caps
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 
 cert_dir :=
diff --git a/hostsidetests/appsecurity/test-apps/KeyRotationTest/ServiceTest/Android.mk b/hostsidetests/appsecurity/test-apps/KeyRotationTest/ServiceTest/Android.mk
index 016687c..0faf3cd 100644
--- a/hostsidetests/appsecurity/test-apps/KeyRotationTest/ServiceTest/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/KeyRotationTest/ServiceTest/Android.mk
@@ -24,6 +24,8 @@
 # the PackageManager checkSignatures APIs.
 include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := CtsSignatureQueryServiceTest
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_STATIC_JAVA_LIBRARIES := cts_signature_query_service androidx.test.core androidx.test.rules
@@ -36,6 +38,8 @@
 # lineage as v2 and v3 of the CtsSignatureQueryService test app.
 include $(CLEAR_VARS)
 LOCAL_PACKAGE_NAME := CtsSignatureQueryServiceTest_v2
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_STATIC_JAVA_LIBRARIES := cts_signature_query_service androidx.test.core androidx.test.rules
@@ -47,4 +51,3 @@
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 
 cert_dir :=
-
diff --git a/hostsidetests/appsecurity/test-apps/MajorVersionApp/OWNERS b/hostsidetests/appsecurity/test-apps/MajorVersionApp/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/MajorVersionApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/MajorVersionApp/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/NoRestartApp/OWNERS b/hostsidetests/appsecurity/test-apps/NoRestartApp/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/NoRestartApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/NoRestartApp/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/OWNERS b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/OrderedActivityApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/OrderedActivityApp/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/PackageAccessApp/OWNERS b/hostsidetests/appsecurity/test-apps/PackageAccessApp/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/PackageAccessApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/PackageAccessApp/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/OWNERS b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk
index b6f7cd0..53d1527 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA.apk
index 112b9db..54a5daa 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA.apk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgradeWrongSHA.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade.apk
index 9f1d735..0c37618 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade.apk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgrade.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA.apk
index a8819be..f8e305b 100644
--- a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA.apk
+++ b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/x86/CtsShimPrivUpgradeWrongSHA.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/test-apps/SharedUidInstall/OWNERS b/hostsidetests/appsecurity/test-apps/SharedUidInstall/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/SharedUidInstall/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/SharedUidInstall/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/OWNERS b/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/OWNERS b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/OWNERS b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/OWNERS
index 87e75ec..bdc654a 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 533114
 toddke@google.com
+patb@google.com
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
index 08a94b5..404ff07 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
@@ -26,6 +26,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsSplitApp
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 4
 LOCAL_PACKAGE_SPLITS := mdpi-v4 hdpi-v4 xhdpi-v4 xxhdpi-v4 v7 fr de
@@ -61,6 +63,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsSplitAppDiffRevision
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_PACKAGE_SPLITS := v7
 
 # Tag this module as a cts test artifact
@@ -91,6 +95,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsSplitAppDiffVersion
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_PACKAGE_SPLITS := v7
 
 # Tag this module as a cts test artifact
@@ -120,6 +126,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsSplitAppDiffCert
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_PACKAGE_SPLITS := v7
 
 # Tag this module as a cts test artifact
@@ -146,6 +154,8 @@
 LOCAL_MANIFEST_FILE := needsplit/AndroidManifest.xml
 
 LOCAL_PACKAGE_NAME := CtsNeedSplitApp
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 4
 LOCAL_PACKAGE_SPLITS := xxhdpi-v4
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/NativeTemplate.mk b/hostsidetests/appsecurity/test-apps/SplitApp/NativeTemplate.mk
index b61c5d6..f9ab818 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/NativeTemplate.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/NativeTemplate.mk
@@ -19,6 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsSplitApp_ARCHARCH
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 LOCAL_JAVA_RESOURCE_DIRS := raw
 
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
index 22d0a1a..42a2160 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
@@ -19,6 +19,8 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 LOCAL_PACKAGE_NAME := CtsSplitAppFeature
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 4
 LOCAL_PACKAGE_SPLITS := v7
@@ -53,6 +55,8 @@
 LOCAL_MANIFEST_FILE := needsplit/AndroidManifest.xml
 
 LOCAL_PACKAGE_NAME := CtsNeedSplitFeature
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 4
 
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/Android.mk
index 8eede6c..6d716aa 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/arm64-v8a/Android.mk
@@ -19,6 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsSplitApp_arm64-v8a
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 
 LOCAL_JAVA_RESOURCE_DIRS := raw
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/Android.mk
index 234a7d8..9e27161 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi-v7a/Android.mk
@@ -19,6 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsSplitApp_armeabi-v7a
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 
 LOCAL_JAVA_RESOURCE_DIRS := raw
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/Android.mk
index 0322dcd..8da02fd 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/armeabi/Android.mk
@@ -19,6 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsSplitApp_armeabi
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 
 LOCAL_JAVA_RESOURCE_DIRS := raw
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/Android.mk
index 4ee13ba..533525b 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips/Android.mk
@@ -19,6 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsSplitApp_mips
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 
 LOCAL_JAVA_RESOURCE_DIRS := raw
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/Android.mk
index 03c4305..64ec09a 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/mips64/Android.mk
@@ -19,6 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsSplitApp_mips64
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 
 LOCAL_JAVA_RESOURCE_DIRS := raw
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/Android.mk
index 14144a6..e6a6cf0b 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86/Android.mk
@@ -19,6 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsSplitApp_x86
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 
 LOCAL_JAVA_RESOURCE_DIRS := raw
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/Android.mk
index 462c1cc..456ef11 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/libs/x86_64/Android.mk
@@ -19,6 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsSplitApp_x86_64
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SDK_VERSION := current
 
 LOCAL_JAVA_RESOURCE_DIRS := raw
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
index f507d40..0d6bcc1 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
@@ -101,26 +101,80 @@
         final UiDevice device = UiDevice.getInstance(getInstrumentation());
         device.waitForIdle();
 
-        if (!isTV(getContext())) {
-            UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true));
-            try {
-                uiScrollable.scrollTextIntoView("internal storage");
-            } catch (UiObjectNotFoundException e) {
-                // Scrolling can fail if the UI is not scrollable
-            }
-            device.findObject(new UiSelector().textContains("internal storage")).click();
-            device.waitForIdle();
+        if (isWatch()) {
+            clearSpaceWatch(device);
+        } else if (isTV()) {
+            clearSpaceTv(device);
+        } else if (isCar()) {
+            clearSpaceCar(device);
+        } else {
+            clearSpaceGeneric(device);
         }
-        String clearString = isCar(getContext()) ? "Clear storage" : "Clear";
-        device.findObject(new UiSelector().textContains(clearString)).click();
-        device.waitForIdle();
-        device.findObject(new UiSelector().text("OK")).click();
         device.waitForIdle();
 
         // Now, disk better be less-full!
         assertTrue(getContext().getDataDir().getUsableSpace() > 256_000_000);
     }
 
+    private void clearSpaceGeneric(UiDevice device) throws UiObjectNotFoundException {
+        UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true));
+        try {
+            uiScrollable.scrollTextIntoView("internal storage");
+        } catch (UiObjectNotFoundException e) {
+            // Scrolling can fail if the UI is not scrollable
+        }
+        device.findObject(new UiSelector().textContains("internal storage")).click();
+        device.waitForIdle();
+
+        device.findObject(new UiSelector().textContains("Clear")).click();
+        device.waitForIdle();
+
+        device.findObject(new UiSelector().text("OK")).click();
+    }
+
+    private void clearSpaceWatch(UiDevice device) throws UiObjectNotFoundException {
+        UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true));
+        uiScrollable.scrollTextIntoView("App info");
+
+        device.findObject(new UiSelector().textContains("App info")).click();
+        device.waitForIdle();
+
+        uiScrollable.scrollTextIntoView("Clear data");
+        device.waitForIdle();
+
+        device.findObject(new UiSelector().textContains("Clear data")).click();
+        device.waitForIdle();
+
+        UiSelector yesButton = new UiSelector().description("Yes");
+        uiScrollable.scrollIntoView(yesButton);
+        device.waitForIdle();
+
+        device.findObject(yesButton).click();
+    }
+
+    private void clearSpaceTv(UiDevice device) throws UiObjectNotFoundException {
+        device.findObject(new UiSelector().textContains("Clear")).click();
+        device.waitForIdle();
+        device.findObject(new UiSelector().text("OK")).click();
+    }
+
+    private void clearSpaceCar(UiDevice device) throws UiObjectNotFoundException {
+        UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true));
+        String storageString = "internal storage";
+        try {
+            uiScrollable.scrollTextIntoView(storageString);
+        } catch (UiObjectNotFoundException e) {
+            // Scrolling can fail if the UI is not scrollable
+        }
+        device.findObject(new UiSelector().textContains(storageString)).click();
+        device.waitForIdle();
+
+        device.findObject(new UiSelector().textContains("Clear storage")).click();
+        device.waitForIdle();
+
+        device.findObject(new UiSelector().text("OK")).click();
+    }
+
     /**
      * Measure ourselves manually.
      */
@@ -326,13 +380,19 @@
         assertFalse(new File("/sdcard/cts_top").exists());
     }
 
-    private static boolean isTV(Context context) {
-        final PackageManager packageManager = context.getPackageManager();
-        return packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    private boolean isTV() {
+        return hasFeature(PackageManager.FEATURE_LEANBACK);
     }
 
-    private static boolean isCar(Context context) {
-        PackageManager pm = context.getPackageManager();
-        return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    private boolean isCar() {
+        return hasFeature(PackageManager.FEATURE_AUTOMOTIVE);
+    }
+
+    private boolean isWatch() {
+        return hasFeature(PackageManager.FEATURE_WATCH);
+    }
+
+    private boolean hasFeature(String feature) {
+        return getContext().getPackageManager().hasSystemFeature(feature);
     }
 }
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/Android.bp b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/Android.bp
index 22bf8d4..a76d28e 100644
--- a/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/Android.bp
+++ b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/Android.bp
@@ -18,48 +18,51 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-android_test_helper_app {
-    name: "CtsUseEmbeddedDexApp_Canonical",
+java_defaults {
+    name: "cts_use_embedded_dex_test_defaults",
     defaults: ["cts_support_defaults"],
-    use_embedded_dex: true,
     srcs: ["src/**/*.java"],
     sdk_version: "current",
-    min_sdk_version: "27",
     test_suites: [
         "cts",
         "general-tests",
     ],
+    static_libs: [
+        "androidx.test.rules",
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "ub-uiautomator",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+}
+
+android_test_helper_app {
+    name: "CtsUseEmbeddedDexApp_Canonical",
+    defaults: ["cts_use_embedded_dex_test_defaults"],
+    min_sdk_version: "27",
+    use_embedded_dex: true,
 }
 
 android_test_helper_app {
     name: "CtsUseEmbeddedDexApp_DexCompressed",
-    defaults: ["cts_support_defaults"],
+    defaults: ["cts_use_embedded_dex_test_defaults"],
     // Not specifying use_embedded_dex keeps dex compressed
-    srcs: ["src/**/*.java"],
-    sdk_version: "current",
     min_sdk_version: "28",
-    test_suites: [
-        "cts",
-        "general-tests",
-    ],
 }
 
 android_test_helper_app {
     name: "CtsUseEmbeddedDexApp_NotPreferred",
-    defaults: ["cts_support_defaults"],
+    defaults: ["cts_use_embedded_dex_test_defaults"],
     manifest: "AndroidManifest_use_extracted_dex.xml",
-    srcs: ["src/**/*.java"],
-    sdk_version: "current",
     min_sdk_version: "28",
-    test_suites: [
-        "cts",
-        "general-tests",
-    ],
 }
 
 android_test_helper_app {
     name: "CtsUseEmbeddedDexAppSplit_Canonical",
-    defaults: ["cts_support_defaults"],
+    defaults: ["cts_use_embedded_dex_test_defaults"],
     manifest: "feature_split/AndroidManifest.xml",
     // We want the dex to be uncompressed, but there is a side effect of extra
     // android:useEmbeddedDex in the manifest (which the framework will ignore
@@ -68,23 +71,14 @@
     srcs: ["feature_split/src/**/*.java"],
     sdk_version: "current",
     min_sdk_version: "27",
-    test_suites: [
-        "cts",
-        "general-tests",
-    ],
 }
 
 android_test_helper_app {
     name: "CtsUseEmbeddedDexAppSplit_CompressedDex",
-    defaults: ["cts_support_defaults"],
+    defaults: ["cts_use_embedded_dex_test_defaults"],
     manifest: "feature_split/AndroidManifest.xml",
     srcs: ["feature_split/src/**/*.java"],
-    sdk_version: "current",
     min_sdk_version: "27",
-    test_suites: [
-        "cts",
-        "general-tests",
-    ],
 }
 
 //android_test_helper_app {
diff --git a/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/AndroidManifest.xml
index 9797cea..dfb6d2a 100644
--- a/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/AndroidManifest.xml
@@ -21,6 +21,16 @@
 
     <application android:useEmbeddedDex="true">
         <activity android:name=".EmptyActivity"/>
+
+      <service
+            android:name=".IsolatedService"
+            android:isolatedProcess="true"
+            android:useAppZygote="true" />
+      <uses-library android:name="android.test.runner" />
     </application>
 
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.useembeddeddex" />
+
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/EmbeddedDexTest.java b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/EmbeddedDexTest.java
new file mode 100644
index 0000000..898e6ae
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/EmbeddedDexTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.useembeddeddex;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+import com.android.compatibility.common.util.PollingCheck;
+
+public class EmbeddedDexTest extends InstrumentationTestCase {
+    static final String TAG = "EmbeddedDexTest";
+
+    boolean mServiceConnected = false;
+
+    public void testIsolatedService() throws Exception {
+        // Start and wait for the isolated service.
+        Context context = getInstrumentation().getTargetContext();
+        Intent intent = new Intent(context, IsolatedService.class);
+        ServiceConnection conn = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                Log.i(TAG, "Isolated service connected " + name + " " + service);
+                EmbeddedDexTest.this.mServiceConnected = true;
+            }
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                Log.i(TAG, "Isolated service disconnected " + name);
+                EmbeddedDexTest.this.mServiceConnected = false;
+            }
+        };
+        try {
+            assertTrue(context.bindService(intent, conn, Context.BIND_AUTO_CREATE));
+            PollingCheck.waitFor(() -> EmbeddedDexTest.this.mServiceConnected);
+        } finally {
+            context.unbindService(conn);
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/EmptyActivity.java b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/EmptyActivity.java
index c25f89a..b33f343 100644
--- a/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/EmptyActivity.java
+++ b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/EmptyActivity.java
@@ -18,5 +18,4 @@
 
 import android.app.Activity;
 
-/** Empty class just to generate some dex */
 public class EmptyActivity extends Activity {}
diff --git a/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/IsolatedService.java b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/IsolatedService.java
new file mode 100644
index 0000000..6e4b969
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UseEmbeddedDexApp/src/com/android/cts/useembeddeddex/IsolatedService.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.useembeddeddex;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Log;
+
+public class IsolatedService extends Service {
+    Binder mBinder = new Binder();
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i("IsolatedService", "Service created in pid " + android.os.Process.myPid());
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.i("IsolatedService", "Service bound with pid " + android.os.Process.myPid());
+        return mBinder;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.i("IsolatedService", "Service destroyed in pid " + android.os.Process.myPid());
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
index 400eeeb..92aca5b 100644
--- a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
@@ -21,12 +21,16 @@
 # This is the default test package signed with the default key.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := CtsPkgInstallTinyApp
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 
 # This is the test package v2 signed with the default key.
 include $(LOCAL_PATH)/base.mk
 LOCAL_MANIFEST_FILE := AndroidManifest-v2.xml
 LOCAL_PACKAGE_NAME := CtsPkgInstallTinyAppV2
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 
 # This is the test package signed using the V1/V2 signature schemes with
@@ -41,6 +45,8 @@
 LOCAL_SDK_VERSION := 30
 LOCAL_MANIFEST_FILE := AndroidManifest-sandbox-v1.xml
 LOCAL_PACKAGE_NAME := v1v2-ec-p256-two-signers-targetSdk-30
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256
 LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256_2
 include $(BUILD_CTS_SUPPORT_PACKAGE)
@@ -49,6 +55,8 @@
 # with the previous key in the lineage and part of a sharedUid.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := v3-ec-p256-1-sharedUid
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest-shareduid.xml
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256
 include $(BUILD_CTS_SUPPORT_PACKAGE)
@@ -57,6 +65,8 @@
 # a rotated key and one signer in the lineage with default capabilities.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := v3-ec-p256-with-por_1_2-default-caps
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
 LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256
 LOCAL_CERTIFICATE_LINEAGE := $(cert_dir)/ec-p256-por_1_2-default-caps
@@ -67,6 +77,8 @@
 # grant access to the previous key in the lineage to join the sharedUid.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := v3-ec-p256-with-por_1_2-default-caps-sharedUid
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest-shareduid.xml
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
 LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256
@@ -80,6 +92,8 @@
 # ancestors are not allowed to be installed in the same sharedUserId.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := v3-por_Y_1_2-default-caps-sharedUid
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest-shareduid.xml
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
 LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/rsa-2048 $(cert_dir)/ec-p256
@@ -91,6 +105,8 @@
 # prevent the previous key in the lineage from joining the sharedUid.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := v3-ec-p256-with-por_1_2-no-shUid-cap-sharedUid
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest-shareduid.xml
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
 LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256
@@ -102,6 +118,8 @@
 # grant access to the previous key in the lineage to join the sharedUid.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := v3-ec-p256-with-por_1_2-default-caps-sharedUid-companion
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest-companion-shareduid.xml
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
 LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256
@@ -115,6 +133,8 @@
 # ancestors are not allowed to be installed in the same sharedUserId.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := v3-por_Z_1_2-default-caps-sharedUid-companion
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest-shareduid.xml
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
 LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/dsa-2048 $(cert_dir)/ec-p256
@@ -127,6 +147,8 @@
 # with the latest key in the lineage.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := v3-ec-p256-2-sharedUid-companion
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest-companion-shareduid.xml
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
 include $(BUILD_CTS_SUPPORT_PACKAGE)
@@ -135,9 +157,10 @@
 # with the previous key in the lineage and part of a sharedUid.
 include $(LOCAL_PATH)/base.mk
 LOCAL_PACKAGE_NAME := v3-ec-p256-1-sharedUid-companion2
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest-companion2-shareduid.xml
 LOCAL_CERTIFICATE := $(cert_dir)/ec-p256
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 
 cert_dir :=
-
diff --git a/hostsidetests/blobstore/OWNERS b/hostsidetests/blobstore/OWNERS
index bf870975..3a47c48 100644
--- a/hostsidetests/blobstore/OWNERS
+++ b/hostsidetests/blobstore/OWNERS
@@ -1,2 +1,2 @@
-# Bug component: 95221
+# Bug component: 533114
 include platform/frameworks/base:/apex/blobstore/OWNERS
diff --git a/hostsidetests/classloaders/useslibrary/app/src/com/android/cts/useslibrary/UsesLibraryTest.java b/hostsidetests/classloaders/useslibrary/app/src/com/android/cts/useslibrary/UsesLibraryTest.java
index 7f8f53e..7fdf2dc 100644
--- a/hostsidetests/classloaders/useslibrary/app/src/com/android/cts/useslibrary/UsesLibraryTest.java
+++ b/hostsidetests/classloaders/useslibrary/app/src/com/android/cts/useslibrary/UsesLibraryTest.java
@@ -47,7 +47,8 @@
     }
 
     /**
-     * Verify that we punt to run from apk when the shared libraries are missing.
+     * Verify that we still use an oat file (backed by a vdex-only file) when the shared
+     * libraries are missing.
      */
     public void testMissingLibrary() throws Exception {
         ClassLoader loader = getClass().getClassLoader();
@@ -59,12 +60,13 @@
             assertTrue(testDexElements != null && testDexElements.length == 1);
 
             DexFile testDexFile = getDexFileFromDexElement(testDexElements[0]);
-            assertFalse(isDexFileBackedByOatFile(testDexFile));
+            assertTrue(isDexFileBackedByOatFile(testDexFile));
         }
     }
 
     /**
-     * Verify that we punt to run from apk if the classpath generates a class collision failure.
+     * Verify that we still use an oat file (backed by a vdex-only file) if the classpath generates
+     * a class collision failure.
      */
     public void testDuplicateLibrary() throws Exception {
         ClassLoader loader = getClass().getClassLoader();
@@ -87,7 +89,7 @@
             assertEquals(Arrays.toString(testDexElements), 2, testDexElements.length);
 
             DexFile testDexFile = getDexFileFromDexElement(testDexElements[1]);
-            assertFalse(isDexFileBackedByOatFile(testDexFile));
+            assertTrue(isDexFileBackedByOatFile(testDexFile));
         }
     }
 
diff --git a/hostsidetests/compilation/src/android/compilation/cts/AdbRootDependentCompilationTest.java b/hostsidetests/compilation/src/android/compilation/cts/AdbRootDependentCompilationTest.java
index 56a50ca..9e453af6 100644
--- a/hostsidetests/compilation/src/android/compilation/cts/AdbRootDependentCompilationTest.java
+++ b/hostsidetests/compilation/src/android/compilation/cts/AdbRootDependentCompilationTest.java
@@ -127,8 +127,28 @@
         if (!canRunTest(EnumSet.noneOf(ProfileLocation.class))) {
             return;
         }
+
+        // Ensure no profile is initially present.
+        for (ProfileLocation profileLocation : ProfileLocation.values()) {
+            String clientPath = profileLocation.getPath();
+            if (doesFileExist(clientPath)) {
+                executeSuShellAdbCommand(0, "rm", clientPath);
+            }
+        }
+
+        // Copy the profile to the reference location so that the bg-dexopt
+        // can actually do work if it's configured to speed-profile.
+        for (ProfileLocation profileLocation : EnumSet.of(ProfileLocation.REF)) {
+            writeProfile(profileLocation);
+        }
+
         // Usually "interpret-only"
         String expectedInstallFilter = Objects.requireNonNull(mDevice.getProperty("pm.dexopt.install"));
+        if (expectedInstallFilter.equals("speed-profile")) {
+            // If the filter is speed-profile but no profile is present, the compiler
+            // will change it to verify.
+            expectedInstallFilter = "verify";
+        }
         // Usually "speed-profile"
         String expectedBgDexoptFilter = Objects.requireNonNull(mDevice.getProperty("pm.dexopt.bg-dexopt"));
 
@@ -170,16 +190,17 @@
     }
 
     public void testCompile_refProfile() throws Exception {
-        compileWithProfilesAndCheckFilter(false /* expectOdexChange */,
+        compileWithProfilesAndCheckFilter(true /* expectOdexChange */,
                  EnumSet.of(ProfileLocation.REF));
-        // We assume that the compiler isn't smart enough to realize that the
-        // previous odex was compiled before the ref profile was in place, even
-        // though theoretically it could be.
+        // expect a change in odex because the of the change form
+        // verify -> speed-profile
     }
 
     public void testCompile_curAndRefProfile() throws Exception {
-        compileWithProfilesAndCheckFilter(false /* expectOdexChange */,
+        compileWithProfilesAndCheckFilter(true /* expectOdexChange */,
                 EnumSet.of(ProfileLocation.CUR, ProfileLocation.REF));
+        // expect a change in odex because the of the change form
+        // verify -> speed-profile
     }
 
     private byte[] readFileOnClient(String clientPath) throws Exception {
@@ -226,7 +247,9 @@
         // Confirm the compiler-filter used in creating the odex file
         String compilerFilter = getCompilerFilter(odexFilePath);
 
-        assertEquals("compiler-filter", "speed-profile", compilerFilter);
+        // Without profiles, the compiler filter should be verify.
+        String expectedCompilerFilter = profileLocations.isEmpty() ? "verify" : "speed-profile";
+        assertEquals("compiler-filter", expectedCompilerFilter, compilerFilter);
 
         byte[] odexFileContents = readFileOnClient(odexFilePath);
         boolean odexChanged = !(Arrays.equals(initialOdexFileContents, odexFileContents));
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
index 9bb371b..a36bd00 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
@@ -20,13 +20,17 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.UiAutomation;
 import android.app.admin.DevicePolicyManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.hardware.camera2.CameraManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 
@@ -34,18 +38,22 @@
 
 import java.util.concurrent.TimeUnit;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 public class UserRestrictionsParentTest extends InstrumentationTestCase {
 
     private static final String TAG = "UserRestrictionsParentTest";
 
     protected Context mContext;
+    private ContentResolver mContentResolver;
+    private UiAutomation mUiAutomation;
     private DevicePolicyManager mDevicePolicyManager;
     private UserManager mUserManager;
 
     private CameraManager mCameraManager;
 
     private HandlerThread mBackgroundThread;
+    private static final long GET_UIAUTOMATION_TIMEOUT_NS = TimeUnit.SECONDS.toNanos(60);
 
     /**
      * A {@link Handler} for running tasks in the background.
@@ -56,6 +64,8 @@
     protected void setUp() throws Exception {
         super.setUp();
         mContext = getInstrumentation().getContext();
+        mContentResolver = mContext.getContentResolver();
+        mUiAutomation = getUiAutomation();
 
         mDevicePolicyManager = (DevicePolicyManager)
                 mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
@@ -72,10 +82,23 @@
 
     @Override
     protected void tearDown() throws Exception {
+        mUiAutomation.dropShellPermissionIdentity();
         stopBackgroundThread();
         super.tearDown();
     }
 
+    private UiAutomation getUiAutomation() throws InterruptedException {
+        final long deadline = System.nanoTime() + GET_UIAUTOMATION_TIMEOUT_NS;
+        while (System.nanoTime() < deadline) {
+            UiAutomation ui = getInstrumentation().getUiAutomation();
+            if (ui != null) {
+                 return ui;
+            }
+            Thread.sleep(1000);
+        }
+        throw new AssertionError("Failed to get UiAutomation");
+    }
+
     public void testAddUserRestrictionDisallowConfigDateTime_onParent() {
         DevicePolicyManager parentDevicePolicyManager =
                 mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
@@ -192,11 +215,18 @@
                     // UserManager.DISALLOW_DEBUGGING_FEATURES
             );
 
-    public void testPerProfileUserRestriction_onParent() {
+    public void testPerProfileUserRestriction_onParent() throws Settings.SettingNotFoundException {
+        mUiAutomation.adoptShellPermissionIdentity(
+                "android.permission.INTERACT_ACROSS_USERS_FULL",
+                "android.permission.CREATE_USERS");
+
         DevicePolicyManager parentDevicePolicyManager =
                 mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
         assertNotNull(parentDevicePolicyManager);
 
+        int locationMode = Settings.Secure.getIntForUser(mContentResolver,
+                Settings.Secure.LOCATION_MODE, UserHandle.USER_SYSTEM);
+
         for (String restriction : PROFILE_OWNER_ORGANIZATION_OWNED_LOCAL_RESTRICTIONS) {
             try {
                 boolean hasRestrictionOnManagedProfile = mUserManager.hasUserRestriction(
@@ -214,6 +244,12 @@
                 assertThat(hasUserRestriction(restriction)).isFalse();
             }
         }
+
+        // Restore the location mode setting after adding and removing the
+        // DISALLOW_SHARE_LOCATION user restriction. This is because, modifying this user
+        // restriction causes the location mode setting to be turned off.
+        Settings.Secure.putIntForUser(mContentResolver, Settings.Secure.LOCATION_MODE, locationMode,
+                UserHandle.USER_SYSTEM);
     }
 
     private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/InstallUpdateTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/InstallUpdateTest.java
old mode 100644
new mode 100755
index 3a3a357..205218d
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/InstallUpdateTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/systemupdate/InstallUpdateTest.java
@@ -206,6 +206,7 @@
                 && SystemClock.elapsedRealtime() <= startTime + BATTERY_STATE_CHANGE_TIMEOUT_MS) {
             Thread.sleep(BATTERY_STATE_CHANGE_SLEEP_PER_CHECK_MS);
         }
+        assertTrue("Battery state update timeout", isBatteryState(plugged, level));
     }
 
     private boolean isBatteryState(boolean plugged, int level) {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NetworkLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NetworkLoggingTest.java
old mode 100644
new mode 100755
index 1856d2c3..0224ba8
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NetworkLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/NetworkLoggingTest.java
@@ -274,7 +274,7 @@
 
     private void verifyNetworkLogs(List<NetworkEvent> networkEvents, int eventsExpected) {
         // allow a batch to be slightly smaller or larger.
-        assertTrue(Math.abs(eventsExpected - networkEvents.size()) <= 50);
+        assertTrue(Math.abs(eventsExpected - networkEvents.size()) <= 150);
         int ctsPackageNameCounter = 0;
         // allow a small down margin for verification, to avoid flakiness
         final int eventsExpectedWithMargin = eventsExpected - 50;
diff --git a/hostsidetests/dexmetadata/host/src/com/android/cts/dexmetadata/InstallDexMetadataHostTest.java b/hostsidetests/dexmetadata/host/src/com/android/cts/dexmetadata/InstallDexMetadataHostTest.java
index ad6aef8..ed52b9f 100644
--- a/hostsidetests/dexmetadata/host/src/com/android/cts/dexmetadata/InstallDexMetadataHostTest.java
+++ b/hostsidetests/dexmetadata/host/src/com/android/cts/dexmetadata/InstallDexMetadataHostTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -201,26 +202,114 @@
         assertTrue(runDeviceTests(TEST_PACKAGE, TEST_CLASS, "testDmForBaseButNoSplit"));
     }
 
-    static class ProfileReader {
-      byte[] data;
+    static class ProfileReaderV10 {
+        byte[] data;
 
-      ProfileReader(byte[] bytes) throws Exception {
-        ByteBuffer bb = ByteBuffer.wrap(bytes);
+        ProfileReaderV10(byte[] bytes) throws Exception {
+            ByteBuffer bb = ByteBuffer.wrap(bytes);
 
-        // Read header.
-        bb.order(ByteOrder.LITTLE_ENDIAN);
-        assertEquals(0x006f7270 /* LE "pro\0" */, bb.getInt());
-        assertEquals(0x00303130 /* LE "010\0" */, bb.getInt());
-        bb.get(); // Skip dex file count.
-        int uncompressed_size = bb.getInt();
-        int compressed_size = bb.getInt();
+            // Read header.
+            bb.order(ByteOrder.LITTLE_ENDIAN);
+            assertEquals(0x006f7270 /* LE "pro\0" */, bb.getInt());
+            assertEquals(0x00303130 /* LE "010\0" */, bb.getInt());
+            bb.get(); // Skip dex file count.
+            int uncompressed_size = bb.getInt();
+            int compressed_size = bb.getInt();
 
-        // Decompress profile.
-        Inflater inflater = new Inflater();
-        inflater.setInput(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining());
-        data = new byte[uncompressed_size];
-        assertEquals(uncompressed_size, inflater.inflate(data));
-      }
+            // Decompress profile.
+            Inflater inflater = new Inflater();
+            inflater.setInput(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining());
+            data = new byte[uncompressed_size];
+            assertEquals(uncompressed_size, inflater.inflate(data));
+        }
+    }
+
+    static class ProfileReaderV13 {
+        byte[] dexFilesData;
+        byte[] extraDescriptorsData;
+        byte[] classesData;
+        byte[] methodsData;
+
+        ProfileReaderV13(byte[] bytes) throws Exception {
+            ByteBuffer bb = ByteBuffer.wrap(bytes);
+
+            // Read header.
+            bb.order(ByteOrder.LITTLE_ENDIAN);
+            assertEquals(0x006f7270 /* LE "pro\0" */, bb.getInt());
+            assertEquals(0x00333130 /* LE "013\0" */, bb.getInt());
+            int section_count = bb.getInt();
+            assertFalse(section_count == 0);
+
+            // Mandatory dex files section.
+            assertEquals(/*kDexFiles*/ 0, bb.getInt());
+            dexFilesData = readSection(bb);
+
+            // Read optional sections. Assume no more than one occurrence of each known section.
+            for (int i = 1; i != section_count; ++i) {
+                int sectionType = bb.getInt();
+                switch (sectionType) {
+                    case 1:  // kExtraDescriptors
+                        assertTrue(extraDescriptorsData == null);
+                        extraDescriptorsData = readSection(bb);
+                        break;
+                    case 2:  // kClasses
+                        assertTrue(classesData == null);
+                        classesData = readSection(bb);
+                        break;
+                    case 3:  // kMethods
+                        assertTrue(methodsData == null);
+                        methodsData = readSection(bb);
+                        break;
+                    default:
+                        // Unknown section. Skip it. New versions of ART are allowed
+                        // to add sections that shall be ignored by old versions.
+                        skipSection(bb);
+                        break;
+                }
+            }
+        }
+
+        private byte[] readSection(ByteBuffer bb) throws Exception {
+            int fileOffset = bb.getInt();
+            int fileSize = bb.getInt();
+            int inflatedSize = bb.getInt();
+            if (inflatedSize != 0) {
+                // Decompress section.
+                byte[] data = new byte[inflatedSize];
+                Inflater inflater = new Inflater();
+                inflater.setInput(bb.array(), fileOffset, fileSize);
+                assertEquals(inflatedSize, inflater.inflate(data));
+                return data;
+            } else {
+                // Copy uncompressed data.
+                byte[] data = new byte[fileSize];
+                System.arraycopy(bb.array(), fileOffset, data, 0, fileSize);
+                return data;
+            }
+        }
+
+        private void skipSection(ByteBuffer bb) {
+            bb.getInt();  // fileOffset
+            bb.getInt();  // fileSize
+            bb.getInt();  // inflatedSize
+        }
+    }
+
+    private static int getProfileVersion(byte[] bytes) {
+        assertEquals(bytes[0], (byte) 'p');
+        assertEquals(bytes[1], (byte) 'r');
+        assertEquals(bytes[2], (byte) 'o');
+        assertEquals(bytes[3], (byte) '\0');
+        assertEquals(bytes[7], (byte) '\0');
+        int version = 0;
+        for (int pos = 4; pos != 7; ++pos) {
+            byte digit = bytes[pos];
+            if (digit < (byte) '0' || digit > (byte) '9') {
+                throw new Error("Non-numeric profile version");
+            }
+            version = version * 10 + (((int) digit) - '0');
+        }
+        return version;
     }
 
     @Test
@@ -236,11 +325,28 @@
         assertTrue(result.trim().isEmpty());
 
         // Extract the profile bytes from the dex metadata and from the profile snapshot.
-        byte[] snapshotProfileBytes = new ProfileReader(extractProfileSnapshotFromDevice()).data;
-        byte[] expectedProfileBytes =
-                new ProfileReader(extractProfileFromDexMetadata(mDmBaseFile)).data;
+        byte[] rawDeviceProfile = extractProfileSnapshotFromDevice();
+        int deviceProfileVersion = getProfileVersion(rawDeviceProfile);
+        switch (deviceProfileVersion) {
+            case 10: {
+                byte[] snapshotProfileBytes = new ProfileReaderV10(rawDeviceProfile).data;
+                byte[] expectedProfileBytes =
+                        new ProfileReaderV10(extractProfileFromDexMetadata(mDmBaseFile)).data;
 
-        assertArrayEquals(expectedProfileBytes, snapshotProfileBytes);
+                assertArrayEquals(expectedProfileBytes, snapshotProfileBytes);
+                break;
+            }
+            case 13: {
+                ProfileReaderV13 reader = new ProfileReaderV13(rawDeviceProfile);
+                // TODO: Support newer profiles implemented for b/148067697.
+                // Currently the .dm file is still version 10, so we cannot compare against it.
+                System.out.println("TODO: Check profile version 13.");
+                break;
+            }
+            default: {
+                throw new Error("Unsupported profile version: " + deviceProfileVersion);
+            }
+        }
     }
 
     /**
diff --git a/hostsidetests/edi/Android.bp b/hostsidetests/edi/Android.bp
index d542654..80a7cc6 100644
--- a/hostsidetests/edi/Android.bp
+++ b/hostsidetests/edi/Android.bp
@@ -25,8 +25,15 @@
         "general-tests",
     ],
     libs: [
-        "cts-tradefed",
-        "tradefed",
         "compatibility-host-util",
+        "cts-tradefed",
+        "dexlib2-no-guava-no-cli",
+        "tradefed",
+    ],
+    static_libs: [
+        "compat-classpaths-testing",
+    ],
+    data: [
+        ":ClasspathDeviceInfoHelperApp",
     ],
 }
diff --git a/hostsidetests/edi/AndroidTest.xml b/hostsidetests/edi/AndroidTest.xml
index acd6b57..c39246f 100644
--- a/hostsidetests/edi/AndroidTest.xml
+++ b/hostsidetests/edi/AndroidTest.xml
@@ -20,6 +20,10 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="ClasspathDeviceInfoHelperApp.apk" />
+    </target_preparer>
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsEdiHostTestCases.jar" />
     </test>
diff --git a/hostsidetests/edi/apps/classpath/Android.bp b/hostsidetests/edi/apps/classpath/Android.bp
new file mode 100644
index 0000000..3b1626e
--- /dev/null
+++ b/hostsidetests/edi/apps/classpath/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+    name: "ClasspathDeviceInfoHelperApp",
+    defaults: ["cts_defaults"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.core",
+        "guava",
+    ],
+    srcs: ["src/**/*.java"],
+    sdk_version: "test_current",
+}
diff --git a/hostsidetests/edi/apps/classpath/AndroidManifest.xml b/hostsidetests/edi/apps/classpath/AndroidManifest.xml
new file mode 100644
index 0000000..88cb689
--- /dev/null
+++ b/hostsidetests/edi/apps/classpath/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.edi.cts.apps.classpath"
+          android:targetSandboxVersion="2">
+
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" />
+
+    <application android:requestLegacyExternalStorage="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.edi.cts.apps.classpath" />
+
+</manifest>
diff --git a/hostsidetests/edi/apps/classpath/src/android/edi/cts/apps/classpath/ClasspathDeviceTest.java b/hostsidetests/edi/apps/classpath/src/android/edi/cts/apps/classpath/ClasspathDeviceTest.java
new file mode 100644
index 0000000..93532aa
--- /dev/null
+++ b/hostsidetests/edi/apps/classpath/src/android/edi/cts/apps/classpath/ClasspathDeviceTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.edi.cts.apps.classpath;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.SharedLibraryInfo;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Device-side helper app for ClasspathDeviceInfo.
+ *
+ * <p>It is not technically a test as it simply collects information, but it simplifies the usage
+ * and communication with host-side ClasspathDeviceInfo.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ClasspathDeviceTest {
+
+    private static final String TAG = "ClasspathDeviceTest";
+
+    private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+    private final Context context = instrumentation.getTargetContext();
+
+    @Before
+    public void before() {
+        instrumentation.getUiAutomation().adoptShellPermissionIdentity();
+    }
+
+    @After
+    public void after() {
+        instrumentation.getUiAutomation().dropShellPermissionIdentity();
+    }
+
+    /**
+     * Collects details about all shared libraries on the device and writes them to disk.
+     */
+    @Test
+    public void collectSharedLibraryPaths() throws Exception {
+        List<SharedLibraryInfo> sharedLibraries =
+                context.getPackageManager().getSharedLibraries(0);
+
+        ImmutableList.Builder<String> content = ImmutableList.builder();
+        for (SharedLibraryInfo sharedLibrary : sharedLibraries) {
+            content.add(String.format(Locale.US, "%s %d %d %s",
+                    sharedLibrary.getName(),
+                    sharedLibrary.getType(),
+                    sharedLibrary.getLongVersion(),
+                    String.join(" ", sharedLibrary.getAllCodePaths())));
+        }
+
+        Path detailsFilepath = new File("/sdcard/shared-libs.txt").toPath();
+        ImmutableList<String> lines = content.build();
+        Log.i(TAG, String.format("Writing details about %d shared libraries to %s",
+                lines.size(), detailsFilepath));
+        Files.write(detailsFilepath, lines);
+    }
+
+}
diff --git a/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java b/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java
new file mode 100644
index 0000000..8dc7b32
--- /dev/null
+++ b/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.edi.cts;
+
+import static android.compat.testing.Classpaths.ClasspathType.BOOTCLASSPATH;
+import static android.compat.testing.Classpaths.ClasspathType.DEX2OATBOOTCLASSPATH;
+import static android.compat.testing.Classpaths.ClasspathType.SYSTEMSERVERCLASSPATH;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.compat.testing.Classpaths;
+import android.compat.testing.Classpaths.ClasspathType;
+
+import com.android.compatibility.common.util.DeviceInfo;
+import com.android.compatibility.common.util.HostInfoStore;
+import com.android.tradefed.device.ITestDevice;
+
+import com.google.common.collect.ImmutableList;
+
+import org.jf.dexlib2.iface.ClassDef;
+
+/**
+ * Collects information about Java classes present in *CLASSPATH variables and Java shared libraries
+ * from the device.
+ */
+public class ClasspathDeviceInfo extends DeviceInfo {
+
+    private static final String HELPER_APP_PACKAGE = "android.edi.cts.apps.classpath";
+    private static final String HELPER_APP_CLASS = HELPER_APP_PACKAGE + ".ClasspathDeviceTest";
+
+    private ITestDevice mDevice;
+
+    @Override
+    protected void collectDeviceInfo(HostInfoStore store) throws Exception {
+        mDevice = getDevice();
+
+        store.startArray("jars");
+        collectBootclasspathJars(store);
+        collectSharedLibraryJars(store);
+        store.endArray();
+    }
+
+    private void collectBootclasspathJars(HostInfoStore store) throws Exception {
+        collectClasspathJarInfo(store, BOOTCLASSPATH);
+        collectClasspathJarInfo(store, SYSTEMSERVERCLASSPATH);
+        collectClasspathJarInfo(store, DEX2OATBOOTCLASSPATH);
+    }
+
+    private void collectClasspathJarInfo(HostInfoStore store, ClasspathType classpath)
+            throws Exception {
+        ImmutableList<String> paths = Classpaths.getJarsOnClasspath(mDevice, classpath);
+        for (int i = 0; i < paths.size(); i++) {
+            store.startGroup();
+            store.addResult("classpath", classpath.name());
+            store.addResult("path", paths.get(i));
+            store.addResult("index", i);
+            collectClassInfo(store, paths.get(i));
+            store.endGroup();
+        }
+    }
+
+    private void collectSharedLibraryJars(HostInfoStore store) throws Exception {
+        // Trigger helper app to collect and write info about shared libraries on the device.
+        assertThat(runDeviceTests(HELPER_APP_PACKAGE, HELPER_APP_CLASS)).isTrue();
+
+        String remoteFile = "/sdcard/shared-libs.txt";
+        String content;
+        try {
+            content = mDevice.pullFileContents(remoteFile);
+        } finally {
+            mDevice.deleteFile(remoteFile);
+        }
+
+        for (String line : content.split("\n")) {
+            String[] words = line.split(" ");
+            assertWithMessage(
+                    "expected each line to be in the format: <name> <type> <version> <path>...")
+                    .that(words.length)
+                    .isAtLeast(4);
+            String libraryName = words[0];
+            String libraryType = words[1];
+            String libraryVersion = words[2];
+            for (int i = 3; i < words.length; i++) {
+                String path = words[i];
+
+                store.startGroup();
+                store.startGroup("shared_library");
+                store.addResult("name", libraryName);
+                store.addResult("type", libraryType);
+                store.addResult("version", libraryVersion);
+                store.endGroup(); // shared_library
+                store.addResult("path", path);
+                store.addResult("index", i - 3); // minus <name> <type> <version>
+                collectClassInfo(store, path);
+                store.endGroup();
+            }
+        }
+    }
+
+    private void collectClassInfo(HostInfoStore store, String path) throws Exception {
+        store.startArray("classes");
+        for (ClassDef classDef : Classpaths.getClassDefsFromJar(mDevice, path)) {
+            store.startGroup();
+            store.addResult("name", classDef.getType());
+            store.endGroup();
+        }
+        store.endArray();
+    }
+}
diff --git a/hostsidetests/edi/src/android/edi/cts/PropertyDeviceInfo.java b/hostsidetests/edi/src/android/edi/cts/PropertyDeviceInfo.java
new file mode 100644
index 0000000..b9f6ea1
--- /dev/null
+++ b/hostsidetests/edi/src/android/edi/cts/PropertyDeviceInfo.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.edi.cts;
+
+import com.android.compatibility.common.util.DeviceInfo;
+import com.android.compatibility.common.util.HostInfoStore;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.CommandResult;
+
+import java.util.Scanner;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * System property info collector.
+ */
+public class PropertyDeviceInfo extends DeviceInfo {
+
+    @Override
+    protected void collectDeviceInfo(HostInfoStore store) throws Exception {
+        store.startArray("ro_property");
+        try {
+            ITestDevice device = getDevice();
+            CommandResult commandResult = device.executeShellV2Command("getprop");
+            if (commandResult.getExitCode() == null) {
+                CLog.e("getprop exit code is null");
+                return;
+            }
+            if (commandResult.getExitCode() != 0) {
+                CLog.e("getprop returns %d: %s", commandResult.getExitCode(),
+                        commandResult.getStderr());
+                return;
+            }
+            if (commandResult.getExitCode() == 0 && !commandResult.getStderr().isEmpty()) {
+                CLog.w("Warnings occur when running getprop:\n%s",
+                        commandResult.getStderr());
+            }
+
+            parseProps(commandResult.getStdout(), store);
+        } finally {
+            store.endArray();
+        }
+    }
+
+    private void parseProps(String stdout, HostInfoStore store) throws Exception {
+        Pattern pattern = Pattern.compile("\\[(ro.+)\\]: \\[(.+)\\]");
+        if (stdout == null) stdout = "";
+        try (Scanner scanner = new Scanner(stdout)) {
+            while (scanner.hasNextLine()) {
+                String line = scanner.nextLine();
+                Matcher matcher = pattern.matcher(line);
+                if (matcher.matches()) {
+                    String name = matcher.group(1);
+                    String value = matcher.group(2);
+
+                    store.startGroup();
+                    store.addResult("name", name);
+                    store.addResult("value", value);
+                    store.endGroup();
+                }
+            }
+        }
+    }
+}
diff --git a/hostsidetests/hdmicec/README.md b/hostsidetests/hdmicec/README.md
index db02090..b774e46 100644
--- a/hostsidetests/hdmicec/README.md
+++ b/hostsidetests/hdmicec/README.md
@@ -17,14 +17,42 @@
 *   Android TV playback device
 *   CEC adapter, see [External CEC Adapter instructions](cec_adapter.md)
 *   Install `cec-client` binary, see [install instructions](cec_adapter.md#software)
-*   HDMI Display (aka a TV) or an HDMI fake plug
+*   HDMI Display (aka a TV) with CEC disabled to avoid interference, or an HDMI fake plug
+
+It is recommended that the playback device has an HDMI physical address of `1.0.0.0` while running
+the tests. In case the DUT takes a physical address other than `1.0.0.0` and this is unavoidable,
+the tests can be configured to expect a different physical address by appending these arguments to
+the tradefed command:
+```
+--module-arg CtsHdmiCecHostTestCases:set-option:cec-phy-addr:<address_in_decimal>
+```
+Thus, for a device that is taking an address `3.0.0.0`, pass `12288` as the `cec-phy-addr` argument.
+
+The CEC adapter may also be installed in-between the TV and the playback device.
 
 ![drawing](setup.png)
 
+### TV panel devices
+
+Running these CTS tests on TV panel devices requires an external CEC adapter.
+
+*   Android TV panel device
+*   CEC adapter, see [External CEC Adapter instructions](cec_adapter.md)
+*   Install `cec-client` binary, see [install instructions](cec_adapter.md#software)
+
+It is recommended to connect the CEC adapter to the HDMI ARC port of the TV device.
+
 ### Automation
 
-Given the setup described above you can run all of these tests with the command
+Given the setup described above you can run this test module with the following commands:
 
+#### cts-tradefed
+
+```
+cts-tradefed > run cts -m CtsHdmiCecHostTestCases
+```
+
+#### atest
 ```
 atest CtsHdmiCecHostTestCases
 ```
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
index 2b6b6d1..853510b 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
@@ -16,8 +16,11 @@
 
 package android.hdmicec.cts;
 
+import android.hdmicec.cts.error.DumpsysParseException;
+
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
@@ -25,6 +28,7 @@
 import org.junit.rules.TestRule;
 
 import java.io.BufferedReader;
+import java.io.IOException;
 import java.io.StringReader;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -33,6 +37,25 @@
 @OptionClass(alias="hdmi-cec-client-cts-test")
 public class BaseHdmiCecCtsTest extends BaseHostJUnit4Test {
 
+    public static final String PROPERTY_LOCALE = "persist.sys.locale";
+
+    /** Enum contains the list of possible address types. */
+    private enum AddressType {
+        DUMPSYS_LOGICAL_ADDRESS("logicalAddress"),
+        DUMPSYS_AS_LOGICAL_ADDRESS("activeSourceLogicalAddress"),
+        DUMPSYS_PHYSICAL_ADDRESS("physicalAddress");
+
+        private String address;
+
+        public String getAddressType() {
+            return this.address;
+        }
+
+        private AddressType(String address) {
+            this.address = address;
+        }
+    }
+
     public final HdmiCecClientWrapper hdmiCecClient;
     public LogicalAddress mDutLogicalAddress;
 
@@ -108,85 +131,114 @@
     public static int dutPhysicalAddress = HdmiCecConstants.DEFAULT_PHYSICAL_ADDRESS;
 
     /** Gets the physical address of the DUT by parsing the dumpsys hdmi_control. */
-    public int getDumpsysPhysicalAddress() throws Exception {
+    public int getDumpsysPhysicalAddress() throws DumpsysParseException {
         return getDumpsysPhysicalAddress(getDevice());
     }
 
     /** Gets the physical address of the specified device by parsing the dumpsys hdmi_control. */
-    public static int getDumpsysPhysicalAddress(ITestDevice device) throws Exception {
-        String line;
-        String pattern = "(.*?)" + "(physical_address: )" + "(?<address>0x\\p{XDigit}{4})" +
-                "(.*?)";
-        Pattern p = Pattern.compile(pattern);
-        Matcher m;
-        String dumpsys = device.executeShellCommand("dumpsys hdmi_control");
-        BufferedReader reader = new BufferedReader(new StringReader(dumpsys));
-        while ((line = reader.readLine()) != null) {
-            m = p.matcher(line);
-            if (m.matches()) {
-                int address = Integer.decode(m.group("address"));
-                return address;
-            }
-        }
-        throw new Exception("Could not parse physical address from dumpsys.");
+    public static int getDumpsysPhysicalAddress(ITestDevice device) throws DumpsysParseException {
+        return parseRequiredAddressFromDumpsys(device, AddressType.DUMPSYS_PHYSICAL_ADDRESS);
     }
 
     /** Gets the logical address of the DUT by parsing the dumpsys hdmi_control. */
-    public int getDumpsysLogicalAddress() throws Exception {
+    public int getDumpsysLogicalAddress() throws DumpsysParseException {
         return getDumpsysLogicalAddress(getDevice());
     }
 
     /** Gets the logical address of the specified device by parsing the dumpsys hdmi_control. */
-    public static int getDumpsysLogicalAddress(ITestDevice device) throws Exception {
-        String line;
-        String pattern = "(.*?)" + "(mAddress: )" + "(?<address>\\d+)" +
-                "(.*?)";
-        Pattern p = Pattern.compile(pattern);
-        Matcher m;
-        String dumpsys = device.executeShellCommand("dumpsys hdmi_control");
-        BufferedReader reader = new BufferedReader(new StringReader(dumpsys));
-        while ((line = reader.readLine()) != null) {
-            m = p.matcher(line);
-            if (m.matches()) {
-                int address = Integer.decode(m.group("address"));
-                return address;
-            }
-        }
-        throw new Exception("Could not parse logical address from dumpsys.");
+    public static int getDumpsysLogicalAddress(ITestDevice device) throws DumpsysParseException {
+        return parseRequiredAddressFromDumpsys(device, AddressType.DUMPSYS_LOGICAL_ADDRESS);
     }
 
     /**
      * Parses the dumpsys hdmi_control to get the logical address of the current device registered
      * as active source.
      */
-    public LogicalAddress getDumpsysActiveSourceLogicalAddress() throws Exception {
-        String line;
-        String pattern =
-                "(.*?)"
-                        + "(mActiveSource: )"
-                        + "(\\(0x)"
-                        + "(?<logicalAddress>\\d+)"
-                        + "(, )"
-                        + "(0x)"
-                        + "(?<physicalAddress>\\d+)"
-                        + "(\\))"
-                        + "(.*?)";
-        Pattern p = Pattern.compile(pattern);
-        Matcher m;
+    public LogicalAddress getDumpsysActiveSourceLogicalAddress() throws DumpsysParseException {
         ITestDevice device = getDevice();
-        String dumpsys = device.executeShellCommand("dumpsys hdmi_control");
-        BufferedReader reader = new BufferedReader(new StringReader(dumpsys));
-        while ((line = reader.readLine()) != null) {
-            m = p.matcher(line);
-            if (m.matches()) {
-                try {
-                    int address = Integer.decode(m.group("logicalAddress"));
-                    return LogicalAddress.getLogicalAddress(address);
-                } catch (NumberFormatException ne) {
-                    throw new Exception("Could not correctly parse the logical address");
+        int address =
+                parseRequiredAddressFromDumpsys(device, AddressType.DUMPSYS_AS_LOGICAL_ADDRESS);
+        return LogicalAddress.getLogicalAddress(address);
+    }
+
+    private static int parseRequiredAddressFromDumpsys(ITestDevice device, AddressType addressType)
+            throws DumpsysParseException {
+        Matcher m;
+        String line;
+        String pattern;
+        switch (addressType) {
+            case DUMPSYS_LOGICAL_ADDRESS:
+                pattern =
+                        "(.*?)"
+                                + "(mAddress: )"
+                                + "(?<"
+                                + addressType.getAddressType()
+                                + ">\\p{XDigit}{1})"
+                                + "(.*?)";
+                break;
+            case DUMPSYS_PHYSICAL_ADDRESS:
+                pattern =
+                        "(.*?)"
+                                + "(physical_address: )"
+                                + "(?<"
+                                + addressType.getAddressType()
+                                + ">0x\\p{XDigit}{4})"
+                                + "(.*?)";
+                break;
+            case DUMPSYS_AS_LOGICAL_ADDRESS:
+                pattern =
+                        "(.*?)"
+                                + "(mActiveSource: )"
+                                + "(\\(0x)"
+                                + "(?<"
+                                + addressType.getAddressType()
+                                + ">\\d+)"
+                                + "(, )"
+                                + "(0x)"
+                                + "(?<physicalAddress>\\d+)"
+                                + "(\\))"
+                                + "(.*?)";
+                break;
+            default:
+                throw new DumpsysParseException(
+                        "Incorrect parameters", new IllegalArgumentException());
+        }
+
+        try {
+            Pattern p = Pattern.compile(pattern);
+            String dumpsys = device.executeShellCommand("dumpsys hdmi_control");
+            BufferedReader reader = new BufferedReader(new StringReader(dumpsys));
+            while ((line = reader.readLine()) != null) {
+                m = p.matcher(line);
+                if (m.matches()) {
+                    int address = Integer.decode(m.group(addressType.getAddressType()));
+                    return address;
                 }
             }
+        } catch (IOException | DeviceNotAvailableException e) {
+            throw new DumpsysParseException(
+                    "Could not parse " + addressType.getAddressType() + " from dumpsys.", e);
         }
-        throw new Exception("Could not parse active source from dumpsys.");
+        throw new DumpsysParseException(
+                "Could not parse " + addressType.getAddressType() + " from dumpsys.");
+    }
+
+    public String getSystemLocale() throws Exception {
+        ITestDevice device = getDevice();
+        return device.executeShellCommand("getprop " + PROPERTY_LOCALE).trim();
+    }
+
+    public static String extractLanguage(String locale) {
+        return locale.split("[^a-zA-Z]")[0];
+    }
+
+    public void setSystemLocale(String locale) throws Exception {
+        ITestDevice device = getDevice();
+        device.executeShellCommand("setprop " + PROPERTY_LOCALE + " " + locale);
+    }
+
+    public boolean isLanguageEditable() throws Exception {
+        String val = getDevice().executeShellCommand("getprop ro.hdmi.set_menu_language");
+        return val.trim().equals("true") ? true : false;
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
index f04a1a7..f3b50b1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
@@ -53,6 +53,7 @@
     REQUEST_SHORT_AUDIO_DESCRIPTOR(0xa4),
     INITIATE_ARC(0xc0),
     ARC_INITIATED(0xc1),
+    ARC_TERMINATED(0xc2),
     REQUEST_ARC_INITIATION(0xc3),
     REQUEST_ARC_TERMINATION(0xc4),
     TERMINATE_ARC(0xc5),
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
index 39cadcb..9d8849f 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
@@ -16,6 +16,10 @@
 
 package android.hdmicec.cts;
 
+import android.hdmicec.cts.error.ErrorCodes;
+import android.hdmicec.cts.error.CecClientWrapperException;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.util.RunUtil;
@@ -69,7 +73,7 @@
         targetDevice = dutLogicalAddress;
     }
 
-    public List<String> getValidCecClientPorts() throws Exception {
+    public List<String> getValidCecClientPorts() throws CecClientWrapperException {
 
         List<String> listPortsCommand = new ArrayList();
 
@@ -77,28 +81,40 @@
         listPortsCommand.add("-l");
 
         List<String> comPorts = new ArrayList();
-        Process cecClient = RunUtil.getDefault().runCmdInBackground(listPortsCommand);
-        BufferedReader inputConsole =
-                new BufferedReader(new InputStreamReader(cecClient.getInputStream()));
-        while (cecClient.isAlive()) {
-            if (inputConsole.ready()) {
-                String line = inputConsole.readLine();
-                if (line.toLowerCase().contains("com port")) {
-                    String port = line.split(":")[1].trim();
-                    comPorts.add(port);
+        try {
+            Process cecClient = RunUtil.getDefault().runCmdInBackground(listPortsCommand);
+            BufferedReader inputConsole =
+                    new BufferedReader(new InputStreamReader(cecClient.getInputStream()));
+            while (cecClient.isAlive()) {
+                if (inputConsole.ready()) {
+                    String line = inputConsole.readLine();
+                    if (line.toLowerCase().contains("com port")) {
+                        String port = line.split(":")[1].trim();
+                        comPorts.add(port);
+                    }
                 }
             }
+            inputConsole.close();
+            cecClient.waitFor();
+        } catch (IOException | InterruptedException ioe) {
+            throw new CecClientWrapperException(ErrorCodes.ReadConsole, ioe);
         }
-        inputConsole.close();
-        cecClient.waitFor();
 
         return comPorts;
     }
 
-    boolean initValidCecClient(ITestDevice device, List<String> clientCommands) throws Exception {
-        String serialNo = device.getProperty("ro.serialno");
-        File mDeviceEntry = new File(HdmiCecConstants.CEC_MAP_FOLDER, serialNo);
+    boolean initValidCecClient(ITestDevice device, List<String> clientCommands)
+            throws CecClientWrapperException {
+
+        String serialNo;
         List<String> launchCommand = new ArrayList(clientCommands);
+        try {
+            serialNo = device.getProperty("ro.serialno");
+        } catch (DeviceNotAvailableException de) {
+            throw new CecClientWrapperException(ErrorCodes.DeviceNotAvailable, de);
+        }
+        File mDeviceEntry = new File(HdmiCecConstants.CEC_MAP_FOLDER, serialNo);
+
         try (BufferedReader reader = new BufferedReader(new FileReader(mDeviceEntry))) {
             String port = reader.readLine();
             launchCommand.add(port);
@@ -119,17 +135,18 @@
                 Process killProcess = mCecClient.destroyForcibly();
                 killProcess.waitFor();
             }
-        } catch (IOException ioe) {
-            throw new Exception("Could not open port mapping file");
+        } catch (IOException | InterruptedException ioe) {
+            throw new CecClientWrapperException(
+                    ErrorCodes.ReadConsole, ioe, "Could not open port mapping file");
         }
-
         return false;
     }
 
     /** Initialise the client */
-    void init(boolean startAsTv, ITestDevice device) throws Exception {
+    void init(boolean startAsTv, ITestDevice device) throws CecClientWrapperException {
         if (targetDevice == LogicalAddress.UNKNOWN) {
-            throw new IllegalStateException("Missing logical address of the target device.");
+            throw new CecClientWrapperException(
+                    ErrorCodes.CecClientStart, "Missing logical address of the target device.");
         }
 
         List<String> commands = new ArrayList();
@@ -155,16 +172,16 @@
         if (!initValidCecClient(device, commands)) {
             mCecClientInitialised = false;
 
-            throw (new Exception("Could not initialise cec-client process"));
+            throw new CecClientWrapperException(ErrorCodes.CecClientStart);
         }
     }
 
-    private void checkCecClient() throws Exception {
+    private void checkCecClient() throws CecClientWrapperException {
         if (!mCecClientInitialised) {
-            throw new Exception("cec-client not initialised!");
+            throw new CecClientWrapperException(ErrorCodes.CecClientStart);
         }
         if (!mCecClient.isAlive()) {
-            throw new Exception("cec-client not running!");
+            throw new CecClientWrapperException(ErrorCodes.CecClientNotRunning);
         }
     }
 
@@ -172,7 +189,7 @@
      * Sends a CEC message with source marked as broadcast to the device passed in the constructor
      * through the output console of the cec-communication channel.
      */
-    public void sendCecMessage(CecOperand message) throws Exception {
+    public void sendCecMessage(CecOperand message) throws CecClientWrapperException {
         sendCecMessage(LogicalAddress.BROADCAST, targetDevice, message, "");
     }
 
@@ -180,7 +197,8 @@
      * Sends a CEC message from source device to the device passed in the constructor through the
      * output console of the cec-communication channel.
      */
-    public void sendCecMessage(LogicalAddress source, CecOperand message) throws Exception {
+    public void sendCecMessage(LogicalAddress source, CecOperand message)
+            throws CecClientWrapperException {
         sendCecMessage(source, targetDevice, message, "");
     }
 
@@ -188,8 +206,9 @@
      * Sends a CEC message from source device to a destination device through the output console of
      * the cec-communication channel.
      */
-    public void sendCecMessage(LogicalAddress source, LogicalAddress destination,
-        CecOperand message) throws Exception {
+    public void sendCecMessage(
+            LogicalAddress source, LogicalAddress destination, CecOperand message)
+            throws CecClientWrapperException {
         sendCecMessage(source, destination, message, "");
     }
 
@@ -197,7 +216,7 @@
      * Broadcasts a CEC ACTIVE_SOURCE message from client device source through the output console
      * of the cec-communication channel.
      */
-    public void broadcastActiveSource(LogicalAddress source) throws Exception {
+    public void broadcastActiveSource(LogicalAddress source) throws CecClientWrapperException {
         int sourcePa = (source == selfDevice) ? physicalAddress : 0xFFFF;
         sendCecMessage(
                 source,
@@ -211,7 +230,7 @@
      * source through the output console of the cec-communication channel.
      */
     public void broadcastActiveSource(LogicalAddress source, int physicalAddressOfActiveDevice)
-            throws Exception {
+            throws CecClientWrapperException {
         sendCecMessage(
                 source,
                 LogicalAddress.BROADCAST,
@@ -224,7 +243,8 @@
      * Broadcasts a CEC REPORT_PHYSICAL_ADDRESS message from client device source through the output
      * console of the cec-communication channel.
      */
-    public void broadcastReportPhysicalAddress(LogicalAddress source) throws Exception {
+    public void broadcastReportPhysicalAddress(LogicalAddress source)
+            throws CecClientWrapperException {
         String deviceType = CecMessage.formatParams(source.getDeviceType());
         int sourcePa = (source == selfDevice) ? physicalAddress : 0xFFFF;
         String physicalAddress =
@@ -241,7 +261,7 @@
      * device source through the output console of the cec-communication channel.
      */
     public void broadcastReportPhysicalAddress(LogicalAddress source, int physicalAddressToReport)
-            throws Exception {
+            throws CecClientWrapperException {
         String deviceType = CecMessage.formatParams(source.getDeviceType());
         String physicalAddress =
                 CecMessage.formatParams(
@@ -257,27 +277,37 @@
      * Sends a CEC message from source device to a destination device through the output console of
      * the cec-communication channel with the appended params.
      */
-    public void sendCecMessage(LogicalAddress source, LogicalAddress destination,
-            CecOperand message, String params) throws Exception {
+    public void sendCecMessage(
+            LogicalAddress source, LogicalAddress destination, CecOperand message, String params)
+            throws CecClientWrapperException {
         checkCecClient();
         String sendMessageString = "tx " + source + destination + ":" + message + params;
-        mOutputConsole.write(sendMessageString);
-        mOutputConsole.newLine();
-        mOutputConsole.flush();
+        try {
+            mOutputConsole.write(sendMessageString);
+            mOutputConsole.newLine();
+            mOutputConsole.flush();
+        } catch (IOException ioe) {
+            throw new CecClientWrapperException(ErrorCodes.WriteConsole, ioe);
+        }
     }
 
     /**
-     * Sends a <USER_CONTROL_PRESSED> and <USER_CONTROL_RELEASED> from source to destination
-     * through the output console of the cec-communication channel with the mentioned keycode.
+     * Sends a <USER_CONTROL_PRESSED> and <USER_CONTROL_RELEASED> from source to destination through
+     * the output console of the cec-communication channel with the mentioned keycode.
      */
-    public void sendUserControlPressAndRelease(LogicalAddress source, LogicalAddress destination,
-            int keycode, boolean holdKey) throws Exception {
+    public void sendUserControlPressAndRelease(
+            LogicalAddress source, LogicalAddress destination, int keycode, boolean holdKey)
+            throws CecClientWrapperException {
         sendUserControlPress(source, destination, keycode, holdKey);
-        /* Sleep less than 200ms between press and release */
-        TimeUnit.MILLISECONDS.sleep(100);
-        mOutputConsole.write("tx " + source + destination + ":" +
-                              CecOperand.USER_CONTROL_RELEASED);
-        mOutputConsole.flush();
+        try {
+            /* Sleep less than 200ms between press and release */
+            TimeUnit.MILLISECONDS.sleep(100);
+            mOutputConsole.write(
+                    "tx " + source + destination + ":" + CecOperand.USER_CONTROL_RELEASED);
+            mOutputConsole.flush();
+        } catch (IOException | InterruptedException ioe) {
+            throw new CecClientWrapperException(ErrorCodes.WriteConsole, ioe);
+        }
     }
 
     /**
@@ -285,59 +315,76 @@
      * cec-communication channel with the mentioned keycode. If holdKey is true, the method will
      * send multiple <UCP> messages to simulate a long press. No <UCR> will be sent.
      */
-    public void sendUserControlPress(LogicalAddress source, LogicalAddress destination,
-            int keycode, boolean holdKey) throws Exception {
+    public void sendUserControlPress(
+            LogicalAddress source, LogicalAddress destination, int keycode, boolean holdKey)
+            throws CecClientWrapperException {
         String key = String.format("%02x", keycode);
         String command = "tx " + source + destination + ":" +
                 CecOperand.USER_CONTROL_PRESSED + ":" + key;
 
-        if (holdKey) {
-            /* Repeat once between 200ms and 450ms for at least 5 seconds. Since message will be
-             * sent once later, send 16 times in loop every 300ms. */
-            int repeat = 16;
-            for (int i = 0; i < repeat; i++) {
-                mOutputConsole.write(command);
-                mOutputConsole.newLine();
-                mOutputConsole.flush();
-                TimeUnit.MILLISECONDS.sleep(300);
+        try {
+            if (holdKey) {
+                /* Repeat once between 200ms and 450ms for at least 5 seconds. Since message will be
+                 * sent once later, send 16 times in loop every 300ms. */
+                int repeat = 16;
+                for (int i = 0; i < repeat; i++) {
+                    mOutputConsole.write(command);
+                    mOutputConsole.newLine();
+                    mOutputConsole.flush();
+                    TimeUnit.MILLISECONDS.sleep(300);
+                }
             }
-        }
 
-        mOutputConsole.write(command);
-        mOutputConsole.newLine();
-        mOutputConsole.flush();
+            mOutputConsole.write(command);
+            mOutputConsole.newLine();
+            mOutputConsole.flush();
+        } catch (IOException | InterruptedException ioe) {
+            throw new CecClientWrapperException(ErrorCodes.WriteConsole, ioe);
+        }
     }
 
     /**
      * Sends a series of <UCP> [firstKeycode] from source to destination through the output console
      * of the cec-communication channel immediately followed by <UCP> [secondKeycode]. No <UCR>
-     *  message is sent.
+     * message is sent.
      */
     public void sendUserControlInterruptedPressAndHold(
-        LogicalAddress source, LogicalAddress destination,
-            int firstKeycode, int secondKeycode, boolean holdKey) throws Exception {
+            LogicalAddress source,
+            LogicalAddress destination,
+            int firstKeycode,
+            int secondKeycode,
+            boolean holdKey)
+            throws CecClientWrapperException {
         sendUserControlPress(source, destination, firstKeycode, holdKey);
-        /* Sleep less than 200ms between press and release */
-        TimeUnit.MILLISECONDS.sleep(100);
+        try {
+            /* Sleep less than 200ms between press and release */
+            TimeUnit.MILLISECONDS.sleep(100);
+        } catch (InterruptedException ie) {
+            throw new CecClientWrapperException(ErrorCodes.WriteConsole, ie);
+        }
         sendUserControlPress(source, destination, secondKeycode, false);
     }
 
     /** Sends a message to the output console of the cec-client */
-    public void sendConsoleMessage(String message) throws Exception {
+    public void sendConsoleMessage(String message) throws CecClientWrapperException {
         checkCecClient();
         CLog.v("Sending message:: " + message);
-        mOutputConsole.write(message);
-        mOutputConsole.flush();
+        try {
+            mOutputConsole.write(message);
+            mOutputConsole.flush();
+        } catch (IOException ioe) {
+            throw new CecClientWrapperException(ErrorCodes.WriteConsole, ioe);
+        }
     }
 
     /** Check for any string on the input console of the cec-client, uses default timeout */
-    public boolean checkConsoleOutput(String expectedMessage) throws Exception {
+    public boolean checkConsoleOutput(String expectedMessage) throws CecClientWrapperException {
         return checkConsoleOutput(expectedMessage, DEFAULT_TIMEOUT);
     }
 
     /** Check for any string on the input console of the cec-client */
-    public boolean checkConsoleOutput(String expectedMessage,
-                                       long timeoutMillis) throws Exception {
+    public boolean checkConsoleOutput(String expectedMessage, long timeoutMillis)
+            throws CecClientWrapperException {
         checkCecClient();
         return checkConsoleOutput(expectedMessage, timeoutMillis, mInputConsole);
     }
@@ -345,29 +392,36 @@
     /** Check for any string on the specified input console */
     public boolean checkConsoleOutput(
             String expectedMessage, long timeoutMillis, BufferedReader inputConsole)
-            throws Exception {
+            throws CecClientWrapperException {
         long startTime = System.currentTimeMillis();
         long endTime = startTime;
 
         while ((endTime - startTime <= timeoutMillis)) {
-            if (inputConsole.ready()) {
-                String line = inputConsole.readLine();
-                if (line != null && line.toLowerCase().contains(expectedMessage)) {
-                    CLog.v("Found " + expectedMessage + " in " + line);
-                    return true;
-                } else if (line.toLowerCase().contains(CEC_PORT_BUSY)) {
-                    throw new CecPortBusyException();
+            try {
+                if (inputConsole.ready()) {
+                    String line = inputConsole.readLine();
+                    if (line != null
+                            && line.toLowerCase().contains(expectedMessage.toLowerCase())) {
+                        CLog.v("Found " + expectedMessage + " in " + line);
+                        return true;
+                    } else if (line.toLowerCase().contains(CEC_PORT_BUSY.toLowerCase())) {
+                        throw new CecClientWrapperException(ErrorCodes.CecPortBusy);
+                    }
                 }
+            } catch (IOException ioe) {
+                throw new CecClientWrapperException(ErrorCodes.ReadConsole, ioe);
             }
             endTime = System.currentTimeMillis();
         }
         return false;
     }
 
-    /** Gets all the messages received from the given source device during a period of duration
+    /**
+     * Gets all the messages received from the given source device during a period of duration
      * seconds.
      */
-    public List<CecOperand> getAllMessages(LogicalAddress source, int duration) throws Exception {
+    public List<CecOperand> getAllMessages(LogicalAddress source, int duration)
+            throws CecClientWrapperException {
         List<CecOperand> receivedOperands = new ArrayList<>();
         long startTime = System.currentTimeMillis();
         long endTime = startTime;
@@ -376,14 +430,18 @@
             Pattern.CASE_INSENSITIVE);
 
         while ((endTime - startTime <= (duration * 1000))) {
-            if (mInputConsole.ready()) {
-                String line = mInputConsole.readLine();
-                if (pattern.matcher(line).matches()) {
-                    CecOperand operand = CecMessage.getOperand(line);
-                    if (!receivedOperands.contains(operand)) {
-                        receivedOperands.add(operand);
+            try {
+                if (mInputConsole.ready()) {
+                    String line = mInputConsole.readLine();
+                    if (pattern.matcher(line).matches()) {
+                        CecOperand operand = CecMessage.getOperand(line);
+                        if (!receivedOperands.contains(operand)) {
+                            receivedOperands.add(operand);
+                        }
                     }
                 }
+            } catch (IOException ioe) {
+                throw new CecClientWrapperException(ErrorCodes.ReadConsole, ioe);
             }
             endTime = System.currentTimeMillis();
         }
@@ -395,7 +453,7 @@
      * during a period of duration seconds.
      */
     public List<LogicalAddress> getAllDestLogicalAddresses(CecOperand expectedMessage, int duration)
-            throws Exception {
+            throws CecClientWrapperException {
         return getAllDestLogicalAddresses(expectedMessage, "", duration);
     }
 
@@ -404,7 +462,8 @@
      * params during a period of duration seconds.
      */
     public List<LogicalAddress> getAllDestLogicalAddresses(
-            CecOperand expectedMessage, String params, int duration) throws Exception {
+            CecOperand expectedMessage, String params, int duration)
+            throws CecClientWrapperException {
         List<LogicalAddress> destinationAddresses = new ArrayList<>();
         long startTime = System.currentTimeMillis();
         long endTime = startTime;
@@ -414,14 +473,18 @@
                         Pattern.CASE_INSENSITIVE);
 
         while ((endTime - startTime <= (duration * 1000))) {
-            if (mInputConsole.ready()) {
-                String line = mInputConsole.readLine();
-                if (pattern.matcher(line).matches()) {
-                    LogicalAddress destination = CecMessage.getDestination(line);
-                    if (!destinationAddresses.contains(destination)) {
-                        destinationAddresses.add(destination);
+            try {
+                if (mInputConsole.ready()) {
+                    String line = mInputConsole.readLine();
+                    if (pattern.matcher(line).matches()) {
+                        LogicalAddress destination = CecMessage.getDestination(line);
+                        if (!destinationAddresses.contains(destination)) {
+                            destinationAddresses.add(destination);
+                        }
                     }
                 }
+            } catch (IOException ioe) {
+                throw new CecClientWrapperException(ErrorCodes.ReadConsole, ioe);
             }
             endTime = System.currentTimeMillis();
         }
@@ -431,30 +494,32 @@
     /**
      * Looks for the CEC expectedMessage broadcast on the cec-client communication channel and
      * returns the first line that contains that message within default timeout. If the CEC message
-     * is not found within the timeout, an exception is thrown.
+     * is not found within the timeout, an CecClientWrapperException is thrown.
      */
-    public String checkExpectedOutput(CecOperand expectedMessage) throws Exception {
+    public String checkExpectedOutput(CecOperand expectedMessage) throws CecClientWrapperException {
         return checkExpectedOutput(
                 targetDevice, LogicalAddress.BROADCAST, expectedMessage, DEFAULT_TIMEOUT, false);
     }
 
     /**
-     * Looks for the CEC expectedMessage sent to CEC device toDevice on the cec-client
-     * communication channel and returns the first line that contains that message within
-     * default timeout. If the CEC message is not found within the timeout, an exception is thrown.
+     * Looks for the CEC expectedMessage sent to CEC device toDevice on the cec-client communication
+     * channel and returns the first line that contains that message within default timeout. If the
+     * CEC message is not found within the timeout, an CecClientWrapperException is thrown.
      */
-    public String checkExpectedOutput(LogicalAddress toDevice,
-                                      CecOperand expectedMessage) throws Exception {
+    public String checkExpectedOutput(LogicalAddress toDevice, CecOperand expectedMessage)
+            throws CecClientWrapperException {
         return checkExpectedOutput(targetDevice, toDevice, expectedMessage, DEFAULT_TIMEOUT, false);
     }
 
     /**
      * Looks for the broadcasted CEC expectedMessage sent from cec-client device fromDevice on the
      * cec-client communication channel and returns the first line that contains that message within
-     * default timeout. If the CEC message is not found within the timeout, an exception is thrown.
+     * default timeout. If the CEC message is not found within the timeout, an
+     * CecClientWrapperException is thrown.
      */
     public String checkExpectedMessageFromClient(
-            LogicalAddress fromDevice, CecOperand expectedMessage) throws Exception {
+            LogicalAddress fromDevice, CecOperand expectedMessage)
+            throws CecClientWrapperException {
         return checkExpectedMessageFromClient(
                 fromDevice, LogicalAddress.BROADCAST, expectedMessage);
     }
@@ -463,41 +528,42 @@
      * Looks for the CEC expectedMessage sent from cec-client device fromDevice to CEC device
      * toDevice on the cec-client communication channel and returns the first line that contains
      * that message within default timeout. If the CEC message is not found within the timeout, an
-     * exception is thrown.
+     * CecClientWrapperException is thrown.
      */
     public String checkExpectedMessageFromClient(
             LogicalAddress fromDevice, LogicalAddress toDevice, CecOperand expectedMessage)
-            throws Exception {
+            throws CecClientWrapperException {
         return checkExpectedOutput(fromDevice, toDevice, expectedMessage, DEFAULT_TIMEOUT, true);
     }
 
     /**
      * Looks for the CEC expectedMessage broadcast on the cec-client communication channel and
-     * returns the first line that contains that message within timeoutMillis. If the CEC message
-     * is not found within the timeout, an exception is thrown.
+     * returns the first line that contains that message within timeoutMillis. If the CEC message is
+     * not found within the timeout, an CecClientWrapperException is thrown.
      */
-    public String checkExpectedOutput(CecOperand expectedMessage,
-                                      long timeoutMillis) throws Exception {
+    public String checkExpectedOutput(CecOperand expectedMessage, long timeoutMillis)
+            throws CecClientWrapperException {
         return checkExpectedOutput(
                 targetDevice, LogicalAddress.BROADCAST, expectedMessage, timeoutMillis, false);
     }
 
     /**
-     * Looks for the CEC expectedMessage sent to CEC device toDevice on the cec-client
-     * communication channel and returns the first line that contains that message within
-     * timeoutMillis. If the CEC message is not found within the timeout, an exception is thrown.
+     * Looks for the CEC expectedMessage sent to CEC device toDevice on the cec-client communication
+     * channel and returns the first line that contains that message within timeoutMillis. If the
+     * CEC message is not found within the timeout, an CecClientWrapperException is thrown.
      */
-    public String checkExpectedOutput(LogicalAddress toDevice, CecOperand expectedMessage,
-                                       long timeoutMillis) throws Exception {
+    public String checkExpectedOutput(
+            LogicalAddress toDevice, CecOperand expectedMessage, long timeoutMillis)
+            throws CecClientWrapperException {
         return checkExpectedOutput(targetDevice, toDevice, expectedMessage, timeoutMillis, false);
     }
 
     /**
      * Looks for the CEC expectedMessage sent from CEC device fromDevice to CEC device toDevice on
      * the cec-client communication channel and returns the first line that contains that message
-     * within timeoutMillis. If the CEC message is not found within the timeout, an exception is
-     * thrown. This method looks for the CEC messages coming from Cec-client if fromCecClient is
-     * true.
+     * within timeoutMillis. If the CEC message is not found within the timeout, an
+     * CecClientWrapperException is thrown. This method looks for the CEC messages coming from
+     * Cec-client if fromCecClient is true.
      */
     public String checkExpectedOutput(
             LogicalAddress fromDevice,
@@ -505,7 +571,7 @@
             CecOperand expectedMessage,
             long timeoutMillis,
             boolean fromCecClient)
-            throws Exception {
+            throws CecClientWrapperException {
         checkCecClient();
         long startTime = System.currentTimeMillis();
         long endTime = startTime;
@@ -525,61 +591,66 @@
                         Pattern.CASE_INSENSITIVE);
 
         while ((endTime - startTime <= timeoutMillis)) {
-            if (mInputConsole.ready()) {
-                String line = mInputConsole.readLine();
-                if (pattern.matcher(line).matches()) {
-                    CLog.v("Found " + expectedMessage.name() + " in " + line);
-                    return line;
+            try {
+                if (mInputConsole.ready()) {
+                    String line = mInputConsole.readLine();
+                    if (pattern.matcher(line).matches()) {
+                        CLog.v("Found " + expectedMessage.name() + " in " + line);
+                        return line;
+                    }
                 }
+            } catch (IOException ioe) {
+                throw new CecClientWrapperException(ErrorCodes.ReadConsole, ioe);
             }
             endTime = System.currentTimeMillis();
         }
-        throw new Exception("Could not find message " + expectedMessage.name());
+        throw new CecClientWrapperException(ErrorCodes.CecMessageNotFound, expectedMessage.name());
     }
 
     /**
      * Looks for the CEC message incorrectMessage sent to CEC device toDevice on the cec-client
-     * communication channel and throws an exception if it finds the line that contains the message
-     * within the default timeout. If the CEC message is not found within the timeout, function
-     * returns without error.
+     * communication channel and throws an CecClientWrapperException if it finds the line that
+     * contains the message within the default timeout. If the CEC message is not found within the
+     * timeout, function returns without error.
      */
-    public void checkOutputDoesNotContainMessage(LogicalAddress toDevice,
-            CecOperand incorrectMessage) throws Exception {
+    public void checkOutputDoesNotContainMessage(
+            LogicalAddress toDevice, CecOperand incorrectMessage) throws CecClientWrapperException {
         checkOutputDoesNotContainMessage(toDevice, incorrectMessage, "", DEFAULT_TIMEOUT);
      }
 
     /**
      * Looks for the CEC message incorrectMessage along with the params sent to CEC device toDevice
-     * on the cec-client communication channel and throws an exception if it finds the line that
-     * contains the message with its params within the default timeout. If the CEC message is not
-     * found within the timeout, function returns without error.
+     * on the cec-client communication channel and throws an CecClientWrapperException if it finds
+     * the line that contains the message with its params within the default timeout. If the CEC
+     * message is not found within the timeout, function returns without error.
      */
     public void checkOutputDoesNotContainMessage(
-            LogicalAddress toDevice, CecOperand incorrectMessage, String params) throws Exception {
+            LogicalAddress toDevice, CecOperand incorrectMessage, String params)
+            throws CecClientWrapperException {
         checkOutputDoesNotContainMessage(toDevice, incorrectMessage, params, DEFAULT_TIMEOUT);
     }
 
     /**
      * Looks for the CEC message incorrectMessage sent to CEC device toDevice on the cec-client
-     * communication channel and throws an exception if it finds the line that contains the message
-     * within timeoutMillis. If the CEC message is not found within the timeout, function returns
-     * without error.
+     * communication channel and throws an CecClientWrapperException if it finds the line that
+     * contains the message within timeoutMillis. If the CEC message is not found within the
+     * timeout, function returns without error.
      */
     public void checkOutputDoesNotContainMessage(
             LogicalAddress toDevice, CecOperand incorrectMessage, long timeoutMillis)
-            throws Exception {
+            throws CecClientWrapperException {
         checkOutputDoesNotContainMessage(toDevice, incorrectMessage, "", timeoutMillis);
     }
 
     /**
      * Looks for the CEC message incorrectMessage along with the params sent to CEC device toDevice
-     * on the cec-client communication channel and throws an exception if it finds the line that
-     * contains the message and params within timeoutMillis. If the CEC message is not found within
-     * the timeout, function returns without error.
+     * on the cec-client communication channel and throws an CecClientWrapperException if it finds
+     * the line that contains the message and params within timeoutMillis. If the CEC message is not
+     * found within the timeout, function returns without error.
      */
     public void checkOutputDoesNotContainMessage(
             LogicalAddress toDevice, CecOperand incorrectMessage, String params, long timeoutMillis)
-            throws Exception {
+            throws CecClientWrapperException {
         checkCecClient();
         long startTime = System.currentTimeMillis();
         long endTime = startTime;
@@ -597,13 +668,22 @@
                         Pattern.CASE_INSENSITIVE);
 
         while ((endTime - startTime <= timeoutMillis)) {
-            if (mInputConsole.ready()) {
-                String line = mInputConsole.readLine();
-                if (pattern.matcher(line).matches()) {
-                    CLog.v("Found " + incorrectMessage.name() + " in " + line);
-                    throw new Exception("Found " + incorrectMessage.name() + " to " + toDevice +
-                            " with params " + CecMessage.getParamsAsString(line));
+            try {
+                if (mInputConsole.ready()) {
+                    String line = mInputConsole.readLine();
+                    if (pattern.matcher(line).matches()) {
+                        CLog.v("Found " + incorrectMessage.name() + " in " + line);
+                        throw new CecClientWrapperException(
+                                ErrorCodes.CecMessageFound,
+                                incorrectMessage.name()
+                                        + " to "
+                                        + toDevice
+                                        + " with params "
+                                        + CecMessage.getParamsAsString(line));
+                    }
                 }
+            } catch (IOException ioe) {
+                throw new CecClientWrapperException(ErrorCodes.ReadConsole, ioe);
             }
             endTime = System.currentTimeMillis();
         }
@@ -615,7 +695,7 @@
     }
 
     /** Set the physical address of the cec-client instance */
-    public void setPhysicalAddress(int newPhysicalAddress) throws Exception {
+    public void setPhysicalAddress(int newPhysicalAddress) throws CecClientWrapperException {
         String command =
                 String.format(
                         "pa %02d %02d",
@@ -650,11 +730,11 @@
                 killProcess = RunUtil.getDefault().runCmdInBackground(commands);
                 killProcess.waitFor();
             }
-        } catch (Exception e) {
-            /* If cec-client is not running, do not throw an exception, just return. */
-            CLog.w(new Exception("Unable to close cec-client", e));
+        } catch (IOException | InterruptedException | CecClientWrapperException e) {
+            /*
+             * If cec-client is not running, do not throw a CecClientWrapperException, just return.
+             */
+            CLog.w(new CecClientWrapperException(ErrorCodes.CecClientStop, e));
         }
     }
-
-    public class CecPortBusyException extends Exception {}
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
index a97b478..2a5b4b7 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
@@ -49,6 +49,7 @@
     public static final int CEC_DEVICE_TYPE_TUNER = 3;
     public static final int CEC_DEVICE_TYPE_PLAYBACK_DEVICE = 4;
     public static final int CEC_DEVICE_TYPE_AUDIO_SYSTEM = 5;
+    public static final int CEC_DEVICE_TYPE_SWITCH = 6;
 
     /** Feature Abort Reasons */
     public static final int ABORT_UNRECOGNIZED_MODE = 0;
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java
index a8e43d3..7814e78 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecAudioReturnChannelControlTest.java
@@ -16,16 +16,14 @@
 
 package android.hdmicec.cts.audio;
 
-import static org.junit.Assume.assumeNoException;
 
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
 import android.hdmicec.cts.CecMessage;
 import android.hdmicec.cts.CecOperand;
-import android.hdmicec.cts.HdmiCecClientWrapper;
+import android.hdmicec.cts.error.CecClientWrapperException;
+import android.hdmicec.cts.error.ErrorCodes;
 import android.hdmicec.cts.HdmiCecConstants;
 import android.hdmicec.cts.LogicalAddress;
-import android.hdmicec.cts.RequiredPropertyRule;
-import android.hdmicec.cts.RequiredFeatureRule;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
@@ -54,13 +52,15 @@
             .around(CecRules.requiresDeviceType(this, AUDIO_DEVICE))
             .around(hdmiCecClient);
 
-    private void checkArcIsInitiated(){
+    private void checkArcIsInitiated() throws CecClientWrapperException {
         try {
             hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
                     CecOperand.REQUEST_ARC_INITIATION);
             hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
-        } catch(Exception e) {
-            assumeNoException(e);
+        } catch (CecClientWrapperException e) {
+            if (e.getErrorCode() != ErrorCodes.CecMessageNotFound) {
+                throw e;
+            }
         }
     }
 
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java
index 2b43cec..e0bccf4 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/audio/HdmiCecInvalidMessagesTest.java
@@ -16,21 +16,14 @@
 
 package android.hdmicec.cts.audio;
 
-import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assume.assumeNoException;
-import static org.junit.Assume.assumeTrue;
 
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
 import android.hdmicec.cts.CecMessage;
 import android.hdmicec.cts.CecOperand;
-import android.hdmicec.cts.HdmiCecClientWrapper;
-import android.hdmicec.cts.HdmiCecConstants;
-import android.hdmicec.cts.LogHelper;
+import android.hdmicec.cts.error.CecClientWrapperException;
+import android.hdmicec.cts.error.ErrorCodes;
 import android.hdmicec.cts.LogicalAddress;
-import android.hdmicec.cts.RequiredPropertyRule;
-import android.hdmicec.cts.RequiredFeatureRule;
 
-import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 
 import org.junit.Ignore;
@@ -45,7 +38,6 @@
 public final class HdmiCecInvalidMessagesTest extends BaseHdmiCecCtsTest {
 
     private static final LogicalAddress AUDIO_DEVICE = LogicalAddress.AUDIO_SYSTEM;
-    private static final String PROPERTY_LOCALE = "persist.sys.locale";
 
     /** The package name of the APK. */
     private static final String PACKAGE = "android.hdmicec.app";
@@ -72,32 +64,15 @@
             .around(CecRules.requiresDeviceType(this, AUDIO_DEVICE))
             .around(hdmiCecClient);
 
-    private String getSystemLocale() throws Exception {
-        ITestDevice device = getDevice();
-        return device.executeShellCommand("getprop " + PROPERTY_LOCALE).trim();
-    }
-
-    private void setSystemLocale(String locale) throws Exception {
-        ITestDevice device = getDevice();
-        device.executeShellCommand("setprop " + PROPERTY_LOCALE + " " + locale);
-    }
-
-    private boolean isLanguageEditable() throws Exception {
-        String val = getDevice().executeShellCommand("getprop ro.hdmi.set_menu_language");
-        return val.trim().equals("true") ? true : false;
-    }
-
-    private static String extractLanguage(String locale) {
-        return locale.split("[^a-zA-Z]")[0];
-    }
-
-    private void checkArcIsInitiated(){
+    private void checkArcIsInitiated() throws CecClientWrapperException {
         try {
             hdmiCecClient.sendCecMessage(LogicalAddress.TV, AUDIO_DEVICE,
                 CecOperand.REQUEST_ARC_INITIATION);
             hdmiCecClient.checkExpectedOutput(LogicalAddress.TV, CecOperand.INITIATE_ARC);
-        } catch(Exception e) {
-            assumeNoException(e);
+        } catch (CecClientWrapperException e) {
+            if (e.getErrorCode() != ErrorCodes.CecMessageNotFound) {
+                throw e;
+            }
         }
     }
 
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecDeviceTypeTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecDeviceTypeTest.java
index 284605e..548170b 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecDeviceTypeTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecDeviceTypeTest.java
@@ -16,17 +16,21 @@
 
 package android.hdmicec.cts.common;
 
-import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.hdmicec.cts.BaseHdmiCecCtsTest.CecRules;
 import android.hdmicec.cts.HdmiCecConstants;
 
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.RuleChain;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
@@ -39,6 +43,9 @@
 @OptionClass(alias="hdmi-cec-cts-test")
 public final class HdmiCecDeviceTypeTest extends BaseHostJUnit4Test {
 
+    @Rule
+    public RuleChain ruleChain = RuleChain.outerRule(CecRules.requiresLeanback(this));
+
     @Option(name = HdmiCecConstants.PHYSICAL_ADDRESS_NAME,
         description = "HDMI CEC physical address of the DUT",
         mandatory = false)
@@ -48,24 +55,60 @@
      */
     public static int dutPhysicalAddress = HdmiCecConstants.DEFAULT_PHYSICAL_ADDRESS;
 
-    private static List<String> validTypes = new ArrayList<>(
-        Arrays.asList("", "0", "4", "4,5", "5,4"));
+    int deviceTvOnly = setBit(HdmiCecConstants.CEC_DEVICE_TYPE_TV);
+    int devicePlaybackOnly = setBit(HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
+    int deviceComboTvSwitch =
+            setBit(HdmiCecConstants.CEC_DEVICE_TYPE_TV)
+                    | setBit(HdmiCecConstants.CEC_DEVICE_TYPE_SWITCH);
+    int deviceComboPlaybackAudioSystem =
+            setBit(HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
+                    | setBit(HdmiCecConstants.CEC_DEVICE_TYPE_AUDIO_SYSTEM);
+    int deviceComboPlaybackAudioSystemSwitch =
+            setBit(HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
+                    | setBit(HdmiCecConstants.CEC_DEVICE_TYPE_AUDIO_SYSTEM)
+                    | setBit(HdmiCecConstants.CEC_DEVICE_TYPE_SWITCH);
+
+    private final List<Integer> allowedDeviceCombos =
+            new ArrayList<>(
+                    Arrays.asList(
+                            deviceTvOnly,
+                            devicePlaybackOnly,
+                            deviceComboTvSwitch,
+                            deviceComboPlaybackAudioSystem,
+                            deviceComboPlaybackAudioSystemSwitch));
+
     /**
      * Tests that the device declares a valid HDMI CEC device type.
      */
     @Test
     public void checkHdmiCecDeviceType() throws Exception {
-        ITestDevice device = getDevice();
-        String logs = device.executeShellCommand("cmd package list features");
-        Scanner in = new Scanner(logs);
-        while (in.hasNextLine()) {
-            String line = in.nextLine();
-            if (line.equals("feature:android.software.leanback")) {
-                // Remove "" as valid device type if android.software.leanback feature is supported
-                validTypes.remove("");
+        int deviceTypes = getAllDeviceTypes(getDevice());
+
+        assertWithMessage("Incorrect device combination")
+                .that(deviceTypes)
+                .isIn(allowedDeviceCombos);
+    }
+
+    private int getAllDeviceTypes(ITestDevice device) {
+        int deviceTypes = 0;
+        String deviceType = "";
+        try {
+            deviceType = device.executeShellCommand("getprop ro.hdmi.device_type").trim();
+        } catch (DeviceNotAvailableException dnae) {
+            return 0;
+        }
+
+        String[] cecDevices = deviceType.split(",");
+        for (String cecDevice : cecDevices) {
+            if (!cecDevice.equals("")) {
+                deviceTypes |= setBit(Integer.parseInt(cecDevice));
             }
         }
-        String deviceType = device.executeShellCommand("getprop ro.hdmi.device_type");
-        assertThat(deviceType.trim()).isIn(validTypes);
+
+        return deviceTypes;
+    }
+
+    private int setBit(int value) {
+        return (1 << value);
     }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecInvalidMessagesTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecInvalidMessagesTest.java
index b36bcac..8ef4f22 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecInvalidMessagesTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecInvalidMessagesTest.java
@@ -39,8 +39,6 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecInvalidMessagesTest extends BaseHdmiCecCtsTest {
 
-    private static final String PROPERTY_LOCALE = "persist.sys.locale";
-
     /** The package name of the APK. */
     private static final String PACKAGE = "android.hdmicec.app";
 
@@ -64,25 +62,6 @@
                     .around(CecRules.requiresLeanback(this))
                     .around(hdmiCecClient);
 
-    private String getSystemLocale() throws Exception {
-        ITestDevice device = getDevice();
-        return device.executeShellCommand("getprop " + PROPERTY_LOCALE).trim();
-    }
-
-    private void setSystemLocale(String locale) throws Exception {
-        ITestDevice device = getDevice();
-        device.executeShellCommand("setprop " + PROPERTY_LOCALE + " " + locale);
-    }
-
-    private boolean isLanguageEditable() throws Exception {
-        String val = getDevice().executeShellCommand("getprop ro.hdmi.set_menu_language");
-        return val.trim().equals("true") ? true : false;
-    }
-
-    private static String extractLanguage(String locale) {
-        return locale.split("[^a-zA-Z]")[0];
-    }
-
     @Before
     public void setup() {
         source =
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/error/CecClientWrapperException.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/error/CecClientWrapperException.java
new file mode 100644
index 0000000..b8a6d63
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/error/CecClientWrapperException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts.error;
+
+/**
+ * CecClientWrapperException to be thrown when there are any issues related with the usage of {@link
+ * HdmiCecClientWrapper}.
+ */
+public class CecClientWrapperException extends Exception {
+
+    private ErrorCodes errorCode;
+
+    public CecClientWrapperException(ErrorCodes errorCode) {
+        super(errorCode.getExceptionMessage());
+        this.errorCode = errorCode;
+    }
+
+    public CecClientWrapperException(ErrorCodes errorCode, String messageToBeAppend) {
+        super(errorCode.getExceptionMessage() + messageToBeAppend);
+        this.errorCode = errorCode;
+    }
+
+    public CecClientWrapperException(
+            ErrorCodes errorCode, Throwable cause, String messageToBeAppend) {
+        super(errorCode.getExceptionMessage() + messageToBeAppend, cause);
+        this.errorCode = errorCode;
+    }
+
+    public CecClientWrapperException(ErrorCodes errorCode, Throwable cause) {
+        super(errorCode.getExceptionMessage(), cause);
+        this.errorCode = errorCode;
+    }
+
+    public ErrorCodes getErrorCode() {
+        return this.errorCode;
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/error/DumpsysParseException.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/error/DumpsysParseException.java
new file mode 100644
index 0000000..7645ff3
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/error/DumpsysParseException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts.error;
+
+/** DumpsysParseException to be thrown when there are any issues while parsing the dumpsys. */
+public class DumpsysParseException extends Exception {
+
+    public DumpsysParseException(String message) {
+        super(message);
+    }
+
+    public DumpsysParseException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/error/ErrorCodes.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/error/ErrorCodes.java
new file mode 100644
index 0000000..4ade5b7
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/error/ErrorCodes.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts.error;
+
+/** Enum contains the list of possible causes for an {@link CecClientWrapperException}. */
+public enum ErrorCodes {
+    CecMessageNotFound("Could not find CEC message "),
+    CecMessageFound("Found the CEC message "),
+    CecClientStart("Could not start the cec-client process "),
+    CecClientStop("Could not stop the cec-client process "),
+    CecClientNotRunning("Cec-client not running"),
+    CecPortBusy("Cec port busy "),
+    DeviceNotAvailable("Device not found "),
+    ReadConsole("Exception while reading from the console"),
+    WriteConsole("Exception while writing into the console");
+
+    private final String message;
+
+    public String getExceptionMessage() {
+        return this.message;
+    }
+
+    private ErrorCodes(String message) {
+        this.message = message;
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
index 893a58e..58e5bf6 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java
@@ -39,8 +39,6 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public final class HdmiCecSystemInformationTest extends BaseHdmiCecCtsTest {
 
-    private static final String PROPERTY_LOCALE = "persist.sys.locale";
-
     @Rule
     public RuleChain ruleChain =
             RuleChain
@@ -66,25 +64,6 @@
         assertThat(CecOperand.getOperand(abortedOpcode)).isEqualTo(CecOperand.GET_MENU_LANGUAGE);
     }
 
-    private String getSystemLocale() throws Exception {
-        ITestDevice device = getDevice();
-        return device.executeShellCommand("getprop " + PROPERTY_LOCALE).trim();
-    }
-
-    private void setSystemLocale(String locale) throws Exception {
-        ITestDevice device = getDevice();
-        device.executeShellCommand("setprop " + PROPERTY_LOCALE + " " + locale);
-    }
-
-    private boolean isLanguageEditable() throws Exception {
-        String val = getDevice().executeShellCommand("getprop ro.hdmi.set_menu_language");
-        return val.trim().equals("true") ? true : false;
-    }
-
-    private static String extractLanguage(String locale) {
-        return locale.split("[^a-zA-Z]")[0];
-    }
-
     /**
      * Test 11.2.6-3
      * Tests that the device handles a <SET_MENU_LANGUAGE> with a valid language correctly.
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/targetprep/CecPortDiscoverer.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/targetprep/CecPortDiscoverer.java
index d9a3a25..5d49cb9 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/targetprep/CecPortDiscoverer.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/targetprep/CecPortDiscoverer.java
@@ -20,6 +20,8 @@
 import android.hdmicec.cts.CecMessage;
 import android.hdmicec.cts.HdmiCecClientWrapper;
 import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.error.CecClientWrapperException;
+import android.hdmicec.cts.error.ErrorCodes;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
@@ -42,7 +44,7 @@
 /* Sets up the CEC tests by discovering which port the CEC adapter connected to */
 public class CecPortDiscoverer extends BaseTargetPreparer {
 
-    private static final int TIMEOUT_MILLIS = 10000;
+    private static final int TIMEOUT_MILLIS = 15000;
     private static final int MAX_RETRY_COUNT = 3;
 
     private File mCecMapDir = HdmiCecConstants.CEC_MAP_FOLDER;
@@ -87,6 +89,7 @@
         HdmiCecClientWrapper cecClientWrapper = new HdmiCecClientWrapper();
 
         launchCommand.add("cec-client");
+        String serialNo = "";
 
         try {
             List<String> comPorts = cecClientWrapper.getValidCecClientPorts();
@@ -106,7 +109,7 @@
                 launchCommand.add("x");
             }
 
-            String serialNo = device.getProperty("ro.serialno");
+            serialNo = device.getProperty("ro.serialno");
             String serialNoParam = CecMessage.convertStringToHexParams(serialNo);
             /*
              * formatParams prefixes with a ':' that we do not want in the vendorcommand
@@ -131,6 +134,7 @@
                      */
                     if (adapterMapping.exists()) {
                         /* Exit the current port's retry loop */
+                        launchCommand.remove(port);
                         break;
                     }
                     mCecClient = RunUtil.getDefault().runCmdInBackground(launchCommand);
@@ -148,11 +152,18 @@
                                 writeMapping(port, serialNo);
                                 return;
                             }
+                            /* Since it did not find the required message. Check another port */
+                            portBeingRetried = false;
                         } else {
                             CLog.e("Console did not get ready!");
+                            throw new CecClientWrapperException(ErrorCodes.CecPortBusy);
                         }
-                    } catch (HdmiCecClientWrapper.CecPortBusyException cpbe) {
-                        retryCount++;
+                    } catch (CecClientWrapperException cwe) {
+                        if (cwe.getErrorCode() != ErrorCodes.CecPortBusy) {
+                            retryCount = MAX_RETRY_COUNT;
+                        } else {
+                            retryCount++;
+                        }
                         if (retryCount >= MAX_RETRY_COUNT) {
                             /* We have retried enough number of times. Check another port */
                             portBeingRetried = false;
@@ -163,25 +174,31 @@
                     } finally {
                         /* Kill the unwanted cec-client process. */
                         Process killProcess = mCecClient.destroyForcibly();
-                        killProcess.waitFor();
-                        launchCommand.remove(port);
+                        killProcess.waitFor(60, TimeUnit.SECONDS);
                     }
                 } while (portBeingRetried);
+                launchCommand.remove(port);
             }
         } catch (IOException | InterruptedException e) {
             throw new TargetSetupError(
                     "Caught "
                             + e.getClass().getSimpleName()
                             + ". "
-                            + "Could not get adapter mapping.");
+                            + "Could not get adapter mapping for device"
+                            + serialNo
+                            + ".",
+                    e);
         } catch (Exception generic) {
             throw new TargetSetupError(
                     "Caught an exception with message '"
                             + generic.getMessage()
                             + "'. "
-                            + "Could not get adapter mapping.");
+                            + "Could not get adapter mapping for device"
+                            + serialNo
+                            + ".",
+                    generic);
         }
-        throw new TargetSetupError("Device not connected to any adapter!");
+        throw new TargetSetupError("Device " + serialNo + " not connected to any adapter!");
     }
 
     private String getPortFilename(String port) {
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecAudioReturnChannelControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecAudioReturnChannelControlTest.java
new file mode 100644
index 0000000..017c7c3
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecAudioReturnChannelControlTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts.tv;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.hdmicec.cts.BaseHdmiCecCtsTest;
+import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.HdmiCecConstants;
+import android.hdmicec.cts.LogicalAddress;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+/** HDMI CEC test to test audio return channel control (Section 11.2.17) */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class HdmiCecAudioReturnChannelControlTest extends BaseHdmiCecCtsTest {
+
+    private static final LogicalAddress TV_DEVICE = LogicalAddress.TV;
+
+    public HdmiCecAudioReturnChannelControlTest() {
+        super(TV_DEVICE, "-t", "a");
+    }
+
+    @Rule
+    public RuleChain ruleChain =
+            RuleChain.outerRule(CecRules.requiresCec(this))
+                    .around(CecRules.requiresLeanback(this))
+                    .around(CecRules.requiresDeviceType(this, TV_DEVICE))
+                    .around(hdmiCecClient);
+
+    /**
+     * Test 11.1.17-1
+     *
+     * <p>Tests that the DUT sends a directly addressed {@code <Request ARC Initiation>} message.
+     */
+    @Ignore("b/187168483")
+    @Test
+    public void cect_11_1_17_1_DutSendsRequestArcInitiation() throws Exception {
+        // Ensure that ARC is off.
+        changeArcState(false);
+        hdmiCecClient.broadcastReportPhysicalAddress(LogicalAddress.AUDIO_SYSTEM);
+        assertWithMessage("DUT does not send a <Request ARC Initiation> message.")
+                .that(changeArcState(true))
+                .isTrue();
+    }
+
+    /**
+     * Test 11.1.17-2
+     *
+     * <p>Tests that the DUT responds with a directly addressed {@code <Report ARC initiated>}
+     * message to the Audio System when ARC is initiated.
+     */
+    @Ignore("b/174813656")
+    @Test
+    public void cect_11_1_17_2_ReportArcInitiated() throws Exception {
+        String params =
+                String.format(
+                        "%04d%02d",
+                        hdmiCecClient.getPhysicalAddress(),
+                        HdmiCecConstants.CEC_DEVICE_TYPE_AUDIO_SYSTEM);
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.AUDIO_SYSTEM,
+                LogicalAddress.BROADCAST,
+                CecOperand.REPORT_PHYSICAL_ADDRESS,
+                CecMessage.formatParams(params));
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.AUDIO_SYSTEM, LogicalAddress.TV, CecOperand.INITIATE_ARC);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM, CecOperand.ARC_INITIATED);
+    }
+
+    /**
+     * Test 11.1.17-3
+     *
+     * <p>Tests that the DUT sends a directly addressed {@code <Request ARC Termination>} message.
+     */
+    @Ignore("b/187168483")
+    @Test
+    public void cect_11_1_17_3_DutSendsRequestArcTermination() throws Exception {
+        // Ensure that ARC is on.
+        changeArcState(true);
+        hdmiCecClient.broadcastReportPhysicalAddress(LogicalAddress.AUDIO_SYSTEM);
+        assertWithMessage("DUT does not send a <Request ARC Termination> message.")
+                .that(changeArcState(false))
+                .isTrue();
+    }
+
+    /**
+     * Test 11.1.17-2,4
+     *
+     * <p>Tests that the DUT responds with a directly addressed {@code <Report ARC terminated>}
+     * message to the Audio System when ARC is terminated.
+     */
+    @Ignore("b/174813656")
+    @Test
+    public void cect_11_1_17_2_4_ReportArcInitiatedTerminated() throws Exception {
+        /* We need to initiate ARC, so call the Initiate ARC test first */
+        cect_11_1_17_2_ReportArcInitiated();
+        hdmiCecClient.sendCecMessage(
+                LogicalAddress.AUDIO_SYSTEM, LogicalAddress.TV, CecOperand.TERMINATE_ARC);
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.AUDIO_SYSTEM, CecOperand.ARC_TERMINATED);
+    }
+
+    /**
+     * Test 11.1.17-5
+     *
+     * <p>Tests that the DUT does not respond with any directly addressed {@code <Report ARC
+     * initiated>} message to a non-adjacent device
+     */
+    @Test
+    public void cect_11_1_17_5_NonAdjacentDeviceArcInitiation() throws Exception {
+        int originalPhyAdd = hdmiCecClient.getPhysicalAddress();
+        try {
+            int nonAdjacentPhyAdd = 0x1100;
+            String params =
+                    CecMessage.formatParams(nonAdjacentPhyAdd)
+                            + CecMessage.formatParams(
+                                    HdmiCecConstants.CEC_DEVICE_TYPE_AUDIO_SYSTEM);
+            /* Take physical address 1.1.0.0 */
+            hdmiCecClient.setPhysicalAddress(nonAdjacentPhyAdd);
+            hdmiCecClient.sendCecMessage(
+                    LogicalAddress.AUDIO_SYSTEM,
+                    LogicalAddress.BROADCAST,
+                    CecOperand.REPORT_PHYSICAL_ADDRESS,
+                    params);
+            hdmiCecClient.sendCecMessage(
+                    LogicalAddress.AUDIO_SYSTEM, LogicalAddress.TV, CecOperand.INITIATE_ARC);
+            hdmiCecClient.checkOutputDoesNotContainMessage(
+                    LogicalAddress.AUDIO_SYSTEM, CecOperand.ARC_INITIATED);
+        } finally {
+            /* Restore physical address */
+            hdmiCecClient.setPhysicalAddress(originalPhyAdd);
+        }
+    }
+
+    /**
+     * This method will turn on/off the ARC and ensure that it is processed successfully.
+     *
+     * @param enabled boolean value. Value true to turn ARC on.
+     * @return {@code true} if ARC process was successful.
+     */
+    private boolean changeArcState(boolean enabled) throws Exception {
+        getDevice().executeShellCommand("cmd hdmi_control setarc " + (enabled ? "on" : "off"));
+        try {
+            hdmiCecClient.checkExpectedOutput(
+                    LogicalAddress.AUDIO_SYSTEM,
+                    enabled
+                            ? CecOperand.REQUEST_ARC_INITIATION
+                            : CecOperand.REQUEST_ARC_TERMINATION);
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java
index ad4dc5f..647f904 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRemoteControlPassThroughTest.java
@@ -22,6 +22,8 @@
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
 import android.hdmicec.cts.CecMessage;
 import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.error.CecClientWrapperException;
+import android.hdmicec.cts.error.ErrorCodes;
 import android.hdmicec.cts.HdmiCecConstants;
 import android.hdmicec.cts.LogicalAddress;
 
@@ -58,7 +60,7 @@
     }
 
     @Before
-    public void checkForInitialActiveSourceMessage() throws Exception {
+    public void checkForInitialActiveSourceMessage() throws CecClientWrapperException {
         try {
             /*
              * Check for the broadcasted <ACTIVE_SOURCE> message from Recorder_1, which was sent as
@@ -67,14 +69,18 @@
             String message =
                     hdmiCecClient.checkExpectedMessageFromClient(
                             LogicalAddress.RECORDER_1, CecOperand.ACTIVE_SOURCE);
-        } catch (Exception e) {
-            /*
-             * In case the TV does not send <Set Stream Path> to CEC adapter, or the client does
-             * not make recorder active source, broadcast an <Active Source> message from the
-             * adapter.
-             */
-            hdmiCecClient.broadcastActiveSource(
-                    LogicalAddress.RECORDER_1, hdmiCecClient.getPhysicalAddress());
+        } catch (CecClientWrapperException e) {
+            if (e.getErrorCode() != ErrorCodes.CecMessageNotFound) {
+                throw e;
+            } else {
+                /*
+                 * In case the TV does not send <Set Stream Path> to CEC adapter, or the client does
+                 * not make recorder active source, broadcast an <Active Source> message from the
+                 * adapter.
+                 */
+                hdmiCecClient.broadcastActiveSource(
+                        LogicalAddress.RECORDER_1, hdmiCecClient.getPhysicalAddress());
+            }
         }
     }
 
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java
index a3c803d..665c7b1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecRoutingControlTest.java
@@ -21,6 +21,8 @@
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
 import android.hdmicec.cts.CecMessage;
 import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.error.CecClientWrapperException;
+import android.hdmicec.cts.error.ErrorCodes;
 import android.hdmicec.cts.HdmiControlManagerUtility;
 import android.hdmicec.cts.LogicalAddress;
 
@@ -50,7 +52,7 @@
     }
 
     @Before
-    public void checkForInitialActiveSourceMessage() throws Exception {
+    public void checkForInitialActiveSourceMessage() throws CecClientWrapperException {
         try {
             /*
              * Check for the broadcasted <ACTIVE_SOURCE> message from Recorder_1, which was sent as
@@ -59,14 +61,18 @@
             String message =
                     hdmiCecClient.checkExpectedMessageFromClient(
                             LogicalAddress.RECORDER_1, CecOperand.ACTIVE_SOURCE);
-        } catch (Exception e) {
-            /*
-             * In case the TV does not send <Set Stream Path> to CEC adapter, or the client does
-             * not make recorder active source, broadcast an <Active Source> message from the
-             * adapter.
-             */
-            hdmiCecClient.broadcastActiveSource(
-                    hdmiCecClient.getSelfDevice(), hdmiCecClient.getPhysicalAddress());
+        } catch (CecClientWrapperException e) {
+            if (e.getErrorCode() != ErrorCodes.CecMessageNotFound) {
+                throw e;
+            } else {
+                /*
+                 * In case the TV does not send <Set Stream Path> to CEC adapter, or the client does
+                 * not make recorder active source, broadcast an <Active Source> message from the
+                 * adapter.
+                 */
+                hdmiCecClient.broadcastActiveSource(
+                        hdmiCecClient.getSelfDevice(), hdmiCecClient.getPhysicalAddress());
+            }
         }
     }
 
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemAudioControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemAudioControlTest.java
index 4a4bf92..ca5a050 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemAudioControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemAudioControlTest.java
@@ -47,7 +47,50 @@
                     .around(hdmiCecClient);
 
     public HdmiCecSystemAudioControlTest() {
-        super(LogicalAddress.TV, "-t", "a");
+        super(LogicalAddress.TV, "-t", "a", "-t", "r");
+    }
+
+    /**
+     * Test 11.1.15-1
+     *
+     * <p>Tests that the DUT sends a correctly formatted {@code <System Audio Mode Request>}
+     * message.
+     */
+    @Test
+    public void cect_11_1_15_1_DutSendsSystemAudioModeRequest() throws Exception {
+        hdmiCecClient.broadcastReportPhysicalAddress(LogicalAddress.AUDIO_SYSTEM);
+        hdmiCecClient.broadcastReportPhysicalAddress(
+                LogicalAddress.RECORDER_1, hdmiCecClient.getPhysicalAddress());
+        hdmiCecClient.sendCecMessage(LogicalAddress.RECORDER_1, CecOperand.IMAGE_VIEW_ON);
+        hdmiCecClient.broadcastActiveSource(
+                LogicalAddress.RECORDER_1, hdmiCecClient.getPhysicalAddress());
+        /*
+         * Invoke the DUT to turn on the system audio mode and check for system audio mode request
+         * message with params.
+         */
+        assertWithMessage("Device did not send a <System Audio Mode Request> message with params")
+                .that(setSystemAudioMode(true))
+                .isTrue();
+    }
+
+    /**
+     * Test 11.1.15-5
+     *
+     * <p>Tests that the DUT sends a correctly formatted {@code <System Audio Mode Request>} message
+     * when the DUT invokes the System Audio Mode to be Off.
+     */
+    @Test
+    public void cect_11_1_15_5_DutResponseWhenSystemAudioModeToOff() throws Exception {
+        // Ensure that system audio mode is on.
+        setSystemAudioMode(true);
+        /*
+         * Invoke the DUT to turn off the system audio mode and check for system audio mode request
+         * message with no params.
+         */
+        assertWithMessage(
+                        "Device did not send a <System Audio Mode Request> message with no params")
+                .that(setSystemAudioMode(false))
+                .isTrue();
     }
 
     /**
@@ -105,4 +148,21 @@
                 .that(AudioManagerHelper.isDeviceMuted(getDevice()))
                 .isFalse();
     }
+
+    private boolean setSystemAudioMode(boolean enabled) throws Exception {
+        getDevice().executeShellCommand("cmd hdmi_control setsam " + (enabled ? "on" : "off"));
+        try {
+            String message =
+                    hdmiCecClient.checkExpectedOutput(
+                            LogicalAddress.AUDIO_SYSTEM, CecOperand.SYSTEM_AUDIO_MODE_REQUEST);
+            /*
+             * When system audio mode is turned off. DUT should send <System Audio Mode Request>
+             * message with no params. And when it is turned on, DUT should send the same message
+             * with params.
+             */
+            return enabled ^ CecMessage.getParamsAsString(message).equals("");
+        } catch (Exception e) {
+            return false;
+        }
+    }
 }
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemInformationTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemInformationTest.java
new file mode 100644
index 0000000..777c5ca
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecSystemInformationTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hdmicec.cts.tv;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hdmicec.cts.BaseHdmiCecCtsTest;
+import android.hdmicec.cts.CecMessage;
+import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.LogicalAddress;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+/** HDMI CEC system information tests (Section 11.1.6) */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class HdmiCecSystemInformationTest extends BaseHdmiCecCtsTest {
+
+    @Rule
+    public RuleChain ruleChain =
+            RuleChain.outerRule(CecRules.requiresCec(this))
+                    .around(CecRules.requiresLeanback(this))
+                    .around(CecRules.requiresDeviceType(this, LogicalAddress.TV))
+                    .around(hdmiCecClient);
+
+    public HdmiCecSystemInformationTest() {
+        super(LogicalAddress.TV);
+    }
+
+    /**
+     * Test 11.1.6-5
+     *
+     * <p>Tests that the device responds correctly to a {@code <Get Menu Language>} message coming
+     * from various logical addresses (1, 3, 4, 5, 13, 14 and 15).
+     */
+    @Test
+    public void cect_11_1_6_5_DutRespondsToGetMenuLanguage() throws Exception {
+        final String tvLanguage = new Locale(extractLanguage(getSystemLocale())).getISO3Language();
+        String message;
+        LogicalAddress sources[] = {
+            LogicalAddress.RECORDER_1,
+            LogicalAddress.TUNER_1,
+            LogicalAddress.PLAYBACK_1,
+            LogicalAddress.AUDIO_SYSTEM,
+            LogicalAddress.RESERVED_2,
+            LogicalAddress.SPECIFIC_USE,
+            LogicalAddress.BROADCAST
+        };
+        for (LogicalAddress source : sources) {
+            hdmiCecClient.sendCecMessage(source, CecOperand.GET_MENU_LANGUAGE);
+            message = hdmiCecClient.checkExpectedOutput(CecOperand.SET_MENU_LANGUAGE);
+            assertThat(CecMessage.getAsciiString(message)).isEqualTo(tvLanguage);
+        }
+    }
+}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java
index ee6fafa..fef8b62 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/tv/HdmiCecTvOneTouchPlayTest.java
@@ -18,6 +18,7 @@
 
 import android.hdmicec.cts.BaseHdmiCecCtsTest;
 import android.hdmicec.cts.CecOperand;
+import android.hdmicec.cts.HdmiControlManagerUtility;
 import android.hdmicec.cts.LogicalAddress;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -94,4 +95,36 @@
                     .isEqualTo(testDevice);
         }
     }
+
+    /**
+     * Test 11.1.1-5
+     *
+     * <p>Tests that the DUT broadcasts an {@code <Active Source>} message when changing to an
+     * internal source from previously displaying an external source.
+     */
+    @Test
+    public void cect_11_1_1_5_DutBroadcastsActiveSourceWhenChangingToInternal() throws Exception {
+        // Ensure that an external source is the active source.
+        try {
+            /*
+             * Check for the broadcasted <ACTIVE_SOURCE> message from Recorder_1, which was sent as
+             * a response to <SET_STREAM_PATH> message from the TV.
+             */
+            String message =
+                    hdmiCecClient.checkExpectedMessageFromClient(
+                            LogicalAddress.RECORDER_1, CecOperand.ACTIVE_SOURCE);
+        } catch (Exception e) {
+            /*
+             * In case the TV does not send <Set Stream Path> to CEC adapter, or the client does
+             * not make recorder active source, broadcast an <Active Source> message from the
+             * adapter.
+             */
+            hdmiCecClient.broadcastActiveSource(
+                    LogicalAddress.RECORDER_1, hdmiCecClient.getPhysicalAddress());
+        }
+        // Make the TV device the active source.
+        HdmiControlManagerUtility.setActiveSource(
+                getDevice(), LogicalAddress.TV.getLogicalAddressAsInt());
+        hdmiCecClient.checkExpectedOutput(LogicalAddress.BROADCAST, CecOperand.ACTIVE_SOURCE);
+    }
 }
diff --git a/hostsidetests/incident/apps/batterystatsapp/Android.bp b/hostsidetests/incident/apps/batterystatsapp/Android.bp
index 96f6c37..cb3e3b7 100644
--- a/hostsidetests/incident/apps/batterystatsapp/Android.bp
+++ b/hostsidetests/incident/apps/batterystatsapp/Android.bp
@@ -29,6 +29,7 @@
         "ctstestrunner-axt",
         "compatibility-device-util-axt",
         "androidx.legacy_legacy-support-v4",
+        "testng", // for org.test.* support
     ],
     sdk_version: "test_current",
     // tag this module as a cts test artifact
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsRadioPowerStateTest.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsRadioPowerStateTest.java
new file mode 100644
index 0000000..d384852
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsRadioPowerStateTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.cts.device.batterystats;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.testng.Assert.assertThrows;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.os.BatteryStatsManager;
+import android.os.SystemClock;
+import android.system.Os;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryStatsRadioPowerStateTest {
+    private static final int UID = Os.getuid();
+    private static final String PATTERN_MOBILE = "modem-data";
+    private static final String PATTERN_WIFI = "wifi-data";
+    private BatteryStatsManager mBsm;
+    private ConnectivityManager mCm;
+    private Instrumentation mInstrumentation;
+    private Context mContext;
+    private UiAutomation mUiAutomation;
+
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getContext();
+        mUiAutomation =  mInstrumentation.getUiAutomation();
+        mBsm = mContext.getSystemService(BatteryStatsManager.class);
+        mCm = mContext.getSystemService(ConnectivityManager.class);
+    }
+
+    @Test
+    public void testReportMobileRadioPowerState() throws Exception {
+        // Expect fail w/o UPDATE_DEVICE_STATS permission.
+        assertThrows(SecurityException.class, () -> mBsm.reportMobileRadioPowerState(true, UID));
+
+        mUiAutomation.adoptShellPermissionIdentity();
+        final NetworkCapabilities activeNc = mCm.getNetworkCapabilities(mCm.getActiveNetwork());
+        final boolean expectedCurrentMobileRadio = (activeNc == null) ? false :
+                activeNc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+        try {
+            // Skip other information before testing to reduce the information contained in the dump
+            // result.
+            final long time = SystemClock.elapsedRealtime();
+            final String cmd = "dumpsys batterystats --history-start " + time;
+            // Mobile radio power stats only updates when:
+            // 1. the radio state is changed, and
+            // 2. the radio power is inactive.
+            // Thus, trigger twice with different radio power state to ensure that it updates.
+            mBsm.reportMobileRadioPowerState(!expectedCurrentMobileRadio, UID);
+            mBsm.reportMobileRadioPowerState(expectedCurrentMobileRadio, UID);
+            final String dump = runShellCommand(mInstrumentation, cmd);
+            assertTrue(Pattern.compile(PATTERN_MOBILE).matcher(dump).find());
+        } finally {
+            // Restore state
+            mBsm.reportMobileRadioPowerState(expectedCurrentMobileRadio, UID);
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    @Test
+    public void testReportWifiRadioPowerState() throws Exception {
+        // Expect fail w/o UPDATE_DEVICE_STATS permission.
+        assertThrows(SecurityException.class, () -> mBsm.reportWifiRadioPowerState(true, UID));
+
+        mUiAutomation.adoptShellPermissionIdentity();
+        final NetworkCapabilities activeNc = mCm.getNetworkCapabilities(mCm.getActiveNetwork());
+        final boolean expectedCurrentWifiRadio = (activeNc == null) ? false :
+                activeNc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+        try {
+            // Skip other information before testing to reduce the information contained in the dump
+            // result.
+            final long time = SystemClock.elapsedRealtime();
+            final String cmd = "dumpsys batterystats --history-start " + time;
+            mBsm.reportWifiRadioPowerState(!expectedCurrentWifiRadio, UID);
+            final String dump = runShellCommand(mInstrumentation, cmd);
+            assertTrue(Pattern.compile(PATTERN_WIFI).matcher(dump).find());
+        } finally {
+            // Restore state
+            mBsm.reportWifiRadioPowerState(expectedCurrentWifiRadio, UID);
+            mUiAutomation.dropShellPermissionIdentity();
+        }
+    }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index ce6f04e..4123ad8 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -254,6 +254,21 @@
         batteryOffScreenOn();
     }
 
+    public void testReportRadioPowerState() throws Exception {
+        // Simulate usb unplugged.
+        batteryOnScreenOff();
+
+        installPackage(DEVICE_SIDE_TEST_APK, true);
+        allowImmediateSyncs();
+
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsRadioPowerStateTest",
+                "testReportMobileRadioPowerState");
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsRadioPowerStateTest",
+                "testReportWifiRadioPowerState");
+
+        batteryOffScreenOn();
+    }
+
     private int getUid() throws Exception {
         String uidLine = getDevice().executeShellCommand("cmd package list packages -U "
                 + DEVICE_SIDE_TEST_PACKAGE);
diff --git a/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
index 3c807fa..27f2c76 100644
--- a/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
@@ -19,6 +19,7 @@
 import android.app.job.StopReasonEnum;
 import android.net.NetworkCapabilitiesProto;
 import android.net.NetworkRequestProto;
+import android.net.Transport;
 import com.android.server.job.ConstantsProto;
 import com.android.server.job.ConstraintEnum;
 import com.android.server.job.DataSetProto;
@@ -228,8 +229,8 @@
     private static void testNetworkCapabilitesProto(NetworkCapabilitiesProto nc) throws Exception {
         assertNotNull(nc);
 
-        for (NetworkCapabilitiesProto.Transport t : nc.getTransportsList()) {
-            assertTrue(NetworkCapabilitiesProto.Transport.getDescriptor().getValues()
+        for (Transport t : nc.getTransportsList()) {
+            assertTrue(Transport.getDescriptor().getValues()
                 .contains(t.getValueDescriptor()));
         }
         for (NetworkCapabilitiesProto.NetCapability c : nc.getCapabilitiesList()) {
diff --git a/hostsidetests/incrementalinstall/OWNERS b/hostsidetests/incrementalinstall/OWNERS
index d16fb6c..49bed85 100644
--- a/hostsidetests/incrementalinstall/OWNERS
+++ b/hostsidetests/incrementalinstall/OWNERS
@@ -3,4 +3,5 @@
 alexbuy@google.com
 schfan@google.com
 toddke@google.com
+patb@google.com
 zyy@google.com
\ No newline at end of file
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
index 2d95ca2..33f0986 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
@@ -71,6 +71,8 @@
 @RunWith(AndroidJUnit4.class)
 public class InputMethodServiceDeviceTest {
 
+    private static final String FEATURE_WATCH = "android.hardware.type.watch";
+
     private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(20);
 
     /** Test to check CtsInputMethod1 receives onCreate and onStartInput. */
@@ -324,6 +326,17 @@
 
         InputMethodVisibilityVerifier.assertIme2Visible(TIMEOUT);
 
+        if (helper.shell("pm list features").contains(FEATURE_WATCH)) {
+            // Skip if running on Wear devices because those devices will go through an activity
+            // change during screen off/on cycle due to Ambient mode. Activity change will cause
+            // EditText to retain focus but not show IME.
+            // We also do not use "assumeFalse" because this test is executed as part of
+            // host-side tests, and throwing AssumptionViolatedException would not give us
+            // better understanding about what is happening.
+            // TODO(b/188662291): Remove this while introducing a dedicated test for b/b/160391516.
+            return;
+        }
+
         // Make sure the IME switch UI still works after device screen off / on with focusing
         // same Editor.
         turnScreenOff(helper);
diff --git a/hostsidetests/jvmti/allocation-tracking/Android.bp b/hostsidetests/jvmti/allocation-tracking/Android.bp
index b74f19a60..07956a3 100644
--- a/hostsidetests/jvmti/allocation-tracking/Android.bp
+++ b/hostsidetests/jvmti/allocation-tracking/Android.bp
@@ -24,4 +24,7 @@
         "cts",
         "general-tests",
     ],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/attaching/Android.bp b/hostsidetests/jvmti/attaching/Android.bp
index 0147b329..3e6a692 100644
--- a/hostsidetests/jvmti/attaching/Android.bp
+++ b/hostsidetests/jvmti/attaching/Android.bp
@@ -89,4 +89,7 @@
     ],
     test_config: "host/AndroidTest.xml",
     data: [":CtsJvmtiAttachingDeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java b/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java
index 0fee0ec..cecb6ed 100644
--- a/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java
+++ b/hostsidetests/jvmti/base/host/src/android/jvmti/cts/JvmtiHostTest.java
@@ -77,9 +77,17 @@
         mAbi = arg0;
     }
 
+    // Constant returned to indicate get-current-user failed. See comment at/near
+    // https://cs.android.com/android/_/android/platform/tools/tradefederation/+/android11-release:device_build_interfaces/com/android/tradefed/device/ITestDevice.java;l=780
+    private static final int GET_USER_FAILURE = -10000;
+
+    // Try getting current user and throw an exception immediately if we fail.
     @Override
     protected void setUp() throws Exception {
         mCurrentUser = getDevice().getCurrentUser();
+        if (mCurrentUser == GET_USER_FAILURE) {
+            throw new RuntimeException("am get-current-user failed!");
+        }
     }
 
     public void testJvmti() throws Exception {
diff --git a/hostsidetests/jvmti/redefining/Android.bp b/hostsidetests/jvmti/redefining/Android.bp
index 4816fd8..b8c1f63 100644
--- a/hostsidetests/jvmti/redefining/Android.bp
+++ b/hostsidetests/jvmti/redefining/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRedefineClassesDeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1900/Android.bp b/hostsidetests/jvmti/run-tests/test-1900/Android.bp
index 4b6326f..741785b 100644
--- a/hostsidetests/jvmti/run-tests/test-1900/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1900/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1900DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1901/Android.bp b/hostsidetests/jvmti/run-tests/test-1901/Android.bp
index 928ae4f..88b2f5e 100644
--- a/hostsidetests/jvmti/run-tests/test-1901/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1901/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1901DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1902/Android.bp b/hostsidetests/jvmti/run-tests/test-1902/Android.bp
index a2a4e8d..7c9b6f1 100644
--- a/hostsidetests/jvmti/run-tests/test-1902/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1902/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1902DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1903/Android.bp b/hostsidetests/jvmti/run-tests/test-1903/Android.bp
index f583f8e..17f981f 100644
--- a/hostsidetests/jvmti/run-tests/test-1903/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1903/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1903DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1904/Android.bp b/hostsidetests/jvmti/run-tests/test-1904/Android.bp
index 71de778..a45f2f6 100644
--- a/hostsidetests/jvmti/run-tests/test-1904/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1904/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1904DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1906/Android.bp b/hostsidetests/jvmti/run-tests/test-1906/Android.bp
index 9665afd..b95c443 100644
--- a/hostsidetests/jvmti/run-tests/test-1906/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1906/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1906DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1907/Android.bp b/hostsidetests/jvmti/run-tests/test-1907/Android.bp
index 960f194..751e1ab 100644
--- a/hostsidetests/jvmti/run-tests/test-1907/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1907/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1907DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1908/Android.bp b/hostsidetests/jvmti/run-tests/test-1908/Android.bp
index 5d6dcbf..8d4ac4c 100644
--- a/hostsidetests/jvmti/run-tests/test-1908/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1908/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1908DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1909/Android.bp b/hostsidetests/jvmti/run-tests/test-1909/Android.bp
index 049ef1d..c12f5d4 100644
--- a/hostsidetests/jvmti/run-tests/test-1909/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1909/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1909DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1910/Android.bp b/hostsidetests/jvmti/run-tests/test-1910/Android.bp
index 9165c27..0fb1630 100644
--- a/hostsidetests/jvmti/run-tests/test-1910/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1910/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1910DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1911/Android.bp b/hostsidetests/jvmti/run-tests/test-1911/Android.bp
index 609793d..fe5cf2a 100644
--- a/hostsidetests/jvmti/run-tests/test-1911/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1911/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1911DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1912/Android.bp b/hostsidetests/jvmti/run-tests/test-1912/Android.bp
index 903e43d..23bf2ce 100644
--- a/hostsidetests/jvmti/run-tests/test-1912/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1912/Android.bp
@@ -24,5 +24,8 @@
         "cts",
         "general-tests",
     ],
-    data: [":CtsJvmtiRunTest1912DeviceApp"]
-}
+    data: [":CtsJvmtiRunTest1912DeviceApp"],
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1913/Android.bp b/hostsidetests/jvmti/run-tests/test-1913/Android.bp
index 96c6368..0fe2d08 100644
--- a/hostsidetests/jvmti/run-tests/test-1913/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1913/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1913DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1914/Android.bp b/hostsidetests/jvmti/run-tests/test-1914/Android.bp
index f5ff8cc..7c0de98 100644
--- a/hostsidetests/jvmti/run-tests/test-1914/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1914/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1914DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1915/Android.bp b/hostsidetests/jvmti/run-tests/test-1915/Android.bp
index 49ee753..245fa3f 100644
--- a/hostsidetests/jvmti/run-tests/test-1915/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1915/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1915DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1916/Android.bp b/hostsidetests/jvmti/run-tests/test-1916/Android.bp
index f4e5c89..c4662a3 100644
--- a/hostsidetests/jvmti/run-tests/test-1916/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1916/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1916DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1917/Android.bp b/hostsidetests/jvmti/run-tests/test-1917/Android.bp
index cb82cdb..82bfdbf 100644
--- a/hostsidetests/jvmti/run-tests/test-1917/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1917/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1917DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1920/Android.bp b/hostsidetests/jvmti/run-tests/test-1920/Android.bp
index 1fac722e..f7f2a16 100644
--- a/hostsidetests/jvmti/run-tests/test-1920/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1920/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1920DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1921/Android.bp b/hostsidetests/jvmti/run-tests/test-1921/Android.bp
index 16ca73d..dbb927c 100644
--- a/hostsidetests/jvmti/run-tests/test-1921/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1921/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1921DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1922/Android.bp b/hostsidetests/jvmti/run-tests/test-1922/Android.bp
index 1cf47ec..def8205 100644
--- a/hostsidetests/jvmti/run-tests/test-1922/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1922/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1922DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1923/Android.bp b/hostsidetests/jvmti/run-tests/test-1923/Android.bp
index 3f20a27..d9a37c4 100644
--- a/hostsidetests/jvmti/run-tests/test-1923/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1923/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1923DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1924/Android.bp b/hostsidetests/jvmti/run-tests/test-1924/Android.bp
index 36fcd19..c370630 100644
--- a/hostsidetests/jvmti/run-tests/test-1924/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1924/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1924DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1925/Android.bp b/hostsidetests/jvmti/run-tests/test-1925/Android.bp
index b3d7d82..131b507 100644
--- a/hostsidetests/jvmti/run-tests/test-1925/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1925/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1925DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1926/Android.bp b/hostsidetests/jvmti/run-tests/test-1926/Android.bp
index 3420b61..c4ca4ea 100644
--- a/hostsidetests/jvmti/run-tests/test-1926/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1926/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1926DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1927/Android.bp b/hostsidetests/jvmti/run-tests/test-1927/Android.bp
index 88229ff..94297f7 100644
--- a/hostsidetests/jvmti/run-tests/test-1927/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1927/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1927DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1928/Android.bp b/hostsidetests/jvmti/run-tests/test-1928/Android.bp
index fe94bb8..8232670 100644
--- a/hostsidetests/jvmti/run-tests/test-1928/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1928/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1928DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1930/Android.bp b/hostsidetests/jvmti/run-tests/test-1930/Android.bp
index b80e3b0..104167d 100644
--- a/hostsidetests/jvmti/run-tests/test-1930/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1930/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1930DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1931/Android.bp b/hostsidetests/jvmti/run-tests/test-1931/Android.bp
index 972f75e..7fef39d 100644
--- a/hostsidetests/jvmti/run-tests/test-1931/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1931/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1931DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1932/Android.bp b/hostsidetests/jvmti/run-tests/test-1932/Android.bp
index ff0d225..0009ba5 100644
--- a/hostsidetests/jvmti/run-tests/test-1932/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1932/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1932DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1933/Android.bp b/hostsidetests/jvmti/run-tests/test-1933/Android.bp
index 1b0f483..b7285d6 100644
--- a/hostsidetests/jvmti/run-tests/test-1933/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1933/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1933DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1934/Android.bp b/hostsidetests/jvmti/run-tests/test-1934/Android.bp
index 079f6de..23055d8 100644
--- a/hostsidetests/jvmti/run-tests/test-1934/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1934/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1934DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1936/Android.bp b/hostsidetests/jvmti/run-tests/test-1936/Android.bp
index ff1c9a5..e2e4484 100644
--- a/hostsidetests/jvmti/run-tests/test-1936/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1936/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1936DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1937/Android.bp b/hostsidetests/jvmti/run-tests/test-1937/Android.bp
index 695edb2..b3eb90d 100644
--- a/hostsidetests/jvmti/run-tests/test-1937/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1937/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1937DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1939/Android.bp b/hostsidetests/jvmti/run-tests/test-1939/Android.bp
index b82555f3..f2b8f8e 100644
--- a/hostsidetests/jvmti/run-tests/test-1939/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1939/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1939DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1941/Android.bp b/hostsidetests/jvmti/run-tests/test-1941/Android.bp
index 7134464..21b4a62 100644
--- a/hostsidetests/jvmti/run-tests/test-1941/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1941/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1941DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1942/Android.bp b/hostsidetests/jvmti/run-tests/test-1942/Android.bp
index a5163e2..cbd2c73 100644
--- a/hostsidetests/jvmti/run-tests/test-1942/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1942/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1942DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1943/Android.bp b/hostsidetests/jvmti/run-tests/test-1943/Android.bp
index a7efc95..c185433 100644
--- a/hostsidetests/jvmti/run-tests/test-1943/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1943/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1943DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1953/Android.bp b/hostsidetests/jvmti/run-tests/test-1953/Android.bp
index d2251b2..67fc6ff 100644
--- a/hostsidetests/jvmti/run-tests/test-1953/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1953/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1953DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1958/Android.bp b/hostsidetests/jvmti/run-tests/test-1958/Android.bp
index 721bb03..eb80ec5 100644
--- a/hostsidetests/jvmti/run-tests/test-1958/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1958/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1958DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1962/Android.bp b/hostsidetests/jvmti/run-tests/test-1962/Android.bp
index 662cacc..04ead5f 100644
--- a/hostsidetests/jvmti/run-tests/test-1962/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1962/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1962DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1967/Android.bp b/hostsidetests/jvmti/run-tests/test-1967/Android.bp
index 995959e..6c9b570 100644
--- a/hostsidetests/jvmti/run-tests/test-1967/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1967/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1967DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1968/Android.bp b/hostsidetests/jvmti/run-tests/test-1968/Android.bp
index 1b57bf6..8b38e1c 100644
--- a/hostsidetests/jvmti/run-tests/test-1968/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1968/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1968DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1969/Android.bp b/hostsidetests/jvmti/run-tests/test-1969/Android.bp
index fa5c6df..ab5fcc5 100644
--- a/hostsidetests/jvmti/run-tests/test-1969/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1969/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1969DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1970/Android.bp b/hostsidetests/jvmti/run-tests/test-1970/Android.bp
index 94e1d9a..71bf089 100644
--- a/hostsidetests/jvmti/run-tests/test-1970/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1970/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1970DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1971/Android.bp b/hostsidetests/jvmti/run-tests/test-1971/Android.bp
index 1ac9d3a..6f053ef 100644
--- a/hostsidetests/jvmti/run-tests/test-1971/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1971/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1971DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1974/Android.bp b/hostsidetests/jvmti/run-tests/test-1974/Android.bp
index d1ccbe3..e3b8d7d 100644
--- a/hostsidetests/jvmti/run-tests/test-1974/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1974/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1974DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1975/Android.bp b/hostsidetests/jvmti/run-tests/test-1975/Android.bp
index 1d72113..3c1443e 100644
--- a/hostsidetests/jvmti/run-tests/test-1975/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1975/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1975DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1976/Android.bp b/hostsidetests/jvmti/run-tests/test-1976/Android.bp
index fba22ee..3e624f8 100644
--- a/hostsidetests/jvmti/run-tests/test-1976/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1976/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1976DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1977/Android.bp b/hostsidetests/jvmti/run-tests/test-1977/Android.bp
index 0b70b9a..d1ad972 100644
--- a/hostsidetests/jvmti/run-tests/test-1977/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1977/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1977DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1978/Android.bp b/hostsidetests/jvmti/run-tests/test-1978/Android.bp
index 62bda0d..4e44e31 100644
--- a/hostsidetests/jvmti/run-tests/test-1978/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1978/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1978DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1979/Android.bp b/hostsidetests/jvmti/run-tests/test-1979/Android.bp
index 8f26f89..fc810fb 100644
--- a/hostsidetests/jvmti/run-tests/test-1979/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1979/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1979DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1981/Android.bp b/hostsidetests/jvmti/run-tests/test-1981/Android.bp
index 510e0aa..8620895 100644
--- a/hostsidetests/jvmti/run-tests/test-1981/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1981/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1981DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1982/Android.bp b/hostsidetests/jvmti/run-tests/test-1982/Android.bp
index 9e070ff7..1354fa9 100644
--- a/hostsidetests/jvmti/run-tests/test-1982/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1982/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1982DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1983/Android.bp b/hostsidetests/jvmti/run-tests/test-1983/Android.bp
index d165fd1..ff7a8d9 100644
--- a/hostsidetests/jvmti/run-tests/test-1983/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1983/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1983DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1984/Android.bp b/hostsidetests/jvmti/run-tests/test-1984/Android.bp
index e84c4a8..153a44b 100644
--- a/hostsidetests/jvmti/run-tests/test-1984/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1984/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1984DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1988/Android.bp b/hostsidetests/jvmti/run-tests/test-1988/Android.bp
index 13e6657..9df236f 100644
--- a/hostsidetests/jvmti/run-tests/test-1988/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1988/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1988DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1989/Android.bp b/hostsidetests/jvmti/run-tests/test-1989/Android.bp
index d2420c7..e500ae2 100644
--- a/hostsidetests/jvmti/run-tests/test-1989/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1989/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1989DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1990/Android.bp b/hostsidetests/jvmti/run-tests/test-1990/Android.bp
index 8c953f3..63207b0 100644
--- a/hostsidetests/jvmti/run-tests/test-1990/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1990/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1990DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1991/Android.bp b/hostsidetests/jvmti/run-tests/test-1991/Android.bp
index 216de8b..de6f046 100644
--- a/hostsidetests/jvmti/run-tests/test-1991/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1991/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1991DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1992/Android.bp b/hostsidetests/jvmti/run-tests/test-1992/Android.bp
index 1fc7f43..89643ec 100644
--- a/hostsidetests/jvmti/run-tests/test-1992/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1992/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1992DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1994/Android.bp b/hostsidetests/jvmti/run-tests/test-1994/Android.bp
index 90c4e59..68b67ba 100644
--- a/hostsidetests/jvmti/run-tests/test-1994/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1994/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1994DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1995/Android.bp b/hostsidetests/jvmti/run-tests/test-1995/Android.bp
index 8561296..348d1b0 100644
--- a/hostsidetests/jvmti/run-tests/test-1995/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1995/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1995DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1996/Android.bp b/hostsidetests/jvmti/run-tests/test-1996/Android.bp
index bc17d45..bf26ada 100644
--- a/hostsidetests/jvmti/run-tests/test-1996/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1996/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1996DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1997/Android.bp b/hostsidetests/jvmti/run-tests/test-1997/Android.bp
index 266e3b3..23f35b5 100644
--- a/hostsidetests/jvmti/run-tests/test-1997/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1997/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1997DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1998/Android.bp b/hostsidetests/jvmti/run-tests/test-1998/Android.bp
index c3d57f1..8d39f5d 100644
--- a/hostsidetests/jvmti/run-tests/test-1998/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1998/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1998DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-1999/Android.bp b/hostsidetests/jvmti/run-tests/test-1999/Android.bp
index 364f2d8..d99c359 100644
--- a/hostsidetests/jvmti/run-tests/test-1999/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-1999/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest1999DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-2001/Android.bp b/hostsidetests/jvmti/run-tests/test-2001/Android.bp
index 8288395..ce54c3c 100644
--- a/hostsidetests/jvmti/run-tests/test-2001/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-2001/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest2001DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-2002/Android.bp b/hostsidetests/jvmti/run-tests/test-2002/Android.bp
index 1a2138d..7ba683f 100644
--- a/hostsidetests/jvmti/run-tests/test-2002/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-2002/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest2002DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-2003/Android.bp b/hostsidetests/jvmti/run-tests/test-2003/Android.bp
index 798ae8e..19f40de 100644
--- a/hostsidetests/jvmti/run-tests/test-2003/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-2003/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest2003DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-2004/Android.bp b/hostsidetests/jvmti/run-tests/test-2004/Android.bp
index 2926764..997d6c7 100644
--- a/hostsidetests/jvmti/run-tests/test-2004/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-2004/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest2004DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-2005/Android.bp b/hostsidetests/jvmti/run-tests/test-2005/Android.bp
index 47af189..51bbed0 100644
--- a/hostsidetests/jvmti/run-tests/test-2005/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-2005/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest2005DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-2006/Android.bp b/hostsidetests/jvmti/run-tests/test-2006/Android.bp
index 8b0ad46..bf25e77 100644
--- a/hostsidetests/jvmti/run-tests/test-2006/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-2006/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest2006DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-2007/Android.bp b/hostsidetests/jvmti/run-tests/test-2007/Android.bp
index b320fbf..7df2310 100644
--- a/hostsidetests/jvmti/run-tests/test-2007/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-2007/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest2007DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-902/Android.bp b/hostsidetests/jvmti/run-tests/test-902/Android.bp
index 2299052..9daff3e 100644
--- a/hostsidetests/jvmti/run-tests/test-902/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-902/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest902DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-903/Android.bp b/hostsidetests/jvmti/run-tests/test-903/Android.bp
index 615f57a..c02dc5a 100644
--- a/hostsidetests/jvmti/run-tests/test-903/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-903/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest903DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-904/Android.bp b/hostsidetests/jvmti/run-tests/test-904/Android.bp
index ce0eeaa..27ad801 100644
--- a/hostsidetests/jvmti/run-tests/test-904/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-904/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest904DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-905/Android.bp b/hostsidetests/jvmti/run-tests/test-905/Android.bp
index cd2c248..752c9b4 100644
--- a/hostsidetests/jvmti/run-tests/test-905/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-905/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest905DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-906/Android.bp b/hostsidetests/jvmti/run-tests/test-906/Android.bp
index 8491820..de13556 100644
--- a/hostsidetests/jvmti/run-tests/test-906/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-906/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest906DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-907/Android.bp b/hostsidetests/jvmti/run-tests/test-907/Android.bp
index 518fc7d..9a396c1 100644
--- a/hostsidetests/jvmti/run-tests/test-907/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-907/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest907DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-908/Android.bp b/hostsidetests/jvmti/run-tests/test-908/Android.bp
index aef930a..a2d36ec 100644
--- a/hostsidetests/jvmti/run-tests/test-908/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-908/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest908DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-910/Android.bp b/hostsidetests/jvmti/run-tests/test-910/Android.bp
index cc46813..788e77f 100644
--- a/hostsidetests/jvmti/run-tests/test-910/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-910/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest910DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-911/Android.bp b/hostsidetests/jvmti/run-tests/test-911/Android.bp
index 156aa0d..02ed50a 100644
--- a/hostsidetests/jvmti/run-tests/test-911/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-911/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest911DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-912/Android.bp b/hostsidetests/jvmti/run-tests/test-912/Android.bp
index 8ff1bec..d87675f 100644
--- a/hostsidetests/jvmti/run-tests/test-912/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-912/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest912DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-913/Android.bp b/hostsidetests/jvmti/run-tests/test-913/Android.bp
index 3ef2747..ebe8f51 100644
--- a/hostsidetests/jvmti/run-tests/test-913/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-913/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest913DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-914/Android.bp b/hostsidetests/jvmti/run-tests/test-914/Android.bp
index ac7be56..c6637c6 100644
--- a/hostsidetests/jvmti/run-tests/test-914/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-914/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest914DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-915/Android.bp b/hostsidetests/jvmti/run-tests/test-915/Android.bp
index 7e778d8..92d8dc6 100644
--- a/hostsidetests/jvmti/run-tests/test-915/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-915/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest915DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-917/Android.bp b/hostsidetests/jvmti/run-tests/test-917/Android.bp
index cef34ce..0bad49b 100644
--- a/hostsidetests/jvmti/run-tests/test-917/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-917/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest917DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-918/Android.bp b/hostsidetests/jvmti/run-tests/test-918/Android.bp
index d6a2f08..a406e09 100644
--- a/hostsidetests/jvmti/run-tests/test-918/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-918/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest918DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-919/Android.bp b/hostsidetests/jvmti/run-tests/test-919/Android.bp
index f3c5c53..dbcf34f 100644
--- a/hostsidetests/jvmti/run-tests/test-919/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-919/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest919DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-920/Android.bp b/hostsidetests/jvmti/run-tests/test-920/Android.bp
index b2fb207..373f5bf 100644
--- a/hostsidetests/jvmti/run-tests/test-920/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-920/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest920DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-922/Android.bp b/hostsidetests/jvmti/run-tests/test-922/Android.bp
index d43497e..487d977 100644
--- a/hostsidetests/jvmti/run-tests/test-922/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-922/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest922DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-923/Android.bp b/hostsidetests/jvmti/run-tests/test-923/Android.bp
index e9e9165..b4ebf50 100644
--- a/hostsidetests/jvmti/run-tests/test-923/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-923/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest923DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-924/Android.bp b/hostsidetests/jvmti/run-tests/test-924/Android.bp
index 383a611..c300102 100644
--- a/hostsidetests/jvmti/run-tests/test-924/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-924/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest924DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-926/Android.bp b/hostsidetests/jvmti/run-tests/test-926/Android.bp
index c60de12..04cb340 100644
--- a/hostsidetests/jvmti/run-tests/test-926/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-926/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest926DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-927/Android.bp b/hostsidetests/jvmti/run-tests/test-927/Android.bp
index 812e67a..bccfb1d 100644
--- a/hostsidetests/jvmti/run-tests/test-927/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-927/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest927DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-928/Android.bp b/hostsidetests/jvmti/run-tests/test-928/Android.bp
index 0745805..7aa4dd4 100644
--- a/hostsidetests/jvmti/run-tests/test-928/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-928/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest928DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-930/Android.bp b/hostsidetests/jvmti/run-tests/test-930/Android.bp
index baf38d3..5f4bcb7 100644
--- a/hostsidetests/jvmti/run-tests/test-930/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-930/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest930DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-931/Android.bp b/hostsidetests/jvmti/run-tests/test-931/Android.bp
index fd401d1..516519c 100644
--- a/hostsidetests/jvmti/run-tests/test-931/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-931/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest931DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-932/Android.bp b/hostsidetests/jvmti/run-tests/test-932/Android.bp
index 46936c3..73657da 100644
--- a/hostsidetests/jvmti/run-tests/test-932/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-932/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest932DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-940/Android.bp b/hostsidetests/jvmti/run-tests/test-940/Android.bp
index 72d9821..da39577 100644
--- a/hostsidetests/jvmti/run-tests/test-940/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-940/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest940DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-942/Android.bp b/hostsidetests/jvmti/run-tests/test-942/Android.bp
index fde1311..594f5fb 100644
--- a/hostsidetests/jvmti/run-tests/test-942/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-942/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest942DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-944/Android.bp b/hostsidetests/jvmti/run-tests/test-944/Android.bp
index 5969e42..a641cf5 100644
--- a/hostsidetests/jvmti/run-tests/test-944/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-944/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest944DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-945/Android.bp b/hostsidetests/jvmti/run-tests/test-945/Android.bp
index 0dbe30c..85c1f63 100644
--- a/hostsidetests/jvmti/run-tests/test-945/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-945/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest945DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-947/Android.bp b/hostsidetests/jvmti/run-tests/test-947/Android.bp
index e14c59b..5858dd8 100644
--- a/hostsidetests/jvmti/run-tests/test-947/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-947/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest947DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-951/Android.bp b/hostsidetests/jvmti/run-tests/test-951/Android.bp
index b6dae71..59dfabd 100644
--- a/hostsidetests/jvmti/run-tests/test-951/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-951/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest951DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-982/Android.bp b/hostsidetests/jvmti/run-tests/test-982/Android.bp
index c62b999..91730d2 100644
--- a/hostsidetests/jvmti/run-tests/test-982/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-982/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest982DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-983/Android.bp b/hostsidetests/jvmti/run-tests/test-983/Android.bp
index ed80849..f642f782 100644
--- a/hostsidetests/jvmti/run-tests/test-983/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-983/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest983DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-984/Android.bp b/hostsidetests/jvmti/run-tests/test-984/Android.bp
index 67dba15..0c4bf7b 100644
--- a/hostsidetests/jvmti/run-tests/test-984/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-984/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest984DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-985/Android.bp b/hostsidetests/jvmti/run-tests/test-985/Android.bp
index 91452c7..7179bd1 100644
--- a/hostsidetests/jvmti/run-tests/test-985/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-985/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest985DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-986/Android.bp b/hostsidetests/jvmti/run-tests/test-986/Android.bp
index cfd9166..3b3856b 100644
--- a/hostsidetests/jvmti/run-tests/test-986/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-986/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest986DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-988/Android.bp b/hostsidetests/jvmti/run-tests/test-988/Android.bp
index 17dcb75..bcfecc4 100644
--- a/hostsidetests/jvmti/run-tests/test-988/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-988/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest988DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-989/Android.bp b/hostsidetests/jvmti/run-tests/test-989/Android.bp
index 0f99abc..414fb67 100644
--- a/hostsidetests/jvmti/run-tests/test-989/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-989/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest989DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-990/Android.bp b/hostsidetests/jvmti/run-tests/test-990/Android.bp
index 7f84835..eba9398 100644
--- a/hostsidetests/jvmti/run-tests/test-990/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-990/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest990DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-991/Android.bp b/hostsidetests/jvmti/run-tests/test-991/Android.bp
index a7d6d5c..89c5587 100644
--- a/hostsidetests/jvmti/run-tests/test-991/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-991/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest991DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-992/Android.bp b/hostsidetests/jvmti/run-tests/test-992/Android.bp
index bdb9f9c..5cd0f63 100644
--- a/hostsidetests/jvmti/run-tests/test-992/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-992/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest992DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-993/Android.bp b/hostsidetests/jvmti/run-tests/test-993/Android.bp
index 37b1cc0..e7e50e4 100644
--- a/hostsidetests/jvmti/run-tests/test-993/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-993/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest993DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-994/Android.bp b/hostsidetests/jvmti/run-tests/test-994/Android.bp
index 63fc348..b8727f1 100644
--- a/hostsidetests/jvmti/run-tests/test-994/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-994/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest994DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-995/Android.bp b/hostsidetests/jvmti/run-tests/test-995/Android.bp
index 818d938..2f1a65a 100644
--- a/hostsidetests/jvmti/run-tests/test-995/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-995/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest995DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-996/Android.bp b/hostsidetests/jvmti/run-tests/test-996/Android.bp
index 5c80177..e4fa057 100644
--- a/hostsidetests/jvmti/run-tests/test-996/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-996/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest996DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/run-tests/test-997/Android.bp b/hostsidetests/jvmti/run-tests/test-997/Android.bp
index 719d51a..89ab9e5 100644
--- a/hostsidetests/jvmti/run-tests/test-997/Android.bp
+++ b/hostsidetests/jvmti/run-tests/test-997/Android.bp
@@ -25,4 +25,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiRunTest997DeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/jvmti/tagging/Android.bp b/hostsidetests/jvmti/tagging/Android.bp
index 32c262c..9571914 100644
--- a/hostsidetests/jvmti/tagging/Android.bp
+++ b/hostsidetests/jvmti/tagging/Android.bp
@@ -26,4 +26,7 @@
         "general-tests",
     ],
     data: [":CtsJvmtiTaggingDeviceApp"],
-}
+    test_options: {
+        unit_test: false,
+    },
+}
\ No newline at end of file
diff --git a/hostsidetests/os/OWNERS b/hostsidetests/os/OWNERS
index 9abffa5..d4251a9 100644
--- a/hostsidetests/os/OWNERS
+++ b/hostsidetests/os/OWNERS
@@ -3,4 +3,5 @@
 santoscordon@google.com
 per-file InattentiveSleepTests.java=rgl@google.com, robhor@google.com
 per-file QuiescentBootTests.java=rgl@google.com, robhor@google.com
+per-file StaticSharedLibsHostTests.java=patb@google.com
 
diff --git a/hostsidetests/os/test-apps/OWNERS b/hostsidetests/os/test-apps/OWNERS
new file mode 100644
index 0000000..0f08fe3
--- /dev/null
+++ b/hostsidetests/os/test-apps/OWNERS
@@ -0,0 +1,16 @@
+per-file StaticSharedLibConsumerApp1/*=patb@google.com
+per-file StaticSharedLibConsumerApp2/*=patb@google.com
+per-file StaticSharedLibConsumerApp3/*=patb@google.com
+per-file StaticSharedLibProviderApp1/*=patb@google.com
+per-file StaticSharedLibProviderApp2/*=patb@google.com
+per-file StaticSharedLibProviderApp3/*=patb@google.com
+per-file StaticSharedLibProviderApp4/*=patb@google.com
+per-file StaticSharedLibProviderApp5/*=patb@google.com
+per-file StaticSharedLibProviderApp6/*=patb@google.com
+per-file StaticSharedLibProviderApp7/*=patb@google.com
+per-file StaticSharedLibProviderAppRecursive/*=patb@google.com
+per-file StaticSharedLibTestApp/*=patb@google.com
+per-file StaticSharedNativeLibConsumer/*=patb@google.com
+per-file StaticSharedNativeLibProvider/*=patb@google.com
+per-file StaticSharedNativeLibProvider1/*=patb@google.com
+
diff --git a/hostsidetests/seccomp/app/jni/android_seccomp_cts_app_SeccompDeviceTest.cpp b/hostsidetests/seccomp/app/jni/android_seccomp_cts_app_SeccompDeviceTest.cpp
index 99080a1..7a40949 100644
--- a/hostsidetests/seccomp/app/jni/android_seccomp_cts_app_SeccompDeviceTest.cpp
+++ b/hostsidetests/seccomp/app/jni/android_seccomp_cts_app_SeccompDeviceTest.cpp
@@ -29,8 +29,6 @@
 #define ALOGI(...) ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)
 #define ALOGE(...) ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)
 
-#define PER_USER_RANGE  100000
-
 /*
  * Function: testSyscallBlocked
  * Purpose: test that the syscall listed is blocked by seccomp
@@ -79,21 +77,17 @@
 }
 
 static jboolean testSetresuidBlocked(JNIEnv *, jobject, jint ruid, jint euid, jint suid) {
-    jint userId = getuid() / PER_USER_RANGE;
-    jint userRuid = userId * PER_USER_RANGE + ruid;
-    jint userEuid = userId * PER_USER_RANGE + euid;
-    jint userSuid = userId * PER_USER_RANGE + suid;
-
-    return doTestSyscallBlocked([&] {ALOGE("Calling setresuid\n"); setresuid(userRuid, userEuid, userSuid);});
+  return doTestSyscallBlocked([&] {
+    ALOGE("Calling setresuid\n");
+    setresuid(ruid, euid, suid);
+  });
 }
 
 static jboolean testSetresgidBlocked(JNIEnv *, jobject, jint rgid, jint egid, jint sgid) {
-    jint userId = getuid() / PER_USER_RANGE;
-    jint userRgid = userId * PER_USER_RANGE + rgid;
-    jint userEgid = userId * PER_USER_RANGE + egid;
-    jint userSgid = userId * PER_USER_RANGE + sgid;
-
-    return doTestSyscallBlocked([&] {ALOGE("Calling setresgid\n"); setresgid(userRgid, userEgid, userSgid);});
+  return doTestSyscallBlocked([&] {
+    ALOGE("Calling setresgid\n");
+    setresgid(rgid, egid, sgid);
+  });
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/hostsidetests/seccomp/app/src/android/seccomp/cts/app/ZygotePreload.java b/hostsidetests/seccomp/app/src/android/seccomp/cts/app/ZygotePreload.java
index fe6053b..021b56b 100644
--- a/hostsidetests/seccomp/app/src/android/seccomp/cts/app/ZygotePreload.java
+++ b/hostsidetests/seccomp/app/src/android/seccomp/cts/app/ZygotePreload.java
@@ -18,6 +18,7 @@
 
 import android.content.pm.ApplicationInfo;
 import android.os.Process;
+import android.os.UserHandle;
 import android.util.Log;
 
 public class ZygotePreload implements android.app.ZygotePreload {
@@ -26,38 +27,41 @@
     static volatile boolean sResult = false;
     static volatile int sStartOfIsolatedRange = -1;
 
-    static private boolean testSetResUidGidBlocked(int rid, int eid, int sid) {
-        if (!SeccompDeviceTest.testSetresuidBlocked(rid, eid, sid)) {
-            Log.e(TAG, "setresuid( " + Integer.toString(rid) + ","
-                    + Integer.toString(eid) + "," + Integer.toString(sid) + ")"
-                    + " is wrongly allowed.");
+    static private boolean testSetResUidGidBlocked(int rid, int eid, int sid,
+            boolean expectBlocked, boolean log) {
+        boolean blocked = SeccompDeviceTest.testSetresuidBlocked(rid, eid, sid);
+        if (blocked != expectBlocked) {
+            if (log) {
+                Log.e(TAG, "setresuid( " + Integer.toString(rid) + ","
+                        + Integer.toString(eid) + "," + Integer.toString(sid) + ")"
+                        + " is wrongly " + (expectBlocked ? "allowed." : "blocked."));
+            }
             return false;
         }
-        if (!SeccompDeviceTest.testSetresgidBlocked(rid, eid, sid)) {
-            Log.e(TAG, "setresguid( " + Integer.toString(rid) + ","
-                    + Integer.toString(eid) + "," + Integer.toString(sid) + ")"
-                    + " is wrongly allowed.");
+
+        blocked = SeccompDeviceTest.testSetresgidBlocked(rid, eid, sid);
+        if (blocked != expectBlocked) {
+            if (log) {
+                Log.e(TAG, "setresguid( " + Integer.toString(rid) + ","
+                        + Integer.toString(eid) + "," + Integer.toString(sid) + ")"
+                        + " is wrongly " + (expectBlocked ? "allowed." : "blocked."));
+            }
             return false;
         }
 
         return true;
     }
 
-    static private boolean testSetResUidGidAllowed(int rid, int eid, int sid) {
-        if (SeccompDeviceTest.testSetresuidBlocked(rid, eid, sid)) {
-            Log.e(TAG, "setresuid( " + Integer.toString(rid) + ","
-                    + Integer.toString(eid) + "," + Integer.toString(sid) + ")"
-                    + " is wrongly blocked.");
-            return false;
-        }
-        if (SeccompDeviceTest.testSetresgidBlocked(rid, eid, sid)) {
-            Log.e(TAG, "setresguid( " + Integer.toString(rid) + ","
-                    + Integer.toString(eid) + "," + Integer.toString(sid) + ")"
-                    + " is wrongly blocked.");
-            return false;
-        }
+    static private boolean testSetResUidGidBlocked(int rid, int eid, int sid) {
+        return testSetResUidGidBlocked(rid, eid, sid, true /*expectBlocked */, true /* log */);
+    }
 
-        return true;
+    static private boolean testSetResUidGidAllowed(int rid, int eid, int sid) {
+        return testSetResUidGidBlocked(rid, eid, sid, false /*expectBlocked */, true /* log */);
+    }
+
+    static private boolean testSetResUidGidAllowedNoLog(int rid, int eid, int sid) {
+        return testSetResUidGidBlocked(rid, eid, sid, false /*expectBlocked */, false /* log */);
     }
 
     static synchronized public boolean getSeccomptestResult() {
@@ -90,25 +94,35 @@
         result &= testSetResUidGidBlocked(0, Process.SYSTEM_UID,
                 Process.SYSTEM_UID);
 
-        // an app uid
-        result &= testSetResUidGidBlocked(Process.FIRST_APPLICATION_UID,
-                Process.FIRST_APPLICATION_UID, Process.FIRST_APPLICATION_UID);
-        result &= testSetResUidGidBlocked(Process.LAST_APPLICATION_UID,
-                Process.LAST_APPLICATION_UID, Process.LAST_APPLICATION_UID);
+        // an app uid for the current user, and another user
+        for (int userId = UserHandle.myUserId(); userId <= UserHandle.myUserId() + 1; userId++) {
+            int appStart = UserHandle.getUid(userId, Process.FIRST_APPLICATION_UID);
+            result &= testSetResUidGidBlocked(appStart, appStart, appStart);
+            int appEnd = UserHandle.getUid(userId, Process.LAST_APPLICATION_UID);
+            result &= testSetResUidGidBlocked(appEnd, appEnd, appEnd);
+        }
 
-        // an isolated process uid
-        result &= testSetResUidGidBlocked(Process.FIRST_ISOLATED_UID,
-                Process.FIRST_ISOLATED_UID, Process.FIRST_ISOLATED_UID);
-        result &= testSetResUidGidBlocked(Process.LAST_ISOLATED_UID, Process.LAST_ISOLATED_UID,
-                Process.LAST_ISOLATED_UID);
+        // an isolated process uid for the current user, and another user
+        for (int userId = UserHandle.myUserId(); userId <= UserHandle.myUserId() + 1; userId++) {
+            int regularIsolatedStart = UserHandle.getUid(userId, Process.FIRST_ISOLATED_UID);
+            result &= testSetResUidGidBlocked(regularIsolatedStart, regularIsolatedStart,
+                    regularIsolatedStart);
+            int regularIsolatedEnd = UserHandle.getUid(userId, Process.LAST_ISOLATED_UID);
+            result &= testSetResUidGidBlocked(regularIsolatedEnd, regularIsolatedEnd,
+                    regularIsolatedEnd);
+        }
 
         // Test all ranges of app zygote UIDs; we don't know here which
         // isolated UID is assigned to our process, so we will test all ranges,
         // and verify only one is allowed; then have the caller verify that
         // this was indeed our allowed range.
-        for (int i = Process.FIRST_APP_ZYGOTE_ISOLATED_UID;
-                i < Process.LAST_APP_ZYGOTE_ISOLATED_UID; i += Process.NUM_UIDS_PER_APP_ZYGOTE) {
-            boolean rangeAllowed = testSetResUidGidAllowed(i, i, i);
+        int isolatedUserStart = UserHandle.getUid(UserHandle.myUserId(),
+                Process.FIRST_APP_ZYGOTE_ISOLATED_UID);
+        int isolatedUserEnd = UserHandle.getUid(UserHandle.myUserId(),
+                Process.LAST_APP_ZYGOTE_ISOLATED_UID);
+
+        for (int i = isolatedUserStart; i < isolatedUserEnd; i += Process.NUM_UIDS_PER_APP_ZYGOTE) {
+            boolean rangeAllowed = testSetResUidGidAllowedNoLog(i, i, i);
             if (rangeAllowed) {
                 if (sStartOfIsolatedRange != -1) {
                     Log.e(TAG, "Found more than one allowed isolated UID range: "
@@ -137,7 +151,6 @@
             }
         }
 
-        // one over the range
         result &= testSetResUidGidBlocked(Process.LAST_APP_ZYGOTE_ISOLATED_UID + 1,
                 Process.LAST_APP_ZYGOTE_ISOLATED_UID + 1, Process.LAST_APP_ZYGOTE_ISOLATED_UID + 1);
 
diff --git a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
index 6de6044..0b0a84b 100644
--- a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
+++ b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java
@@ -249,6 +249,36 @@
         put("mt6873", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
         put("MT6853V/TZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
         put("MT6853V/TNZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6833V/ZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6833V/NZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6833V/TZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6833V/TNZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6833V/MZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6833V/MNZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6877V/ZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6877V/NZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6877V/TZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6877V/TNZA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6768V/WA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6768V/CA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6768V/WB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6768V/CB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6767V/WA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6767V/CA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6767V/WB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6767V/CB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/WA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/CA", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/WB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/CB", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/WT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/CT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/WU", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/CU", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/WZ", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/CZ", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/WY", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
+        put("MT6769V/CY", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
         put("SDMMAGPIE", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
         put("SM6150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
         put("SM7150", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
@@ -264,6 +294,7 @@
         put("QM215", null);
         put("ATOLL", null);
         put("ATOLL-AB", null);
+        put("SDM660", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
         put("BENGAL", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y"});
         put("DEFAULT", new String[]{"CONFIG_HARDEN_BRANCH_PREDICTOR=y",
             "CONFIG_UNMAP_KERNEL_AT_EL0=y"});
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 382c931..9fb00d9 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -82,6 +82,7 @@
     private static final String VINTF_DEVICE_JSON = VINTF_DEVICE_CLASS + DEVICE_INFO_SUFFIX;
     // Keep in sync with com.android.compatibility.common.deviceinfo.VintfDeviceInfo
     private static final String SEPOLICY_VERSION_JSON_KEY = "sepolicy_version";
+    private static final String PLATFORM_SEPOLICY_VERSION_JSON_KEY = "platform_sepolicy_version";
 
     private static final Map<ITestDevice, File> cachedDevicePolicyFiles = new HashMap<>(1);
     private static final Map<ITestDevice, File> cachedDevicePlatFcFiles = new HashMap<>(1);
@@ -239,6 +240,8 @@
 
         File systemSepolicyCilFile = File.createTempFile("plat_sepolicy", ".cil");
         systemSepolicyCilFile.deleteOnExit();
+        File fileContextsFile = File.createTempFile("file_contexts", ".txt");
+        fileContextsFile.deleteOnExit();
 
         assertTrue(device.pullFile("/system/etc/selinux/plat_sepolicy.cil", systemSepolicyCilFile));
 
@@ -246,6 +249,7 @@
             secilc.getAbsolutePath(),
             "-m", "-M", "true", "-c", "30",
             "-o", builtPolicyFile.getAbsolutePath(),
+	    "-f", fileContextsFile.getAbsolutePath(),
             systemSepolicyCilFile.getAbsolutePath());
         pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
         pb.redirectErrorStream(true);
@@ -345,7 +349,7 @@
         String content = FileUtil.readStringFromFile(vintfJson);
         JSONObject object = new JSONObject(content);
         String version = object.getString(SEPOLICY_VERSION_JSON_KEY);
-        return getVendorSepolicyVersionFromMajorMinor(version);
+        return getSepolicyVersionFromMajorMinor(version);
     }
 
     /**
@@ -368,13 +372,26 @@
         Element root = doc.getDocumentElement();
         Element sepolicy = (Element) root.getElementsByTagName("sepolicy").item(0);
         Element version = (Element) sepolicy.getElementsByTagName("version").item(0);
-        return getVendorSepolicyVersionFromMajorMinor(version.getTextContent());
+        return getSepolicyVersionFromMajorMinor(version.getTextContent());
+    }
+
+    // NOTE: cts/tools/selinux depends on this method. Rename/change with caution.
+    /**
+     * Returns the major number of sepolicy version of system.
+     */
+    public static int getSystemSepolicyVersion(IBuildInfo build) throws Exception {
+        File deviceInfoDir = build.getFile(DeviceInfoCollector.DEVICE_INFO_DIR);
+        File vintfJson = deviceInfoDir.toPath().resolve(VINTF_DEVICE_JSON).toFile();
+        String content = FileUtil.readStringFromFile(vintfJson);
+        JSONObject object = new JSONObject(content);
+        String version = object.getString(PLATFORM_SEPOLICY_VERSION_JSON_KEY);
+        return getSepolicyVersionFromMajorMinor(version);
     }
 
     /**
      * Get the major number from an SEPolicy version string, e.g. "27.0" => 27.
      */
-    private static int getVendorSepolicyVersionFromMajorMinor(String version) {
+    private static int getSepolicyVersionFromMajorMinor(String version) {
         String sepolicyVersion = version.split("\\.")[0];
         return Integer.parseInt(sepolicyVersion);
     }
@@ -977,6 +994,26 @@
     }
 
     /**
+     * Tests that tracefs files(/sys/kernel/tracing and /d/tracing) are correctly labeled.
+     *
+     * @throws Exception
+     */
+    public void testTracefsTypeViolators() throws Exception {
+        assertSepolicyTests("TestTracefsTypeViolations", "/sepolicy_tests",
+                PropertyUtil.isVendorApiLevelNewerThan(mDevice, 30) /* includeVendorSepolicy */);
+    }
+
+    /**
+     * Tests that debugfs files(from /sys/kernel/debug) are correctly labeled.
+     *
+     * @throws Exception
+     */
+    public void testDebugfsTypeViolators() throws Exception {
+        assertSepolicyTests("TestDebugfsTypeViolations", "/sepolicy_tests",
+                PropertyUtil.isVendorApiLevelNewerThan(mDevice, 30) /* includeVendorSepolicy */);
+    }
+
+    /**
      * Tests that all domains with entrypoints on /system have the coredomain
      * attribute, and that all domains with entrypoints on /vendor do not have the
      * coredomain attribute.
@@ -1295,7 +1332,7 @@
     /* keystore is always running */
     @CddTest(requirement="9.7")
     public void testKeystoreDomain() throws DeviceNotAvailableException {
-        assertDomainOne("u:r:keystore:s0", "/system/bin/keystore");
+        assertDomainOne("u:r:keystore:s0", "/system/bin/keystore2");
     }
 
     /* System server better be running :-P */
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/Android.bp
deleted file mode 100644
index f4554af..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/Android.bp
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_test {
-    name: "CVE-2020-0408",
-    defaults: ["cts_hostsidetests_securitybulletin_defaults"],
-    srcs: ["poc.cpp"],
-    shared_libs: [
-        "libutils",
-    ],
-}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/poc.cpp
deleted file mode 100644
index dcccd2e..0000000
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0408/poc.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "utils/String16.h"
-
-int main(void) {
-  android::String16 str{u"hello world"};
-  android::String16 substr{u"hello"};
-  const size_t begin = substr.size();
-  const size_t len = std::numeric_limits<size_t>::max();
-  if (str.remove(len, begin) != android::OK) {
-    return EXIT_FAILURE;
-  }
-  if (strcmp16(str, substr) != 0) {
-    return EXIT_FAILURE;
-  }
-  return EXIT_SUCCESS;
-}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/poc.cpp
index 268153b..0499a84 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0450/poc.cpp
@@ -41,43 +41,6 @@
 void *kVulnPtr = nullptr;
 uint16_t kVulnSize = 0;
 
-// borrowed from rw_i93.cc
-enum {
-  RW_I93_STATE_NOT_ACTIVATED, /* ISO15693 is not activated            */
-  RW_I93_STATE_IDLE,          /* waiting for upper layer API          */
-  RW_I93_STATE_BUSY,          /* waiting for response from tag        */
-
-  RW_I93_STATE_DETECT_NDEF,   /* performing NDEF detection precedure  */
-  RW_I93_STATE_READ_NDEF,     /* performing read NDEF procedure       */
-  RW_I93_STATE_UPDATE_NDEF,   /* performing update NDEF procedure     */
-  RW_I93_STATE_FORMAT,        /* performing format procedure          */
-  RW_I93_STATE_SET_READ_ONLY, /* performing set read-only procedure   */
-
-  RW_I93_STATE_PRESENCE_CHECK /* checking presence of tag             */
-};
-
-// borrowed from rw_i93.cc
-enum {
-  RW_I93_SUBSTATE_WAIT_UID,          /* waiting for response of inventory    */
-  RW_I93_SUBSTATE_WAIT_SYS_INFO,     /* waiting for response of get sys info */
-  RW_I93_SUBSTATE_WAIT_CC,           /* waiting for reading CC               */
-  RW_I93_SUBSTATE_SEARCH_NDEF_TLV,   /* searching NDEF TLV                   */
-  RW_I93_SUBSTATE_CHECK_LOCK_STATUS, /* check if any NDEF TLV is locked      */
-
-  RW_I93_SUBSTATE_RESET_LEN,  /* set length to 0 to update NDEF TLV   */
-  RW_I93_SUBSTATE_WRITE_NDEF, /* writing NDEF and Terminator TLV      */
-  RW_I93_SUBSTATE_UPDATE_LEN, /* set length into NDEF TLV             */
-
-  RW_I93_SUBSTATE_WAIT_RESET_DSFID_AFI, /* reset DSFID and AFI */
-  RW_I93_SUBSTATE_CHECK_READ_ONLY,   /* check if any block is locked         */
-  RW_I93_SUBSTATE_WRITE_CC_NDEF_TLV, /* write CC and empty NDEF/Terminator TLV
-                                      */
-
-  RW_I93_SUBSTATE_WAIT_UPDATE_CC, /* updating CC as read-only             */
-  RW_I93_SUBSTATE_LOCK_NDEF_TLV,  /* lock blocks of NDEF TLV              */
-  RW_I93_SUBSTATE_WAIT_LOCK_CC    /* lock block of CC                     */
-};
-
 static tNFC_STATUS (*real_rw_i93_send_cmd_write_single_block)(
     uint16_t block_number, uint8_t *p_data) = nullptr;
 
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
index 80fcf24..8acba06 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
@@ -117,21 +117,6 @@
     }
 
     /**
-     * b/156999009
-     * Vulnerability Behaviour: SIGABRT in self
-     */
-    @SecurityTest(minPatchLevel = "2020-10")
-    @Test
-    public void testPocCVE_2020_0408() throws Exception {
-        String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT};
-        String binaryName = "CVE-2020-0408";
-        AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
-        testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName);
-        testConfig.config.setSignals(signals);
-        AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
-    }
-
-    /**
      * b/161894517
      * Vulnerability Behaviour: SIGABRT in self
      */
diff --git a/hostsidetests/stagedinstall/Android.bp b/hostsidetests/stagedinstall/Android.bp
index 8ea3089..e3e84f1 100644
--- a/hostsidetests/stagedinstall/Android.bp
+++ b/hostsidetests/stagedinstall/Android.bp
@@ -66,6 +66,7 @@
         ":StagedInstallTestCorruptedApex_b146895998",
         ":StagedInstallTestApexV2_NoApkSignature",
         ":StagedInstallTestApexV2_UnsignedPayload",
+        ":StagedInstallTestApexV2_SignPayloadWithDifferentKey",
     ],
     static_libs: [
         "androidx.test.runner",
@@ -533,6 +534,28 @@
   installable: false,
 }
 
+ApexFilenameSigningPayloadWithDifferentKey =
+  "com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex"
+prebuilt_apex {
+  name: "StagedInstallTestApexV2_SignPayloadWithDifferentKey",
+  arch: {
+        arm: {
+              src: "testdata/apex/arm/" + ApexFilenameSigningPayloadWithDifferentKey,
+        },
+        arm64: {
+              src: "testdata/apex/arm/" + ApexFilenameSigningPayloadWithDifferentKey,
+        },
+        x86: {
+              src: "testdata/apex/x86/" + ApexFilenameSigningPayloadWithDifferentKey,
+        },
+        x86_64: {
+              src: "testdata/apex/x86/" + ApexFilenameSigningPayloadWithDifferentKey,
+        },
+    },
+  filename: ApexFilenameSigningPayloadWithDifferentKey,
+  installable: false,
+}
+
 // collects deapexer and its dependency modules (libc++ and debugfs_static) to the zip file.
 genrule {
   name: "deapexer.zip",
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
index f61d887..d5af898 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -157,6 +157,9 @@
     private static final TestApp Apex2UnsignedPayload = new TestApp(
             "StagedInstallTestApexV2_UnsignedPayload", SHIM_APEX_PACKAGE_NAME, 1,
             /*isApex*/true, "com.android.apex.cts.shim.v2_unsigned_payload.apex");
+    private static final TestApp Apex2SignPayloadWithDifferentKey = new TestApp(
+            "StagedInstallTestApexV2_SignPayloadWithDifferentKey", SHIM_APEX_PACKAGE_NAME, 1,
+            /*isApex*/true, "com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex");
 
     @Before
     public void adoptShellPermissions() {
@@ -1174,6 +1177,19 @@
     }
 
     /**
+     * Should fail to verify apex signed payload with a different key
+     */
+    @Test
+    public void testApexSignPayloadWithDifferentKeyFailsVerification() throws Exception {
+        int sessionId = stageSingleApk(
+                Apex2SignPayloadWithDifferentKey).assertSuccessful().getSessionId();
+        PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId);
+        assertThat(sessionInfo).isStagedSessionFailed();
+        assertThat(sessionInfo.getStagedSessionErrorMessage())
+                .contains("public key doesn't match the pre-installed one");
+    }
+
+    /**
      * Test non-priv apps cannot access /data/app-staging folder contents
      */
     @Test
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index 209cb6a..d540536 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -651,6 +651,16 @@
     }
 
     /**
+     * Should fail to verify apex signed payload with a different key
+     */
+    @Test
+    public void testApexSignPayloadWithDifferentKeyFailsVerification() throws Exception {
+        assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+
+        runPhase("testApexSignPayloadWithDifferentKeyFailsVerification");
+    }
+
+    /**
      * Should fail to verify apex with unsigned payload
      */
     @Test
@@ -668,6 +678,21 @@
         runPhase("testAppStagingDirCannotBeReadByNonPrivApps");
     }
 
+    @Test
+    @LargeTest
+    public void testUpdatedApexFromDataApexActiveCanBePulled() throws Exception {
+        assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+
+        installV2Apex();
+
+        final ITestDevice.ApexInfo shimApex = mHostUtils.getShimApex().orElseThrow(
+                () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME)
+        );
+
+        assertThat(shimApex.sourceDir).startsWith("/data/apex/active");
+        assertThat(getDevice().pullFile(shimApex.sourceDir)).isNotNull();
+    }
+
     /**
      * Store the component name of the default launcher. This value will be used to reset the
      * default launcher to its correct component upon test completion.
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v1.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v1.apex
index 7b5758d..519a5e6 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v1.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v1.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2.apex
index dc2c8c9..b82b961 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_additional_file.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_additional_file.apex
index 3cf81757..0558952 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_additional_file.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_additional_file.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_additional_folder.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_additional_folder.apex
index 5335818..f909639 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_additional_folder.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_additional_folder.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex
index 4f0edff..4037bd1 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_different_certificate.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_different_certificate.apex
index af89d20..8909a72 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_different_certificate.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_different_certificate.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_different_package_name.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_different_package_name.apex
index 12b85e4..714ac09 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_different_package_name.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_different_package_name.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_no_hashtree.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_no_hashtree.apex
index 00c0c19..b5a1a80 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_no_hashtree.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_no_hashtree.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_sdk_target_p.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_sdk_target_p.apex
index 50e77bd..d7aef82 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_sdk_target_p.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_sdk_target_p.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex
new file mode 100644
index 0000000..3e8a2d6
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob.apex
index 3bde9ba..30e70bc 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob_rot.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob_rot.apex
index 26c3f27a..de8da11 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob_rot.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob_rot.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex
index 81aaecf..26da4fa 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_unsigned_payload.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_unsigned_payload.apex
index d2be4a2..db6f51b 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_unsigned_payload.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_unsigned_payload.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_with_post_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_with_post_install_hook.apex
index e1f904e..b724f0d 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_with_post_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_with_post_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_with_pre_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
index c8aa9d9..0da0bf9 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_without_apk_in_apex.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_without_apk_in_apex.apex
index 41c29bf..e359b01 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_without_apk_in_apex.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_without_apk_in_apex.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_wrong_sha.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_wrong_sha.apex
index 1447e7d..9c7e747 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_wrong_sha.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v2_wrong_sha.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3.apex
index e538f83..8de4b10 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3_signed_bob.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3_signed_bob.apex
index 86b418e..0ef71fc 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3_signed_bob.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3_signed_bob.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3_signed_bob_rot.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3_signed_bob_rot.apex
index 29a0877..fc8e3e7 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3_signed_bob_rot.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim.v3_signed_bob_rot.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim_not_pre_installed.apex b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim_not_pre_installed.apex
index e8705c7..894e1682 100644
--- a/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim_not_pre_installed.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/arm/com.android.apex.cts.shim_not_pre_installed.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v1.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v1.apex
index 04ec92d..fd79365 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v1.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v1.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2.apex
index ff54789..a629874 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_additional_file.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_additional_file.apex
index 3cf81757..0558952 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_additional_file.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_additional_file.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_additional_folder.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_additional_folder.apex
index 5335818..f909639 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_additional_folder.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_additional_folder.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex
index 4f0edff..4037bd1 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_different_certificate.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_different_certificate.apex
index af89d20..8909a72 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_different_certificate.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_different_certificate.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_different_package_name.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_different_package_name.apex
index eca1c37..183915a 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_different_package_name.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_different_package_name.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_no_hashtree.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_no_hashtree.apex
index 19da864..03f484b 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_no_hashtree.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_no_hashtree.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_sdk_target_p.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_sdk_target_p.apex
index 6b7436b..362bed2 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_sdk_target_p.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_sdk_target_p.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex
new file mode 100644
index 0000000..3e8a2d6
--- /dev/null
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob.apex
index 659f462..c66f0a3 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob_rot.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob_rot.apex
index c5e2210..4c062a1 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob_rot.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob_rot.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex
index 3ff88a9..aef2e04 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_unsigned_payload.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_unsigned_payload.apex
index cd702268..28c0893 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_unsigned_payload.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_unsigned_payload.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_with_post_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_with_post_install_hook.apex
index e1f904e..b724f0d 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_with_post_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_with_post_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_with_pre_install_hook.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
index c8aa9d9..0da0bf9 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_with_pre_install_hook.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_without_apk_in_apex.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_without_apk_in_apex.apex
index 41c29bf..e359b01 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_without_apk_in_apex.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_without_apk_in_apex.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_wrong_sha.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_wrong_sha.apex
index 1447e7d..9c7e747 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_wrong_sha.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v2_wrong_sha.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3.apex
index b3ecef7..6e166d9 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3_signed_bob.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3_signed_bob.apex
index 9281ee3..7644bb93 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3_signed_bob.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3_signed_bob.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3_signed_bob_rot.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3_signed_bob_rot.apex
index 23b2378..2d54902 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3_signed_bob_rot.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v3_signed_bob_rot.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim_not_pre_installed.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim_not_pre_installed.apex
index e8705c7..894e1682 100644
--- a/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim_not_pre_installed.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim_not_pre_installed.apex
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apk/CtsShimTargetPSdk/arm/CtsShimTargetPSdk.apk b/hostsidetests/stagedinstall/testdata/apk/CtsShimTargetPSdk/arm/CtsShimTargetPSdk.apk
index b6afbd0..e910cf6 100644
--- a/hostsidetests/stagedinstall/testdata/apk/CtsShimTargetPSdk/arm/CtsShimTargetPSdk.apk
+++ b/hostsidetests/stagedinstall/testdata/apk/CtsShimTargetPSdk/arm/CtsShimTargetPSdk.apk
Binary files differ
diff --git a/hostsidetests/stagedinstall/testdata/apk/CtsShimTargetPSdk/x86/CtsShimTargetPSdk.apk b/hostsidetests/stagedinstall/testdata/apk/CtsShimTargetPSdk/x86/CtsShimTargetPSdk.apk
index b6afbd0..e910cf6 100644
--- a/hostsidetests/stagedinstall/testdata/apk/CtsShimTargetPSdk/x86/CtsShimTargetPSdk.apk
+++ b/hostsidetests/stagedinstall/testdata/apk/CtsShimTargetPSdk/x86/CtsShimTargetPSdk.apk
Binary files differ
diff --git a/hostsidetests/sustainedperf/app/Android.bp b/hostsidetests/sustainedperf/app/Android.bp
new file mode 100644
index 0000000..2413f22
--- /dev/null
+++ b/hostsidetests/sustainedperf/app/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "CtsSustainedPerformanceDeviceTestApp",
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    srcs: ["src/**/*.java"],
+    // tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    sdk_version: "current",
+}
diff --git a/hostsidetests/sustainedperf/app/Android.mk b/hostsidetests/sustainedperf/app/Android.mk
deleted file mode 100644
index 473621a..0000000
--- a/hostsidetests/sustainedperf/app/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target
-LOCAL_MODULE_TAGS := tests
-# When built, explicitly put it in the data partition.
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_DEX_PREOPT := false
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts general-tests
-
-LOCAL_PACKAGE_NAME := CtsSustainedPerformanceDeviceTestApp
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_PACKAGE)
diff --git a/hostsidetests/sustainedperf/shadertoy_android/Android.mk b/hostsidetests/sustainedperf/shadertoy_android/Android.mk
index 59cc55f..e85b172 100644
--- a/hostsidetests/sustainedperf/shadertoy_android/Android.mk
+++ b/hostsidetests/sustainedperf/shadertoy_android/Android.mk
@@ -29,6 +29,8 @@
 #LOCAL_STATIC_LIBRARIES := libc++_static
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_PACKAGE_NAME := CtsSustainedPerformanceTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 LOCAL_SDK_VERSION := current
 LOCAL_MIN_SDK_VERSION := 5
diff --git a/hostsidetests/tagging/.clang-format b/hostsidetests/tagging/.clang-format
new file mode 100644
index 0000000..9a22ead
--- /dev/null
+++ b/hostsidetests/tagging/.clang-format
@@ -0,0 +1,6 @@
+BasedOnStyle: Google
+ColumnLimit: 100
+IndentWidth: 2
+AllowShortFunctionsOnASingleLine: Inline
+UseTab: Never
+
diff --git a/hostsidetests/tagging/common/Android.bp b/hostsidetests/tagging/common/Android.bp
index 468940e..147cbde 100644
--- a/hostsidetests/tagging/common/Android.bp
+++ b/hostsidetests/tagging/common/Android.bp
@@ -40,14 +40,10 @@
 
 android_test_helper_app {
     name: "DeviceKernelHelpers",
-    defaults: ["cts_defaults"],
-    compile_multilib: "both",
+    defaults: ["cts_tagging_app_defaults"],
     test_suites: [
         "cts",
         "general-tests",
     ],
-    static_libs: ["tagging-common-devicesidelib"],
-    jni_libs: ["libtagging-common-devicesidelib-jni"],
     srcs: ["src/**/DeviceKernelHelpers.java"],
-    sdk_version: "current",
 }
diff --git a/hostsidetests/tagging/common/AndroidManifest.xml b/hostsidetests/tagging/common/AndroidManifest.xml
index 41d0459..dc5ec58 100644
--- a/hostsidetests/tagging/common/AndroidManifest.xml
+++ b/hostsidetests/tagging/common/AndroidManifest.xml
@@ -21,16 +21,11 @@
 
     <application android:debuggable="true"
                  android:allowNativeHeapPointerTagging="true">
-        <activity android:name=".DeviceKernelHelpers">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
+        <uses-library android:name="android.test.runner" />
     </application>
 
     <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
         android:targetPackage="android.cts.tagging.support" />
 </manifest>
 
diff --git a/hostsidetests/tagging/common/jni/android_cts_tagging_Utils.cpp b/hostsidetests/tagging/common/jni/android_cts_tagging_Utils.cpp
index af48ace..05220eb 100644
--- a/hostsidetests/tagging/common/jni/android_cts_tagging_Utils.cpp
+++ b/hostsidetests/tagging/common/jni/android_cts_tagging_Utils.cpp
@@ -14,18 +14,14 @@
  * limitations under the License.
  */
 
-/*
- * Native implementation for the JniStaticTest parts.
- */
-
 #include <errno.h>
 #include <jni.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/prctl.h>
 #include <sys/utsname.h>
 
-extern "C" JNIEXPORT jboolean
-Java_android_cts_tagging_Utils_kernelSupportsTaggedPointers() {
+extern "C" JNIEXPORT jboolean Java_android_cts_tagging_Utils_kernelSupportsTaggedPointers() {
 #ifdef __aarch64__
 #define PR_SET_TAGGED_ADDR_CTRL 55
 #define PR_TAGGED_ADDR_ENABLE (1UL << 0)
@@ -36,8 +32,7 @@
 #endif
 }
 
-extern "C" JNIEXPORT jint JNICALL
-Java_android_cts_tagging_Utils_nativeHeapPointerTag(JNIEnv *) {
+extern "C" JNIEXPORT jint JNICALL Java_android_cts_tagging_Utils_nativeHeapPointerTag(JNIEnv *) {
 #ifdef __aarch64__
   void *p = malloc(10);
   jint tag = reinterpret_cast<uintptr_t>(p) >> 56;
@@ -48,22 +43,41 @@
 #endif
 }
 
-extern "C" __attribute__((no_sanitize("address", "hwaddress")))
-JNIEXPORT void JNICALL
+extern "C" __attribute__((no_sanitize("address", "hwaddress"))) JNIEXPORT void JNICALL
 Java_android_cts_tagging_Utils_accessMistaggedPointer(JNIEnv *) {
-  int* p = new int[4];
-  int* mistagged_p = reinterpret_cast<int*>(reinterpret_cast<uintptr_t>(p) + (1ULL << 56));
+  int *p = new int[4];
+  int *mistagged_p = reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(p) + (1ULL << 56));
   volatile int load = *mistagged_p;
   (void)load;
   delete[] p;
 }
 
-extern "C"
-JNIEXPORT jboolean JNICALL
+extern "C" JNIEXPORT jboolean JNICALL
 Java_android_cts_tagging_Utils_mistaggedKernelUaccessFails(JNIEnv *) {
   auto *p = new utsname;
-  utsname* mistagged_p = reinterpret_cast<utsname*>(reinterpret_cast<uintptr_t>(p) + (1ULL << 56));
+  utsname *mistagged_p = reinterpret_cast<utsname *>(reinterpret_cast<uintptr_t>(p) + (1ULL << 56));
   bool result = uname(mistagged_p) != 0 && errno == EFAULT;
   delete p;
   return result;
 }
+
+__attribute__((optnone)) static bool sizeIsZeroInitialized(size_t size) {
+  const int kCount = 200;
+  for (int i = 0; i < kCount; ++i) {
+    char *volatile p = reinterpret_cast<char *>(malloc(size));
+    for (int j = 0; j < size; ++j) {
+      if (p[j] != 0) {
+        free(p);
+        return false;
+      }
+    }
+    memset(p, 42, size);
+    free(p);
+  }
+  return true;
+}
+
+extern "C" JNIEXPORT jboolean JNICALL
+Java_android_cts_tagging_Utils_heapIsZeroInitialized(JNIEnv *) {
+  return sizeIsZeroInitialized(100) && sizeIsZeroInitialized(2000) && sizeIsZeroInitialized(200000);
+}
diff --git a/hostsidetests/tagging/common/src/android/cts/tagging/Utils.java b/hostsidetests/tagging/common/src/android/cts/tagging/Utils.java
index dbd62d3..4f5e387 100644
--- a/hostsidetests/tagging/common/src/android/cts/tagging/Utils.java
+++ b/hostsidetests/tagging/common/src/android/cts/tagging/Utils.java
@@ -24,4 +24,6 @@
     public static native int nativeHeapPointerTag();
     public static native void accessMistaggedPointer();
     public static native boolean mistaggedKernelUaccessFails();
+
+    public static native boolean heapIsZeroInitialized();
 }
diff --git a/hostsidetests/tagging/common/src/android/cts/tagging/support/DeviceKernelHelpers.java b/hostsidetests/tagging/common/src/android/cts/tagging/support/DeviceKernelHelpers.java
index 2453192..bb956bd 100644
--- a/hostsidetests/tagging/common/src/android/cts/tagging/support/DeviceKernelHelpers.java
+++ b/hostsidetests/tagging/common/src/android/cts/tagging/support/DeviceKernelHelpers.java
@@ -16,18 +16,20 @@
 
 package android.cts.tagging.support;
 
-import android.app.Activity;
 import android.cts.tagging.Utils;
-import android.os.Bundle;
 import android.util.Log;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
-public class DeviceKernelHelpers extends Activity {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DeviceKernelHelpers {
     private static final String TAG = DeviceKernelHelpers.class.getSimpleName();
 
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
+    @Test
+    public void logKernelTaggedAddressABISupport() {
         if (Utils.kernelSupportsTaggedPointers()) {
             Log.i(TAG, "Kernel supports tagged pointers: true");
         } else {
diff --git a/hostsidetests/tagging/sdk_30/AndroidManifest.xml b/hostsidetests/tagging/sdk_30/AndroidManifest.xml
index cc1c6c4..e381988 100644
--- a/hostsidetests/tagging/sdk_30/AndroidManifest.xml
+++ b/hostsidetests/tagging/sdk_30/AndroidManifest.xml
@@ -30,11 +30,21 @@
         <process android:process=":CrashMemtagAsync"
                  android:memtagMode="async" />
         <process android:process=":CrashProcess" />
+        <process android:process=":HeapZeroInitProcess"
+                 android:nativeHeapZeroInitialized="true" />
+        <process android:process=":HeapZeroInitMemtagAsyncProcess"
+                 android:memtagMode="async"
+                 android:nativeHeapZeroInitialized="true" />
       </processes>
 
+      <activity android:name=".TestActivity" />
+
       <activity android:name=".CrashActivity" android:process=":CrashProcess" />
       <activity android:name=".CrashMemtagSyncActivity" android:process=":CrashMemtagSync" />
       <activity android:name=".CrashMemtagAsyncActivity" android:process=":CrashMemtagAsync" />
+      <activity android:name=".HeapZeroInitActivity" android:process=":HeapZeroInitProcess" />
+      <activity android:name=".HeapZeroInitMemtagAsyncActivity"
+                android:process=":HeapZeroInitMemtagAsyncProcess" />
     </application>
 
     <instrumentation
diff --git a/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/HeapZeroInitActivity.java b/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/HeapZeroInitActivity.java
new file mode 100644
index 0000000..0ae44a8
--- /dev/null
+++ b/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/HeapZeroInitActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.tagging.sdk30;
+
+import android.app.Activity;
+import android.cts.tagging.Utils;
+import android.os.Bundle;
+import android.util.Log;
+
+public class HeapZeroInitActivity extends Activity {
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        boolean result = Utils.heapIsZeroInitialized();
+        setResult(RESULT_FIRST_USER + (result ? 1 : 0));
+        finish();
+    }
+}
diff --git a/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/HeapZeroInitMemtagAsyncActivity.java b/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/HeapZeroInitMemtagAsyncActivity.java
new file mode 100644
index 0000000..053681a
--- /dev/null
+++ b/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/HeapZeroInitMemtagAsyncActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.tagging.sdk30;
+
+import android.app.Activity;
+import android.cts.tagging.Utils;
+import android.os.Bundle;
+import android.util.Log;
+
+public class HeapZeroInitMemtagAsyncActivity extends Activity {
+    @Override
+    public void onCreate(Bundle bundle) {
+        super.onCreate(bundle);
+        boolean result = Utils.heapIsZeroInitialized();
+        setResult(RESULT_FIRST_USER + (result ? 1 : 0));
+        finish();
+    }
+}
diff --git a/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/TaggingTest.java b/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/TaggingTest.java
index 6261de1..cc5bf02 100644
--- a/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/TaggingTest.java
+++ b/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/TaggingTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.app.Instrumentation.ActivityResult;
 import android.content.Context;
 import android.content.Intent;
 import android.cts.tagging.Utils;
@@ -28,10 +29,12 @@
 
 import androidx.test.filters.SmallTest;
 import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.runner.RunWith;
+import org.junit.Rule;
 import org.junit.Test;
 
 import com.android.compatibility.common.util.DropBoxReceiver;
@@ -73,6 +76,7 @@
         mContext.startActivity(intent);
 
         assertTrue(receiver.await());
+        assertTrue(Utils.mistaggedKernelUaccessFails());
     }
 
     @Test
@@ -90,7 +94,7 @@
         mContext.startActivity(intent);
 
         assertTrue(receiver.await());
-        assertTrue(Utils.mistaggedKernelUaccessFails());
+        assertFalse(Utils.mistaggedKernelUaccessFails());
     }
 
     @Test
@@ -132,4 +136,23 @@
 
         assertTrue(receiver.await());
     }
+
+    @Rule
+    public ActivityTestRule<TestActivity> mTestActivityRule =
+            new ActivityTestRule<>(
+                    TestActivity.class, false /*initialTouchMode*/, true /*launchActivity*/);
+
+    @Test
+    public void testHeapZeroInitActivity() throws Exception {
+      TestActivity activity = mTestActivityRule.getActivity();
+      boolean result = activity.callActivity(HeapZeroInitActivity.class);
+      assertTrue(result);
+    }
+
+    @Test
+    public void testHeapZeroInitMemtagAsyncActivity() throws Exception {
+      TestActivity activity = mTestActivityRule.getActivity();
+      boolean result = activity.callActivity(HeapZeroInitMemtagAsyncActivity.class);
+      assertTrue(result);
+    }
 }
diff --git a/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/TestActivity.java b/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/TestActivity.java
new file mode 100644
index 0000000..ec099af
--- /dev/null
+++ b/hostsidetests/tagging/sdk_30/src/android/cts/tagging/sdk30/TestActivity.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.cts.tagging.sdk30;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import java.lang.Override;
+
+
+public class TestActivity extends Activity {
+    static final String TAG = "TestActivity";
+
+    private int mResult;
+    private final Object mFinishEvent = new Object();
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        mResult = resultCode;
+        synchronized (mFinishEvent) {
+            mFinishEvent.notify();
+        }
+    }
+
+    public boolean callActivity(Class<?> cls) throws Exception {
+        Thread thread = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    Context context = getApplicationContext();
+                    Intent intent = new Intent(context, cls);
+                    startActivityForResult(intent, 0);
+
+                    synchronized (mFinishEvent) {
+                        mFinishEvent.wait();
+                    }
+                } catch(Exception e) {
+                    Log.d(TAG, "callActivity got an exception " + e.toString());
+                }
+            }
+        };
+        thread.start();
+        thread.join(50000 /* millis */);
+
+        if (mResult == RESULT_OK) {
+          throw new Exception();
+        }
+        return (mResult - RESULT_FIRST_USER) == 1;
+    }
+}
diff --git a/hostsidetests/tagging/src/com/android/cts/tagging/TaggingBaseTest.java b/hostsidetests/tagging/src/com/android/cts/tagging/TaggingBaseTest.java
index 30d6841..6fa5cd9 100644
--- a/hostsidetests/tagging/src/com/android/cts/tagging/TaggingBaseTest.java
+++ b/hostsidetests/tagging/src/com/android/cts/tagging/TaggingBaseTest.java
@@ -25,9 +25,9 @@
     private static final String DEVICE_KERNEL_HELPER_CLASS_NAME = "DeviceKernelHelpers";
     private static final String DEVICE_KERNEL_HELPER_APK_NAME = "DeviceKernelHelpers.apk";
     private static final String DEVICE_KERNEL_HELPER_PKG_NAME = "android.cts.tagging.support";
-    private static final String KERNEL_HELPER_START_COMMAND =
-            String.format("am start -W -a android.intent.action.MAIN -n %s/.%s",
-                    DEVICE_KERNEL_HELPER_PKG_NAME, DEVICE_KERNEL_HELPER_CLASS_NAME);
+    private static final String KERNEL_HELPER_START_COMMAND = String.format(
+        "am instrument -w -e package %1$s %1$s/androidx.test.runner.AndroidJUnitRunner",
+        DEVICE_KERNEL_HELPER_PKG_NAME);
 
     protected static final long NATIVE_HEAP_POINTER_TAGGING_CHANGE_ID = 135754954;
     protected static final String DEVICE_TEST_CLASS_NAME = ".TaggingTest";
diff --git a/hostsidetests/tagging/src/com/android/cts/tagging/TaggingSdk30Test.java b/hostsidetests/tagging/src/com/android/cts/tagging/TaggingSdk30Test.java
index c43a015..b6510d7 100644
--- a/hostsidetests/tagging/src/com/android/cts/tagging/TaggingSdk30Test.java
+++ b/hostsidetests/tagging/src/com/android/cts/tagging/TaggingSdk30Test.java
@@ -144,4 +144,16 @@
                 /*enabledChanges*/ ImmutableSet.of(),
                 /*disabledChanges*/ ImmutableSet.of());
     }
+
+    public void testHeapZeroInitActivity() throws Exception {
+        runDeviceCompatTest(TEST_PKG, ".TaggingTest", "testHeapZeroInitActivity",
+                /*enabledChanges*/ ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+    }
+
+    public void testHeapZeroInitMemtagAsyncActivity() throws Exception {
+        runDeviceCompatTest(TEST_PKG, ".TaggingTest", "testHeapZeroInitMemtagAsyncActivity",
+                /*enabledChanges*/ ImmutableSet.of(),
+                /*disabledChanges*/ ImmutableSet.of());
+    }
 }
diff --git a/hostsidetests/theme/README b/hostsidetests/theme/README
index 4bf32cd..9ecbe7d 100644
--- a/hostsidetests/theme/README
+++ b/hostsidetests/theme/README
@@ -47,7 +47,7 @@
 
   4. From the console, set up your build environment for x86_64 and build CTS:
 
-     lunch sdk_x86_64-eng && make cts -j32
+     source build/envsetup.sh && lunch sdk_x86_64-eng && make cts -j32
 
   5. Use the reference image script to generate the reference images. The script
      will automatically start the emulator in the required configurations and
diff --git a/hostsidetests/theme/app/src/android/theme/app/AssetBucketVerifier.java b/hostsidetests/theme/app/src/android/theme/app/AssetBucketVerifier.java
deleted file mode 100644
index 09875b1..0000000
--- a/hostsidetests/theme/app/src/android/theme/app/AssetBucketVerifier.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.theme.app;
-
-import android.content.Context;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-class AssetBucketVerifier {
-    /** Asset file to verify. */
-    private static final String ASSET_NAME = "ic_star_black_16dp.png";
-
-    /** Densities at which {@link #ASSET_NAME} may be defined. */
-    private static final int[] DENSITIES_DPI = new int[] {
-            160, // mdpi
-            240, // hdpi
-            320, // xhdpi
-            480, // xxhdpi
-            640, // xxxhdpi
-    };
-
-    /** Bucket names corresponding to {@link #DENSITIES_DPI} entries. */
-    private static final String[] DENSITIES_NAME = new String[] {
-            "mdpi",
-            "hdpi",
-            "xhdpi",
-            "xxhdpi",
-            "xxxhdpi"
-    };
-
-    static class Result {
-        String expectedAtDensity;
-        List<String> foundAtDensity;
-    }
-
-    static Result verifyAssetBucket(Context context) {
-        List<String> foundAtDensity = new ArrayList<>();
-        String expectedAtDensity = null;
-
-        int deviceDensityDpi = context.getResources().getConfiguration().densityDpi;
-        for (int i = 0; i < DENSITIES_DPI.length; i++) {
-            // Find the matching or next-highest density bucket.
-            if (expectedAtDensity == null && DENSITIES_DPI[i] >= deviceDensityDpi) {
-                expectedAtDensity = DENSITIES_NAME[i];
-            }
-
-            // Try to load and close the asset from the current density.
-            try {
-                context.getAssets().openNonAssetFd(1,
-                        "res/drawable-" + DENSITIES_NAME[i] + "-v4/" + ASSET_NAME).close();
-                foundAtDensity.add(DENSITIES_NAME[i]);
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-
-        if (expectedAtDensity == null) {
-            expectedAtDensity = DENSITIES_NAME[DENSITIES_NAME.length - 1];
-        }
-
-        Result result = new Result();
-        result.expectedAtDensity = expectedAtDensity;
-        result.foundAtDensity = foundAtDensity;
-        return result;
-    }
-}
diff --git a/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
index 6be679b..b764752 100644
--- a/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/GenerateImagesActivity.java
@@ -65,41 +65,14 @@
         // Useful for local testing. Not required for CTS harness.
         getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
 
-        // Make sure the device has reasonable assets.
-        String assetDensityFailureMsg = checkAssetDensity();
-        if (assetDensityFailureMsg != null) {
-            finish("Failed to verify device assets: "+ assetDensityFailureMsg, false);
+        mOutputDir = setupOutputDirectory();
+        if (mOutputDir == null) {
+            finish("Failed to create output directory " + mOutputDir.getAbsolutePath(), false);
         } else {
-            mOutputDir = setupOutputDirectory();
-            if (mOutputDir == null) {
-                finish("Failed to create output directory: " + OUT_DIR, false);
-            } else {
-                generateNextImage();
-            }
+            generateNextImage();
         }
     }
 
-    private String checkAssetDensity() {
-        AssetBucketVerifier.Result result = AssetBucketVerifier.verifyAssetBucket(this);
-
-        String message;
-        if (result.foundAtDensity.contains(result.expectedAtDensity)) {
-            message = null;
-        } else if (result.foundAtDensity.isEmpty()) {
-            message = "Failed to find expected device assets at any density";
-        } else {
-            StringBuilder foundAtDensityStr = new StringBuilder(result.foundAtDensity.get(0));
-            for (int i = 1; i < result.foundAtDensity.size(); i++) {
-                foundAtDensityStr.append(", ");
-                foundAtDensityStr.append(result.foundAtDensity.get(i));
-            }
-            message = "Failed to find device assets at expected density ("
-                    + result.expectedAtDensity + "), but found at " + foundAtDensityStr;
-        }
-
-        return message;
-    }
-
     private File setupOutputDirectory() {
         mOutputDir = new File(Environment.getExternalStorageDirectory(), OUT_DIR);
         ThemeTestUtils.deleteDirectory(mOutputDir);
@@ -132,9 +105,8 @@
     private void generateNextImage() {
         // Keep trying themes until one works.
         boolean success = false;
-        while (mCurrentTheme < THEMES.length && !success) {
+        while (++mCurrentTheme < THEMES.length && !success) {
             success = launchThemeDeviceActivity();
-            mCurrentTheme++;
         }
 
         // If we ran out of themes, we're done.
diff --git a/hostsidetests/theme/assets/30/600dpi.zip b/hostsidetests/theme/assets/30/600dpi.zip
new file mode 100644
index 0000000..b9a13aa4
--- /dev/null
+++ b/hostsidetests/theme/assets/30/600dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/140dpi.zip b/hostsidetests/theme/assets/T/140dpi.zip
new file mode 100644
index 0000000..48de32b
--- /dev/null
+++ b/hostsidetests/theme/assets/T/140dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/180dpi.zip b/hostsidetests/theme/assets/T/180dpi.zip
new file mode 100644
index 0000000..4def986
--- /dev/null
+++ b/hostsidetests/theme/assets/T/180dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/200dpi.zip b/hostsidetests/theme/assets/T/200dpi.zip
new file mode 100644
index 0000000..fe2495e
--- /dev/null
+++ b/hostsidetests/theme/assets/T/200dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/220dpi.zip b/hostsidetests/theme/assets/T/220dpi.zip
new file mode 100644
index 0000000..a2c2ea2
--- /dev/null
+++ b/hostsidetests/theme/assets/T/220dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/260dpi.zip b/hostsidetests/theme/assets/T/260dpi.zip
new file mode 100644
index 0000000..74890a2
--- /dev/null
+++ b/hostsidetests/theme/assets/T/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/280dpi.zip b/hostsidetests/theme/assets/T/280dpi.zip
new file mode 100644
index 0000000..83fd290
--- /dev/null
+++ b/hostsidetests/theme/assets/T/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/300dpi.zip b/hostsidetests/theme/assets/T/300dpi.zip
new file mode 100644
index 0000000..25334d8
--- /dev/null
+++ b/hostsidetests/theme/assets/T/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/340dpi.zip b/hostsidetests/theme/assets/T/340dpi.zip
new file mode 100644
index 0000000..2092326
--- /dev/null
+++ b/hostsidetests/theme/assets/T/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/360dpi.zip b/hostsidetests/theme/assets/T/360dpi.zip
new file mode 100644
index 0000000..c13ad5c
--- /dev/null
+++ b/hostsidetests/theme/assets/T/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/400dpi.zip b/hostsidetests/theme/assets/T/400dpi.zip
new file mode 100644
index 0000000..1df1a95
--- /dev/null
+++ b/hostsidetests/theme/assets/T/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/420dpi.zip b/hostsidetests/theme/assets/T/420dpi.zip
new file mode 100644
index 0000000..d58d418
--- /dev/null
+++ b/hostsidetests/theme/assets/T/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/440dpi.zip b/hostsidetests/theme/assets/T/440dpi.zip
new file mode 100644
index 0000000..535102f
--- /dev/null
+++ b/hostsidetests/theme/assets/T/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/560dpi.zip b/hostsidetests/theme/assets/T/560dpi.zip
new file mode 100644
index 0000000..20f1c7b
--- /dev/null
+++ b/hostsidetests/theme/assets/T/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/hdpi.zip b/hostsidetests/theme/assets/T/hdpi.zip
new file mode 100644
index 0000000..582989d
--- /dev/null
+++ b/hostsidetests/theme/assets/T/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/ldpi.zip b/hostsidetests/theme/assets/T/ldpi.zip
new file mode 100644
index 0000000..3035146
--- /dev/null
+++ b/hostsidetests/theme/assets/T/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/mdpi.zip b/hostsidetests/theme/assets/T/mdpi.zip
new file mode 100644
index 0000000..a831e7b
--- /dev/null
+++ b/hostsidetests/theme/assets/T/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/tvdpi.zip b/hostsidetests/theme/assets/T/tvdpi.zip
new file mode 100644
index 0000000..bd4bf75
--- /dev/null
+++ b/hostsidetests/theme/assets/T/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/xhdpi.zip b/hostsidetests/theme/assets/T/xhdpi.zip
new file mode 100644
index 0000000..0dd72e2
--- /dev/null
+++ b/hostsidetests/theme/assets/T/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/xxhdpi.zip b/hostsidetests/theme/assets/T/xxhdpi.zip
new file mode 100644
index 0000000..64fc846
--- /dev/null
+++ b/hostsidetests/theme/assets/T/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/T/xxxhdpi.zip b/hostsidetests/theme/assets/T/xxxhdpi.zip
new file mode 100644
index 0000000..87beb9a
--- /dev/null
+++ b/hostsidetests/theme/assets/T/xxxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/generate_images.py b/hostsidetests/theme/generate_images.py
index 4c812b2..0b0d6d8 100755
--- a/hostsidetests/theme/generate_images.py
+++ b/hostsidetests/theme/generate_images.py
@@ -27,7 +27,10 @@
 from queue import Queue, Empty
 
 
-# This dict should contain one entry for every density listed in CDD 7.1.1.3.
+# This dict should contain one entry for every density listed in DisplayMetrics.
+# See CDD 7.1.1.3 for more information on densities at which CTS can run. If you
+# are only generating reference images for a single density, you can comment out
+# the other densities and then run the script.
 CTS_THEME_dict = {
     120: "ldpi",
     140: "140dpi",
@@ -49,6 +52,7 @@
     450: "450dpi",
     480: "xxhdpi",
     560: "560dpi",
+    600: "600dpi",
     640: "xxxhdpi",
 }
 
diff --git a/hostsidetests/ui/Android.bp b/hostsidetests/ui/Android.bp
new file mode 100644
index 0000000..6490706
--- /dev/null
+++ b/hostsidetests/ui/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+    name: "CtsUiHostTestCases",
+    defaults: ["cts_defaults"],
+    srcs: ["src/**/*.java"],
+    libs: [
+        "cts-tradefed",
+        "tradefed",
+        "compatibility-host-util",
+    ],
+
+    // *Not* tagged as a cts test artifact intentionally: b/109660132
+    //$(COMPATIBILITY_TESTCASES_OUT_cts)/CtsUiHostTestCases.jar : $(COMPATIBILITY_TESTCASES_OUT_cts)/com.replica.replicaisland.apk
+    test_suites: ["general-tests"],
+}
diff --git a/hostsidetests/ui/Android.mk b/hostsidetests/ui/Android.mk
deleted file mode 100644
index 096c33d..0000000
--- a/hostsidetests/ui/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := CtsUiHostTestCases
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed compatibility-host-util
-
-# *Not* tagged as a cts test artifact intentionally: b/109660132
-#$(COMPATIBILITY_TESTCASES_OUT_cts)/CtsUiHostTestCases.jar : $(COMPATIBILITY_TESTCASES_OUT_cts)/com.replica.replicaisland.apk
-LOCAL_COMPATIBILITY_SUITE := general-tests
-
-include $(BUILD_CTS_HOST_JAVA_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/deviceutillegacy/src/android/webkit/cts/OWNERS b/libs/deviceutillegacy/src/android/webkit/cts/OWNERS
new file mode 100644
index 0000000..a30553b
--- /dev/null
+++ b/libs/deviceutillegacy/src/android/webkit/cts/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 76427
+file:/tests/tests/webkit/OWNERS
diff --git a/libs/install/Android.bp b/libs/install/Android.bp
index 5b492a2..284f2fe 100644
--- a/libs/install/Android.bp
+++ b/libs/install/Android.bp
@@ -126,6 +126,7 @@
     static_libs: [
         "cts-install-lib-java",
     ],
+    min_sdk_version: "30",
 }
 
 java_library_host {
diff --git a/suite/audio_quality/client/Android.mk b/suite/audio_quality/client/Android.mk
index 6d60541..edef96b 100644
--- a/suite/audio_quality/client/Android.mk
+++ b/suite/audio_quality/client/Android.mk
@@ -24,6 +24,8 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAudioClient
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
 
diff --git a/tests/BlobStore/OWNERS b/tests/BlobStore/OWNERS
index bf870975..3a47c48 100644
--- a/tests/BlobStore/OWNERS
+++ b/tests/BlobStore/OWNERS
@@ -1,2 +1,2 @@
-# Bug component: 95221
+# Bug component: 533114
 include platform/frameworks/base:/apex/blobstore/OWNERS
diff --git a/tests/DropBoxManager/src/android/dropboxmanager/cts/DropBoxTests.java b/tests/DropBoxManager/src/android/dropboxmanager/cts/DropBoxTests.java
index 3b6c7da..879286f 100644
--- a/tests/DropBoxManager/src/android/dropboxmanager/cts/DropBoxTests.java
+++ b/tests/DropBoxManager/src/android/dropboxmanager/cts/DropBoxTests.java
@@ -133,12 +133,18 @@
         restoreDropboxDefaults();
     }
 
-    private void sendExcessiveDropBoxEntries(String tag, int count, long delayPerEntry)
+    private void sendExcessiveDropBoxEntries(String tag, int count, long interval)
             throws Exception {
+        // addText() can take dozens of milliseconds. In order to ensure addText is called at the
+        // given interval, we keep track of the timestamp when the next addText call should occur.
+        long nextTime = SystemClock.elapsedRealtime();
         int i = 0;
         mDropBoxManager.addText(tag, String.valueOf(i++));
         for (; i < count; i++) {
-            Thread.sleep(delayPerEntry);
+            nextTime += interval;
+            // Sleep until when we should send the next entry.
+            final long delay = nextTime - SystemClock.elapsedRealtime();
+            if (delay > 0) Thread.sleep(delay);
             mDropBoxManager.addText(tag, String.valueOf(i));
         }
     }
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
index faeb8f96..8bed13d 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
@@ -15,12 +15,15 @@
  */
 package android.jobscheduler.cts;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
 import android.annotation.TargetApi;
 import android.app.job.JobInfo;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
-import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
@@ -490,9 +493,11 @@
     static void setWifiState(final boolean enable,
             final ConnectivityManager cm, final WifiManager wm) throws InterruptedException {
         if (enable != isWiFiConnected(cm, wm)) {
-            NetworkRequest nr = new NetworkRequest.Builder().addCapability(
-                    NetworkCapabilities.NET_CAPABILITY_NOT_METERED).build();
-            NetworkTracker tracker = new NetworkTracker(false, enable, cm);
+            NetworkRequest nr = new NetworkRequest.Builder().clearCapabilities().build();
+            NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                    .addTransportType(TRANSPORT_WIFI)
+                    .build();
+            NetworkTracker tracker = new NetworkTracker(nc, enable, cm);
             cm.registerNetworkCallback(nr, tracker);
 
             if (enable) {
@@ -515,7 +520,16 @@
     }
 
     private static boolean isWiFiConnected(final ConnectivityManager cm, final WifiManager wm) {
-        return wm.isWifiEnabled() && cm.getActiveNetwork() != null && !cm.isActiveNetworkMetered();
+        if (!wm.isWifiEnabled()) {
+            return false;
+        }
+        final Network network = cm.getActiveNetwork();
+        if (network == null) {
+            return false;
+        }
+        final NetworkCapabilities networkCapabilities = cm.getNetworkCapabilities(network);
+        return networkCapabilities != null && networkCapabilities.hasTransport(TRANSPORT_WIFI)
+                && networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
     }
 
     /**
@@ -528,8 +542,11 @@
      */
     private void disconnectWifiToConnectToMobile() throws InterruptedException {
         if (mHasWifi && mWifiManager.isWifiEnabled()) {
-            NetworkRequest nr = new NetworkRequest.Builder().build();
-            NetworkTracker tracker = new NetworkTracker(true, true, mCm);
+            NetworkRequest nr = new NetworkRequest.Builder().clearCapabilities().build();
+            NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                    .addTransportType(TRANSPORT_CELLULAR)
+                    .build();
+            NetworkTracker tracker = new NetworkTracker(nc, true, mCm);
             mCm.registerNetworkCallback(nr, tracker);
 
             disconnectFromWifi();
@@ -556,7 +573,7 @@
 
         private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
 
-        private final boolean mNetworkMetered;
+        private final NetworkCapabilities mExpectedCapabilities;
 
         private final boolean mExpectedConnected;
 
@@ -569,16 +586,15 @@
             }
         };
 
-        private NetworkTracker(boolean networkMetered, boolean expectedConnected,
+        private NetworkTracker(NetworkCapabilities expectedCapabilities, boolean expectedConnected,
                 ConnectivityManager cm) {
-            mNetworkMetered = networkMetered;
+            mExpectedCapabilities = expectedCapabilities;
             mExpectedConnected = expectedConnected;
             mCm = cm;
         }
 
         @Override
-        public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
-                LinkProperties linkProperties, boolean blocked) {
+        public void onAvailable(Network network) {
             // Available doesn't mean it's the active network. We need to check that separately.
             checkActiveNetwork();
         }
@@ -594,20 +610,23 @@
         }
 
         private void checkActiveNetwork() {
+            mHandler.removeMessages(MSG_CHECK_ACTIVE_NETWORK);
             if (mReceiveLatch.getCount() == 0) {
                 return;
             }
 
+            Network activeNetwork = mCm.getActiveNetwork();
             if (mExpectedConnected) {
-                if (mCm.getActiveNetwork() != null
-                        && mNetworkMetered == mCm.isActiveNetworkMetered()) {
+                if (activeNetwork != null && mExpectedCapabilities.satisfiedByNetworkCapabilities(
+                        mCm.getNetworkCapabilities(activeNetwork))) {
                     mReceiveLatch.countDown();
                 } else {
                     mHandler.sendEmptyMessageDelayed(MSG_CHECK_ACTIVE_NETWORK, 5000);
                 }
             } else {
-                if (mCm.getActiveNetwork() != null
-                        && mNetworkMetered != mCm.isActiveNetworkMetered()) {
+                if (activeNetwork != null
+                        || !mExpectedCapabilities.satisfiedByNetworkCapabilities(
+                        mCm.getNetworkCapabilities(activeNetwork))) {
                     mReceiveLatch.countDown();
                 } else {
                     mHandler.sendEmptyMessageDelayed(MSG_CHECK_ACTIVE_NETWORK, 5000);
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index 7b6be1e..4dfe78b 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -32,7 +32,7 @@
     <string name="text_input_blah_blah">Blah blah</string>
 
     <!-- String title of the button -->
-    <string name="button_title">Click me</string>
+    <string name="button_title">Click</string>
 
     <string name="button_tooltip">Never press this button</string>
 
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
index 37a4997..d57794a 100755
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDetectorTest.java
@@ -69,7 +69,7 @@
 public class AccessibilityGestureDetectorTest {
 
     // Constants
-    private static final float GESTURE_LENGTH_INCHES = 1.0f;
+    private static final float GESTURE_LENGTH_INCHES = 2.0f;
     // The movement should exceed the threshold 1 cm in 150 ms defined in Swipe.java. It means the
     // swipe velocity in testing should be greater than 2.54 cm / 381 ms. Therefore the
     // duration should be smaller than 381.
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
index b0f3570..3ceab31 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/TouchExplorerTest.java
@@ -153,7 +153,11 @@
                     mView.getLocationOnScreen(viewLocation);
                     mTapLocation = new PointF(viewLocation[0] + midX, viewLocation[1] + midY);
                     mSwipeDistance = mView.getWidth() / 4;
-                    mSwipeTimeMillis = (long) mSwipeDistance * 4;
+
+                    // This must be slower than 10mm per 150ms to be detected as touch exploration.
+                    final double swipeDistanceMm = mSwipeDistance / metrics.xdpi * 25.4;
+                    mSwipeTimeMillis = (long) swipeDistanceMm * 20;
+
                     mView.setOnClickListener(mClickListener);
                     mView.setOnLongClickListener(mLongClickListener);
                 });
diff --git a/tests/app/Android.bp b/tests/app/Android.bp
index bc5125a..d66376a 100644
--- a/tests/app/Android.bp
+++ b/tests/app/Android.bp
@@ -83,6 +83,9 @@
     min_sdk_version: "14",
     manifest: "DownloadManagerApi28Test/AndroidManifest.xml",
     test_config: "DownloadManagerApi28Test/AndroidTest.xml",
+    lint: {
+    	baseline_filename: "lint-baseline-api-28.xml",
+    },
 }
 
 android_test {
@@ -117,6 +120,9 @@
     min_sdk_version: "14",
     manifest: "DownloadManagerInstallerTest/AndroidManifest.xml",
     test_config: "DownloadManagerInstallerTest/AndroidTest.xml",
+    lint: {
+    	baseline_filename: "lint-baseline-installer.xml",
+    },
 }
 
 android_test {
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index 77973bb..db74563 100644
--- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
+++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
@@ -825,6 +825,11 @@
         // Start a process and crash it
         startService(ACTION_NATIVE_CRASH, STUB_SERVICE_NAME, true, false);
 
+        // Native crashes are handled asynchronously from the actual crash, so
+        // it's possible for us to notice that the process crashed before an
+        // actual tombstone exists.
+        Thread.sleep(1000);
+
         long now2 = System.currentTimeMillis();
         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
diff --git a/tests/app/lint-baseline-api-28.xml b/tests/app/lint-baseline-api-28.xml
new file mode 100644
index 0000000..57e7aee
--- /dev/null
+++ b/tests/app/lint-baseline-api-28.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="`@android:style/Theme.Material.Dialog` requires API level 21 (current min is 14)"
+        errorLine1="    &lt;style name=&quot;DialogTheme_Test&quot; parent=&quot;@android:style/Theme.Material.Dialog&quot;>"
+        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/app/app/res/values/styles.xml"
+            line="168"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="View requires API level 21 (current min is 14): `&lt;Toolbar>`"
+        errorLine1="&lt;Toolbar android:id=&quot;@+id/toolbar&quot;"
+        errorLine2=" ~~~~~~~">
+        <location
+            file="cts/tests/app/app/res/layout/toolbar_activity.xml"
+            line="23"
+            column="2"/>
+    </issue>
+
+</issues>
diff --git a/tests/app/lint-baseline-installer.xml b/tests/app/lint-baseline-installer.xml
new file mode 100644
index 0000000..57e7aee
--- /dev/null
+++ b/tests/app/lint-baseline-installer.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="`@android:style/Theme.Material.Dialog` requires API level 21 (current min is 14)"
+        errorLine1="    &lt;style name=&quot;DialogTheme_Test&quot; parent=&quot;@android:style/Theme.Material.Dialog&quot;>"
+        errorLine2="                                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/app/app/res/values/styles.xml"
+            line="168"
+            column="36"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="View requires API level 21 (current min is 14): `&lt;Toolbar>`"
+        errorLine1="&lt;Toolbar android:id=&quot;@+id/toolbar&quot;"
+        errorLine2=" ~~~~~~~">
+        <location
+            file="cts/tests/app/app/res/layout/toolbar_activity.xml"
+            line="23"
+            column="2"/>
+    </issue>
+
+</issues>
diff --git a/tests/app/lint-baseline.xml b/tests/app/lint-baseline.xml
new file mode 100644
index 0000000..9bd7004
--- /dev/null
+++ b/tests/app/lint-baseline.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="`&lt;vector>` requires API level 21 (current min is 14) or building with Android Gradle plugin 1.4 or higher"
+        errorLine1="&lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
+        errorLine2=" ~~~~~~">
+        <location
+            file="cts/tests/app/res/drawable/ic_android.xml"
+            line="17"
+            column="2"/>
+    </issue>
+
+</issues>
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index 03012c8..19dc7a3 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -644,7 +644,7 @@
     }
 
     private boolean isAndroidEmulator() {
-        return PropertyUtil.propertyEquals("ro.kernel.qemu", "1");
+        return PropertyUtil.propertyEquals("ro.boot.qemu", "1");
     }
 
     private void assertFeature(boolean exist, String feature) {
diff --git a/tests/app/src/android/app/cts/UserHandleTest.java b/tests/app/src/android/app/cts/UserHandleTest.java
index 4f4b829..ec7cc40 100644
--- a/tests/app/src/android/app/cts/UserHandleTest.java
+++ b/tests/app/src/android/app/cts/UserHandleTest.java
@@ -60,15 +60,15 @@
     public void testGetUid() {
         assertEquals(
                 UserHandle.getUid(UserHandle.USER_ALL, TEST_APP_ID),
-                UserHandle.getUid(UserHandle.ALL, TEST_APP_ID));
+                UserHandle.ALL.getUid(TEST_APP_ID));
         assertEquals(
                 UserHandle.getUid(UserHandle.USER_SYSTEM, TEST_APP_ID),
-                UserHandle.getUid(UserHandle.SYSTEM, TEST_APP_ID));
+                UserHandle.SYSTEM.getUid(TEST_APP_ID));
         assertEquals(
-                UserHandle.getUid(UserHandle.USER_ALL, TEST_APP_ID),
+                UserHandle.ALL.getUid(TEST_APP_ID),
                 UserHandle.getUid(UserHandle.ALL.getIdentifier(), TEST_APP_ID));
         assertEquals(
-                UserHandle.getUid(UserHandle.USER_SYSTEM, TEST_APP_ID),
+                UserHandle.SYSTEM.getUid(TEST_APP_ID),
                 UserHandle.getUid(UserHandle.SYSTEM.getIdentifier(), TEST_APP_ID));
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
index af2837b..65f3697 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DisableAutofillTest.java
@@ -120,7 +120,7 @@
             // Asserts isEnabled() status.
             assertAutofillEnabled(activity, action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL);
         } finally {
-            activity.finish();
+            mUiBot.waitForWindowChange(() -> activity.finish());
         }
         return SystemClock.elapsedRealtime() - before;
     }
diff --git a/tests/bugreport/src/android/bugreport/cts/BugreportManagerTest.java b/tests/bugreport/src/android/bugreport/cts/BugreportManagerTest.java
index bea0dd1..975b08c 100644
--- a/tests/bugreport/src/android/bugreport/cts/BugreportManagerTest.java
+++ b/tests/bugreport/src/android/bugreport/cts/BugreportManagerTest.java
@@ -76,6 +76,7 @@
         assertThat(bp.getMode()).isEqualTo(expected_mode);
     }
 
+    @LargeTest
     @Test
     public void testTelephonyBugreport() throws Exception {
         Pair<String, String> brFiles = triggerBugreport(BugreportParams.BUGREPORT_MODE_TELEPHONY);
diff --git a/tests/camera/Android.mk b/tests/camera/Android.mk
index 749fed5..f2bf9fc 100644
--- a/tests/camera/Android.mk
+++ b/tests/camera/Android.mk
@@ -46,6 +46,8 @@
 LOCAL_COMPATIBILITY_SUITE := cts general-tests
 
 LOCAL_PACKAGE_NAME := CtsCameraTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 LOCAL_SDK_VERSION := test_current
 
diff --git a/tests/camera/api25test/Android.mk b/tests/camera/api25test/Android.mk
index c00b2d5..eaa99aa 100644
--- a/tests/camera/api25test/Android.mk
+++ b/tests/camera/api25test/Android.mk
@@ -32,6 +32,8 @@
     CtsCameraUtils
 
 LOCAL_PACKAGE_NAME := CtsCameraApi25TestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 LOCAL_SDK_VERSION := 25
 
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index f853468..a97ac9b 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -314,6 +314,7 @@
     ~CaptureResultListener() {
         std::unique_lock<std::mutex> l(mMutex);
         clearSavedRequestsLocked();
+        mCompletedFrameNumbers.clear();
         clearFailedLostFrameNumbersLocked();
     }
 
@@ -361,7 +362,7 @@
             thiz->mCompletedRequests.push_back(ACaptureRequest_copy(request));
         }
 
-        thiz->mLastCompletedFrameNumber = entry.data.i64[0];
+        thiz->mCompletedFrameNumbers.insert(entry.data.i64[0]);
         thiz->mResultCondition.notify_one();
     }
 
@@ -439,7 +440,7 @@
             thiz->mCompletedRequests.push_back(ACaptureRequest_copy(request));
         }
 
-        thiz->mLastCompletedFrameNumber = entry.data.i64[0];
+        thiz->mCompletedFrameNumbers.insert(entry.data.i64[0]);
         thiz->mResultCondition.notify_one();
     }
 
@@ -523,7 +524,7 @@
         bool ret = false;
         std::unique_lock<std::mutex> l(mMutex);
 
-        while ((mLastCompletedFrameNumber != frameNumber) &&
+        while ((mCompletedFrameNumbers.find(frameNumber) == mCompletedFrameNumbers.end()) &&
                 !checkForFailureOrLossLocked(frameNumber)) {
             auto timeout = std::chrono::system_clock::now() +
                            std::chrono::seconds(timeoutSec);
@@ -532,7 +533,7 @@
             }
         }
 
-        if ((mLastCompletedFrameNumber == frameNumber) ||
+        if ((mCompletedFrameNumbers.find(frameNumber) != mCompletedFrameNumbers.end()) ||
                 checkForFailureOrLossLocked(frameNumber)) {
             ret = true;
         }
@@ -571,9 +572,9 @@
         std::lock_guard<std::mutex> lock(mMutex);
         mLastSequenceIdCompleted = -1;
         mLastSequenceFrameNumber = -1;
-        mLastCompletedFrameNumber = -1;
         mSaveCompletedRequests = false;
         clearSavedRequestsLocked();
+        mCompletedFrameNumbers.clear();
         clearFailedLostFrameNumbersLocked();
     }
 
@@ -582,7 +583,7 @@
     std::condition_variable mResultCondition;
     int mLastSequenceIdCompleted = -1;
     int64_t mLastSequenceFrameNumber = -1;
-    int64_t mLastCompletedFrameNumber = -1;
+    std::set<int64_t> mCompletedFrameNumbers;
     std::set<int64_t> mFailedFrameNumbers, mBufferLostFrameNumbers;
     bool    mSaveCompletedRequests = false;
     std::vector<ACaptureRequest*> mCompletedRequests;
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index b9fc5a8..add4e2c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -17,6 +17,7 @@
 package android.hardware.camera2.cts;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
@@ -38,6 +39,7 @@
 import android.media.CamcorderProfile;
 import android.media.ImageReader;
 import android.os.Build;
+import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Rational;
@@ -93,6 +95,8 @@
     private static final Size HD = new Size(1280, 720);
     private static final Size VGA = new Size(640, 480);
     private static final Size QVGA = new Size(320, 240);
+    private static final Size UHD = new Size(3840, 2160);
+    private static final Size DC4K = new Size(4096, 2160);
 
     private static final long MIN_BACK_SENSOR_RESOLUTION = 2000000;
     private static final long MIN_FRONT_SENSOR_RESOLUTION = VGA.getHeight() * VGA.getWidth();
@@ -101,6 +105,10 @@
     private static final int MAX_NUM_IMAGES = 5;
     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_PERFORMANCE_CLASS_RESOLUTION = 12000000;
+    private static final long MIN_FRONT_SENSOR_PERFORMANCE_CLASS_RESOLUTION = 6000000;
+
     /*
      * HW Levels short hand
      */
@@ -601,7 +609,14 @@
                 c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
         Size arraySize = new Size(activeRect.width(), activeRect.height());
 
-        Set<Size> snapshotSizeSet = snapshotConfig.getOutputSizes(ImageFormat.JPEG);
+
+        ArraySet<Size> snapshotSizeSet = new ArraySet<>(snapshotConfig.getOutputSizes(
+                    ImageFormat.JPEG));
+        Set<Size> highResSnapshotSizeSet = snapshotConfig.getHighResolutionOutputSizes(
+                ImageFormat.JPEG);
+        if (highResSnapshotSizeSet != null) {
+            snapshotSizeSet.addAll(highResSnapshotSizeSet);
+        }
         Size[] snapshotSizes = new Size[snapshotSizeSet.size()];
         snapshotSizes = snapshotSizeSet.toArray(snapshotSizes);
         Size maxJpegSize = CameraTestUtils.getMaxSize(snapshotSizes);
@@ -2423,6 +2438,146 @@
     }
 
     /**
+     * Check camera characteristics for S Performance class requirements as specified
+     * in CDD camera section 7.5
+     */
+    @Test
+    @CddTest(requirement="7.5")
+    public void testCameraSPerfClassCharacteristics() throws Exception {
+        if (mAdoptShellPerm) {
+            // Skip test for system camera. Performance class is only applicable for public camera
+            // ids.
+            return;
+        }
+        boolean isSPerfClass = CameraTestUtils.isSPerfClass();
+        if (!isSPerfClass) {
+            return;
+        }
+
+        boolean hasPrimaryRear = false;
+        boolean hasPrimaryFront = false;
+        for (int i = 0; i < mCameraIdsUnderTest.length; i++) {
+            String cameraId = mCameraIdsUnderTest[i];
+            boolean isPrimaryRear = CameraTestUtils.isPrimaryRearFacingCamera(
+                    mCameraManager, cameraId);
+            boolean isPrimaryFront = CameraTestUtils.isPrimaryFrontFacingCamera(
+                    mCameraManager, cameraId);
+            if (!isPrimaryRear && !isPrimaryFront) {
+                continue;
+            }
+
+            CameraCharacteristics c = mCharacteristics.get(i);
+            StaticMetadata staticInfo = mAllStaticInfo.get(cameraId);
+
+            // H-1-1, H-1-2
+            Size pixelArraySize = CameraTestUtils.getValueNotNull(
+                    c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE);
+            long sensorResolution = pixelArraySize.getHeight() * pixelArraySize.getWidth();
+            StreamConfigurationMap config = staticInfo.getValueFromKeyNonNull(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            assertNotNull("No stream configuration map found for ID " + cameraId, config);
+            List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(cameraId,
+                    mCameraManager, null /*bound*/);
+
+            if (isPrimaryRear) {
+                hasPrimaryRear = true;
+                mCollector.expectTrue("Primary rear camera resolution should be at least " +
+                        MIN_BACK_SENSOR_PERFORMANCE_CLASS_RESOLUTION + " pixels, is "+
+                        sensorResolution,
+                        sensorResolution >= MIN_BACK_SENSOR_PERFORMANCE_CLASS_RESOLUTION);
+
+                // 720P @ 240fps
+                boolean supportHighSpeed = staticInfo.isCapabilitySupported(CONSTRAINED_HIGH_SPEED);
+                mCollector.expectTrue("Primary rear camera should support high speed recording",
+                        supportHighSpeed);
+                if (supportHighSpeed) {
+                    boolean supportHD240 = false;
+                    Size[] availableHighSpeedSizes = config.getHighSpeedVideoSizes();
+                    for (Size size : availableHighSpeedSizes) {
+                        if (!size.equals(HD)) {
+                            continue;
+                        }
+                        Range<Integer>[] availableFpsRanges =
+                                config.getHighSpeedVideoFpsRangesFor(size);
+                        for (Range<Integer> fpsRange : availableFpsRanges) {
+                            if (fpsRange.getUpper() == 240) {
+                                supportHD240 = true;
+                                break;
+                            }
+                        }
+                        if (supportHD240) {
+                            break;
+                        }
+                    }
+                    mCollector.expectTrue("Primary rear camera should support HD @ 240fps",
+                            supportHD240);
+                }
+
+                // 4K @ 30fps
+                boolean supportUHD = videoSizes.contains(UHD);
+                boolean supportDC4K = videoSizes.contains(DC4K);
+                mCollector.expectTrue("Primary rear camera should support 4k video recording",
+                        supportUHD || supportDC4K);
+                if (supportUHD || supportDC4K) {
+                    long minFrameDuration = config.getOutputMinFrameDuration(
+                            android.media.MediaRecorder.class, supportDC4K ? DC4K : UHD);
+                    mCollector.expectTrue("Primary rear camera should support 4k video @ 30fps",
+                            minFrameDuration < (1e9 / 29.9));
+                }
+            } else {
+                hasPrimaryFront = true;
+                mCollector.expectTrue("Primary front camera resolution should be at least " +
+                        MIN_FRONT_SENSOR_PERFORMANCE_CLASS_RESOLUTION + " pixels, is "+
+                        sensorResolution,
+                        sensorResolution >= MIN_FRONT_SENSOR_PERFORMANCE_CLASS_RESOLUTION);
+                // 1080P @ 30fps
+                boolean supportFULLHD = videoSizes.contains(FULLHD);
+                mCollector.expectTrue("Primary front camera should support 1080P video recording",
+                        supportFULLHD);
+                if (supportFULLHD) {
+                    long minFrameDuration = config.getOutputMinFrameDuration(
+                            android.media.MediaRecorder.class, FULLHD);
+                    mCollector.expectTrue("Primary front camera should support 1080P video @ 30fps",
+                            minFrameDuration < (1e9 / 29.9));
+                }
+            }
+
+            // H-1-3
+            mCollector.expectTrue("Primary rear/front camera should be at least FULL, but is " +
+                   staticInfo.getHardwareLevelChecked(), staticInfo.isHardwareLevelAtLeastFull());
+
+            // H-1-4
+            if (isPrimaryRear) {
+                mCollector.expectTrue("Primary rear camera should support RAW capability",
+                        staticInfo.isCapabilitySupported(RAW));
+            }
+
+            // H-1-5
+            Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
+            mCollector.expectTrue(
+                    "Primary rear/front camera should support real-time timestamp source",
+                    timestampSource != null &&
+                    timestampSource.equals(CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME));
+
+            // H-1-8
+            Size[] jpegSizes = staticInfo.getJpegOutputSizesChecked();
+            assertTrue("Primary rear/front cameras must support JPEG formats",
+                    jpegSizes != null && jpegSizes.length > 0);
+            for (Size jpegSize : jpegSizes) {
+                mCollector.expectTrue(
+                        "Primary rear/front camera's JPEG size must be at least 1080p, but is " +
+                        jpegSize,
+                        jpegSize.getWidth() >= FULLHD.getWidth() &&
+                        jpegSize.getHeight() >= FULLHD.getHeight());
+            }
+        }
+        mCollector.expectTrue("There must be a primary rear camera for S performance class.",
+                hasPrimaryRear);
+        mCollector.expectTrue("There must be a primary front camera for S performance class.",
+                hasPrimaryFront);
+    }
+
+    /**
      * Get lens distortion coefficients, as a list of 6 floats; returns null if no valid
      * distortion field is available
      */
diff --git a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
index b7b5061..10632ff 100644
--- a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -321,6 +321,7 @@
     private void testSingleCaptureForFormat(int[] formats, String formatDescription,
             boolean addPreviewDelay) throws Exception {
         double[] avgResultTimes = new double[mTestRule.getCameraIdsUnderTest().length];
+        double[] avgCaptureTimes = new double[mTestRule.getCameraIdsUnderTest().length];
 
         int counter = 0;
         for (String id : mTestRule.getCameraIdsUnderTest()) {
@@ -376,11 +377,12 @@
                     SimpleImageListener[] imageListeners = new SimpleImageListener[formats.length];
                     Size[] imageSizes = new Size[formats.length];
                     for (int j = 0; j < formats.length; j++) {
+                        Size sizeBound = mTestRule.isPerfClassTest() ? new Size(1920, 1080) : null;
                         imageSizes[j] = CameraTestUtils.getSortedSizesForFormat(
                                 id,
                                 mTestRule.getCameraManager(),
                                 formats[j],
-                                /*bound*/null).get(0);
+                                sizeBound).get(0);
                         imageListeners[j] = new SimpleImageListener();
                     }
 
@@ -454,6 +456,7 @@
                         ResultUnit.MS);
 
                 avgResultTimes[counter] = Stat.getAverage(getResultTimes);
+                avgCaptureTimes[counter] = Stat.getAverage(captureTimes);
             }
             finally {
                 CameraTestUtils.closeImageReaders(readers);
@@ -470,10 +473,19 @@
             String streamName = appendFormatDescription("test_single_capture_average",
                     formatDescription);
             mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
-            String message = appendFormatDescription(
-                    "camera_capture_result_average_latency_for_all_cameras", formatDescription);
-            mReportLog.setSummary(message, Stat.getAverage(avgResultTimes),
-                    ResultType.LOWER_BETTER, ResultUnit.MS);
+            // In performance measurement mode, capture the buffer latency rather than result
+            // latency.
+            if (mTestRule.isPerfMeasure()) {
+                String message = appendFormatDescription(
+                        "camera_capture_average_latency_for_all_cameras", formatDescription);
+                mReportLog.setSummary(message, Stat.getAverage(avgCaptureTimes),
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+            } else {
+                String message = appendFormatDescription(
+                        "camera_capture_result_average_latency_for_all_cameras", formatDescription);
+                mReportLog.setSummary(message, Stat.getAverage(avgResultTimes),
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+            }
             mReportLog.submit(mInstrumentation);
         }
     }
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java
index 394f346..6f9eff1 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestRule.java
@@ -92,9 +92,11 @@
 
     private static final String CAMERA_ID_INSTR_ARG_KEY = "camera-id";
     private static final String CAMERA_PERF_MEASURE = "perf-measure";
+    private static final String CAMERA_PERF_CLASS_TEST = "perf-class-test";
     private static final Bundle mBundle = InstrumentationRegistry.getArguments();
     private static final String mOverrideCameraId = mBundle.getString(CAMERA_ID_INSTR_ARG_KEY);
     private static final String mPerfMeasure = mBundle.getString(CAMERA_PERF_MEASURE);
+    private static final String mPerfClassTest = mBundle.getString(CAMERA_PERF_CLASS_TEST);
 
     public Camera2AndroidTestRule(Context context) {
         mContext = context;
@@ -188,6 +190,10 @@
         return mPerfMeasure != null && mPerfMeasure.equals("on");
     }
 
+    public boolean isPerfClassTest() {
+        return mPerfClassTest != null && mPerfClassTest.equals("on");
+    }
+
     private String[] deriveCameraIdsUnderTest() throws Exception {
         String[] idsUnderTest = mCameraManager.getCameraIdList();
         assertNotNull("Camera ids shouldn't be null", idsUnderTest);
diff --git a/tests/camera/src/android/hardware/cts/CameraTest.java b/tests/camera/src/android/hardware/cts/CameraTest.java
index cedf82c..a34465f 100644
--- a/tests/camera/src/android/hardware/cts/CameraTest.java
+++ b/tests/camera/src/android/hardware/cts/CameraTest.java
@@ -67,7 +67,7 @@
 import java.util.List;
 import java.util.TimeZone;
 
-import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.WindowUtil;
 
 /**
  * This test case must run with hardware. It can't be tested in emulator
@@ -139,7 +139,7 @@
         // Some of the tests run on the UI thread. In case some of the operations take a long time to complete,
         // wait for window to receive focus. This ensure that the focus event from input flinger has been handled,
         // and avoids getting ANR.
-        PollingCheck.waitFor(mActivityRule.getActivity()::hasWindowFocus);
+        WindowUtil.waitForFocus(mActivityRule.getActivity());
         mCameraIds = CameraUtils.deriveCameraIdsUnderTest();
     }
 
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 6737b54..7147442 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -52,6 +52,7 @@
 import android.media.Image.Plane;
 import android.os.Build;
 import android.os.Handler;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
@@ -3205,4 +3206,52 @@
         }
         return targetRange;
     }
+
+    private static final int perfClass = SystemProperties.getInt(
+            "ro.odm.build.media_performance_class", 0);
+
+    private static final int PERFORMANCE_CLASS_S = Build.VERSION_CODES.R + 1;
+
+    /**
+     * Check whether this mobile device is S performance class as defined in CDD
+     */
+    public static boolean isSPerfClass() {
+        return perfClass == PERFORMANCE_CLASS_S;
+    }
+
+    /**
+     * Check whether a camera Id is a primary rear facing camera
+     */
+    public static boolean isPrimaryRearFacingCamera(CameraManager manager, String cameraId)
+            throws Exception {
+        return isPrimaryCamera(manager, cameraId, CameraCharacteristics.LENS_FACING_BACK);
+    }
+
+    /**
+     * Check whether a camera Id is a primary front facing camera
+     */
+    public static boolean isPrimaryFrontFacingCamera(CameraManager manager, String cameraId)
+            throws Exception {
+        return isPrimaryCamera(manager, cameraId, CameraCharacteristics.LENS_FACING_FRONT);
+    }
+
+    private static boolean isPrimaryCamera(CameraManager manager, String cameraId,
+            Integer lensFacing) throws Exception {
+        CameraCharacteristics characteristics;
+        Integer facing;
+
+        String [] ids = manager.getCameraIdList();
+        for (String id : ids) {
+            characteristics = manager.getCameraCharacteristics(id);
+            facing = characteristics.get(CameraCharacteristics.LENS_FACING);
+            if (lensFacing.equals(facing)) {
+                if (cameraId.equals(id)) {
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+        }
+        return false;
+    }
 }
diff --git a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
index 72a8318..05d43b7 100644
--- a/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
+++ b/tests/camera/utils/src/android/hardware/cts/helpers/CameraUtils.java
@@ -81,6 +81,11 @@
         Integer facing = ch.get(CameraCharacteristics.LENS_FACING);
         switch (facing.intValue()) {
             case CameraMetadata.LENS_FACING_EXTERNAL:
+                if (info.facing != Camera.CameraInfo.CAMERA_FACING_FRONT &&
+                    info.facing != Camera.CameraInfo.CAMERA_FACING_BACK) {
+                    return false;
+                }
+                break;
             case CameraMetadata.LENS_FACING_FRONT:
                 if (info.facing != Camera.CameraInfo.CAMERA_FACING_FRONT) {
                     return false;
diff --git a/tests/framework/base/windowmanager/Android.mk b/tests/framework/base/windowmanager/Android.mk
index 8a7f090..c4fb78e 100644
--- a/tests/framework/base/windowmanager/Android.mk
+++ b/tests/framework/base/windowmanager/Android.mk
@@ -30,6 +30,8 @@
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/intent_tests
 
 LOCAL_PACKAGE_NAME := CtsWindowManagerDeviceTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
 
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index ca0ea74..c66a71a 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -139,6 +139,9 @@
 
         <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$ResultActivity"/>
 
+        <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$TranslucentResultActivity"
+                  android:theme="@android:style/Theme.Dialog"/>
+
         <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$SingleTopActivity"
                   android:launchMode="singleTop" />
 
diff --git a/tests/framework/base/windowmanager/OWNERS b/tests/framework/base/windowmanager/OWNERS
index 2efb8fc..aa788b8 100644
--- a/tests/framework/base/windowmanager/OWNERS
+++ b/tests/framework/base/windowmanager/OWNERS
@@ -1,4 +1,31 @@
-# Bug component: 316125
-include ../OWNERS
-louischang@google.com
-riddlehsu@google.com
+# animation & tranistion
+# Bug template url: https://b.corp.google.com/issues/new?component=316275&template=1018192 = per-file *Transition*, SplashscreenTests.java
+# foldables
+# Bug template url: https://b.corp.google.com/issues/new?component=943781&template=1502790 = per-file *WindowContext*, *WindowMetrics*
+# insets & cutout
+# Bug template url: https://b.corp.google.com/issues/new?component=339570&template=1037597 = per-file *Inset*, *Cutout*, *RoundedCorner*, ForceRelayoutTest.java
+# shell
+# Bug template url: https://b.corp.google.com/issues/new?component=928594&template=1490782 = per-file DragDropTest.java
+# surface
+# Bug template url: https://b.corp.google.com/issues/new?component=316245&template=1018194 = per-file DisplayHashManagerTest.java, *Surface*
+# activity
+# Bug template url: https://b.corp.google.com/issues/new?component=316020&template=1018174 = per-file *Config*, Activity*, AmStartOptionsTests.java, AspectRatioTests.java, AssistantStackTests.java, CloseOnOutsideTests.java
+# multi-display
+# Bug template url: https://b.corp.google.com/issues/new?component=316280&template=1018196 = per-file MultiDisplay*, *DisplayTest*, PresentationTest.java
+# pip
+# Bug template url: https://b.corp.google.com/issues/new?component=316251&template=1018197 = per-file PinnedStackTests.java
+# bubbles
+# Bug template url: https://b.corp.google.com/issues/new?component=555586&template=1210986 = per-file ActivityViewTest.java
+# split-screen
+# Bug template url: https://b.corp.google.com/issues/new?component=928697&template=1490890 = per-file MultiWindowTests.java, ReplaceWindowTests.java
+# app-compat
+# Bug template url: https://b.corp.google.com/issues/new?component=970984&template=1516678 = per-file *Compat*, DeprecatedTargetSdkTest.java, DisplaySizeTest.java, PrereleaseSdkTest.java
+# freeform
+# Bug template url: https://b.corp.google.com/issues/new?component=929241&template=1490914 = per-file *Freeform*
+# input
+# Bug template url: https://b.corp.google.com/issues/new?component=136048&template=115201 = per-file *Input*, *Touch*
+# Ime
+# Bug template url: https://b.corp.google.com/issues/new?component=34867&template=195938 = per-file *Ime*
+# others
+# Bug template url: https://b.corp.google.com/issues/new?component=316125&template=1018199
+include platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml
index c6ab9f2..2a800eb 100755
--- a/tests/framework/base/windowmanager/app/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml
@@ -434,7 +434,7 @@
 
         <activity android:name=".ShowWhenLockedAttrRotationActivity"
                   android:showWhenLocked="true"
-                  android:configChanges="orientation|screenSize"
+                  android:configChanges="orientation|screenSize|screenLayout"
                   android:exported="true" />
 
         <activity android:name=".ToastActivity"
diff --git a/tests/framework/base/windowmanager/jetpack/OWNERS b/tests/framework/base/windowmanager/jetpack/OWNERS
new file mode 100644
index 0000000..72fa917
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/OWNERS
@@ -0,0 +1,2 @@
+# Bug template url: https://b.corp.google.com/issues/new?component=812646&template=1393556
+akulian@google.com
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
index d777db2..7259720 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
@@ -68,6 +68,7 @@
 public class AssistantStackTests extends ActivityManagerTestBase {
 
     private int mAssistantDisplayId = DEFAULT_DISPLAY;
+    private int mDefaultWindowingMode;
 
     public void setUp() throws Exception {
         super.setUp();
@@ -78,6 +79,7 @@
             WindowManagerState.ActivityTask assistantStack =
                     mWmState.getStackByActivityType(ACTIVITY_TYPE_ASSISTANT);
             mAssistantDisplayId = assistantStack.mDisplayId;
+            mDefaultWindowingMode = getDefaultDisplayWindowingMode();
         }
     }
 
@@ -92,6 +94,8 @@
 
             // Ensure that the activity launched in the fullscreen assistant stack
             assertAssistantStackExists();
+            // In a multi-window environment the assistant might not be fullscreen
+            assumeTrue(mDefaultWindowingMode == WINDOWING_MODE_FULLSCREEN);
             assertTrue("Expected assistant stack to be fullscreen",
                     mWmState.getStackByActivityType(
                             ACTIVITY_TYPE_ASSISTANT).isFullscreen());
@@ -137,7 +141,7 @@
 
     @Test
     public void testAssistantStackLaunchNewTask() throws Exception {
-        assertAssistantStackCanLaunchAndReturnFromNewTask(WINDOWING_MODE_FULLSCREEN);
+        assertAssistantStackCanLaunchAndReturnFromNewTask(mDefaultWindowingMode);
     }
 
     @Test
@@ -183,9 +187,9 @@
                     WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
         } else {
             mWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY);
-            mWmState.assertFrontStack("Fullscreen stack should be on top.",
+            mWmState.assertFrontStack("TestActivity stack should be on top.",
                     expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
-            mWmState.assertFocusedStack("Fullscreen stack should be focused.",
+            mWmState.assertFocusedStack("TestActivity stack should be focused.",
                     expectedWindowingMode, ACTIVITY_TYPE_STANDARD);
         }
 
@@ -219,14 +223,14 @@
                     getActivityName(ASSISTANT_ACTIVITY) + " finished");
         }
         waitForValidStateWithActivityTypeAndWindowingMode(
-                TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, WINDOWING_MODE_FULLSCREEN);
+                TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, mDefaultWindowingMode);
         waitAndAssertTopResumedActivity(TEST_ACTIVITY, mAssistantDisplayId,
                 "TestActivity should be resumed");
         mWmState.assertFocusedActivity("TestActivity should be focused", TEST_ACTIVITY);
         mWmState.assertFrontStack("Fullscreen stack should be on top.",
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+                mDefaultWindowingMode, ACTIVITY_TYPE_STANDARD);
         mWmState.assertFocusedStack("Fullscreen stack should be focused.",
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+                mDefaultWindowingMode, ACTIVITY_TYPE_STANDARD);
     }
 
     @Test
@@ -282,9 +286,12 @@
                     EXTRA_ASSISTANT_IS_TRANSLUCENT, "true",
                     EXTRA_ASSISTANT_LAUNCH_NEW_TASK, getActivityName(TEST_ACTIVITY));
             waitForValidStateWithActivityTypeAndWindowingMode(
-                    TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, WINDOWING_MODE_FULLSCREEN);
+                    TEST_ACTIVITY, ACTIVITY_TYPE_STANDARD, mDefaultWindowingMode);
 
             final ComponentName homeActivity = mWmState.getHomeActivityName();
+            int windowingMode = mWmState.getFocusedStackWindowingMode();
+            // In a multi-window environment the home activity might not be fully covered
+            assumeTrue(windowingMode == WINDOWING_MODE_FULLSCREEN);
             mWmState.waitAndAssertVisibilityGone(homeActivity);
             mBroadcastActionTrigger.doAction(TEST_ACTIVITY_ACTION_FINISH_SELF);
             mWmState.waitForFocusedStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
@@ -410,6 +417,14 @@
         return mAssistantDisplayId == DEFAULT_DISPLAY;
     }
 
+    /**
+     * @return Windowing Mode from the default display
+     */
+    private int getDefaultDisplayWindowingMode() {
+        mWmState.computeState();
+        return mWmState.getDisplay(DEFAULT_DISPLAY).getWindowingMode();
+    }
+
     /** Helper class to save, set, and restore
      * {@link Settings.Secure#VOICE_INTERACTION_SERVICE} system preference.
      */
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java
index ddc8163..4edd318 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java
@@ -33,6 +33,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.view.Surface;
+import android.content.res.Resources;
 
 import androidx.test.filters.FlakyTest;
 
@@ -62,7 +63,9 @@
     }
 
     @Before
-    public void setDreamEnabled() {
+    public void setup() {
+        assumeTrue("Skipping test: no dream support", supportsDream());
+
         mDefaultDreamServiceEnabled =
                 Settings.Secure.getInt(mContext.getContentResolver(),
                                 "screensaver_enabled", 1) != 0;
@@ -74,7 +77,7 @@
     }
 
     @After
-    public void resetDreamEnabled()  {
+    public void reset()  {
         if (!mDefaultDreamServiceEnabled) {
             SystemUtil.runWithShellPermissionIdentity(() -> {
                 Settings.Secure.putInt(mContext.getContentResolver(), "screensaver_enabled", 0);
@@ -111,6 +114,11 @@
         });
     }
 
+    private boolean supportsDream() {
+        return mContext.getResources().getBoolean(
+                Resources.getSystem().getIdentifier("config_dreamsSupported", "bool", "android"));
+    }
+
     private void assertDreamActivityGone() {
         mWmState.computeState();
         assertTrue(!mWmState.containsWindow(getWindowName(mDreamActivityName))
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
index 115310f..1654839 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
@@ -390,7 +390,7 @@
 
         lockScreenSession.setLockCredential().gotoKeyguard();
         assertTrue("Keyguard is showing", mWmState.getKeyguardControllerState().keyguardShowing);
-        lockScreenSession.enterAndConfirmLockCredential();
+        lockScreenSession.unlockDevice().enterAndConfirmLockCredential();
         mWmState.waitAndAssertKeyguardGone();
 
         final ImeEventStream stream = mockImeSession.openEventStream();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
index 44bd353..86de555 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -196,6 +196,11 @@
      */
     @Test
     public void testLaunchExternalDisplayActivityWhilePrimaryOff() {
+        if (isOperatorTierDevice()) {
+            // This test is not applicable for the device who uses launch_after_boot configuration
+            return;
+        }
+
         // Launch something on the primary display so we know there is a resumed activity there
         launchActivity(RESIZEABLE_ACTIVITY);
         waitAndAssertTopResumedActivity(RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY,
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
index 0dd955e..2e1b5c9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
@@ -543,9 +543,9 @@
 
         // Expect onStartInput / showSoftInput would be executed when user tapping on the
         // non-system created display intentionally.
-        final Rect drawRect = new Rect();
-        imeTestActivitySession.getActivity().mEditText.getDrawingRect(drawRect);
-        tapOnDisplaySync(drawRect.left, drawRect.top, newDisplay.mId);
+        final int[] location = new int[2];
+        imeTestActivitySession.getActivity().mEditText.getLocationOnScreen(location);
+        tapOnDisplaySync(location[0], location[1], newDisplay.mId);
 
         // Verify the activity to show soft input on the default display.
         final ImeEventStream stream = mockImeSession.openEventStream();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
index 35b06ba..46b706a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
@@ -16,6 +16,7 @@
 
 package android.server.wm;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.server.wm.WindowInsetsAnimationControllerTests.ControlListener.Event.CANCELLED;
 import static android.server.wm.WindowInsetsAnimationControllerTests.ControlListener.Event.FINISHED;
 import static android.server.wm.WindowInsetsAnimationControllerTests.ControlListener.Event.READY;
@@ -152,7 +153,7 @@
             mockImeEventStream = null;
         }
 
-        mActivity = startActivity(TestActivity.class);
+        mActivity = startActivityInWindowingMode(TestActivity.class, WINDOWING_MODE_FULLSCREEN);
         mRootView = mActivity.getWindow().getDecorView();
         mListener = new ControlListener(mErrorCollector);
         assumeTestCompatibility();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
index b384588..60afe29 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
@@ -408,7 +408,8 @@
 
         // Swiping from top of display can show bars.
         dragFromTopToCenter(rootView);
-        PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types));
+        PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types)
+            && rootView.getSystemUiVisibility() != targetFlags);
 
         // Use flags to hide status bar again.
         ANIMATION_CALLBACK.reset();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsLayoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsLayoutTests.java
index e622ad3..4838c01 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsLayoutTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsLayoutTests.java
@@ -27,15 +27,15 @@
 import static org.junit.Assert.assertEquals;
 
 import android.graphics.Insets;
-import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
-import android.view.Display;
 import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Side;
 import android.view.WindowInsets.Type;
 import android.view.WindowManager;
+import android.view.WindowMetrics;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.FlakyTest;
@@ -68,7 +68,7 @@
         PollingCheck.waitFor(TIMEOUT, () -> mainWindowRoot.getWidth() > 0);
 
         getInstrumentation().runOnMainSync(() -> {
-            activity.assertMatchDisplay();
+            activity.assertMatchesWindowBounds();
         });
 
         testSetFitInsetsTypesInner(Type.statusBars(), activity, mainWindowRoot);
@@ -112,7 +112,7 @@
         PollingCheck.waitFor(TIMEOUT, () -> mainWindowRoot.getWidth() > 0);
 
         getInstrumentation().runOnMainSync(() -> {
-            activity.assertMatchDisplay();
+            activity.assertMatchesWindowBounds();
         });
 
         testSetFitInsetsSidesInner(Side.LEFT, activity, mainWindowRoot);
@@ -163,7 +163,7 @@
         final int[] locationAndSize2 = new int[4];
 
         getInstrumentation().runOnMainSync(() -> {
-            activity.assertMatchDisplay();
+            activity.assertMatchesWindowBounds();
             activity.addChildWindow(types, sides, false);
         });
 
@@ -232,17 +232,16 @@
             return mChildWindowRoot;
         }
 
-        void assertMatchDisplay() {
+        void assertMatchesWindowBounds() {
             final View rootView = getWindow().getDecorView();
-            final Display display = rootView.getDisplay();
-            final Point size = new Point();
-            display.getRealSize(size);
-            assertEquals(size.x, rootView.getWidth());
-            assertEquals(size.y, rootView.getHeight());
+            final Rect windowMetricsBounds =
+                    getWindowManager().getCurrentWindowMetrics().getBounds();
+            assertEquals(windowMetricsBounds.width(), rootView.getWidth());
+            assertEquals(windowMetricsBounds.height(), rootView.getHeight());
             final int[] locationOnScreen = new int[2];
             rootView.getLocationOnScreen(locationOnScreen);
-            assertEquals(0 /* expected x */, locationOnScreen[0]);
-            assertEquals(0 /* expected y */, locationOnScreen[1]);
+            assertEquals(locationOnScreen[0] /* expected x */, windowMetricsBounds.left);
+            assertEquals(locationOnScreen[1] /* expected y */, windowMetricsBounds.top);
         }
     }
 }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/OWNERS b/tests/framework/base/windowmanager/src/android/server/wm/intent/OWNERS
new file mode 100644
index 0000000..b923e94
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/OWNERS
@@ -0,0 +1,2 @@
+# Bug template url: https://b.corp.google.com/issues/new?component=316020&template=1018174
+louischang@google.com
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java
index 58b6325..0308a38 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleClientTestBase.java
@@ -496,6 +496,8 @@
         public static final String EXTRA_LAUNCH_ON_RESULT = "LAUNCH_ON_RESULT";
         public static final String EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT =
                 "LAUNCH_ON_RESUME_AFTER_RESULT";
+        public static final String EXTRA_USE_TRANSLUCENT_RESULT =
+                "USE_TRANSLUCENT_RESULT";
 
         boolean mReceivedResultOk;
 
@@ -511,7 +513,14 @@
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
-            final Intent intent = new Intent(this, ResultActivity.class);
+
+            final Intent intent;
+            if (getIntent().hasExtra(EXTRA_USE_TRANSLUCENT_RESULT)) {
+                intent = new Intent(this, TranslucentResultActivity.class);
+            } else {
+                intent = new Intent(this, ResultActivity.class);
+            }
+
             final Bundle forwardExtras = getIntent().getBundleExtra(EXTRA_FORWARD_EXTRAS);
             if (forwardExtras != null) {
                 intent.putExtras(forwardExtras);
@@ -539,6 +548,15 @@
     }
 
     /** Test activity that is started for result. */
+    public static class TranslucentResultActivity extends CallbackTrackingActivity {
+        @Override
+        protected void onCreate(Bundle savedInstanceState) {
+            setResult(RESULT_OK);
+            super.onCreate(savedInstanceState);
+        }
+    }
+
+    /** Test activity that is started for result. */
     public static class ResultActivity extends CallbackTrackingActivity {
         @Override
         protected void onCreate(Bundle savedInstanceState) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java
index 3c8132c..22001a5 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleFreeformTests.java
@@ -135,7 +135,7 @@
                 .setOptions(launchOptions)
                 .launch();
 
-        final Activity secondActivity = launchActivityInFullscreenAndWait(SecondActivity.class);
+        final Activity secondActivity = launchActivityAndWait(SecondActivity.class);
 
         new Launcher(ThirdActivity.class)
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
@@ -193,8 +193,7 @@
                 .setOptions(launchOptions)
                 .launch();
 
-        final Activity transparentActivity =
-            launchActivityInFullscreenAndWait(TranslucentActivity.class);
+        final Activity transparentActivity = launchActivityAndWait(TranslucentActivity.class);
 
         new Launcher(ThirdActivity.class)
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java
index 7bbf9c1..68ebae9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTopResumedStateTests.java
@@ -238,6 +238,7 @@
         getLifecycleLog().clear();
         final Activity launchForResultActivity = new Launcher(LaunchForResultActivity.class)
                 .customizeIntent(LaunchForResultActivity.forwardFlag(EXTRA_FINISH_IN_ON_RESUME))
+                .setExtraFlags(LaunchForResultActivity.EXTRA_USE_TRANSLUCENT_RESULT)
                 .launch();
 
         waitAndAssertActivityStates(state(baseActivity, ON_STOP));
@@ -252,7 +253,7 @@
                 Arrays.asList(
                         PRE_ON_CREATE, ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME,
                         ON_TOP_POSITION_GAINED);
-        waitForActivityTransitions(ResultActivity.class, expectedTopActivitySequence);
+        waitForActivityTransitions(TranslucentResultActivity.class, expectedTopActivitySequence);
 
         LifecycleVerifier.assertEntireSequence(Arrays.asList(
                 transition(CallbackTrackingActivity.class, ON_TOP_POSITION_LOST),
@@ -265,19 +266,19 @@
                 transition(LaunchForResultActivity.class, ON_TOP_POSITION_GAINED),
                 transition(LaunchForResultActivity.class, ON_TOP_POSITION_LOST),
                 transition(LaunchForResultActivity.class, ON_PAUSE),
-                transition(ResultActivity.class, PRE_ON_CREATE),
-                transition(ResultActivity.class, ON_CREATE),
-                transition(ResultActivity.class, ON_START),
-                transition(ResultActivity.class, ON_POST_CREATE),
-                transition(ResultActivity.class, ON_RESUME),
-                transition(ResultActivity.class, ON_TOP_POSITION_GAINED),
-                transition(ResultActivity.class, ON_TOP_POSITION_LOST),
-                transition(ResultActivity.class, ON_PAUSE),
+                transition(TranslucentResultActivity.class, PRE_ON_CREATE),
+                transition(TranslucentResultActivity.class, ON_CREATE),
+                transition(TranslucentResultActivity.class, ON_START),
+                transition(TranslucentResultActivity.class, ON_POST_CREATE),
+                transition(TranslucentResultActivity.class, ON_RESUME),
+                transition(TranslucentResultActivity.class, ON_TOP_POSITION_GAINED),
+                transition(TranslucentResultActivity.class, ON_TOP_POSITION_LOST),
+                transition(TranslucentResultActivity.class, ON_PAUSE),
                 transition(LaunchForResultActivity.class, ON_ACTIVITY_RESULT),
                 transition(LaunchForResultActivity.class, ON_RESUME),
                 transition(LaunchForResultActivity.class, ON_TOP_POSITION_GAINED),
-                transition(ResultActivity.class, ON_STOP),
-                transition(ResultActivity.class, ON_DESTROY),
+                transition(TranslucentResultActivity.class, ON_STOP),
+                transition(TranslucentResultActivity.class, ON_DESTROY),
                 transition(CallbackTrackingActivity.class, ON_STOP)),
                 getLifecycleLog(), "Double launch sequence must match");
     }
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/OWNERS b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/OWNERS
new file mode 100644
index 0000000..1bcd73d
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/OWNERS
@@ -0,0 +1,2 @@
+# Bug template url: https://b.corp.google.com/issues/new?component=316020&template=1018174
+include /tests/framework/base/windowmanager/src/android/server/wm/intent/OWNERS
diff --git a/tests/framework/base/windowmanager/testsdk28/src/android/server/wm/AspectRatioSdk28Tests.java b/tests/framework/base/windowmanager/testsdk28/src/android/server/wm/AspectRatioSdk28Tests.java
index ea1a00a..30a94df 100644
--- a/tests/framework/base/windowmanager/testsdk28/src/android/server/wm/AspectRatioSdk28Tests.java
+++ b/tests/framework/base/windowmanager/testsdk28/src/android/server/wm/AspectRatioSdk28Tests.java
@@ -20,7 +20,8 @@
 
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
 
 import android.app.Activity;
 import android.platform.test.annotations.Presubmit;
@@ -37,9 +38,6 @@
 @Presubmit
 public class AspectRatioSdk28Tests extends AspectRatioTestsBase {
 
-    // The minimum supported device aspect ratio for pre-Q devices.
-    private static final float MIN_DEVICE_ASPECT_RATIO = 1.333f;
-
     // The minimum supported device aspect ratio for watches.
     private static final float MIN_WATCH_DEVICE_ASPECT_RATIO = 1.0f;
 
@@ -53,14 +51,17 @@
             Sdk28MinAspectRatioActivity.class, false /* initialTouchMode */,
             false /* launchActivity */);
 
+    /**
+     * Device implementations with the Configuration.uiMode set as UI_MODE_TYPE_WATCH MUST have an
+     * aspect ratio value set as 1.0
+     */
     @Test
-    public void testMaxAspectRatioPreQActivity() {
-        boolean isWatch = getInstrumentation().getContext().getPackageManager()
-                .hasSystemFeature(FEATURE_WATCH);
-        float minAspectRatio = isWatch ? MIN_WATCH_DEVICE_ASPECT_RATIO : MIN_DEVICE_ASPECT_RATIO;
+    public void testMinAspectRatioPreQActivityOnWatch() {
+        // Only test for watch.
+        assumeTrue(getInstrumentation().getContext().getPackageManager()
+                .hasSystemFeature(FEATURE_WATCH));
 
-        runAspectRatioTest(mSdk28MinAspectRatioActivity, (actual, displayId, size) -> {
-            assertThat(actual, greaterThanOrEqualToInexact(minAspectRatio));
-        });
+        runAspectRatioTest(mSdk28MinAspectRatioActivity, (actual, displayId, size) ->
+                assertEquals(actual, MIN_WATCH_DEVICE_ASPECT_RATIO, 0.0f));
     }
 }
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index 03fe4a5..9e11ebb 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -1083,6 +1083,10 @@
         return mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
     }
 
+    protected boolean isOperatorTierDevice() {
+        return hasDeviceFeature("com.google.android.tv.operator_tier");
+    }
+
     protected void waitAndAssertActivityState(ComponentName activityName,
             String state, String message) {
         mWmState.waitForActivityState(activityName, state);
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WaitForValidActivityState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WaitForValidActivityState.java
index fd9127d..486e612 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WaitForValidActivityState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WaitForValidActivityState.java
@@ -95,7 +95,7 @@
             case WINDOWING_MODE_FREEFORM: return "FREEFORM";
             case WINDOWING_MODE_MULTI_WINDOW: return "MULTI_WINDOW";
             default:
-                throw new IllegalArgumentException("Unknown WINDOWING_MODE_: " + windowingMode);
+                return "Unknown WINDOWING_MODE_: " + windowingMode;
         }
     }
 
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsControllerTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsControllerTest.java
index dd8d070..f2d1761 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsControllerTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsControllerTest.java
@@ -89,7 +89,7 @@
     }
 
     private static final int INITIAL_KEYBOARD_HEIGHT = 200;
-    private static final int NEW_KEYBOARD_HEIGHT = 400;
+    private static final int NEW_KEYBOARD_HEIGHT = 300;
 
     @Test
     public void testChangeSizeWhileControlling() throws Exception {
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java
index d104450..06fe578 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/ImeInsetsVisibilityTest.java
@@ -34,6 +34,8 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.os.SystemClock;
@@ -77,7 +79,7 @@
 @RunWith(AndroidJUnit4.class)
 public class ImeInsetsVisibilityTest extends EndToEndImeTestBase {
     private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
-    private static final int NEW_KEYBOARD_HEIGHT = 400;
+    private static final int NEW_KEYBOARD_HEIGHT = 300;
 
     private static final String TEST_MARKER_PREFIX =
             "android.view.inputmethod.cts.ImeInsetsVisibilityTest";
@@ -177,9 +179,23 @@
 
             Point lastEditTextPos = new Point(curEditPos);
             curEditPos = getLocationOnScreenForView(editText);
-            assertTrue("Insets should visible and EditText position should be adjusted",
-                    isInsetsVisible(insetsFromActivity[0], WindowInsets.Type.ime())
-                            && curEditPos.y < lastEditTextPos.y);
+            // Watch doesn't support navigation bar and has limited screen size, so no transition
+            // in editbox with respect to x and y coordinates
+            Configuration config = InstrumentationRegistry.getInstrumentation()
+                    .getContext()
+                    .getResources()
+                    .getConfiguration();
+            boolean isSmallScreenLayout =
+                    config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_SMALL);
+
+            if (isSmallScreenLayout) {
+              assertTrue("Insets should visible",
+                  isInsetsVisible(insetsFromActivity[0], WindowInsets.Type.ime()));
+            } else {
+              assertTrue("Insets should visible and EditText position should be adjusted",
+                  isInsetsVisible(insetsFromActivity[0], WindowInsets.Type.ime())
+                      && curEditPos.y < lastEditTextPos.y);
+            }
 
             imm.showInputMethodPicker();
             TestUtils.waitOnMainUntil(() -> imm.isInputMethodPickerShown() && editText.isLaidOut(),
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceStrictModeTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceStrictModeTest.java
new file mode 100644
index 0000000..2f3541c
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodServiceStrictModeTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+
+import static com.android.cts.mockime.ImeEventStreamTestUtils.clearAllEvents;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.inputmethodservice.InputMethodService;
+import android.os.StrictMode;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
+import android.view.inputmethod.cts.util.TestActivity;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import androidx.annotation.IntDef;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.cts.mockime.ImeEvent;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
+
+/** Tests for verifying {@link StrictMode} violations on {@link InputMethodService} APIs. */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodServiceStrictModeTest extends EndToEndImeTestBase {
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+    private static final long EXPECTED_TIMEOUT = TimeUnit.SECONDS.toMillis(2);
+
+    /**
+     * Verifies if {@link Context#getDisplay} from {@link InputMethodService} and context created
+     * from {@link InputMethodService#createConfigurationContext(Configuration)} violates
+     * incorrect context violation.
+     */
+    private static final int VERIFY_MODE_GET_DISPLAY = 1;
+    /**
+     * Verifies if get {@link android.view.WindowManager} from {@link InputMethodService} and
+     * context created from {@link InputMethodService#createConfigurationContext(Configuration)}
+     * violates incorrect context violation.
+     *
+     * @see Context#getSystemService(String)
+     * @see Context#getSystemService(Class)
+     */
+    private static final int VERIFY_MODE_GET_WINDOW_MANAGER = 2;
+    /**
+     * Verifies if passing {@link InputMethodService} and context created
+     * from {@link InputMethodService#createConfigurationContext(Configuration)} to
+     * {@link android.view.ViewConfiguration#get(Context)} violates incorrect context violation.
+     */
+    private static final int VERIFY_MODE_GET_VIEW_CONFIGURATION = 3;
+
+    /**
+     * Verify mode to verifying if APIs violates incorrect context violation.
+     *
+     * @see #VERIFY_MODE_GET_DISPLAY
+     * @see #VERIFY_MODE_GET_WINDOW_MANAGER
+     * @see #VERIFY_MODE_GET_VIEW_CONFIGURATION
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {
+            VERIFY_MODE_GET_DISPLAY,
+            VERIFY_MODE_GET_WINDOW_MANAGER,
+            VERIFY_MODE_GET_VIEW_CONFIGURATION,
+    })
+    private @interface VerifyMode {}
+
+    @Test
+    public void testIncorrectContextUseOnGetSystemService() throws Exception {
+        verifyIms(VERIFY_MODE_GET_WINDOW_MANAGER);
+    }
+
+    @Test
+    public void testIncorrectContextUseOnGetDisplay() throws Exception {
+        verifyIms(VERIFY_MODE_GET_DISPLAY);
+    }
+
+    @Test
+    public void testIncorrectContextUse_GetViewConfiguration() throws Exception {
+        verifyIms(VERIFY_MODE_GET_VIEW_CONFIGURATION);
+    }
+
+    /**
+     * Verify if APIs violates incorrect context violations by {@code mode}.
+     *
+     * @see VerifyMode
+     */
+    private void verifyIms(@VerifyMode int mode) throws Exception {
+        try (MockImeSession imeSession = MockImeSession.create(
+                InstrumentationRegistry.getInstrumentation().getContext(),
+                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+                new ImeSettings.Builder().setStrictModeEnabled(true))) {
+            final ImeEventStream stream = imeSession.openEventStream();
+
+            createTestActivity(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+            expectEvent(stream, event -> "onStartInput".equals(event.getEventName()), TIMEOUT);
+
+            final ImeEventStream forkedStream = clearAllEvents(stream, "onStrictModeViolated");
+            final ImeEvent imeEvent;
+            switch (mode) {
+                case VERIFY_MODE_GET_DISPLAY:
+                    imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetDisplay(),
+                            TIMEOUT);
+                    break;
+                case VERIFY_MODE_GET_WINDOW_MANAGER:
+                    imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetWindowManager(),
+                            TIMEOUT);
+                    break;
+                case VERIFY_MODE_GET_VIEW_CONFIGURATION:
+                    imeEvent = expectCommand(forkedStream,
+                            imeSession.callVerifyGetViewConfiguration(), TIMEOUT);
+                    break;
+                default:
+                    imeEvent = null;
+            }
+            assertTrue(imeEvent.getReturnBooleanValue());
+            notExpectEvent(stream, event -> "onStrictModeViolated".equals(event.getEventName()),
+                    EXPECTED_TIMEOUT);
+        }
+    }
+
+    private TestActivity createTestActivity(final int windowFlags) {
+        return TestActivity.startSync(activity -> {
+            final LinearLayout layout = new LinearLayout(activity);
+            layout.setOrientation(LinearLayout.VERTICAL);
+
+            final EditText editText = new EditText(activity);
+            editText.setText("Editable");
+            layout.addView(editText);
+            editText.requestFocus();
+
+            activity.getWindow().setSoftInputMode(windowFlags);
+            return layout;
+        });
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
index cad3309..7b4b0f6 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java
@@ -36,9 +36,11 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.app.AlertDialog;
 import android.app.Instrumentation;
+import android.content.pm.PackageManager;
 import android.graphics.Color;
 import android.os.SystemClock;
 import android.support.test.uiautomator.UiObject2;
@@ -270,6 +272,10 @@
 
     @Test
     public void testShowHideKeyboardOnWebView() throws Exception {
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+        assumeTrue(pm.hasSystemFeature("android.software.webview"));
+
         try (MockImeSession imeSession = MockImeSession.create(
                 InstrumentationRegistry.getInstrumentation().getContext(),
                 InstrumentationRegistry.getInstrumentation().getUiAutomation(),
diff --git a/tests/libcore/jsr166/Android.bp b/tests/libcore/jsr166/Android.bp
index 67fb0b74..0ab6329 100644
--- a/tests/libcore/jsr166/Android.bp
+++ b/tests/libcore/jsr166/Android.bp
@@ -37,5 +37,6 @@
     test_suites: [
         "cts",
         "general-tests",
+        "mts",
     ],
 }
diff --git a/tests/libcore/jsr166/AndroidTest.xml b/tests/libcore/jsr166/AndroidTest.xml
index f2aaee5..93a2b76 100644
--- a/tests/libcore/jsr166/AndroidTest.xml
+++ b/tests/libcore/jsr166/AndroidTest.xml
@@ -45,4 +45,13 @@
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.TestFailureModuleController">
         <option name="screenshot-on-failure" value="false" />
     </object>
+
+    <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+         one of the Mainline modules below is present on the device used for testing. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <!-- ART Mainline Module (internal version). -->
+        <option name="mainline-module-package-name" value="com.google.android.art" />
+        <!-- ART Mainline Module (external (AOSP) version). -->
+        <option name="mainline-module-package-name" value="com.android.art" />
+    </object>
 </configuration>
diff --git a/tests/libcore/luni/AndroidTest.xml b/tests/libcore/luni/AndroidTest.xml
index 59857db..2173c92 100644
--- a/tests/libcore/luni/AndroidTest.xml
+++ b/tests/libcore/luni/AndroidTest.xml
@@ -73,7 +73,7 @@
     </object>
 
     <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
-         one the Mainline modules below is present on the device used for testing. -->
+         one of the Mainline modules below is present on the device used for testing. -->
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
         <!-- ART Mainline Module (internal version). -->
         <option name="mainline-module-package-name" value="com.google.android.art" />
diff --git a/tests/libcore/ojluni/AndroidTest.xml b/tests/libcore/ojluni/AndroidTest.xml
index d69101e..86e04f6 100644
--- a/tests/libcore/ojluni/AndroidTest.xml
+++ b/tests/libcore/ojluni/AndroidTest.xml
@@ -49,4 +49,13 @@
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.TestFailureModuleController">
         <option name="screenshot-on-failure" value="false" />
     </object>
+
+    <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+         one of the Mainline modules below is present on the device used for testing. -->
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <!-- ART Mainline Module (internal version). -->
+        <option name="mainline-module-package-name" value="com.google.android.art" />
+        <!-- ART Mainline Module (external (AOSP) version). -->
+        <option name="mainline-module-package-name" value="com.android.art" />
+    </object>
 </configuration>
diff --git a/tests/media/Android.bp b/tests/media/Android.bp
index d78e2b7..81c56af 100644
--- a/tests/media/Android.bp
+++ b/tests/media/Android.bp
@@ -42,6 +42,7 @@
     test_suites: [
         "cts",
         "general-tests",
+        "mts-media",
     ],
     min_sdk_version: "29",
 }
diff --git a/tests/media/AndroidTest.xml b/tests/media/AndroidTest.xml
index 872e8b1..061a609 100644
--- a/tests/media/AndroidTest.xml
+++ b/tests/media/AndroidTest.xml
@@ -26,7 +26,7 @@
     </target_preparer>
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
         <option name="push-all" value="true" />
-        <option name="media-folder-name" value="CtsMediaV2TestCases-1.10" />
+        <option name="media-folder-name" value="CtsMediaV2TestCases-1.12" />
         <option name="dynamic-config-module" value="CtsMediaV2TestCases" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/media/DynamicConfig.xml b/tests/media/DynamicConfig.xml
index 5ad3c66..75dfad3 100644
--- a/tests/media/DynamicConfig.xml
+++ b/tests/media/DynamicConfig.xml
@@ -1,6 +1,5 @@
 <dynamicConfig>
     <entry key="media_files_url">
-      <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.10.zip</value>
+      <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.12.zip</value>
     </entry>
 </dynamicConfig>
-
diff --git a/tests/media/README.md b/tests/media/README.md
index 54de250..fb10920 100644
--- a/tests/media/README.md
+++ b/tests/media/README.md
@@ -3,7 +3,7 @@
 
 The aim of these tests is not solely to verify the CDD requirements but also to test components, their plugins and their interactions with media framework.
 
-The test vectors used by the test suite is available at [link](https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.10.zip) and is downloaded automatically while running tests. Manual installation of these can be done using install_media.sh script in this directory.
+The test vectors used by the test suite is available at [link](https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-1.12.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory.
 
 The test suite looks to cover sdk/ndk api in normal and error scenarios. Error scenarios are separated from regular usage and are placed under class *UnitTest (MuxerUnitTest, ExtractorUnitTest, ...).
 
@@ -16,12 +16,48 @@
 ```
 
 ### Features
-All tests accepts attributes that offer selective run of tests. Media codec tests parses the value of key *codec-sel* to determine the list of components on which the tests are to be tried. Similarly for Media extractor and media muxer parses the value of keys *ext-sel* and *mux-sel* to determine the list of components on which the tests are to be tried.
+All tests accepts attributes that offer selective run of tests.
 
-To limit media codec decoder tests to mp3 and vorbis decoder,
+#### Select codecs by name
+To select codecs by name, *codec-prefix* can be passed to media codec tests to select one or more codecs that start with a given prefix.
+
+Example: To limit the tests to run for codecs whose names start with c2.android.
+
 ```sh
-adb shell am instrument -w -r  -e codec-sel 'mp3;vorbis'  -e debug false -e class 'android.mediav2.cts.CodecDecoderTest' android.mediav2.cts.test/androidx.test.runner.AndroidJUnitRunner
+atest CtsMediaV2TestCases -- --module-arg CtsMediaV2TestCases:instrumentation-arg:codec-prefix:=c2.android.
 ```
+
+Example: To limit the tests to run for c2.android.hevc.decoder
+
+```sh
+atest CtsMediaV2TestCases -- --module-arg CtsMediaV2TestCases:instrumentation-arg:codec-prefix:=c2.android.hevc.decoder
+```
+
+#### Select codecs by type
+To select codecs by type, *mime-sel* can be passed to media codec tests to select one or more codecs.
+
+Example: To limit media codec decoder tests to mp3 and vorbis decoder
+
+```sh
+atest android.mediav2.cts.CodecDecoderTest -- --module-arg  CtsMediaV2TestCases:instrumentation-arg:mime-sel:=mp3,vorbis
+```
+
+#### Select extractors by type
+To select extractors by type, *ext-sel* can be passed to extractor tests to select one or more extractors.
+
+Example: To limit extractor tests to mp4 and webm types
+```sh
+atest android.mediav2.cts.ExtractorTest -- --module-arg  CtsMediaV2TestCases:instrumentation-arg:ext-sel:=mp4,webm
+```
+
+#### Select muxers by type
+To select muxers by type, *mux-sel* can be passed to muxer tests to select one or more muxers.
+
+Example: To limit muxer tests to mp4 and webm types
+```sh
+atest android.mediav2.cts.MuxerTest -- --module-arg  CtsMediaV2TestCases:instrumentation-arg:mux-sel:=mp4,webm
+```
+
 ### Appendix
 | Identifier for codec-sel | Mime |
 | ------ | ------ |
diff --git a/tests/media/copy_media.sh b/tests/media/copy_media.sh
index 9e6b741..3bfd4b3 100755
--- a/tests/media/copy_media.sh
+++ b/tests/media/copy_media.sh
@@ -17,7 +17,7 @@
 ## script to install mediav2 test files manually
 
 adbOptions=" "
-resLabel=CtsMediaV2TestCases-1.10
+resLabel=CtsMediaV2TestCases-1.12
 srcDir="/tmp/$resLabel"
 tgtDir="/sdcard/test"
 usage="Usage: $0 [-h] [-s serial]"
diff --git a/tests/media/jni/NativeCodecDecoderTest.cpp b/tests/media/jni/NativeCodecDecoderTest.cpp
index 0b17d7e..d68a040 100644
--- a/tests/media/jni/NativeCodecDecoderTest.cpp
+++ b/tests/media/jni/NativeCodecDecoderTest.cpp
@@ -388,10 +388,12 @@
     }
     if (mSaveToMem && refFile && rmsError >= 0) {
         setUpAudioReference(refFile);
-        float error = ref->getRmsError(mRefData, mRefLength);
-        if (error > rmsError) {
+        float currError = ref->getRmsError(mRefData, mRefLength);
+        float errMargin = rmsError * kRmsErrorTolerance;
+        if (currError > errMargin) {
             isPass = false;
-            ALOGE("rms error too high for file %s, act/exp: %f/%f", testFile, error, rmsError);
+            ALOGE("rms error too high for file %s, ref/exp/got: %f/%f/%f", testFile, rmsError,
+                  errMargin, currError);
         }
     }
     return isPass;
diff --git a/tests/media/jni/NativeMediaCommon.cpp b/tests/media/jni/NativeMediaCommon.cpp
index 6d87084..2c7ec7a 100644
--- a/tests/media/jni/NativeMediaCommon.cpp
+++ b/tests/media/jni/NativeMediaCommon.cpp
@@ -46,6 +46,9 @@
 const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
 const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
 
+// NDK counterpart of RMS_ERROR_TOLERANCE of CodecDecoderTest class
+const float kRmsErrorTolerance = 1.05f;
+
 // NDK counterpart of Q_DEQ_TIMEOUT_US and RETRY_LIMIT of CodecTestBase class
 const long kQDeQTimeOutUs = 5000; // block at most 5ms while looking for io buffers
 const int kRetryLimit = 100; // max poll counter before test aborts and returns error
diff --git a/tests/media/jni/NativeMediaCommon.h b/tests/media/jni/NativeMediaCommon.h
index 9db5af0..e8f83f6 100644
--- a/tests/media/jni/NativeMediaCommon.h
+++ b/tests/media/jni/NativeMediaCommon.h
@@ -35,6 +35,8 @@
 extern const char* AMEDIA_MIMETYPE_AUDIO_VORBIS;
 extern const char* AMEDIA_MIMETYPE_AUDIO_OPUS;
 
+extern const float kRmsErrorTolerance;
+
 extern const long kQDeQTimeOutUs;
 extern const int kRetryLimit;
 
diff --git a/tests/media/src/android/mediav2/cts/AdaptivePlaybackTest.java b/tests/media/src/android/mediav2/cts/AdaptivePlaybackTest.java
new file mode 100644
index 0000000..f95fa54
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/AdaptivePlaybackTest.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediav2.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.Assert.fail;
+
+@RunWith(Parameterized.class)
+public class AdaptivePlaybackTest extends CodecDecoderTestBase {
+    private final String mMime;
+    private final String[] mSrcFiles;
+    private final int mSupport;
+
+    private long mMaxPts = 0;
+
+    public AdaptivePlaybackTest(String mime, String[] srcFiles, int support) {
+        super(mime, null);
+        mMime = mime;
+        mSrcFiles = srcFiles;
+        mSupport = support;
+    }
+
+    @Rule
+    public ActivityTestRule<CodecTestActivity> mActivityRule =
+            new ActivityTestRule<>(CodecTestActivity.class);
+
+    @Parameterized.Parameters(name = "{index}({0})")
+    public static Collection<Object[]> input() {
+        final boolean isEncoder = false;
+        final boolean needAudio = false;
+        final boolean needVideo = true;
+        // mime, array list of test files we'll play, codec should support adaptive feature
+        final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
+                {MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{
+                        "bbb_800x640_768kbps_30fps_avc_2b.mp4",
+                        "bbb_800x640_768kbps_30fps_avc_nob.mp4",
+                        "bbb_1280x720_1mbps_30fps_avc_2b.mp4",
+                        "bbb_640x360_512kbps_30fps_avc_nob.mp4",
+                        "bbb_1280x720_1mbps_30fps_avc_nob.mp4",
+                        "bbb_640x360_512kbps_30fps_avc_2b.mp4",
+                        "bbb_1280x720_1mbps_30fps_avc_nob.mp4",
+                        "bbb_640x360_512kbps_30fps_avc_nob.mp4",
+                        "bbb_640x360_512kbps_30fps_avc_2b.mp4"}, CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_HEVC, new String[]{
+                        "bbb_800x640_768kbps_30fps_hevc_2b.mp4",
+                        "bbb_800x640_768kbps_30fps_hevc_nob.mp4",
+                        "bbb_1280x720_1mbps_30fps_hevc_2b.mp4",
+                        "bbb_640x360_512kbps_30fps_hevc_nob.mp4",
+                        "bbb_1280x720_1mbps_30fps_hevc_nob.mp4",
+                        "bbb_640x360_512kbps_30fps_hevc_2b.mp4",
+                        "bbb_1280x720_1mbps_30fps_hevc_nob.mp4",
+                        "bbb_640x360_512kbps_30fps_hevc_nob.mp4",
+                        "bbb_640x360_512kbps_30fps_hevc_2b.mp4"}, CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_VP8, new String[]{
+                        "bbb_800x640_768kbps_30fps_vp8.webm",
+                        "bbb_1280x720_1mbps_30fps_vp8.webm",
+                        "bbb_640x360_512kbps_30fps_vp8.webm"}, CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{
+                        "bbb_800x640_768kbps_30fps_vp9.webm",
+                        "bbb_1280x720_1mbps_30fps_vp9.webm",
+                        "bbb_640x360_512kbps_30fps_vp9.webm"}, CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_MPEG4, new String[]{
+                        "bbb_128x96_64kbps_12fps_mpeg4.mp4",
+                        "bbb_176x144_192kbps_15fps_mpeg4.mp4",
+                        "bbb_128x96_64kbps_12fps_mpeg4.mp4"}, CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_AV1, new String[]{
+                        "bbb_800x640_768kbps_30fps_av1.webm",
+                        "bbb_1280x720_1mbps_30fps_av1.webm",
+                        "bbb_640x360_512kbps_30fps_av1.webm"}, CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{
+                        "bbb_800x640_768kbps_30fps_mpeg2_2b.mp4",
+                        "bbb_800x640_768kbps_30fps_mpeg2_nob.mp4",
+                        "bbb_1280x720_1mbps_30fps_mpeg2_2b.mp4",
+                        "bbb_640x360_512kbps_30fps_mpeg2_nob.mp4",
+                        "bbb_1280x720_1mbps_30fps_mpeg2_nob.mp4",
+                        "bbb_640x360_512kbps_30fps_mpeg2_2b.mp4",
+                        "bbb_1280x720_1mbps_30fps_mpeg2_nob.mp4",
+                        "bbb_640x360_512kbps_30fps_mpeg2_nob.mp4",
+                        "bbb_640x360_512kbps_30fps_mpeg2_2b.mp4"}, CODEC_ALL},
+        });
+        return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false);
+    }
+
+    @Override
+    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawOutputEOS = true;
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mOutputBuff.saveOutPTS(info.presentationTimeUs);
+            mOutputCount++;
+        }
+        mCodec.releaseOutputBuffer(bufferIndex, mSurface != null);
+    }
+
+    private MediaFormat createInputList(MediaFormat format, ByteBuffer buffer,
+            ArrayList<MediaCodec.BufferInfo> list, int offset, long ptsOffset) {
+        if (hasCSD(format)) {
+            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+            bufferInfo.offset = offset;
+            bufferInfo.size = 0;
+            bufferInfo.presentationTimeUs = 0;
+            bufferInfo.flags = MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
+            for (int i = 0; ; i++) {
+                String csdKey = "csd-" + i;
+                if (format.containsKey(csdKey)) {
+                    ByteBuffer csdBuffer = format.getByteBuffer(csdKey);
+                    bufferInfo.size += csdBuffer.limit();
+                    buffer.put(csdBuffer);
+                    format.removeKey(csdKey);
+                } else break;
+            }
+            list.add(bufferInfo);
+            offset += bufferInfo.size;
+        }
+        while (true) {
+            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+            bufferInfo.size = mExtractor.readSampleData(buffer, offset);
+            if (bufferInfo.size < 0) break;
+            bufferInfo.offset = offset;
+            bufferInfo.presentationTimeUs = ptsOffset + mExtractor.getSampleTime();
+            mMaxPts = Math.max(mMaxPts, bufferInfo.presentationTimeUs);
+            int flags = mExtractor.getSampleFlags();
+            bufferInfo.flags = 0;
+            if ((flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                bufferInfo.flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+            }
+            list.add(bufferInfo);
+            mExtractor.advance();
+            offset += bufferInfo.size;
+        }
+        buffer.clear();
+        buffer.position(offset);
+        return format;
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testAdaptivePlayback() throws IOException, InterruptedException {
+        CodecTestActivity activity = mActivityRule.getActivity();
+        setUpSurface(activity);
+        ArrayList<MediaFormat> formats = new ArrayList<>();
+        if (mSupport != CODEC_ALL) {
+            formats = new ArrayList<>();
+            for (String file : mSrcFiles) {
+                formats.add(setUpSource(file));
+                mExtractor.release();
+            }
+        }
+        ArrayList<String> listOfDecoders = selectCodecs(mMime, formats,
+                new String[]{MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback}, false);
+        if (listOfDecoders.isEmpty()) {
+            tearDownSurface();
+            if (mSupport == CODEC_OPTIONAL) return;
+            else fail("no suitable codecs found for mime: " + mMime);
+        }
+        formats.clear();
+        int totalSize = 0;
+        for (String srcFile : mSrcFiles) {
+            File file = new File(mInpPrefix + srcFile);
+            totalSize += (int) file.length();
+        }
+        totalSize <<= 1;
+        long ptsOffset = 0;
+        int buffOffset = 0;
+        ArrayList<MediaCodec.BufferInfo> list = new ArrayList<>();
+        ByteBuffer buffer = ByteBuffer.allocate(totalSize);
+        for (String file : mSrcFiles) {
+            formats.add(createInputList(setUpSource(file), buffer, list, buffOffset, ptsOffset));
+            mExtractor.release();
+            ptsOffset = mMaxPts + 1000000L;
+            buffOffset = (list.get(list.size() - 1).offset) + (list.get(list.size() - 1).size);
+        }
+        boolean[] boolStates = {false, true};
+        mOutputBuff = new OutputManager();
+        for (String decoder : listOfDecoders) {
+            mCodec = MediaCodec.createByCodecName(decoder);
+            MediaFormat format = formats.get(0);
+            activity.setScreenParams(getWidth(format), getHeight(format), true);
+            for (boolean isAsync : boolStates) {
+                mOutputBuff.reset();
+                configureCodec(format, isAsync, false, false);
+                mCodec.start();
+                doWork(buffer, list);
+                queueEOS();
+                waitForAllOutputs();
+                mCodec.reset();
+            }
+        }
+        tearDownSurface();
+    }
+}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderPauseTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderPauseTest.java
new file mode 100644
index 0000000..f07dbfd
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderPauseTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediav2.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * The following test validates that the decode can be paused
+ */
+@RunWith(Parameterized.class)
+public class CodecDecoderPauseTest extends CodecDecoderTestBase {
+    private static final String LOG_TAG = CodecDecoderPauseTest.class.getSimpleName();
+    private final long PAUSE_TIME_MS = 10000;
+    private final int NUM_FRAMES = 8;
+    private final int mSupport;
+
+    public CodecDecoderPauseTest(String mime, String srcFile, int support) {
+        super(mime, srcFile);
+        mSupport = support;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0})")
+    public static Collection<Object[]> input() {
+        final boolean isEncoder = false;
+        final boolean needAudio = true;
+        final boolean needVideo = true;
+        // mime, source file, codecs required to support
+        final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
+                {MediaFormat.MIMETYPE_AUDIO_AAC, "bbb_2ch_48kHz_he_aac.mp4", CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_cif_avc_delay16.mp4", CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_H263, "bbb_cif_768kbps_30fps_h263.mp4", CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_cif_hevc_delay15.mp4", CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_MPEG2, "bbb_640x360_512kbps_30fps_mpeg2_2b.mp4", CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_176x144_192kbps_15fps_mpeg4.mp4", CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_640x360_512kbps_30fps_vp8.webm", CODEC_ALL},
+                {MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_cif_768kbps_30fps_vp9.mkv", CODEC_ALL},
+        });
+        return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false);
+    }
+
+    /**
+     * Test decodes and compares decoded output of two files.
+     */
+    @LargeTest
+    @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testPause() throws IOException, InterruptedException {
+        ArrayList<MediaFormat> formats = null;
+        if (mSupport != CODEC_ALL) {
+            formats = new ArrayList<>();
+            formats.add(setUpSource(mTestFile));
+            mExtractor.release();
+        }
+        ArrayList<String> listOfDecoders = selectCodecs(mMime, formats, null, false);
+        if (listOfDecoders.isEmpty()) {
+            if (mSupport == CODEC_OPTIONAL) return;
+            else fail("no suitable codecs found for mime: " + mMime);
+        }
+        final boolean isAsync = true;
+        MediaFormat format = setUpSource(mTestFile);
+        for (String decoder : listOfDecoders) {
+            mCodec = MediaCodec.createByCodecName(decoder);
+            int loopCounter = 0;
+            boolean[] boolStates = {true, false};
+            OutputManager ref = new OutputManager();
+            OutputManager test = new OutputManager();
+            for (boolean enablePause : boolStates) {
+                String log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
+                        mTestFile, (isAsync ? "async" : "sync"));
+                mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+                configureCodec(format, isAsync, false, false);
+                mOutputBuff = loopCounter == 0 ? ref : test;
+                mOutputBuff.reset();
+                mCodec.start();
+                if (enablePause) {
+                    doWork(NUM_FRAMES);
+                    Thread.sleep(PAUSE_TIME_MS);
+                }
+                doWork(Integer.MAX_VALUE);
+                queueEOS();
+                waitForAllOutputs();
+                mCodec.reset();
+                assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+                if (loopCounter != 0) {
+                    assertTrue(log + "decoder output is flaky", ref.equals(test));
+                } else {
+                    if (mIsAudio) {
+                        assertTrue(log + " pts is not strictly increasing",
+                                ref.isPtsStrictlyIncreasing(mPrevOutputPts));
+                    } else {
+                        assertTrue(log + " input pts list and output pts list are not identical",
+                                ref.isOutPtsListIdenticalToInpPtsList(false));
+                    }
+                }
+                loopCounter++;
+            }
+            mCodec.release();
+        }
+        mExtractor.release();
+    }
+}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
index ad212ff..7e47493 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
@@ -25,6 +25,7 @@
 import androidx.test.filters.LargeTest;
 import androidx.test.rule.ActivityTestRule;
 
+import org.junit.Assume;
 import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
@@ -299,6 +300,8 @@
     @LargeTest
     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
     public void testReconfigure() throws IOException, InterruptedException {
+        Assume.assumeTrue("Test needs Android 11", IS_AT_LEAST_R);
+
         MediaFormat format = setUpSource(mTestFile);
         mExtractor.release();
         MediaFormat newFormat = setUpSource(mReconfigFile);
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index 8ba5428..dfd79297 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -75,8 +75,8 @@
         mRefCRC = refCRC;
     }
 
-    private short[] setUpAudioReference() throws IOException {
-        File refFile = new File(mInpPrefix + mRefFile);
+    static short[] setUpAudioReference(String file) throws IOException {
+        File refFile = new File(file);
         short[] refData;
         try (FileInputStream refStream = new FileInputStream(refFile)) {
             FileChannel fileChannel = refStream.getChannel();
@@ -184,17 +184,16 @@
     private native boolean nativeTestSimpleDecode(String decoder, Surface surface, String mime,
             String testFile, String refFile, float rmsError, long checksum);
 
-    private void verify() throws IOException {
-        if (mRmsError >= 0) {
-            assertTrue(mRefFile != null);
-            short[] refData = setUpAudioReference();
-            float currError = mOutputBuff.getRmsError(refData);
-            float errMargin = mRmsError * RMS_ERROR_TOLERANCE;
-            assertTrue(String.format("%s rms error too high exp/got %f/%f", mTestFile,
-                    errMargin, currError), currError <= errMargin);
-        } else if (mRefCRC >= 0) {
-            assertEquals(String.format("%s checksum mismatch", mTestFile), mRefCRC,
-                    mOutputBuff.getCheckSumImage());
+    static void verify(OutputManager outBuff, String refFile, float rmsError, long refCRC)
+            throws IOException {
+        if (rmsError >= 0) {
+            short[] refData = setUpAudioReference(mInpPrefix + refFile);
+            float currError = outBuff.getRmsError(refData);
+            float errMargin = rmsError * RMS_ERROR_TOLERANCE;
+            assertTrue(String.format("%s rms error too high ref/exp/got %f/%f/%f", refFile,
+                    rmsError, errMargin, currError), currError <= errMargin);
+        } else if (refCRC >= 0) {
+            assertEquals("checksum mismatch", refCRC, outBuff.getCheckSumImage());
         }
     }
 
@@ -281,10 +280,9 @@
                 }
             }
             mCodec.release();
-            if (mSaveToMem) verify();
+            if (mSaveToMem) verify(mOutputBuff, mRefFile, mRmsError, mRefCRC);
             assertTrue(nativeTestSimpleDecode(decoder, null, mMime, mInpPrefix + mTestFile,
-                    mInpPrefix + mRefFile, mRmsError * RMS_ERROR_TOLERANCE,
-                    ref.getCheckSumBuffer()));
+                    mInpPrefix + mRefFile, mRmsError, ref.getCheckSumBuffer()));
         }
         mExtractor.release();
     }
@@ -429,6 +427,8 @@
     @LargeTest
     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
     public void testReconfigure() throws IOException, InterruptedException {
+        Assume.assumeTrue("Test needs Android 11", IS_AT_LEAST_R);
+
         MediaFormat format = setUpSource(mTestFile);
         mExtractor.release();
         MediaFormat newFormat = setUpSource(mReconfigFile);
@@ -506,7 +506,7 @@
                 doWork(Integer.MAX_VALUE);
                 queueEOS();
                 waitForAllOutputs();
-                if (mSaveToMem) verify();
+                if (mSaveToMem) verify(mOutputBuff, mRefFile, mRmsError, mRefCRC);
                 /* TODO(b/147348711) */
                 if (false) mCodec.stop();
                 else mCodec.reset();
@@ -532,7 +532,7 @@
                 doWork(Integer.MAX_VALUE);
                 queueEOS();
                 waitForAllOutputs();
-                if (mSaveToMem) verify();
+                if (mSaveToMem) verify(mOutputBuff, mRefFile, mRmsError, mRefCRC);
                 /* TODO(b/147348711) */
                 if (false) mCodec.stop();
                 else mCodec.reset();
@@ -672,7 +672,7 @@
         for (int i = 0; ; i++) {
             String csdKey = "csd-" + i;
             if (format.containsKey(csdKey)) {
-                mCsdBuffers.add(format.getByteBuffer(csdKey));
+                mCsdBuffers.add(format.getByteBuffer(csdKey).duplicate());
                 format.removeKey(csdKey);
             } else break;
         }
@@ -688,7 +688,8 @@
         for (String decoder : listOfDecoders) {
             mCodec = MediaCodec.createByCodecName(decoder);
             int loopCounter = 0;
-            for (MediaFormat fmt : formats) {
+            for (int i = 0; i < formats.size(); i++) {
+                MediaFormat fmt = formats.get(i);
                 for (boolean eosMode : boolStates) {
                     for (boolean isAsync : boolStates) {
                         boolean validateFormat = true;
@@ -707,7 +708,7 @@
                             validateFormat = false;
                         }
                         mCodec.start();
-                        queueCodecConfig();
+                        if (i == 0) queueCodecConfig();
                         doWork(Integer.MAX_VALUE);
                         queueEOS();
                         waitForAllOutputs();
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderValidationTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderValidationTest.java
index ba2a3ea..1696561 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderValidationTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderValidationTest.java
@@ -25,18 +25,12 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -51,7 +45,6 @@
 @RunWith(Parameterized.class)
 public class CodecDecoderValidationTest extends CodecDecoderTestBase {
     private static final String LOG_TAG = CodecDecoderValidationTest.class.getSimpleName();
-    private static final float RMS_ERROR_TOLERANCE = 1.05f;        // 5%
 
     private final String[] mSrcFiles;
     private final String mRefFile;
@@ -77,28 +70,39 @@
         // mime, array list of test files (underlying elementary stream is same, except they
         // are placed in different containers), ref file, rms error, checksum
         final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
+                // vp9 test vectors with no-show frames signalled in alternate ways
                 {MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{
                         "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm",
                         "bbb_340x280_768kbps_30fps_vp9.webm"}, null, -1.0f, 4122701060L, CODEC_ALL},
                 {MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{
                         "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm",
                         "bbb_520x390_1mbps_30fps_vp9.webm"}, null, -1.0f, 1201859039L, CODEC_ALL},
+
+                // mpeg2 test vectors with interlaced fields signalled in alternate ways
                 {MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{
                         "bbb_512x288_30fps_1mbps_mpeg2_interlaced_nob_1field.ts",
                         "bbb_512x288_30fps_1mbps_mpeg2_interlaced_nob_2fields.mp4"}, null, -1.0f,
                         -1L, CODEC_ALL},
+
+                // clips with crop parameters
 //                /* TODO(b/163299340) */
 //                {MediaFormat.MIMETYPE_VIDEO_HEVC, new String[]{"bbb_560x280_1mbps_30fps_hevc.mkv"},
 //                        null, -1.0f, 26298353L, CODEC_ALL},
 //                /* TODO(b/163299340) */
 //                {MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{"bbb_504x224_768kbps_30fps_avc.mp4"},
 //                        null, -1.0f, 4060874918L, CODEC_ALL},
+
+                // misc mp3 test vectors
                 {MediaFormat.MIMETYPE_AUDIO_MPEG, new String[]{"bbb_1ch_16kHz_lame_vbr.mp3"},
                         "bbb_1ch_16kHz_s16le.raw", 119.256f, -1L, CODEC_ALL},
                 {MediaFormat.MIMETYPE_AUDIO_MPEG, new String[]{"bbb_2ch_44kHz_lame_vbr.mp3"},
                         "bbb_2ch_44kHz_s16le.raw", 53.066f, -1L, CODEC_ALL},
+
+                // mp3 test vectors with CRC
                 {MediaFormat.MIMETYPE_AUDIO_MPEG, new String[]{"bbb_2ch_44kHz_lame_crc.mp3"},
                         "bbb_2ch_44kHz_s16le.raw", 104.09f, -1L, CODEC_ALL},
+
+                // vp9 test vectors with AQ mode enabled
                 {MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{"bbb_1280x720_800kbps_30fps_vp9" +
                         ".webm"}, null, -1.0f, 1319105122L, CODEC_ALL},
                 {MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{"bbb_1280x720_1200kbps_30fps_vp9" +
@@ -107,41 +111,355 @@
                         ".webm"}, null, -1.0f, 156928091L, CODEC_ALL},
                 {MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{"bbb_1280x720_2000kbps_30fps_vp9" +
                         ".webm"}, null, -1.0f, 3902485256L, CODEC_ALL},
+
+                // video test vectors of non standard sizes
+                {MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{
+                        "bbb_642x642_2mbps_30fps_mpeg2.mp4"}, null, -1.0f, -1L, CODEC_ANY},
+                {MediaFormat.MIMETYPE_VIDEO_AVC, new String[]{
+                        "bbb_642x642_1mbps_30fps_avc.mp4"}, null, -1.0f, 3947092788L, CODEC_ANY},
+                {MediaFormat.MIMETYPE_VIDEO_VP8, new String[]{
+                        "bbb_642x642_1mbps_30fps_vp8.webm"}, null, -1.0f, 516982978L, CODEC_ANY},
+                {MediaFormat.MIMETYPE_VIDEO_HEVC, new String[]{
+                        "bbb_642x642_768kbps_30fps_hevc.mp4"}, null, -1.0f, 3018465268L, CODEC_ANY},
+                {MediaFormat.MIMETYPE_VIDEO_VP9, new String[]{
+                        "bbb_642x642_768kbps_30fps_vp9.webm"}, null, -1.0f, 4032809269L, CODEC_ANY},
+                {MediaFormat.MIMETYPE_VIDEO_AV1, new String[]{
+                        "bbb_642x642_768kbps_30fps_av1.mp4"}, null, -1.0f, 3684481474L, CODEC_ANY},
+                {MediaFormat.MIMETYPE_VIDEO_MPEG4, new String[]{
+                        "bbb_130x130_192kbps_15fps_mpeg4.mp4"}, null, -1.0f, -1L, CODEC_ANY},
+
+                //  audio test vectors covering cdd sec 5.1.3
+                // amr nb
+                {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{
+                        "audio/bbb_mono_8kHz_12.2kbps_amrnb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{
+                        "audio/bbb_mono_8kHz_10.2kbps_amrnb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{
+                        "audio/bbb_mono_8kHz_7.95kbps_amrnb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{
+                        "audio/bbb_mono_8kHz_7.40kbps_amrnb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{
+                        "audio/bbb_mono_8kHz_6.70kbps_amrnb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{
+                        "audio/bbb_mono_8kHz_5.90kbps_amrnb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{
+                        "audio/bbb_mono_8kHz_5.15kbps_amrnb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_NB, new String[]{
+                        "audio/bbb_mono_8kHz_4.75kbps_amrnb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+
+                // amr wb
+                {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
+                        "audio/bbb_mono_16kHz_6.6kbps_amrwb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
+                        "audio/bbb_mono_16kHz_8.85kbps_amrwb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
+                        "audio/bbb_mono_16kHz_12.65kbps_amrwb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
+                        "audio/bbb_mono_16kHz_14.25kbps_amrwb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
+                        "audio/bbb_mono_16kHz_15.85kbps_amrwb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
+                        "audio/bbb_mono_16kHz_18.25kbps_amrwb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
+                        "audio/bbb_mono_16kHz_19.85kbps_amrwb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
+                        "audio/bbb_mono_16kHz_23.05kbps_amrwb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AMR_WB, new String[]{
+                        "audio/bbb_mono_16kHz_23.85kbps_amrwb.3gp"}, null, -1.0f, -1L, CODEC_ALL},
+
+                // flac
+                // TODO(add content for 96kHz and 192kHz)
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_1ch_8kHz_lvl4_flac.mka"},
+                        "audio/bbb_1ch_8kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_1ch_12kHz_lvl4_flac.mka"},
+                        "audio/bbb_1ch_12kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_1ch_16kHz_lvl4_flac.mka"},
+                        "audio/bbb_1ch_16kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_1ch_22kHz_lvl4_flac.mka"},
+                        "audio/bbb_1ch_22kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_1ch_24kHz_lvl4_flac.mka"},
+                        "audio/bbb_1ch_24kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_1ch_32kHz_lvl4_flac.mka"},
+                        "audio/bbb_1ch_32kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_1ch_44kHz_lvl4_flac.mka"},
+                        "audio/bbb_1ch_44kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_1ch_48kHz_lvl4_flac.mka"},
+                        "audio/bbb_1ch_48kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_2ch_8kHz_lvl4_flac.mka"},
+                        "audio/bbb_2ch_8kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_2ch_12kHz_lvl4_flac.mka"},
+                        "audio/bbb_2ch_12kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_2ch_16kHz_lvl4_flac.mka"},
+                        "audio/bbb_2ch_16kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_2ch_22kHz_lvl4_flac.mka"},
+                        "audio/bbb_2ch_22kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_2ch_24kHz_lvl4_flac.mka"},
+                        "audio/bbb_2ch_24kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_2ch_32kHz_lvl4_flac.mka"},
+                        "audio/bbb_2ch_32kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_2ch_44kHz_lvl4_flac.mka"},
+                        "audio/bbb_2ch_44kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_FLAC, new String[]{"audio/bbb_2ch_48kHz_lvl4_flac.mka"},
+                        "audio/bbb_2ch_48kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+
+                // opus
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_1ch_8kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_1ch_12kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_1ch_16kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_1ch_24kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_1ch_32kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_1ch_48kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_2ch_8kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_2ch_12kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_2ch_16kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_2ch_24kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_2ch_32kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_2ch_48kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_5ch_8kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_5ch_12kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_5ch_16kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_5ch_24kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_5ch_32kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_5ch_48kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_6ch_8kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_6ch_12kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_6ch_16kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_6ch_24kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_6ch_32kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_OPUS, new String[]{
+                        "audio/bbb_6ch_48kHz_opus.ogg"}, null, -1.0f, -1L, CODEC_ALL},
+
+                // raw
+                // TODO: add content for larger sampling rates and float pcm
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_1ch_8kHz.wav"},
+                        "audio/bbb_1ch_8kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_1ch_16kHz.wav"},
+                        "audio/bbb_1ch_16kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_1ch_22kHz.wav"},
+                        "audio/bbb_1ch_22kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_1ch_24kHz.wav"},
+                        "audio/bbb_1ch_24kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_1ch_32kHz.wav"},
+                        "audio/bbb_1ch_32kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_1ch_44kHz.wav"},
+                        "audio/bbb_1ch_44kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_1ch_48kHz.wav"},
+                        "audio/bbb_1ch_48kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_2ch_8kHz.wav"},
+                        "audio/bbb_2ch_8kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_2ch_16kHz.wav"},
+                        "audio/bbb_2ch_16kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_2ch_22kHz.wav"},
+                        "audio/bbb_2ch_22kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_2ch_24kHz.wav"},
+                        "audio/bbb_2ch_24kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_2ch_32kHz.wav"},
+                        "audio/bbb_2ch_32kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_2ch_44kHz.wav"},
+                        "audio/bbb_2ch_44kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_RAW, new String[]{"audio/bbb_2ch_48kHz.wav"},
+                        "audio/bbb_2ch_48kHz_s16le_3s.raw", 0.0f, -1L, CODEC_ALL},
+
+                // aac-lc
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_8kHz_aac_lc.m4a"},
+                        "audio/bbb_1ch_8kHz_s16le_3s.raw", 26.907248f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_12kHz_aac_lc.m4a"},
+                        "audio/bbb_1ch_12kHz_s16le_3s.raw", 23.366642f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_16kHz_aac_lc.m4a"},
+                        "audio/bbb_1ch_16kHz_s16le_3s.raw", 21.354156f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_22kHz_aac_lc.m4a"},
+                        "audio/bbb_1ch_22kHz_s16le_3s.raw", 25.980762f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_24kHz_aac_lc.m4a"},
+                        "audio/bbb_1ch_24kHz_s16le_3s.raw", 26.362852f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_32kHz_aac_lc.m4a"},
+                        "audio/bbb_1ch_32kHz_s16le_3s.raw", 28.635643f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_44kHz_aac_lc.m4a"},
+                        "audio/bbb_1ch_44kHz_s16le_3s.raw", 29.291637f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_48kHz_aac_lc.m4a"},
+                        "audio/bbb_1ch_48kHz_s16le_3s.raw", 29.325756f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_8kHz_aac_lc.m4a"},
+                        "audio/bbb_2ch_8kHz_s16le_3s.raw", 26.362852f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_12kHz_aac_lc.m4a"},
+                        "audio/bbb_2ch_12kHz_s16le_3s.raw", 21.931713f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_16kHz_aac_lc.m4a"},
+                        "audio/bbb_2ch_16kHz_s16le_3s.raw", 22.068077f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_22kHz_aac_lc.m4a"},
+                        "audio/bbb_2ch_22kHz_s16le_3s.raw", 25.317978f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_24kHz_aac_lc.m4a"},
+                        "audio/bbb_2ch_24kHz_s16le_3s.raw", 25.651510f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_32kHz_aac_lc.m4a"},
+                        "audio/bbb_2ch_32kHz_s16le_3s.raw", 27.294687f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_44kHz_aac_lc.m4a"},
+                        "audio/bbb_2ch_44kHz_s16le_3s.raw", 27.313000f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_48kHz_aac_lc.m4a"},
+                        "audio/bbb_2ch_48kHz_s16le_3s.raw", 27.676704f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_5ch_8kHz_aac_lc.m4a"},
+                        "audio/bbb_5ch_8kHz_s16le_3s.raw", 43.116123f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_5ch_12kHz_aac_lc.m4a"},
+                        "audio/bbb_5ch_12kHz_s16le_3s.raw", 35.972210f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_5ch_16kHz_aac_lc.m4a"},
+                        "audio/bbb_5ch_16kHz_s16le_3s.raw", 32.710854f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_5ch_22kHz_aac_lc.m4a"},
+                        "audio/bbb_5ch_22kHz_s16le_3s.raw", 39.281040f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_5ch_24kHz_aac_lc.m4a"},
+                        "audio/bbb_5ch_24kHz_s16le_3s.raw", 40.951191f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_5ch_32kHz_aac_lc.m4a"},
+                        "audio/bbb_5ch_32kHz_s16le_3s.raw", 49.436829f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_5ch_44kHz_aac_lc.m4a"},
+                        "audio/bbb_5ch_44kHz_s16le_3s.raw", 43.886215f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_5ch_48kHz_aac_lc.m4a"},
+                        "audio/bbb_5ch_48kHz_s16le_3s.raw", 44.271889f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_6ch_8kHz_aac_lc.m4a"},
+                        "audio/bbb_6ch_8kHz_s16le_3s.raw", 39.661064f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_6ch_12kHz_aac_lc.m4a"},
+                        "audio/bbb_6ch_12kHz_s16le_3s.raw", 34.971416f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_6ch_16kHz_aac_lc.m4a"},
+                        "audio/bbb_6ch_16kHz_s16le_3s.raw", 29.068884f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_6ch_22kHz_aac_lc.m4a"},
+                        "audio/bbb_6ch_22kHz_s16le_3s.raw", 29.427877f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_6ch_24kHz_aac_lc.m4a"},
+                        "audio/bbb_6ch_24kHz_s16le_3s.raw", 30.331501f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_6ch_32kHz_aac_lc.m4a"},
+                        "audio/bbb_6ch_32kHz_s16le_3s.raw", 33.926392f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_6ch_44kHz_aac_lc.m4a"},
+                        "audio/bbb_6ch_44kHz_s16le_3s.raw", 31.733263f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_6ch_48kHz_aac_lc.m4a"},
+                        "audio/bbb_6ch_48kHz_s16le_3s.raw", 31.032242f, -1L, CODEC_ALL},
+
+                // aac-he
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_16kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_22kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_24kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_32kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_44kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_48kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_5ch_16kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_5ch_22kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_5ch_24kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_5ch_32kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_5ch_44kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_5ch_48kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_6ch_16kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_6ch_22kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_6ch_24kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_6ch_32kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_6ch_44kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_6ch_48kHz_aac_he.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+
+                // aac-eld
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_16kHz_aac_eld.m4a"},
+                        "audio/bbb_1ch_16kHz_s16le_3s.raw", -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_22kHz_aac_eld.m4a"},
+                        "audio/bbb_1ch_22kHz_s16le_3s.raw", 24.959969f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_24kHz_aac_eld.m4a"},
+                        "audio/bbb_1ch_24kHz_s16le_3s.raw", 26.495283f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_32kHz_aac_eld.m4a"},
+                        "audio/bbb_1ch_32kHz_s16le_3s.raw", 31.464266f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_44kHz_aac_eld.m4a"},
+                        "audio/bbb_1ch_44kHz_s16le_3s.raw", 33.852623f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_48kHz_aac_eld.m4a"},
+                        "audio/bbb_1ch_48kHz_s16le_3s.raw", 33.136082f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_16kHz_aac_eld.m4a"},
+                        "audio/bbb_2ch_16kHz_s16le_3s.raw", -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_22kHz_aac_eld.m4a"},
+                        "audio/bbb_2ch_22kHz_s16le_3s.raw", 24.959969f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_24kHz_aac_eld.m4a"},
+                        "audio/bbb_2ch_24kHz_s16le_3s.raw", 26.962938f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_32kHz_aac_eld.m4a"},
+                        "audio/bbb_2ch_32kHz_s16le_3s.raw", 27.784887f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_44kHz_aac_eld.m4a"},
+                        "audio/bbb_2ch_44kHz_s16le_3s.raw", 29.223278f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_48kHz_aac_eld.m4a"},
+                        "audio/bbb_2ch_48kHz_s16le_3s.raw", 29.171904f, -1L, CODEC_ALL},
+
+                // aac-hev2
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_16kHz_aac_hev2.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_22kHz_aac_hev2.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_24kHz_aac_hev2.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_32kHz_aac_hev2.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_44kHz_aac_hev2.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{
+                        "audio/bbb_2ch_48kHz_aac_hev2.m4a"}, null, -1.0f, -1L, CODEC_ALL},
+
+                // aac-usac
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_8kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_16kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_22kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_24kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_32kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_44kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_1ch_48kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_8kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_16kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_22kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_24kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_32kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_44kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
+                {MediaFormat.MIMETYPE_AUDIO_AAC, new String[]{"audio/bbb_2ch_48kHz_usac.m4a"},
+                        null, -1.0f, -1L, CODEC_ALL},
         });
         return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false);
     }
 
-    private short[] setUpAudioReference() throws IOException {
-        File refFile = new File(mInpPrefix + mRefFile);
-        short[] refData;
-        try (FileInputStream refStream = new FileInputStream(refFile)) {
-            FileChannel fileChannel = refStream.getChannel();
-            int length = (int) refFile.length();
-            ByteBuffer refBuffer = ByteBuffer.allocate(length);
-            refBuffer.order(ByteOrder.LITTLE_ENDIAN);
-            fileChannel.read(refBuffer);
-            refData = new short[length / 2];
-            refBuffer.position(0);
-            for (int i = 0; i < length / 2; i++) {
-                refData[i] = refBuffer.getShort();
-            }
-        }
-        return refData;
-    }
-
-    private void verify() throws IOException {
-        if (mRmsError >= 0) {
-            assertTrue(mRefFile != null);
-            short[] refData = setUpAudioReference();
-            float currError = mOutputBuff.getRmsError(refData);
-            float errMargin = mRmsError * RMS_ERROR_TOLERANCE;
-            assertTrue(String.format("rms error too high exp/got %f/%f", errMargin, currError),
-                    currError <= errMargin);
-        } else if (mRefCRC >= 0) {
-            assertEquals("checksum mismatch", mRefCRC, mOutputBuff.getCheckSumImage());
-        }
-    }
-
     /**
      * Test decodes and compares decoded output of two files.
      */
@@ -185,7 +503,7 @@
                     assertTrue(log + "decoder outputs are not identical", ref.equals(mOutputBuff));
                 }
             }
-            verify();
+            CodecDecoderTest.verify(mOutputBuff, mRefFile, mRmsError, mRefCRC);
         }
     }
 }
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
index 8a578f7..59aee43 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
@@ -83,9 +83,7 @@
 
     static {
         android.os.Bundle args = InstrumentationRegistry.getArguments();
-        CodecTestBase.codecSelKeys = args.getString(CodecTestBase.CODEC_SEL_KEY);
-        if (CodecTestBase.codecSelKeys == null)
-            CodecTestBase.codecSelKeys = CodecTestBase.CODEC_SEL_VALUE;
+        CodecTestBase.mimeSelKeys = args.getString(CodecTestBase.MIME_SEL_KEY);
     }
 
     public CodecEncoderSurfaceTest(String mime, String testFile, int bitrate, int frameRate) {
@@ -567,4 +565,3 @@
         }
     }
 }
-
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
index 0d5f49c..3d16bd7 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
@@ -759,7 +759,6 @@
     /**
      * Test set parameters : force key frame
      */
-    @Ignore("TODO(b/151302863)")
     @LargeTest
     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
     public void testSetForceSyncFrame() throws IOException, InterruptedException {
@@ -856,7 +855,6 @@
     /**
      * Test set parameters : change bitrate dynamically
      */
-    @Ignore("TODO(b/151302863)")
     @LargeTest
     @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
     public void testAdaptiveBitRate() throws IOException, InterruptedException {
diff --git a/tests/media/src/android/mediav2/cts/CodecTestActivity.java b/tests/media/src/android/mediav2/cts/CodecTestActivity.java
index fb20f2c..12bcf38 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestActivity.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestActivity.java
@@ -25,6 +25,7 @@
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Condition;
@@ -42,6 +43,11 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        setTurnScreenOn(true);
+        setShowWhenLocked(true);
+
         setContentView(R.layout.media_decoder_surface_layout);
         mSurfaceView = findViewById(R.id.surface);
         mHolder = mSurfaceView.getHolder();
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index 795ff91..93e8c90 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -35,6 +35,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Assert;
+import org.junit.Assume;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -55,6 +56,8 @@
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.zip.CRC32;
 
+import com.android.compatibility.common.util.ApiLevelUtil;
+
 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
 import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
 import static org.junit.Assert.assertEquals;
@@ -503,9 +506,11 @@
 }
 
 abstract class CodecTestBase {
+    public static final boolean IS_AT_LEAST_R = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
     private static final String LOG_TAG = CodecTestBase.class.getSimpleName();
-    static final String CODEC_SEL_KEY = "codec-sel";
-    static final String CODEC_SEL_VALUE = "default";
+
+    static final String CODEC_PREFIX_KEY = "codec-prefix";
+    static final String MIME_SEL_KEY = "mime-sel";
     static final Map<String, String> codecSelKeyMimeMap = new HashMap<>();
     static final boolean ENABLE_LOGS = false;
     static final int PER_TEST_TIMEOUT_LARGE_TEST_MS = 300000;
@@ -520,7 +525,8 @@
     static final String mInpPrefix = WorkDir.getMediaDirString();
     static final PackageManager pm =
             InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
-    static String codecSelKeys;
+    static String mimeSelKeys;
+    static String codecPrefix;
 
     CodecAsyncHandler mAsyncHandle;
     boolean mIsCodecInAsyncMode;
@@ -565,8 +571,8 @@
         codecSelKeyMimeMap.put("gsm", MediaFormat.MIMETYPE_AUDIO_MSGSM);
 
         android.os.Bundle args = InstrumentationRegistry.getArguments();
-        codecSelKeys = args.getString(CODEC_SEL_KEY);
-        if (codecSelKeys == null) codecSelKeys = CODEC_SEL_VALUE;
+        mimeSelKeys = args.getString(MIME_SEL_KEY);
+        codecPrefix = args.getString(CODEC_PREFIX_KEY);
     }
 
     static boolean isTv() {
@@ -605,11 +611,11 @@
     }
 
     static boolean hasDecoder(String mime) {
-        return CodecTestBase.selectCodecs(mime, null, null, false).size() != 0;
+        return CodecTestBase.selectCodecs(mime, null, null, false, false).size() != 0;
     }
 
     static boolean hasEncoder(String mime) {
-        return CodecTestBase.selectCodecs(mime, null, null, true).size() != 0;
+        return CodecTestBase.selectCodecs(mime, null, null, true, false).size() != 0;
     }
 
     static ArrayList<String> prepareRequiredArgsList(boolean isEncoder, boolean needAudio,
@@ -685,7 +691,7 @@
         ArrayList<String> cddRequiredMimeList =
                 prepareRequiredArgsList(isEncoder, needAudio, needVideo);
         ArrayList<String> mimes = new ArrayList<>();
-        if (codecSelKeys.contains(CODEC_SEL_VALUE)) {
+        if (mimeSelKeys == null) {
             MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
             MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
             for (MediaCodecInfo codecInfo : codecInfos) {
@@ -723,7 +729,7 @@
             for (Map.Entry<String, String> entry : codecSelKeyMimeMap.entrySet()) {
                 String key = entry.getKey();
                 String value = entry.getValue();
-                if (codecSelKeys.contains(key) && !mimes.contains(value)) mimes.add(value);
+                if (mimeSelKeys.contains(key) && !mimes.contains(value)) mimes.add(value);
             }
         }
         final List<Object[]> argsList = new ArrayList<>();
@@ -905,13 +911,22 @@
     }
 
     static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
-            String[] features, boolean isEncoder) {
+                                          String[] features, boolean isEncoder) {
+        return selectCodecs(mime, formats, features, isEncoder, true);
+    }
+
+    static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
+            String[] features, boolean isEncoder, boolean enableCodecFilter) {
         MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
         MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
         ArrayList<String> listOfCodecs = new ArrayList<>();
         for (MediaCodecInfo codecInfo : codecInfos) {
             if (codecInfo.isEncoder() != isEncoder) continue;
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
+            if (enableCodecFilter && codecPrefix != null &&
+                    !codecInfo.getName().startsWith(codecPrefix)) {
+                continue;
+            }
             String[] types = codecInfo.getSupportedTypes();
             for (String type : types) {
                 if (type.equalsIgnoreCase(mime)) {
@@ -938,6 +953,10 @@
                 }
             }
         }
+        if (enableCodecFilter && codecPrefix != null) {
+            Assume.assumeFalse("Skipping test because of --codec-prefix " + codecPrefix,
+                    listOfCodecs.isEmpty());
+        }
         return listOfCodecs;
     }
 
diff --git a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
index 5d780f0..8b77f73 100644
--- a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
@@ -209,7 +209,8 @@
                         AVCProfileConstrainedBaseline, AVCProfileConstrainedHigh});
         mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_HEVC,
                 new int[]{HEVCProfileMain, HEVCProfileMain10, HEVCProfileMainStill,
-                        HEVCProfileMain10HDR10, HEVCProfileMain10HDR10Plus});
+                          // TODO: test HDR profiles once they are supported by MediaMuxer
+                          /* HEVCProfileMain10HDR10, HEVCProfileMain10HDR10Plus */});
         mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_H263,
                 new int[]{H263ProfileBaseline, H263ProfileH320Coding,
                         H263ProfileBackwardCompatible, H263ProfileISWV2, H263ProfileISWV3,
@@ -229,8 +230,9 @@
         mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_VP8, new int[]{VP8ProfileMain});
         mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, new int[]{VP9Profile0, VP9Profile1});
         mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_AV1,
-                new int[]{AV1ProfileMain8, AV1ProfileMain10, AV1ProfileMain10HDR10,
-                        AV1ProfileMain10HDR10Plus});
+                new int[]{AV1ProfileMain8, AV1ProfileMain10,
+                          // TODO: test HDR profiles once they are supported by MediaMuxer
+                          /* AV1ProfileMain10HDR10, AV1ProfileMain10HDR10Plus */});
         mProfileMap.put(MediaFormat.MIMETYPE_AUDIO_AAC,
                 new int[]{AACObjectMain, AACObjectLC, AACObjectSSR, AACObjectLTP, AACObjectHE,
                         AACObjectScalable, AACObjectERLC, AACObjectERScalable, AACObjectLD,
@@ -622,6 +624,26 @@
         super.dequeueOutput(bufferIndex, info);
     }
 
+    private int getAacProfile(MediaFormat format) {
+        int aacProfile = format.getInteger(MediaFormat.KEY_AAC_PROFILE, -1);
+        int profile = format.getInteger(MediaFormat.KEY_PROFILE, -1);
+
+        if (aacProfile != -1 && profile != -1) {
+            // If both aac-profile and profile are present in format, then they must be the same
+            assertTrue("aac-profile " + aacProfile + " and profile " + profile + " are different.",
+                    aacProfile == profile);
+            return aacProfile;
+        } else if (aacProfile != -1) {
+            return aacProfile;
+        } else if (profile != -1) {
+            return profile;
+        } else {
+            Log.e(LOG_TAG,
+                    "format doesn't contain either KEY_AAC_PROFILE or KEY_PROFILE");
+            return -1;
+        }
+    }
+
     @Override
     boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) {
         if (!super.isFormatSimilar(inpFormat, outFormat)) {
@@ -634,28 +656,11 @@
         if (outMime.startsWith("audio/")) {
             if (outFormat.getString(MediaFormat.KEY_MIME).equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
                 int inputProfileKey, outputProfileKey;
-                if (outFormat.containsKey(MediaFormat.KEY_AAC_PROFILE)) {
-                    outputProfileKey = outFormat.getInteger(MediaFormat.KEY_AAC_PROFILE);
-                } else if (outFormat.containsKey(MediaFormat.KEY_PROFILE)) {
-                    outputProfileKey = outFormat.getInteger(MediaFormat.KEY_PROFILE);
-                } else {
-                    Log.e(LOG_TAG,
-                            "Output format doesn't contain either KEY_AAC_PROFILE or KEY_PROFILE");
-                    return false;
-                }
-                if (inpFormat.containsKey(MediaFormat.KEY_AAC_PROFILE)) {
-                    inputProfileKey = inpFormat.getInteger(MediaFormat.KEY_AAC_PROFILE);
-                } else if (inpFormat.containsKey(MediaFormat.KEY_PROFILE)) {
-                    inputProfileKey = inpFormat.getInteger(MediaFormat.KEY_PROFILE);
-                } else {
-                    Log.e(LOG_TAG,
-                            "Input format doesn't contain either KEY_AAC_PROFILE or KEY_PROFILE");
-                    return false;
-                }
+                outputProfileKey = getAacProfile(outFormat);
+                inputProfileKey = getAacProfile(inpFormat);
                 if (outputProfileKey != inputProfileKey) {
-                    Log.e(LOG_TAG, "aac-profile in output doesn't match configured input");
-                    //TODO (b/151429829)
-                    if (true) return true;
+                    Log.e(LOG_TAG, "aac-profile in output " + outputProfileKey +
+                            " doesn't match configured input " + inputProfileKey);
                     return false;
                 }
             }
@@ -730,8 +735,12 @@
             MediaCodecInfo.CodecCapabilities codecCapabilities =
                     mCodec.getCodecInfo().getCapabilitiesForType(mMime);
             for (int profile : profiles) {
-                format.setInteger(mIsAudio ? MediaFormat.KEY_AAC_PROFILE : MediaFormat.KEY_PROFILE,
-                        profile);
+                format.setInteger(MediaFormat.KEY_PROFILE, profile);
+                // for aac encoder, alongwith setting profile, also set aac-profile as some
+                // encoders may only support one of the two keys
+                if (mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
+                    format.setInteger(MediaFormat.KEY_AAC_PROFILE, profile);
+                }
                 int level = mIsAudio ? 0 : getMinLevel(mMime, mWidth, mHeight,
                         format.getInteger(MediaFormat.KEY_FRAME_RATE),
                         format.getInteger(MediaFormat.KEY_BIT_RATE), profile);
@@ -752,6 +761,8 @@
                     continue;
                 }
                 for (boolean isAsync : boolStates) {
+                    mOutputBuff.reset();
+                    mInfoList.clear();
                     configureCodec(format, isAsync, true, true);
                     mCodec.start();
                     doWork(1);
@@ -769,7 +780,7 @@
                                     (ENABLE_LOGS ? "\n output format:" + outFormat : ""),
                             isFormatSimilar(format, outFormat));
 
-                    // TODO (b/151429829) (b/151398466)
+                    // TODO (b/151398466)
                     if (mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
                         Assume.assumeTrue("neither KEY_AAC_PROFILE nor KEY_PROFILE are present",
                                 outFormat.containsKey(MediaFormat.KEY_AAC_PROFILE) ||
@@ -795,6 +806,15 @@
                         Log.w(LOG_TAG, "Skip validation after muxing for mime = " + mMime);
                         continue;
                     }
+                    // TODO (b/184889671) aac for profile AACObjectHE fails validation
+                    // TODO (b/184890155) aac for profile AACObjectLD, AACObjectELD fails validation
+                    if (mMime.equals(MediaFormat.MIMETYPE_AUDIO_AAC) &&
+                                profile != AACObjectLC) {
+                        Log.w(LOG_TAG, "Skip validation after muxing for mime = " + mMime +
+                                " profile " + profile);
+                        continue;
+                    }
+
                     for (int muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_FIRST;
                          muxerFormat <= MediaMuxer.OutputFormat.MUXER_OUTPUT_LAST; muxerFormat++) {
                         if (!MuxerTest.isCodecContainerPairValid(mMime, muxerFormat)) continue;
diff --git a/tests/media/src/android/mediav2/cts/ExtractorTest.java b/tests/media/src/android/mediav2/cts/ExtractorTest.java
index e2a9b57..ad0a448 100644
--- a/tests/media/src/android/mediav2/cts/ExtractorTest.java
+++ b/tests/media/src/android/mediav2/cts/ExtractorTest.java
@@ -765,7 +765,6 @@
 
         @Parameterized.Parameters(name = "{index}({0})")
         public static Collection<Object[]> input() {
-            /* TODO(b/157108639) - add missing test files */
             return Arrays.asList(new Object[][]{
                     {MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{
                             "bbb_cif_768kbps_30fps_mpeg2_stereo_48kHz_192kbps_mp3.mp4",
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
index dd5f795..adf6ebc 100644
--- a/tests/media/src/android/mediav2/cts/MuxerTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -420,6 +420,7 @@
      * setOrientationHint are dependent on the mime type and OutputFormat. Legality of these APIs
      * are tested in this class.
      */
+    @NonMediaMainlineTest
     @SmallTest
     @RunWith(Parameterized.class)
     public static class TestApi {
@@ -702,6 +703,7 @@
     /**
      * Tests muxing multiple Video/Audio Tracks
      */
+    @NonMediaMainlineTest
     @LargeTest
     @RunWith(Parameterized.class)
     public static class TestMultiTrack {
@@ -840,6 +842,7 @@
      * Add an offset to the presentation time of samples of a track. Mux with the added offset,
      * validate by re-extracting the muxer output file and compare with original.
      */
+    @NonMediaMainlineTest
     @LargeTest
     @RunWith(Parameterized.class)
     public static class TestOffsetPts {
@@ -954,6 +957,7 @@
      * This test takes the output of a codec and muxes it in to all possible container formats.
      * The results are checked for inconsistencies with the requirements of CDD.
      */
+    @NonMediaMainlineTest
     @LargeTest
     @RunWith(Parameterized.class)
     public static class TestSimpleMux {
@@ -1102,6 +1106,7 @@
         }
     }
 
+    @NonMediaMainlineTest
     @LargeTest
     @RunWith(Parameterized.class)
     public static class TestAddEmptyTracks {
diff --git a/tests/media/src/android/mediav2/cts/MuxerUnitTest.java b/tests/media/src/android/mediav2/cts/MuxerUnitTest.java
index b648ca2..f23535d 100644
--- a/tests/media/src/android/mediav2/cts/MuxerUnitTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerUnitTest.java
@@ -53,6 +53,7 @@
     // duplicate definitions of hide fields of MediaMuxer.OutputFormat.
     private static final int MUXER_OUTPUT_LAST = MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG;
 
+    @NonMediaMainlineTest
     @SmallTest
     public static class TestApi {
         @Rule
@@ -739,6 +740,7 @@
         }
     }
 
+    @NonMediaMainlineTest
     @SmallTest
     public static class TestApiNative {
         @Rule
diff --git a/tests/media/src/android/mediav2/cts/NonMediaMainlineTest.java b/tests/media/src/android/mediav2/cts/NonMediaMainlineTest.java
new file mode 100644
index 0000000..83bc166
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/NonMediaMainlineTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediav2.cts;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for tests that are not related to media mainline.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface NonMediaMainlineTest {
+}
diff --git a/tests/media/src/android/mediav2/cts/WorkDir.java b/tests/media/src/android/mediav2/cts/WorkDir.java
index 27f0429..5d23fdf 100644
--- a/tests/media/src/android/mediav2/cts/WorkDir.java
+++ b/tests/media/src/android/mediav2/cts/WorkDir.java
@@ -40,7 +40,7 @@
             // user has specified the mediaDirString via instrumentation-arg
             return mediaDirString + ((mediaDirString.endsWith("/")) ? "" : "/");
         } else {
-            return (getTopDirString() + "test/CtsMediaV2TestCases-1.10/");
+            return (getTopDirString() + "test/CtsMediaV2TestCases-1.12/");
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/mediapc/Android.bp b/tests/mediapc/Android.bp
new file mode 100644
index 0000000..dcfdb23
--- /dev/null
+++ b/tests/mediapc/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "CtsMediaPerformanceClassTestCases",
+    defaults: ["cts_defaults"],
+    compile_multilib: "both",
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "ctstestserver",
+    ],
+    libs: [
+        "org.apache.http.legacy",
+        "android.test.runner",
+        "android.test.base",
+    ],
+    platform_apis: true,
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    min_sdk_version: "30",
+}
diff --git a/tests/mediapc/AndroidManifest.xml b/tests/mediapc/AndroidManifest.xml
new file mode 100644
index 0000000..20d030e
--- /dev/null
+++ b/tests/mediapc/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.mediapc.cts">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+    <application
+        android:requestLegacyExternalStorage="true"
+        android:usesCleartextTraffic="true">
+        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="org.apache.http.legacy" />
+        <activity android:name="android.mediapc.cts.TestActivity" />
+    </application>
+    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="android.mediapc.cts"
+            android:label="CTS MediaPerformanceClass tests of android.media" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
diff --git a/tests/mediapc/AndroidTest.xml b/tests/mediapc/AndroidTest.xml
new file mode 100644
index 0000000..1303ba9
--- /dev/null
+++ b/tests/mediapc/AndroidTest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS Media Performance Class test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="media" />
+    <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="host" />
+        <option name="config-filename" value="CtsMediaPerformanceClassTestCases" />
+        <option name="version" value="1.0"/>
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
+        <option name="push-all" value="true" />
+        <option name="media-folder-name" value="CtsMediaPerformanceClassTestCases-1.0" />
+        <option name="dynamic-config-module" value="CtsMediaPerformanceClassTestCases" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsMediaPerformanceClassTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.mediapc.cts" />
+    </test>
+</configuration>
diff --git a/tests/mediapc/DynamicConfig.xml b/tests/mediapc/DynamicConfig.xml
new file mode 100644
index 0000000..73348b4
--- /dev/null
+++ b/tests/mediapc/DynamicConfig.xml
@@ -0,0 +1,6 @@
+<dynamicConfig>
+    <entry key="media_files_url">
+      <value>https://storage.googleapis.com/android_media/cts/tests/mediapc/CtsMediaPerformanceClassTestCases-1.0.zip</value>
+    </entry>
+</dynamicConfig>
+
diff --git a/tests/mediapc/OWNERS b/tests/mediapc/OWNERS
new file mode 100644
index 0000000..091c4df
--- /dev/null
+++ b/tests/mediapc/OWNERS
@@ -0,0 +1,14 @@
+# Bug component: 1344
+# include media developers and framework video team
+include platform/frameworks/av:/media/OWNERS
+chz@google.com
+dichenzhang@google.com
+essick@google.com
+gokrishnan@google.com
+lajos@google.com
+taklee@google.com
+wonsik@google.com
+
+# LON
+olly@google.com
+andrewlewis@google.com
diff --git a/tests/mediapc/README.md b/tests/mediapc/README.md
new file mode 100644
index 0000000..d1d8a21
--- /dev/null
+++ b/tests/mediapc/README.md
@@ -0,0 +1,10 @@
+## Media Performance Class CTS Tests
+Current folder comprises of files necessary for testing media performance class.
+
+The test vectors used by the test suite is available at [link](https://storage.googleapis.com/android_media/cts/tests/mediapc/CtsMediaPerformanceClassTestCases-1.0.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory.
+
+### Commands
+```sh
+$ atest android.mediapc.cts
+$ atest android.mediapc.cts.PeformanceClassTest
+```
diff --git a/tests/mediapc/copy_media.sh b/tests/mediapc/copy_media.sh
new file mode 100644
index 0000000..95919a5
--- /dev/null
+++ b/tests/mediapc/copy_media.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+## script to install media performance class test files manually
+
+adbOptions=" "
+resLabel=CtsMediaPerformanceClassTestCases-1.0
+srcDir="/tmp/$resLabel"
+tgtDir="/sdcard/test"
+usage="Usage: $0 [-h] [-s serial]"
+
+if [ $# -gt 0 ]; then
+  if [ "$1" = "-h" ]; then
+    echo $usage
+    exit 1
+  elif [ "$1" = "-s" -a "$2" != "" ] ; then
+    adbOptions=""$1" "$2""
+  else
+    echo "bad options"
+    echo $usage
+    exit 1
+  fi
+fi
+
+## download resources if not already done
+if [ ! -f "/tmp/$resLabel.zip" ]; then
+  wget "https://storage.googleapis.com/android_media/cts/tests/mediapc/$resLabel.zip" -O /tmp/$resLabel.zip
+fi
+unzip -qo "/tmp/$resLabel" -d $srcDir
+
+## install on target device
+echo "adb $adbOptions push $srcDir $tgtDir"
+adb $adbOptions shell mkdir -p $tgtDir
+adb $adbOptions push $srcDir/. $tgtDir
diff --git a/tests/mediapc/res/layout/media_decoder_surface_layout.xml b/tests/mediapc/res/layout/media_decoder_surface_layout.xml
new file mode 100644
index 0000000..bff09bb
--- /dev/null
+++ b/tests/mediapc/res/layout/media_decoder_surface_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <SurfaceView android:id="@+id/surface"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+    </SurfaceView>
+</LinearLayout>
diff --git a/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java b/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java
new file mode 100644
index 0000000..495eae1
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.view.Surface;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class AdaptivePlaybackFrameDropTest extends FrameDropTestBase {
+    private static final String LOG_TAG = AdaptivePlaybackFrameDropTest.class.getSimpleName();
+
+    public AdaptivePlaybackFrameDropTest(String mimeType, String decoderName, boolean isAsync) {
+        super(mimeType, decoderName, isAsync);
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{1}_{2})")
+    public static Collection<Object[]> inputParams() {
+        return prepareArgumentsList(new String[]{
+                MediaCodecInfo.CodecCapabilities.FEATURE_AdaptivePlayback});
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testAdaptivePlaybackFrameDrop() throws Exception {
+        for (int i = 0; i < 5; i++) {
+            AdaptivePlayback adaptivePlayback = new AdaptivePlayback(mMime,
+                    new String[]{m1080pTestFiles.get(mMime), m540pTestFiles.get(mMime),
+                            m1080pTestFiles.get(mMime)},
+                    mDecoderName, mSurface, mIsAsync);
+            adaptivePlayback.doAdaptivePlaybackAndCalculateFrameDrop();
+        }
+    }
+}
+
+class AdaptivePlayback extends DecodeExtractedSamplesTestBase {
+    private final String mDecoderName;
+
+    AdaptivePlayback(String mime, String[] testFiles, String decoderName, Surface surface,
+            boolean isAsync) {
+        super(mime, testFiles, surface, isAsync);
+        mDecoderName = decoderName;
+    }
+
+    public void doAdaptivePlaybackAndCalculateFrameDrop() throws Exception {
+        ArrayList<MediaFormat> formats = setUpSourceFiles();
+        mCodec = MediaCodec.createByCodecName(mDecoderName);
+        configureCodec(formats.get(0), mIsAsync, false, false);
+        mCodec.start();
+        doWork(mBuff, mBufferInfos);
+        queueEOS();
+        waitForAllOutputs();
+        mCodec.stop();
+        mCodec.release();
+        assertTrue("FrameDrop count for mime: " + mMime + " decoder: " + mDecoderName +
+                " is not as expected. act/exp: " + mFrameDropCount + "/0", mFrameDropCount == 0);
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/AudioPlaybackLoad.java b/tests/mediapc/src/android/mediapc/cts/AudioPlaybackLoad.java
new file mode 100644
index 0000000..bc9cc22
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/AudioPlaybackLoad.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+
+import java.nio.ByteBuffer;
+
+class AudioPlaybackLoad extends CodecDecoderTestBase {
+    private final String mDecoderName;
+    private final LoadStatus mLoadStatus;
+
+    private long mBasePts;
+    private long mMaxPts;
+    private AudioTrack mTrack;
+
+    AudioPlaybackLoad(String mime, String testFile, String decoderName, LoadStatus loadStatus) {
+        super(mime, testFile);
+        mDecoderName = decoderName;
+        mLoadStatus = loadStatus;
+        mBasePts = 0;
+        mMaxPts = 0;
+    }
+
+    public void doDecodeAndPlayback() throws Exception {
+        MediaFormat format = setUpSource(mTestFile);
+        mTrack = createAudioTrack(format);
+        mTrack.play();
+        mCodec = MediaCodec.createByCodecName(mDecoderName);
+        mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+        configureCodec(format, false, false, false);
+        mCodec.start();
+        doWork(Integer.MAX_VALUE);
+        queueEOS();
+        waitForAllOutputs();
+        mCodec.stop();
+        mCodec.release();
+        mExtractor.release();
+        mTrack.pause();
+        mTrack.flush();
+        mTrack.release();
+    }
+
+    private AudioTrack createAudioTrack(MediaFormat format) {
+        final int channelMask = getChannelMask(format);
+        final int encoding = AudioFormat.ENCODING_PCM_16BIT;
+        final int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+        final AudioFormat audioFormat = new AudioFormat.Builder()
+                .setEncoding(encoding)
+                .setSampleRate(sampleRate)
+                .setChannelMask(channelMask)
+                .build();
+        final AudioAttributes audioAttributes = new AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_MEDIA)
+                .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
+                .build();
+        final int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelMask, encoding);
+        final int bufferSize = 2 * minBufferSize;
+        return new AudioTrack.Builder()
+                .setBufferSizeInBytes(bufferSize)
+                .setAudioAttributes(audioAttributes)
+                .setAudioFormat(audioFormat)
+                .build();
+    }
+
+    private int getChannelMask(MediaFormat format) {
+        final int count = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+        if (count == 1) {
+            return AudioFormat.CHANNEL_OUT_MONO;
+        } else if (count == 2) {
+            return AudioFormat.CHANNEL_OUT_STEREO;
+        } else if (count == 6) {
+            return AudioFormat.CHANNEL_OUT_5POINT1;
+        }
+        return -1;
+    }
+
+    @Override
+    void enqueueInput(int bufferIndex) {
+        if (mExtractor.getSampleSize() < 0) {
+            enqueueEOS(bufferIndex);
+        } else {
+            ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+            int size = mExtractor.readSampleData(inputBuffer, 0);
+            long pts = mExtractor.getSampleTime();
+            mMaxPts = Math.max(mMaxPts, mBasePts + pts);
+            int extractorFlags = mExtractor.getSampleFlags();
+            int codecFlags = 0;
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+            }
+            mCodec.queueInputBuffer(bufferIndex, 0, size, mBasePts + pts, codecFlags);
+            if (size > 0 && (codecFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+                mInputCount++;
+            }
+            if (!mExtractor.advance() && !mLoadStatus.isLoadFinished()) {
+                mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+                mBasePts = mMaxPts + 1000000L;
+            }
+        }
+    }
+
+    @Override
+    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawOutputEOS = true;
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mOutputCount++;
+        }
+        final ByteBuffer buffer = mCodec.getOutputBuffer(bufferIndex);
+        final byte[] audio = new byte[info.size];
+        buffer.clear(); // prepare buffer for reading
+        buffer.get(audio);
+        mTrack.write(audio, 0, audio.length);
+        mCodec.releaseOutputBuffer(bufferIndex, false);
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java b/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java
new file mode 100644
index 0000000..5aad0bd
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java
@@ -0,0 +1,825 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.graphics.ImageFormat;
+import android.media.Image;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Assert;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.concurrent.Callable;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+class CodecAsyncHandler extends MediaCodec.Callback {
+    private static final String LOG_TAG = CodecAsyncHandler.class.getSimpleName();
+    private final Lock mLock = new ReentrantLock();
+    private final Condition mCondition = mLock.newCondition();
+    private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbInputQueue;
+    private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mCbOutputQueue;
+    private MediaFormat mOutFormat;
+    private boolean mSignalledOutFormatChanged;
+    private volatile boolean mSignalledError;
+
+    CodecAsyncHandler() {
+        mCbInputQueue = new LinkedList<>();
+        mCbOutputQueue = new LinkedList<>();
+        mSignalledError = false;
+        mSignalledOutFormatChanged = false;
+    }
+
+    void clearQueues() {
+        mLock.lock();
+        mCbInputQueue.clear();
+        mCbOutputQueue.clear();
+        mLock.unlock();
+    }
+
+    void resetContext() {
+        clearQueues();
+        mOutFormat = null;
+        mSignalledOutFormatChanged = false;
+        mSignalledError = false;
+    }
+
+    @Override
+    public void onInputBufferAvailable(@NonNull MediaCodec codec, int bufferIndex) {
+        assertTrue(bufferIndex >= 0);
+        mLock.lock();
+        mCbInputQueue.add(new Pair<>(bufferIndex, (MediaCodec.BufferInfo) null));
+        mCondition.signalAll();
+        mLock.unlock();
+    }
+
+    @Override
+    public void onOutputBufferAvailable(@NonNull MediaCodec codec, int bufferIndex,
+            @NonNull MediaCodec.BufferInfo info) {
+        assertTrue(bufferIndex >= 0);
+        mLock.lock();
+        mCbOutputQueue.add(new Pair<>(bufferIndex, info));
+        mCondition.signalAll();
+        mLock.unlock();
+    }
+
+    @Override
+    public void onError(@NonNull MediaCodec codec, MediaCodec.CodecException e) {
+        mLock.lock();
+        mSignalledError = true;
+        mCondition.signalAll();
+        mLock.unlock();
+        Log.e(LOG_TAG, "received media codec error : " + e.getMessage());
+    }
+
+    @Override
+    public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
+        mOutFormat = format;
+        mSignalledOutFormatChanged = true;
+        Log.i(LOG_TAG, "Output format changed: " + format.toString());
+    }
+
+    void setCallBack(MediaCodec codec, boolean isCodecInAsyncMode) {
+        if (isCodecInAsyncMode) {
+            codec.setCallback(this);
+        } else {
+            codec.setCallback(null);
+        }
+    }
+
+    Pair<Integer, MediaCodec.BufferInfo> getOutput() throws InterruptedException {
+        Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
+        while (!mSignalledError) {
+            if (mCbOutputQueue.isEmpty()) {
+                mCondition.await();
+            } else {
+                element = mCbOutputQueue.remove(0);
+                break;
+            }
+        }
+        mLock.unlock();
+        return element;
+    }
+
+    Pair<Integer, MediaCodec.BufferInfo> getWork() throws InterruptedException {
+        Pair<Integer, MediaCodec.BufferInfo> element = null;
+        mLock.lock();
+        while (!mSignalledError) {
+            if (mCbInputQueue.isEmpty() && mCbOutputQueue.isEmpty()) {
+                mCondition.await();
+            } else {
+                if (!mCbOutputQueue.isEmpty()) {
+                    element = mCbOutputQueue.remove(0);
+                    break;
+                }
+                if (!mCbInputQueue.isEmpty()) {
+                    element = mCbInputQueue.remove(0);
+                    break;
+                }
+            }
+        }
+        mLock.unlock();
+        return element;
+    }
+
+    boolean hasSeenError() {
+        return mSignalledError;
+    }
+
+    boolean hasOutputFormatChanged() {
+        return mSignalledOutFormatChanged;
+    }
+
+    MediaFormat getOutputFormat() {
+        return mOutFormat;
+    }
+}
+
+abstract class CodecTestBase {
+    private static final String LOG_TAG = CodecTestBase.class.getSimpleName();
+    static final boolean ENABLE_LOGS = false;
+    static final int PER_TEST_TIMEOUT_LARGE_TEST_MS = 300000;
+    static final int SELECT_ALL = 0; // Select all codecs
+    static final int SELECT_HARDWARE = 1; // Select Hardware codecs only
+    static final int SELECT_SOFTWARE = 2; // Select Software codecs only
+    // Maintain Timeouts in sync with their counterpart in NativeMediaCommon.h
+    static final long Q_DEQ_TIMEOUT_US = 5000; // block at most 5ms while looking for io buffers
+    static final int RETRY_LIMIT = 100; // max poll counter before test aborts and returns error
+    static final String mInpPrefix = WorkDir.getMediaDirString();
+
+    CodecAsyncHandler mAsyncHandle;
+    boolean mIsCodecInAsyncMode;
+    boolean mSawInputEOS;
+    boolean mSawOutputEOS;
+    boolean mSignalEOSWithLastFrame;
+    int mInputCount;
+    int mOutputCount;
+    long mPrevOutputPts;
+    boolean mSignalledOutFormatChanged;
+    MediaFormat mOutFormat;
+    boolean mIsAudio;
+
+    MediaCodec mCodec;
+    Surface mSurface;
+
+    abstract void enqueueInput(int bufferIndex) throws IOException;
+
+    abstract void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info);
+
+    void configureCodec(MediaFormat format, boolean isAsync, boolean signalEOSWithLastFrame,
+            boolean isEncoder) {
+        resetContext(isAsync, signalEOSWithLastFrame);
+        mAsyncHandle.setCallBack(mCodec, isAsync);
+        // signalEOS flag has nothing to do with configure. We are using this flag to try all
+        // available configure apis
+        if (signalEOSWithLastFrame) {
+            mCodec.configure(format, mSurface, null,
+                    isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0);
+        } else {
+            mCodec.configure(format, mSurface, isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0,
+                    null);
+        }
+    }
+
+    void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
+        mAsyncHandle.resetContext();
+        mIsCodecInAsyncMode = isAsync;
+        mSawInputEOS = false;
+        mSawOutputEOS = false;
+        mSignalEOSWithLastFrame = signalEOSWithLastFrame;
+        mInputCount = 0;
+        mOutputCount = 0;
+        mPrevOutputPts = Long.MIN_VALUE;
+        mSignalledOutFormatChanged = false;
+    }
+
+    void enqueueEOS(int bufferIndex) {
+        if (!mSawInputEOS) {
+            mCodec.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+            mSawInputEOS = true;
+            if (ENABLE_LOGS) {
+                Log.v(LOG_TAG, "Queued End of Stream");
+            }
+        }
+    }
+
+    void doWork(int frameLimit) throws InterruptedException, IOException {
+        int frameCount = 0;
+        if (mIsCodecInAsyncMode) {
+            // dequeue output after inputEOS is expected to be done in waitForAllOutputs()
+            while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < frameLimit) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
+                if (element != null) {
+                    int bufferID = element.first;
+                    MediaCodec.BufferInfo info = element.second;
+                    if (info != null) {
+                        // <id, info> corresponds to output callback. Handle it accordingly
+                        dequeueOutput(bufferID, info);
+                    } else {
+                        // <id, null> corresponds to input callback. Handle it accordingly
+                        enqueueInput(bufferID);
+                        frameCount++;
+                    }
+                }
+            }
+        } else {
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            // dequeue output after inputEOS is expected to be done in waitForAllOutputs()
+            while (!mSawInputEOS && frameCount < frameLimit) {
+                int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+                if (outputBufferId >= 0) {
+                    dequeueOutput(outputBufferId, outInfo);
+                } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    mOutFormat = mCodec.getOutputFormat();
+                    mSignalledOutFormatChanged = true;
+                }
+                int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+                if (inputBufferId != -1) {
+                    enqueueInput(inputBufferId);
+                    frameCount++;
+                }
+            }
+        }
+    }
+
+    void queueEOS() throws InterruptedException {
+        if (mIsCodecInAsyncMode) {
+            while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
+                if (element != null) {
+                    int bufferID = element.first;
+                    MediaCodec.BufferInfo info = element.second;
+                    if (info != null) {
+                        dequeueOutput(bufferID, info);
+                    } else {
+                        enqueueEOS(element.first);
+                    }
+                }
+            }
+        } else {
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            while (!mSawInputEOS) {
+                int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+                if (outputBufferId >= 0) {
+                    dequeueOutput(outputBufferId, outInfo);
+                } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    mOutFormat = mCodec.getOutputFormat();
+                    mSignalledOutFormatChanged = true;
+                }
+                int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+                if (inputBufferId != -1) {
+                    enqueueEOS(inputBufferId);
+                }
+            }
+        }
+    }
+
+    void waitForAllOutputs() throws InterruptedException {
+        if (mIsCodecInAsyncMode) {
+            while (!mAsyncHandle.hasSeenError() && !mSawOutputEOS) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getOutput();
+                if (element != null) {
+                    dequeueOutput(element.first, element.second);
+                }
+            }
+        } else {
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            while (!mSawOutputEOS) {
+                int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+                if (outputBufferId >= 0) {
+                    dequeueOutput(outputBufferId, outInfo);
+                } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    mOutFormat = mCodec.getOutputFormat();
+                    mSignalledOutFormatChanged = true;
+                }
+            }
+        }
+    }
+
+    static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
+            String[] features, boolean isEncoder) {
+        return selectCodecs(mime, formats, features, isEncoder, SELECT_ALL);
+    }
+
+    static ArrayList<String> selectHardwareCodecs(String mime, ArrayList<MediaFormat> formats,
+            String[] features, boolean isEncoder) {
+        return selectCodecs(mime, formats, features, isEncoder, SELECT_HARDWARE);
+    }
+
+    static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
+            String[] features, boolean isEncoder, int selectCodecOption) {
+        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+        ArrayList<String> listOfCodecs = new ArrayList<>();
+        for (MediaCodecInfo codecInfo : codecInfos) {
+            if (codecInfo.isEncoder() != isEncoder) continue;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
+            if (selectCodecOption == SELECT_HARDWARE && !codecInfo.isHardwareAccelerated())
+                continue;
+            else if (selectCodecOption == SELECT_SOFTWARE && !codecInfo.isSoftwareOnly())
+                continue;
+            String[] types = codecInfo.getSupportedTypes();
+            for (String type : types) {
+                if (type.equalsIgnoreCase(mime)) {
+                    boolean isOk = true;
+                    MediaCodecInfo.CodecCapabilities codecCapabilities =
+                            codecInfo.getCapabilitiesForType(type);
+                    if (formats != null) {
+                        for (MediaFormat format : formats) {
+                            if (!codecCapabilities.isFormatSupported(format)) {
+                                isOk = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (features != null) {
+                        for (String feature : features) {
+                            if (!codecCapabilities.isFeatureSupported(feature)) {
+                                isOk = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (isOk) listOfCodecs.add(codecInfo.getName());
+                }
+            }
+        }
+        return listOfCodecs;
+    }
+}
+
+class CodecDecoderTestBase extends CodecTestBase {
+    private static final String LOG_TAG = CodecDecoderTestBase.class.getSimpleName();
+
+    String mMime;
+    String mTestFile;
+    boolean mIsInterlaced;
+
+    ArrayList<ByteBuffer> mCsdBuffers;
+
+    MediaExtractor mExtractor;
+
+    CodecDecoderTestBase(String mime, String testFile) {
+        mMime = mime;
+        mTestFile = testFile;
+        mAsyncHandle = new CodecAsyncHandler();
+        mCsdBuffers = new ArrayList<>();
+        mIsAudio = mMime.startsWith("audio/");
+    }
+
+    MediaFormat setUpSource(String srcFile) throws IOException {
+        return setUpSource(mInpPrefix, srcFile);
+    }
+
+    boolean hasCSD(MediaFormat format) {
+        return format.containsKey("csd-0");
+    }
+
+    MediaFormat setUpSource(String prefix, String srcFile) throws IOException {
+        mExtractor = new MediaExtractor();
+        mExtractor.setDataSource(prefix + srcFile);
+        for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
+            MediaFormat format = mExtractor.getTrackFormat(trackID);
+            if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
+                mExtractor.selectTrack(trackID);
+                if (!mIsAudio) {
+                    if (mSurface == null) {
+                        // COLOR_FormatYUV420Flexible must be supported by all components
+                        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
+                    } else {
+                        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatSurface);
+                    }
+                }
+                // TODO: determine this from the extractor format when it becomes exposed.
+                mIsInterlaced = srcFile.contains("_interlaced_");
+                return format;
+            }
+        }
+        fail("No track with mime: " + mMime + " found in file: " + srcFile);
+        return null;
+    }
+
+    void enqueueInput(int bufferIndex, ByteBuffer buffer, MediaCodec.BufferInfo info) {
+        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+        inputBuffer.put(buffer.array(), info.offset, info.size);
+        mCodec.queueInputBuffer(bufferIndex, 0, info.size, info.presentationTimeUs,
+                info.flags);
+        if (info.size > 0 && ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) &&
+                ((info.flags & MediaCodec.BUFFER_FLAG_PARTIAL_FRAME) == 0)) {
+            mInputCount++;
+        }
+    }
+
+    void enqueueInput(int bufferIndex) {
+        if (mExtractor.getSampleSize() < 0) {
+            enqueueEOS(bufferIndex);
+        } else {
+            ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+            int size = mExtractor.readSampleData(inputBuffer, 0);
+            long pts = mExtractor.getSampleTime();
+            int extractorFlags = mExtractor.getSampleFlags();
+            int codecFlags = 0;
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+            }
+            if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                mSawInputEOS = true;
+            }
+            mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+            if (size > 0 && (codecFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+                mInputCount++;
+            }
+        }
+    }
+
+    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawOutputEOS = true;
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mOutputCount++;
+        }
+        mCodec.releaseOutputBuffer(bufferIndex, false);
+    }
+
+    void doWork(ByteBuffer buffer, ArrayList<MediaCodec.BufferInfo> list)
+            throws InterruptedException {
+        int frameCount = 0;
+        if (mIsCodecInAsyncMode) {
+            // output processing after queuing EOS is done in waitForAllOutputs()
+            while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < list.size()) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
+                if (element != null) {
+                    int bufferID = element.first;
+                    MediaCodec.BufferInfo info = element.second;
+                    if (info != null) {
+                        dequeueOutput(bufferID, info);
+                    } else {
+                        enqueueInput(bufferID, buffer, list.get(frameCount));
+                        frameCount++;
+                    }
+                }
+            }
+        } else {
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            // output processing after queuing EOS is done in waitForAllOutputs()
+            while (!mSawInputEOS && frameCount < list.size()) {
+                int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+                if (outputBufferId >= 0) {
+                    dequeueOutput(outputBufferId, outInfo);
+                } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    mOutFormat = mCodec.getOutputFormat();
+                    mSignalledOutFormatChanged = true;
+                }
+                int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+                if (inputBufferId != -1) {
+                    enqueueInput(inputBufferId, buffer, list.get(frameCount));
+                    frameCount++;
+                }
+            }
+        }
+    }
+}
+
+class CodecEncoderTestBase extends CodecTestBase {
+    private static final String LOG_TAG = CodecEncoderTestBase.class.getSimpleName();
+
+    // files are in WorkDir.getMediaDirString();
+    private static final String INPUT_AUDIO_FILE = "bbb_2ch_44kHz_s16le.raw";
+    private static final String INPUT_VIDEO_FILE = "bbb_cif_yuv420p_30fps.yuv";
+    private final int INP_FRM_WIDTH = 352;
+    private final int INP_FRM_HEIGHT = 288;
+
+    final String mMime;
+    final String mInputFile;
+    byte[] mInputData;
+    int mNumBytesSubmitted;
+    long mInputOffsetPts;
+
+    int mWidth, mHeight;
+    int mFrameRate;
+    int mMaxBFrames;
+    int mChannels;
+    int mSampleRate;
+
+    CodecEncoderTestBase(String mime) {
+        mMime = mime;
+        mWidth = INP_FRM_WIDTH;
+        mHeight = INP_FRM_HEIGHT;
+        mChannels = 1;
+        mSampleRate = 8000;
+        mFrameRate = 30;
+        mMaxBFrames = 0;
+        if (mime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) mFrameRate = 12;
+        else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_H263)) mFrameRate = 12;
+        mAsyncHandle = new CodecAsyncHandler();
+        mIsAudio = mMime.startsWith("audio/");
+        mInputFile = mIsAudio ? INPUT_AUDIO_FILE : INPUT_VIDEO_FILE;
+    }
+
+    @Override
+    void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
+        super.resetContext(isAsync, signalEOSWithLastFrame);
+        mNumBytesSubmitted = 0;
+        mInputOffsetPts = 0;
+    }
+
+    void setUpSource(String srcFile) throws IOException {
+        String inpPath = mInpPrefix + srcFile;
+        try (FileInputStream fInp = new FileInputStream(inpPath)) {
+            int size = (int) new File(inpPath).length();
+            mInputData = new byte[size];
+            fInp.read(mInputData, 0, size);
+        }
+    }
+
+    void fillImage(Image image) {
+        Assert.assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
+        int imageWidth = image.getWidth();
+        int imageHeight = image.getHeight();
+        Image.Plane[] planes = image.getPlanes();
+        int offset = mNumBytesSubmitted;
+        for (int i = 0; i < planes.length; ++i) {
+            ByteBuffer buf = planes[i].getBuffer();
+            int width = imageWidth;
+            int height = imageHeight;
+            int tileWidth = INP_FRM_WIDTH;
+            int tileHeight = INP_FRM_HEIGHT;
+            int rowStride = planes[i].getRowStride();
+            int pixelStride = planes[i].getPixelStride();
+            if (i != 0) {
+                width = imageWidth / 2;
+                height = imageHeight / 2;
+                tileWidth = INP_FRM_WIDTH / 2;
+                tileHeight = INP_FRM_HEIGHT / 2;
+            }
+            if (pixelStride == 1) {
+                if (width == rowStride && width == tileWidth && height == tileHeight) {
+                    buf.put(mInputData, offset, width * height);
+                } else {
+                    for (int z = 0; z < height; z += tileHeight) {
+                        int rowsToCopy = Math.min(height - z, tileHeight);
+                        for (int y = 0; y < rowsToCopy; y++) {
+                            for (int x = 0; x < width; x += tileWidth) {
+                                int colsToCopy = Math.min(width - x, tileWidth);
+                                buf.position((z + y) * rowStride + x);
+                                buf.put(mInputData, offset + y * tileWidth, colsToCopy);
+                            }
+                        }
+                    }
+                }
+            } else {
+                // do it pixel-by-pixel
+                for (int z = 0; z < height; z += tileHeight) {
+                    int rowsToCopy = Math.min(height - z, tileHeight);
+                    for (int y = 0; y < rowsToCopy; y++) {
+                        int lineOffset = (z + y) * rowStride;
+                        for (int x = 0; x < width; x += tileWidth) {
+                            int colsToCopy = Math.min(width - x, tileWidth);
+                            for (int w = 0; w < colsToCopy; w++) {
+                                buf.position(lineOffset + (x + w) * pixelStride);
+                                buf.put(mInputData[offset + y * tileWidth + w]);
+                            }
+                        }
+                    }
+                }
+            }
+            offset += tileWidth * tileHeight;
+        }
+    }
+
+    void fillByteBuffer(ByteBuffer inputBuffer) {
+        int offset = 0, frmOffset = mNumBytesSubmitted;
+        for (int plane = 0; plane < 3; plane++) {
+            int width = mWidth;
+            int height = mHeight;
+            int tileWidth = INP_FRM_WIDTH;
+            int tileHeight = INP_FRM_HEIGHT;
+            if (plane != 0) {
+                width = mWidth / 2;
+                height = mHeight / 2;
+                tileWidth = INP_FRM_WIDTH / 2;
+                tileHeight = INP_FRM_HEIGHT / 2;
+            }
+            for (int k = 0; k < height; k += tileHeight) {
+                int rowsToCopy = Math.min(height - k, tileHeight);
+                for (int j = 0; j < rowsToCopy; j++) {
+                    for (int i = 0; i < width; i += tileWidth) {
+                        int colsToCopy = Math.min(width - i, tileWidth);
+                        inputBuffer.position(offset + (k + j) * width + i);
+                        inputBuffer.put(mInputData, frmOffset + j * tileWidth, colsToCopy);
+                    }
+                }
+            }
+            offset += width * height;
+            frmOffset += tileWidth * tileHeight;
+        }
+    }
+
+    void enqueueInput(int bufferIndex) {
+        ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+        if (mNumBytesSubmitted >= mInputData.length) {
+            enqueueEOS(bufferIndex);
+        } else {
+            int size;
+            int flags = 0;
+            long pts = mInputOffsetPts;
+            if (mIsAudio) {
+                pts += mNumBytesSubmitted * 1000000L / (2 * mChannels * mSampleRate);
+                size = Math.min(inputBuffer.capacity(), mInputData.length - mNumBytesSubmitted);
+                inputBuffer.put(mInputData, mNumBytesSubmitted, size);
+                if (mNumBytesSubmitted + size >= mInputData.length && mSignalEOSWithLastFrame) {
+                    flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                    mSawInputEOS = true;
+                }
+                mNumBytesSubmitted += size;
+            } else {
+                pts += mInputCount * 1000000L / mFrameRate;
+                size = mWidth * mHeight * 3 / 2;
+                int frmSize = INP_FRM_WIDTH * INP_FRM_HEIGHT * 3 / 2;
+                if (mNumBytesSubmitted + frmSize > mInputData.length) {
+                    fail("received partial frame to encode");
+                } else {
+                    Image img = mCodec.getInputImage(bufferIndex);
+                    if (img != null) {
+                        fillImage(img);
+                    } else {
+                        if (mWidth == INP_FRM_WIDTH && mHeight == INP_FRM_HEIGHT) {
+                            inputBuffer.put(mInputData, mNumBytesSubmitted, size);
+                        } else {
+                            fillByteBuffer(inputBuffer);
+                        }
+                    }
+                }
+                if (mNumBytesSubmitted + frmSize >= mInputData.length && mSignalEOSWithLastFrame) {
+                    flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                    mSawInputEOS = true;
+                }
+                mNumBytesSubmitted += frmSize;
+            }
+            mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
+            mInputCount++;
+        }
+    }
+
+    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawOutputEOS = true;
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mOutputCount++;
+        }
+        mCodec.releaseOutputBuffer(bufferIndex, false);
+    }
+}
+
+class Decode extends CodecDecoderTestBase implements Callable<Double> {
+    private static final String LOG_TAG = Decode.class.getSimpleName();
+
+    final String mDecoderName;
+    final boolean mIsAsync;
+
+    Decode(String mime, String testFile, String decoderName, boolean isAsync) {
+        super(mime, testFile);
+        mDecoderName = decoderName;
+        mSurface = MediaCodec.createPersistentInputSurface();
+        mIsAsync = isAsync;
+    }
+
+    public Double doDecode() throws Exception {
+        MediaFormat format = setUpSource(mTestFile);
+        mCodec = MediaCodec.createByCodecName(mDecoderName);
+        mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+        configureCodec(format, mIsAsync, false, false);
+        mCodec.start();
+        long start = System.currentTimeMillis();
+        doWork(Integer.MAX_VALUE);
+        queueEOS();
+        waitForAllOutputs();
+        long end = System.currentTimeMillis();
+        mCodec.stop();
+        mCodec.release();
+        mExtractor.release();
+        double fps = mOutputCount / ((end - start) / 1000.0);
+        Log.d(LOG_TAG, "Decode Mime: " + mMime + " Decoder: " + mDecoderName +
+                " Achieved fps: " + fps);
+        return fps;
+    }
+
+    @Override
+    public Double call() throws Exception {
+        return doDecode();
+    }
+}
+
+class DecodeToSurface extends Decode {
+
+    DecodeToSurface(String mime, String testFile, String decoderName, Surface surface,
+            boolean isAsync) {
+        super(mime, testFile, decoderName, isAsync);
+        mSurface = surface;
+    }
+
+    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawOutputEOS = true;
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mOutputCount++;
+        }
+        mCodec.releaseOutputBuffer(bufferIndex, true);
+    }
+}
+
+class Encode extends CodecEncoderTestBase implements Callable<Double> {
+    private static final String LOG_TAG = Encode.class.getSimpleName();
+
+    private final String mEncoderName;
+    private final boolean mIsAsync;
+
+    Encode(String mime, String encoderName, boolean isAsync) {
+        super(mime);
+        mEncoderName = encoderName;
+        mIsAsync = isAsync;
+        mSurface = MediaCodec.createPersistentInputSurface();
+        mFrameRate = 30;
+    }
+
+    private MediaFormat setUpFormat() {
+        MediaFormat format = new MediaFormat();
+        format.setString(MediaFormat.KEY_MIME, mMime);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, 4000000);
+        format.setInteger(MediaFormat.KEY_WIDTH, 1280);
+        format.setInteger(MediaFormat.KEY_HEIGHT, 720);
+        format.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
+        format.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 0);
+        format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
+        return format;
+    }
+
+
+    public Double doEncode() throws Exception {
+        MediaFormat format = setUpFormat();
+        mWidth = format.getInteger(MediaFormat.KEY_WIDTH);
+        mHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
+        setUpSource(mInputFile);
+        mCodec = MediaCodec.createByCodecName(mEncoderName);
+        configureCodec(format, mIsAsync, false, true);
+        mCodec.start();
+        long start = System.currentTimeMillis();
+        doWork(Integer.MAX_VALUE);
+        queueEOS();
+        waitForAllOutputs();
+        long end = System.currentTimeMillis();
+        mCodec.stop();
+        mCodec.release();
+        double fps = mOutputCount / ((end - start) / 1000.0);
+        Log.d(LOG_TAG, "Encode Mime: " + mMime + " Encoder: " + mEncoderName +
+                " Achieved fps: " + fps);
+        return fps;
+    }
+
+    @Override
+    public Double call() throws Exception {
+        return doEncode();
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/CodecTranscoderTestBase.java b/tests/mediapc/src/android/mediapc/cts/CodecTranscoderTestBase.java
new file mode 100644
index 0000000..b0c6da2
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/CodecTranscoderTestBase.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Callable;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class CodecTranscoderTestBase {
+    private static final String LOG_TAG = CodecTranscoderTestBase.class.getSimpleName();
+    private static final boolean ENABLE_LOGS = false;
+    static final String mInpPrefix = WorkDir.getMediaDirString();
+    String mMime;
+    String mTestFile;
+    int mBitrate;
+    int mFrameRate;
+    MediaExtractor mExtractor;
+    int mMaxBFrames;
+    int mLatency;
+
+    MediaCodec mEncoder;
+    CodecAsyncHandler mAsyncHandleEncoder;
+    MediaCodec mDecoder;
+    CodecAsyncHandler mAsyncHandleDecoder;
+    Surface mSurface;
+
+    boolean mSawDecInputEOS;
+    boolean mSawDecOutputEOS;
+    boolean mSawEncOutputEOS;
+    boolean mIsCodecInAsyncMode;
+    boolean mSignalEOSWithLastFrame;
+    boolean mReviseLatency;
+    int mDecInputCount;
+    int mDecOutputCount;
+    int mEncOutputCount;
+
+    CodecTranscoderTestBase(String mime, String testfile, int bitrate, int frameRate) {
+        mMime = mime;
+        mTestFile = testfile;
+        mBitrate = bitrate;
+        mFrameRate = frameRate;
+        mMaxBFrames = 0;
+        mLatency = mMaxBFrames;
+        mReviseLatency = false;
+        mAsyncHandleDecoder = new CodecAsyncHandler();
+        mAsyncHandleEncoder = new CodecAsyncHandler();
+    }
+
+    boolean hasSeenError() {
+        return mAsyncHandleDecoder.hasSeenError() || mAsyncHandleEncoder.hasSeenError();
+    }
+
+    MediaFormat setUpSource(String srcFile) throws IOException {
+        mExtractor = new MediaExtractor();
+        mExtractor.setDataSource(mInpPrefix + srcFile);
+        for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
+            MediaFormat format = mExtractor.getTrackFormat(trackID);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            if (mime.startsWith("video/")) {
+                mExtractor.selectTrack(trackID);
+                // COLOR_FormatYUV420Flexible by default should be supported by all components
+                // This call shouldn't effect configure() call for any codec
+                format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                        MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
+                return format;
+            }
+        }
+        mExtractor.release();
+        fail("No video track found in file: " + srcFile);
+        return null;
+    }
+
+    void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
+        mAsyncHandleDecoder.resetContext();
+        mAsyncHandleEncoder.resetContext();
+        mIsCodecInAsyncMode = isAsync;
+        mSignalEOSWithLastFrame = signalEOSWithLastFrame;
+        mSawDecInputEOS = false;
+        mSawDecOutputEOS = false;
+        mSawEncOutputEOS = false;
+        mDecInputCount = 0;
+        mDecOutputCount = 0;
+        mEncOutputCount = 0;
+    }
+
+    void configureCodec(MediaFormat decFormat, MediaFormat encFormat, boolean isAsync,
+            boolean signalEOSWithLastFrame) {
+        resetContext(isAsync, signalEOSWithLastFrame);
+        mAsyncHandleEncoder.setCallBack(mEncoder, isAsync);
+        mEncoder.configure(encFormat, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null);
+        if (mEncoder.getInputFormat().containsKey(MediaFormat.KEY_LATENCY)) {
+            mReviseLatency = true;
+            mLatency = mEncoder.getInputFormat().getInteger(MediaFormat.KEY_LATENCY);
+        }
+        mSurface = mEncoder.createInputSurface();
+        assertTrue("Surface is not valid", mSurface.isValid());
+        mAsyncHandleDecoder.setCallBack(mDecoder, isAsync);
+        mDecoder.configure(decFormat, mSurface, null, 0);
+        if (ENABLE_LOGS) {
+            Log.v(LOG_TAG, "codec configured");
+        }
+    }
+
+    void enqueueDecoderEOS(int bufferIndex) {
+        if (!mSawDecInputEOS) {
+            mDecoder.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+            mSawDecInputEOS = true;
+            if (ENABLE_LOGS) {
+                Log.v(LOG_TAG, "Queued End of Stream");
+            }
+        }
+    }
+
+    void enqueueDecoderInput(int bufferIndex) {
+        if (mExtractor.getSampleSize() < 0) {
+            enqueueDecoderEOS(bufferIndex);
+        } else {
+            ByteBuffer inputBuffer = mDecoder.getInputBuffer(bufferIndex);
+            int size = mExtractor.readSampleData(inputBuffer, 0);
+            long pts = mExtractor.getSampleTime();
+            int extractorFlags = mExtractor.getSampleFlags();
+            int codecFlags = 0;
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+            }
+            if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                mSawDecInputEOS = true;
+            }
+            mDecoder.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+            if (size > 0 && (codecFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+                mDecInputCount++;
+            }
+        }
+    }
+
+    void dequeueDecoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawDecOutputEOS = true;
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mDecOutputCount++;
+        }
+        mDecoder.releaseOutputBuffer(bufferIndex, mSurface != null);
+    }
+
+    void dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawEncOutputEOS = true;
+        }
+        if (info.size > 0) {
+            if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+                mEncOutputCount++;
+            }
+        }
+        mEncoder.releaseOutputBuffer(bufferIndex, false);
+    }
+
+    void tryEncoderOutput(long timeOutUs) throws InterruptedException {
+        if (mIsCodecInAsyncMode) {
+            if (!hasSeenError() && !mSawEncOutputEOS) {
+                int retry = 0;
+                while (mReviseLatency) {
+                    if (mAsyncHandleEncoder.hasOutputFormatChanged()) {
+                        mReviseLatency = false;
+                        int actualLatency = mAsyncHandleEncoder.getOutputFormat()
+                                .getInteger(MediaFormat.KEY_LATENCY, mLatency);
+                        if (mLatency < actualLatency) {
+                            mLatency = actualLatency;
+                            return;
+                        }
+                    } else {
+                        if (retry > CodecTestBase.RETRY_LIMIT) throw new InterruptedException(
+                                "did not receive output format changed for encoder after " +
+                                        CodecTestBase.Q_DEQ_TIMEOUT_US * CodecTestBase.RETRY_LIMIT +
+                                        " us");
+                        Thread.sleep(CodecTestBase.Q_DEQ_TIMEOUT_US / 1000);
+                        retry ++;
+                    }
+                }
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleEncoder.getOutput();
+                if (element != null) {
+                    dequeueEncoderOutput(element.first, element.second);
+                }
+            }
+        } else {
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            if (!mSawEncOutputEOS) {
+                int outputBufferId = mEncoder.dequeueOutputBuffer(outInfo, timeOutUs);
+                if (outputBufferId >= 0) {
+                    dequeueEncoderOutput(outputBufferId, outInfo);
+                } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    mLatency = mEncoder.getOutputFormat()
+                            .getInteger(MediaFormat.KEY_LATENCY, mLatency);
+                }
+            }
+        }
+    }
+
+    void waitForAllEncoderOutputs() throws InterruptedException {
+        if (mIsCodecInAsyncMode) {
+            while (!hasSeenError() && !mSawEncOutputEOS) {
+                tryEncoderOutput(CodecTestBase.Q_DEQ_TIMEOUT_US);
+            }
+        } else {
+            while (!mSawEncOutputEOS) {
+                tryEncoderOutput(CodecTestBase.Q_DEQ_TIMEOUT_US);
+            }
+        }
+    }
+
+    void queueEOS() throws InterruptedException {
+        if (mIsCodecInAsyncMode) {
+            while (!mAsyncHandleDecoder.hasSeenError() && !mSawDecInputEOS) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
+                if (element != null) {
+                    int bufferID = element.first;
+                    MediaCodec.BufferInfo info = element.second;
+                    if (info != null) {
+                        dequeueDecoderOutput(bufferID, info);
+                    } else {
+                        enqueueDecoderEOS(element.first);
+                    }
+                }
+            }
+        } else {
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            while (!mSawDecInputEOS) {
+                int outputBufferId =
+                        mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US);
+                if (outputBufferId >= 0) {
+                    dequeueDecoderOutput(outputBufferId, outInfo);
+                }
+                int inputBufferId = mDecoder.dequeueInputBuffer(CodecTestBase.Q_DEQ_TIMEOUT_US);
+                if (inputBufferId != -1) {
+                    enqueueDecoderEOS(inputBufferId);
+                }
+            }
+        }
+        if (mIsCodecInAsyncMode) {
+            while (!hasSeenError() && !mSawDecOutputEOS) {
+                Pair<Integer, MediaCodec.BufferInfo> decOp = mAsyncHandleDecoder.getOutput();
+                if (decOp != null) dequeueDecoderOutput(decOp.first, decOp.second);
+                if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
+                if (mDecOutputCount - mEncOutputCount > mLatency) {
+                    tryEncoderOutput(-1);
+                }
+            }
+        } else {
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            while (!mSawDecOutputEOS) {
+                int outputBufferId =
+                        mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US);
+                if (outputBufferId >= 0) {
+                    dequeueDecoderOutput(outputBufferId, outInfo);
+                }
+                if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
+                if (mDecOutputCount - mEncOutputCount > mLatency) {
+                    tryEncoderOutput(-1);
+                }
+            }
+        }
+    }
+
+    void doWork(int frameLimit) throws InterruptedException {
+        int frameCnt = 0;
+        if (mIsCodecInAsyncMode) {
+            // dequeue output after inputEOS is expected to be done in waitForAllOutputs()
+            while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
+                if (element != null) {
+                    int bufferID = element.first;
+                    MediaCodec.BufferInfo info = element.second;
+                    if (info != null) {
+                        // <id, info> corresponds to output callback. Handle it accordingly
+                        dequeueDecoderOutput(bufferID, info);
+                    } else {
+                        // <id, null> corresponds to input callback. Handle it accordingly
+                        enqueueDecoderInput(bufferID);
+                        frameCnt++;
+                    }
+                }
+                // check decoder EOS
+                if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
+                // encoder output
+                if (mDecOutputCount - mEncOutputCount > mLatency) {
+                    tryEncoderOutput(-1);
+                }
+            }
+        } else {
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            while (!mSawDecInputEOS && frameCnt < frameLimit) {
+                // decoder input
+                int inputBufferId = mDecoder.dequeueInputBuffer(CodecTestBase.Q_DEQ_TIMEOUT_US);
+                if (inputBufferId != -1) {
+                    enqueueDecoderInput(inputBufferId);
+                    frameCnt++;
+                }
+                // decoder output
+                int outputBufferId =
+                        mDecoder.dequeueOutputBuffer(outInfo, CodecTestBase.Q_DEQ_TIMEOUT_US);
+                if (outputBufferId >= 0) {
+                    dequeueDecoderOutput(outputBufferId, outInfo);
+                }
+                // check decoder EOS
+                if (mSawDecOutputEOS) mEncoder.signalEndOfInputStream();
+                // encoder output
+                if (mDecOutputCount - mEncOutputCount > mLatency) {
+                    tryEncoderOutput(-1);
+                }
+            }
+        }
+    }
+
+    MediaFormat setUpEncoderFormat(MediaFormat decoderFormat) {
+        MediaFormat encoderFormat = new MediaFormat();
+        encoderFormat.setString(MediaFormat.KEY_MIME, mMime);
+        encoderFormat.setInteger(MediaFormat.KEY_WIDTH,
+                decoderFormat.getInteger(MediaFormat.KEY_WIDTH));
+        encoderFormat.setInteger(MediaFormat.KEY_HEIGHT,
+                decoderFormat.getInteger(MediaFormat.KEY_HEIGHT));
+        encoderFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
+        encoderFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitrate);
+        encoderFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
+        encoderFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+        encoderFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, mMaxBFrames);
+        return encoderFormat;
+    }
+}
+
+class Transcode extends CodecTranscoderTestBase implements Callable<Double> {
+    private static final String LOG_TAG = Transcode.class.getSimpleName();
+
+    private final String mDecoderName;
+    private final String mEncoderName;
+    private final boolean mIsAsync;
+
+    Transcode(String mime, String testFile, String decoderName, String encoderName,
+            boolean isAsync) {
+        super(mime, testFile, 3000000, 30);
+        mDecoderName = decoderName;
+        mEncoderName = encoderName;
+        mIsAsync = isAsync;
+    }
+
+    public Double doTranscode() throws Exception {
+        MediaFormat decoderFormat = setUpSource(mTestFile);
+        mDecoder = MediaCodec.createByCodecName(mDecoderName);
+        MediaFormat encoderFormat = setUpEncoderFormat(decoderFormat);
+        mEncoder = MediaCodec.createByCodecName(mEncoderName);
+        mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+        configureCodec(decoderFormat, encoderFormat, mIsAsync, false);
+        mEncoder.start();
+        mDecoder.start();
+        long start = System.currentTimeMillis();
+        doWork(Integer.MAX_VALUE);
+        queueEOS();
+        waitForAllEncoderOutputs();
+        long end = System.currentTimeMillis();
+        mSurface.release();
+        mDecoder.stop();
+        mDecoder.release();
+        mEncoder.stop();
+        mEncoder.release();
+        mExtractor.release();
+        double fps = mEncOutputCount / ((end - start) / 1000.0);
+        Log.d(LOG_TAG, "Mime: " + mMime + " Decoder: " + mDecoderName + " Encoder: " +
+                mEncoderName + " Achieved fps: " + fps);
+        return fps;
+    }
+
+    @Override
+    public Double call() throws Exception {
+        return doTranscode();
+    }
+}
+
+class TranscodeLoad extends Transcode {
+    private final LoadStatus mLoadStatus;
+
+    private long mMaxPts;
+    private long mBasePts;
+
+    TranscodeLoad(String mime, String testFile, String decoderName, String encoderName,
+            LoadStatus loadStatus) {
+        super(mime, testFile, decoderName, encoderName, false);
+        mLoadStatus = loadStatus;
+        mMaxPts = 0;
+        mBasePts = 0;
+    }
+
+    @Override
+    void configureCodec(MediaFormat decFormat, MediaFormat encFormat, boolean isAsync,
+            boolean signalEOSWithLastFrame) {
+        decFormat.setInteger(MediaFormat.KEY_PRIORITY, 1);
+        encFormat.setInteger(MediaFormat.KEY_PRIORITY, 1);
+        resetContext(isAsync, signalEOSWithLastFrame);
+        mAsyncHandleEncoder.setCallBack(mEncoder, isAsync);
+        mEncoder.configure(encFormat, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null);
+        if (mEncoder.getInputFormat().containsKey(MediaFormat.KEY_LATENCY)) {
+            mReviseLatency = true;
+            mLatency = mEncoder.getInputFormat().getInteger(MediaFormat.KEY_LATENCY);
+        }
+        mSurface = mEncoder.createInputSurface();
+        assertTrue("Surface is not valid", mSurface.isValid());
+        mAsyncHandleDecoder.setCallBack(mDecoder, isAsync);
+        mDecoder.configure(decFormat, mSurface, null, 0);
+    }
+
+    @Override
+    void enqueueDecoderInput(int bufferIndex) {
+        if (mExtractor.getSampleSize() < 0) {
+            enqueueDecoderEOS(bufferIndex);
+        } else {
+            ByteBuffer inputBuffer = mDecoder.getInputBuffer(bufferIndex);
+            int size = mExtractor.readSampleData(inputBuffer, 0);
+            long pts = mExtractor.getSampleTime();
+            mMaxPts = Math.max(mMaxPts, mBasePts + pts);
+            int extractorFlags = mExtractor.getSampleFlags();
+            int codecFlags = 0;
+            if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+            }
+            mDecoder.queueInputBuffer(bufferIndex, 0, size, mBasePts + pts, codecFlags);
+            if (size > 0 && (codecFlags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+                mDecInputCount++;
+            }
+            if (!mExtractor.advance() && !mLoadStatus.isLoadFinished()) {
+                mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+                mBasePts = mMaxPts + 1000000L;
+            }
+        }
+    }
+}
+
+class LoadStatus {
+    private boolean mLoadFinished;
+
+    public LoadStatus() { mLoadFinished = false; }
+
+    public synchronized void setLoadFinished() { mLoadFinished = true; }
+
+    public synchronized boolean isLoadFinished() { return mLoadFinished; }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/DecodeExtractedSamplesTestBase.java b/tests/mediapc/src/android/mediapc/cts/DecodeExtractedSamplesTestBase.java
new file mode 100644
index 0000000..f47152e
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/DecodeExtractedSamplesTestBase.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.view.Surface;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+public class DecodeExtractedSamplesTestBase extends CodecDecoderTestBase {
+    private static final long EACH_FRAME_TIME_INTERVAL_US = 1000000 / 60;
+
+    final String[] mTestFiles;
+    final boolean mIsAsync;
+
+    long mFrameDropCount;
+    ByteBuffer mBuff;
+    ArrayList<MediaCodec.BufferInfo> mBufferInfos;
+
+    private long mMaxPtsUs;
+    private long mRenderStartTimeUs;
+
+    DecodeExtractedSamplesTestBase(String mime, String[] testFiles, Surface surface,
+            boolean isAsync) {
+        super(mime, null);
+        mTestFiles = testFiles;
+        mSurface = surface;
+        mIsAsync = isAsync;
+        mMaxPtsUs = 0;
+        mFrameDropCount = 0;
+        mBufferInfos = new ArrayList<>();
+    }
+
+    private MediaFormat createInputList(MediaFormat format, ByteBuffer buffer,
+            ArrayList<MediaCodec.BufferInfo> list, int offset, long ptsOffset) {
+        if (hasCSD(format)) {
+            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+            bufferInfo.offset = offset;
+            bufferInfo.size = 0;
+            bufferInfo.presentationTimeUs = 0;
+            bufferInfo.flags = MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
+            for (int i = 0; ; i++) {
+                String csdKey = "csd-" + i;
+                if (format.containsKey(csdKey)) {
+                    ByteBuffer csdBuffer = format.getByteBuffer(csdKey);
+                    bufferInfo.size += csdBuffer.limit();
+                    buffer.put(csdBuffer);
+                    format.removeKey(csdKey);
+                } else break;
+            }
+            list.add(bufferInfo);
+            offset += bufferInfo.size;
+        }
+        while (true) {
+            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+            bufferInfo.size = mExtractor.readSampleData(buffer, offset);
+            if (bufferInfo.size < 0) break;
+            bufferInfo.offset = offset;
+            bufferInfo.presentationTimeUs = ptsOffset + mExtractor.getSampleTime();
+            mMaxPtsUs = Math.max(mMaxPtsUs, bufferInfo.presentationTimeUs);
+            int flags = mExtractor.getSampleFlags();
+            bufferInfo.flags = 0;
+            if ((flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                bufferInfo.flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+            }
+            list.add(bufferInfo);
+            mExtractor.advance();
+            offset += bufferInfo.size;
+        }
+        buffer.clear();
+        buffer.position(offset);
+        return format;
+    }
+
+    public ArrayList<MediaFormat> setUpSourceFiles() throws Exception {
+        ArrayList<MediaFormat> formats = new ArrayList<>();
+        for (String file : mTestFiles) {
+            formats.add(setUpSource(file));
+            mExtractor.release();
+        }
+        int totalSize = 0;
+        for (String srcFile : mTestFiles) {
+            File file = new File(mInpPrefix + srcFile);
+            totalSize += (int) file.length();
+        }
+        totalSize <<= 1;
+        long ptsOffset = 0;
+        int buffOffset = 0;
+        mBuff = ByteBuffer.allocate(totalSize);
+        for (String file : mTestFiles) {
+            formats.add(createInputList(setUpSource(file), mBuff, mBufferInfos, buffOffset,
+                    ptsOffset));
+            mExtractor.release();
+            ptsOffset = mMaxPtsUs + 1000000L;
+            buffOffset = (mBufferInfos.get(mBufferInfos.size() - 1).offset) +
+                    (mBufferInfos.get(mBufferInfos.size() - 1).size);
+        }
+        return formats;
+    }
+
+    private long getRenderTimeUs(int frameIndex) {
+        return mRenderStartTimeUs + frameIndex * EACH_FRAME_TIME_INTERVAL_US;
+    }
+
+    @Override
+    void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawOutputEOS = true;
+        }
+        long nowUs = System.nanoTime() / 1000;
+        if (mOutputCount == 0) {
+            mRenderStartTimeUs = nowUs;
+            mCodec.releaseOutputBuffer(bufferIndex, true);
+        } else if (nowUs > getRenderTimeUs(mOutputCount + 1)) {
+            mFrameDropCount++;
+            mCodec.releaseOutputBuffer(bufferIndex, false);
+        } else if (nowUs > getRenderTimeUs(mOutputCount)) {
+            mCodec.releaseOutputBuffer(bufferIndex, true);
+        } else {
+            if ((getRenderTimeUs(mOutputCount) - nowUs) > (EACH_FRAME_TIME_INTERVAL_US / 2)) {
+                try {
+                    Thread.sleep(((getRenderTimeUs(mOutputCount) - nowUs) -
+                            (EACH_FRAME_TIME_INTERVAL_US / 2)) / 1000);
+                } catch (InterruptedException e) {
+                    // Do nothing.
+                }
+            }
+            mCodec.releaseOutputBuffer(bufferIndex, true);
+        }
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mOutputCount++;
+        }
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java b/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java
new file mode 100644
index 0000000..0312ebc
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.media.MediaRecorder;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static android.mediapc.cts.CodecTestBase.selectCodecs;
+import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+@RunWith(Parameterized.class)
+public class EncoderInitializationLatencyTest {
+    private static final String LOG_TAG = EncoderInitializationLatencyTest.class.getSimpleName();
+    private static final boolean[] boolStates = {false, true};
+    private static final int MAX_AUDIOENC_INITIALIZATION_LATENCY_MS = 30;
+    private static final int MAX_VIDEOENC_INITIALIZATION_LATENCY_MS = 40;
+    private static final String AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
+    private static final String HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC;
+    private static final String AVC_TRANSCODE_FILE = "bbb_1280x720_3mbps_30fps_avc.mp4";
+    private static String AVC_DECODER_NAME;
+    private static String AVC_ENCODER_NAME;
+    static {
+        AVC_DECODER_NAME = selectHardwareCodecs(AVC, null, null, false).get(0);
+        AVC_ENCODER_NAME = selectHardwareCodecs(AVC, null, null, true).get(0);
+    }
+
+    private final String mMime;
+    private final String mEncoderName;
+
+    private LoadStatus mTranscodeLoadStatus = null;
+    private Thread mTranscodeLoadThread = null;
+    private MediaRecorder mMediaRecorderLoad = null;
+    private File mTempRecordedFile = null;
+    private Surface mSurface = null;
+    private Exception mException = null;
+
+    @Before
+    public void setUp() throws Exception {
+        assumeTrue("Test requires performance class.", Utils.isPerfClass());
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        Context context = instrumentation.getTargetContext();
+        PackageManager packageManager = context.getPackageManager();
+        assertNotNull(packageManager.getSystemAvailableFeatures());
+        assumeTrue("The device doesn't have a camera",
+                packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY));
+        assumeTrue("The device doesn't have a microphone",
+                packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE));
+        createSurface();
+        startLoad();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        stopLoad();
+        releaseSurface();
+    }
+
+    public EncoderInitializationLatencyTest(String mimeType, String encoderName) {
+        mMime = mimeType;
+        mEncoderName = encoderName;
+    }
+
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule =
+            new ActivityTestRule<>(TestActivity.class);
+
+    static ArrayList<String> getMimesOfAvailableHardwareVideoEncoders() {
+        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+        ArrayList<String> listOfMimes = new ArrayList<>();
+        for (MediaCodecInfo codecInfo : codecInfos) {
+            if (!codecInfo.isEncoder() || !codecInfo.isHardwareAccelerated()) continue;
+            String[] types = codecInfo.getSupportedTypes();
+            for (String type : types) {
+                if (type.startsWith("video/") && !listOfMimes.contains(type)) {
+                    listOfMimes.add(type);
+                }
+            }
+        }
+        return listOfMimes;
+    }
+
+    static ArrayList<String> getMimesOfAvailableAudioEncoders() {
+        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+        ArrayList<String> listOfMimes = new ArrayList<>();
+        for (MediaCodecInfo codecInfo : codecInfos) {
+            if (!codecInfo.isEncoder()) continue;
+            String[] types = codecInfo.getSupportedTypes();
+            for (String type : types) {
+                if (type.startsWith("audio/") && !listOfMimes.contains(type)) {
+                    listOfMimes.add(type);
+                }
+            }
+        }
+        return listOfMimes;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{1})")
+    public static Collection<Object[]> inputParams() {
+        // Prepares the params list with the required Hardware video encoders and all available
+        // audio encoders present in the device.
+        final List<Object[]> argsList = new ArrayList<>();
+        ArrayList<String> mimesList = getMimesOfAvailableHardwareVideoEncoders();
+        mimesList.addAll(getMimesOfAvailableAudioEncoders());
+        for (String mime : mimesList) {
+            ArrayList<String> listOfEncoders;
+            if (mime.startsWith("audio/")) {
+                listOfEncoders = selectCodecs(mime, null, null, true);
+            } else {
+                listOfEncoders = selectHardwareCodecs(mime, null, null, true);
+            }
+            for (String encoder : listOfEncoders) {
+                argsList.add(new Object[] {mime, encoder});
+            }
+        }
+        return argsList;
+    }
+
+    private MediaRecorder createMediaRecorderLoad(Surface surface) throws Exception {
+        MediaRecorder mediaRecorder = new MediaRecorder();
+        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
+        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+        mediaRecorder.setVideoEncoder(mMime.equalsIgnoreCase(HEVC) ?
+                MediaRecorder.VideoEncoder.HEVC : MediaRecorder.VideoEncoder.H264);
+        mediaRecorder.setOutputFile(mTempRecordedFile);
+        mediaRecorder.setVideoSize(1920, 1080);
+        mediaRecorder.setOrientationHint(0);
+        mediaRecorder.setPreviewDisplay(surface);
+        mediaRecorder.prepare();
+        return mediaRecorder;
+    }
+
+    private void startLoad() throws Exception {
+        // TODO: b/183671436
+        // Create Transcode load (AVC Decoder(720p) + AVC Encoder(720p))
+        mTranscodeLoadStatus = new LoadStatus();
+        mTranscodeLoadThread = new Thread(() -> {
+            try {
+                TranscodeLoad transcodeLoad = new TranscodeLoad(AVC, AVC_TRANSCODE_FILE,
+                        AVC_DECODER_NAME, AVC_ENCODER_NAME, mTranscodeLoadStatus);
+                transcodeLoad.doTranscode();
+            } catch (Exception e) {
+                mException = e;
+            }
+        });
+        // Create MediaRecorder Session - Audio (Microphone) + 1080p Video (Camera)
+        mTempRecordedFile = new File(WorkDir.getMediaDirString() + "tempOut.mp4");
+        mTempRecordedFile.createNewFile();
+        mMediaRecorderLoad = createMediaRecorderLoad(mSurface);
+        // Start the Loads
+        mTranscodeLoadThread.start();
+        mMediaRecorderLoad.start();
+    }
+
+    private void stopLoad() throws Exception {
+        if (mTranscodeLoadStatus != null) {
+            mTranscodeLoadStatus.setLoadFinished();
+            mTranscodeLoadStatus = null;
+        }
+        if (mTranscodeLoadThread != null) {
+            mTranscodeLoadThread.join();
+            mTranscodeLoadThread = null;
+        }
+        if (mMediaRecorderLoad != null) {
+            // Note that a RuntimeException is intentionally thrown to the application, if no valid
+            // audio/video data has been received when stop() is called. This happens if stop() is
+            // called immediately after start(). So Sleep for 300ms.
+            Thread.sleep(300);
+            mMediaRecorderLoad.stop();
+            mMediaRecorderLoad.release();
+            mMediaRecorderLoad = null;
+            if(mTempRecordedFile != null && mTempRecordedFile.exists()) {
+                mTempRecordedFile.delete();
+                mTempRecordedFile = null;
+            }
+        }
+        if (mException != null) throw mException;
+    }
+
+    private void createSurface() throws InterruptedException {
+        mActivityRule.getActivity().waitTillSurfaceIsCreated();
+        mSurface = mActivityRule.getActivity().getSurface();
+        assertTrue("Surface created is null.", mSurface != null);
+        assertTrue("Surface created is invalid.", mSurface.isValid());
+        mActivityRule.getActivity().setScreenParams(1920, 1080, true);
+    }
+
+    private void releaseSurface() {
+        if (mSurface != null) {
+            mSurface.release();
+            mSurface = null;
+        }
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testInitializationLatency() throws Exception {
+        int maxCodecInitializationLatencyMs = mMime.startsWith("audio/") ?
+                MAX_AUDIOENC_INITIALIZATION_LATENCY_MS : MAX_VIDEOENC_INITIALIZATION_LATENCY_MS;
+        for (int i = 0; i < 5; i++) {
+            for (boolean isAsync : boolStates) {
+                EncoderInitializationLatency encoderInitializationLatency =
+                        new EncoderInitializationLatency(mMime, mEncoderName, isAsync);
+                long encoderInitializationLatencyMs = encoderInitializationLatency
+                        .calculateEncoderInitializationLatency();
+                String errorLog = String.format("CodecInitialization latency for mime: %s, " +
+                        "Encoder: %s, Iteration: %d, mode: %s  is not as expected. act/exp: " +
+                        " %d/%d", mMime, mEncoderName, i, (isAsync ? "async" : "sync"),
+                        encoderInitializationLatencyMs, maxCodecInitializationLatencyMs);
+                assertTrue(errorLog,
+                        encoderInitializationLatencyMs <= maxCodecInitializationLatencyMs);
+            }
+        }
+    }
+}
+
+class EncoderInitializationLatency extends CodecEncoderTestBase {
+    private static final String LOG_TAG = EncoderInitializationLatency.class.getSimpleName();
+
+    private final String mEncoderName;
+    private final boolean mIsAsync;
+
+    EncoderInitializationLatency(String mime, String encoderName, boolean isAsync) {
+        super(mime);
+        mEncoderName = encoderName;
+        mIsAsync = isAsync;
+        mSampleRate = 8000;
+        mFrameRate = 60;
+    }
+
+    private MediaFormat setUpFormat() {
+        MediaFormat format = new MediaFormat();
+        format.setString(MediaFormat.KEY_MIME, mMime);
+        if (mIsAudio) {
+            if (mMime.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
+                format.setInteger(MediaFormat.KEY_FLAC_COMPRESSION_LEVEL, 10000);
+            } else {
+                format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
+            }
+            format.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRate);
+            format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
+        } else {
+            format.setInteger(MediaFormat.KEY_WIDTH, 1920);
+            format.setInteger(MediaFormat.KEY_HEIGHT, 1080);
+            format.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
+            format.setInteger(MediaFormat.KEY_BIT_RATE, 8000000);
+            format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
+            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                    MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
+        }
+        return format;
+    }
+
+    public long calculateEncoderInitializationLatency() throws Exception {
+        MediaFormat format = setUpFormat();
+        if (mIsAudio) {
+            mSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+            mChannels = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+        } else {
+            mWidth = format.getInteger(MediaFormat.KEY_WIDTH);
+            mHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
+        }
+        setUpSource(mInputFile);
+        MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+        long step1TimeMs; // Time of (create + configure)
+        long step2TimeMs; // Time of (create + configure + start)
+        long step3TimeMs = 0; // Time of (create + configure + start + first frame to enqueue)
+        long step4TimeMs = 0; // Time of (create + configure + start + first frame to dequeue)
+        long start = System.currentTimeMillis();
+        mCodec = MediaCodec.createByCodecName(mEncoderName);
+        resetContext(mIsAsync, false);
+        mAsyncHandle.setCallBack(mCodec, mIsAsync);
+        mCodec.configure(format, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null);
+        step1TimeMs = System.currentTimeMillis() - start;
+        mCodec.start();
+        step2TimeMs = System.currentTimeMillis() - start;
+        if (mIsAsync) {
+            while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
+                Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
+                if (element != null) {
+                    int bufferID = element.first;
+                    MediaCodec.BufferInfo info = element.second;
+                    if (info != null) {
+                        step4TimeMs = System.currentTimeMillis() - start;
+                        dequeueOutput(bufferID, info);
+                        break;
+                    } else {
+                        if (step3TimeMs == 0) step3TimeMs = System.currentTimeMillis() - start;
+                        enqueueInput(bufferID);
+                    }
+                }
+            }
+        } else {
+            while (!mSawOutputEOS) {
+                if (!mSawInputEOS) {
+                    int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+                    if (inputBufferId > 0) {
+                        if (step3TimeMs == 0) step3TimeMs = System.currentTimeMillis() - start;
+                        enqueueInput(inputBufferId);
+                    }
+                }
+                int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+                if (outputBufferId >= 0) {
+                    step4TimeMs = System.currentTimeMillis() - start;
+                    dequeueOutput(outputBufferId, outInfo);
+                    break;
+                }
+            }
+        }
+        queueEOS();
+        waitForAllOutputs();
+        mCodec.stop();
+        mCodec.release();
+        Log.d(LOG_TAG, "Encode mMime: " + mMime + " Encoder: " + mEncoderName +
+                " Time for (create + configure): " + step1TimeMs);
+        Log.d(LOG_TAG, "Encode mMime: " + mMime + " Encoder: " + mEncoderName +
+                " Time for (create + configure + start): " + step2TimeMs);
+        Log.d(LOG_TAG, "Encode mMime: " + mMime + " Encoder: " + mEncoderName +
+                " Time for (create + configure + start + first frame to enqueue): " + step3TimeMs);
+        Log.d(LOG_TAG, "Encode mMime: " + mMime + " Encoder: " + mEncoderName +
+                " Time for (create + configure + start + first frame to dequeue): " + step4TimeMs);
+        return step1TimeMs;
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java b/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java
new file mode 100644
index 0000000..10cc040
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaFormat;
+import android.view.Surface;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static android.mediapc.cts.FrameDropTestBase.DECODE_30S;
+import static android.mediapc.cts.FrameDropTestBase.MAX_FRAME_DROP_FOR_30S;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class FrameDropTest extends FrameDropTestBase {
+    private static final String LOG_TAG = FrameDropTest.class.getSimpleName();
+
+    public FrameDropTest(String mimeType, String decoderName, boolean isAsync) {
+        super(mimeType, decoderName, isAsync);
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{1}_{2})")
+    public static Collection<Object[]> inputParams() {
+        return prepareArgumentsList(null);
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testDecodeToSurface() throws Exception {
+        DecodeToSurfaceFrameDrop decodeToSurfaceFrameDrop = new DecodeToSurfaceFrameDrop(mMime,
+                m1080pTestFiles.get(mMime), mDecoderName, mSurface, mIsAsync);
+        decodeToSurfaceFrameDrop.doDecodeAndCalculateFrameDrop();
+    }
+}
+
+class DecodeToSurfaceFrameDrop extends DecodeExtractedSamplesTestBase {
+    private final String mDecoderName;
+
+    private long mBasePts;
+    private long mMaxPts;
+    private long mDecodeStartTimeMs;
+    private int mSampleIndex;
+
+    DecodeToSurfaceFrameDrop(String mime, String testFile, String decoderName, Surface surface,
+            boolean isAsync) {
+        super(mime, new String[]{testFile}, surface, isAsync);
+        mDecoderName = decoderName;
+        mBasePts = 0;
+        mMaxPts = 0;
+        mSampleIndex = 0;
+    }
+
+    public void doDecodeAndCalculateFrameDrop() throws Exception {
+        ArrayList<MediaFormat> formats = setUpSourceFiles();
+        mCodec = MediaCodec.createByCodecName(mDecoderName);
+        configureCodec(formats.get(0), mIsAsync, false, false);
+        mCodec.start();
+        mDecodeStartTimeMs = System.currentTimeMillis();
+        doWork(Integer.MAX_VALUE);
+        queueEOS();
+        waitForAllOutputs();
+        mCodec.stop();
+        mCodec.release();
+        assertTrue("FrameDrop count for mime: " + mMime + " decoder: " + mDecoderName +
+                " is not as expected. act/exp: " + mFrameDropCount + "/" + MAX_FRAME_DROP_FOR_30S,
+                mFrameDropCount <= MAX_FRAME_DROP_FOR_30S);
+    }
+
+    @Override
+    void enqueueInput(int bufferIndex) {
+        if (mSampleIndex == mBufferInfos.size()) {
+            enqueueEOS(bufferIndex);
+        } else {
+            MediaCodec.BufferInfo info = mBufferInfos.get(mSampleIndex++);
+            if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+                ByteBuffer dstBuf = mCodec.getInputBuffer(bufferIndex);
+                dstBuf.put(mBuff.array(), info.offset, info.size);
+                mInputCount++;
+            }
+            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                mSawInputEOS = true;
+            }
+            long pts = info.presentationTimeUs;
+            mMaxPts = Math.max(mMaxPts, mBasePts + pts);
+            mCodec.queueInputBuffer(bufferIndex, 0, info.size, mBasePts + pts, info.flags);
+            if (mSampleIndex == mBufferInfos.size() &&
+                    // Decode for at least 30s
+                    (System.currentTimeMillis() - mDecodeStartTimeMs < DECODE_30S)) {
+                mSampleIndex = 0;
+                mBasePts = mMaxPts + 1000000L;
+            }
+        }
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java b/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java
new file mode 100644
index 0000000..631fe52
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.media.MediaFormat;
+import android.view.Surface;
+
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static android.mediapc.cts.CodecTestBase.selectCodecs;
+import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+public class FrameDropTestBase {
+    private static final String LOG_TAG = FrameDropTestBase.class.getSimpleName();
+    static final boolean[] boolStates = {false, true};
+    static final String AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
+    static final String HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC;
+    static final String VP8 = MediaFormat.MIMETYPE_VIDEO_VP8;
+    static final String VP9 = MediaFormat.MIMETYPE_VIDEO_VP9;
+    static final String AV1 = MediaFormat.MIMETYPE_VIDEO_AV1;
+    static final String AAC = MediaFormat.MIMETYPE_AUDIO_AAC;
+    static final String AAC_LOAD_FILE_NAME = "bbb_1c_128kbps_aac_audio.mp4";
+    static final String AVC_LOAD_FILE_NAME = "bbb_1280x720_3mbps_30fps_avc.mp4";
+    static final long DECODE_30S = 30000; // In ms
+    static final long MAX_FRAME_DROP_FOR_30S = 3;
+
+    final String mMime;
+    final String mDecoderName;
+    final boolean mIsAsync;
+    Surface mSurface;
+
+    private LoadStatus mLoadStatus = null;
+    private Thread mTranscodeLoadThread = null;
+    private Thread mAudioPlaybackLoadThread = null;
+    private Exception mTranscodeLoadException = null;
+    private Exception mAudioPlaybackLoadException = null;
+
+    static String AVC_DECODER_NAME;
+    static String AVC_ENCODER_NAME;
+    static String AAC_DECODER_NAME;
+    static Map<String, String> m540pTestFiles = new HashMap<>();
+    static Map<String, String> m1080pTestFiles = new HashMap<>();
+    static {
+        AVC_DECODER_NAME = selectHardwareCodecs(AVC, null, null, false).get(0);
+        AVC_ENCODER_NAME = selectHardwareCodecs(AVC, null, null, true).get(0);
+        AAC_DECODER_NAME = selectCodecs(AAC, null, null, false).get(0);
+    }
+    static {
+        m540pTestFiles.put(AVC, "bbb_960x540_3mbps_60fps_avc.mp4");
+        m540pTestFiles.put(HEVC, "bbb_960x540_3mbps_60fps_hevc.mp4");
+        m540pTestFiles.put(VP8, "bbb_960x540_3mbps_60fps_vp8.webm");
+        m540pTestFiles.put(VP9, "bbb_960x540_3mbps_60fps_vp9.webm");
+        m540pTestFiles.put(AV1, "bbb_960x540_3mbps_60fps_av1.mp4");
+    }
+    static {
+        m1080pTestFiles.put(AVC, "bbb_1920x1080_8mbps_60fps_avc.mp4");
+        m1080pTestFiles.put(HEVC, "bbb_1920x1080_8mbps_60fps_hevc.mp4");
+        m1080pTestFiles.put(VP8, "bbb_1920x1080_8mbps_60fps_vp8.webm");
+        m1080pTestFiles.put(VP9, "bbb_1920x1080_8mbps_60fps_vp9.webm");
+        m1080pTestFiles.put(AV1, "bbb_1920x1080_8mbps_60fps_av1.mp4");
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        assumeTrue("Test requires performance class.", Utils.isPerfClass());
+        createSurface();
+        startLoad();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        stopLoad();
+        releaseSurface();
+    }
+
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule =
+            new ActivityTestRule<>(TestActivity.class);
+
+    public FrameDropTestBase(String mimeType, String decoderName, boolean isAsync) {
+        mMime = mimeType;
+        mDecoderName = decoderName;
+        mIsAsync = isAsync;
+    }
+
+    static List<Object[]> prepareArgumentsList(String[] features) {
+        final List<Object[]> argsList = new ArrayList<>();
+        final String[] mimesList = new String[] {AVC, HEVC, VP8, VP9, AV1};
+        for (String mime : mimesList) {
+            ArrayList<String> listOfDecoders = selectHardwareCodecs(mime, null, features, false);
+            for (String decoder : listOfDecoders) {
+                for (boolean isAsync : boolStates) {
+                    argsList.add(new Object[]{mime, decoder, isAsync});
+                }
+            }
+        }
+        return argsList;
+    }
+
+    private void createSurface() throws InterruptedException {
+        mActivityRule.getActivity().waitTillSurfaceIsCreated();
+        mSurface = mActivityRule.getActivity().getSurface();
+        assertTrue("Surface created is null.", mSurface != null);
+        assertTrue("Surface created is invalid.", mSurface.isValid());
+        // As we display 1920x1080 and 960x540 only which are of same aspect ratio, we will
+        // be setting screen params to 1920x1080
+        mActivityRule.getActivity().setScreenParams(1920, 1080, true);
+    }
+
+    private void releaseSurface() {
+        if (mSurface != null) {
+            mSurface.release();
+            mSurface = null;
+        }
+    }
+
+    private Thread createTranscodeLoad() {
+        Thread transcodeLoadThread = new Thread(() -> {
+            try {
+                TranscodeLoad transcodeLoad = new TranscodeLoad(AVC, AVC_LOAD_FILE_NAME,
+                        AVC_DECODER_NAME, AVC_ENCODER_NAME, mLoadStatus);
+                transcodeLoad.doTranscode();
+            } catch (Exception e) {
+                mTranscodeLoadException = e;
+            }
+        });
+        return transcodeLoadThread;
+    }
+
+    private Thread createAudioPlaybackLoad() {
+        Thread audioPlaybackLoadThread = new Thread(() -> {
+            try {
+                AudioPlaybackLoad audioPlaybackLoad = new AudioPlaybackLoad(AAC, AAC_LOAD_FILE_NAME,
+                        AAC_DECODER_NAME, mLoadStatus);
+                audioPlaybackLoad.doDecodeAndPlayback();
+            } catch (Exception e) {
+                mAudioPlaybackLoadException = e;
+            }
+        });
+        return audioPlaybackLoadThread;
+    }
+
+    private void startLoad() {
+        // TODO: b/183671436
+        // Start Transcode load (Decoder(720p) + Encoder(720p))
+        mLoadStatus = new LoadStatus();
+        mTranscodeLoadThread = createTranscodeLoad();
+        mTranscodeLoadThread.start();
+        // Start 128kbps AAC audio playback
+        mAudioPlaybackLoadThread = createAudioPlaybackLoad();
+        mAudioPlaybackLoadThread.start();
+    }
+
+    private void stopLoad() throws Exception {
+        if (mLoadStatus != null) {
+            mLoadStatus.setLoadFinished();
+            mLoadStatus = null;
+        }
+        if (mTranscodeLoadThread != null) {
+            mTranscodeLoadThread.join();
+            mTranscodeLoadThread = null;
+        }
+        if (mAudioPlaybackLoadThread != null) {
+            mAudioPlaybackLoadThread.join();
+            mAudioPlaybackLoadThread = null;
+        }
+        if (mTranscodeLoadException != null) throw mTranscodeLoadException;
+        if (mAudioPlaybackLoadException != null) throw mAudioPlaybackLoadException;
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java
new file mode 100644
index 0000000..86104ce
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint;
+import android.media.MediaFormat;
+import android.util.Pair;
+
+import org.junit.Before;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+public class MultiCodecPerfTestBase {
+    private static final String LOG_TAG = MultiCodecPerfTestBase.class.getSimpleName();
+    static final boolean[] boolStates = {true, false};
+    static final int REQUIRED_MIN_CONCURRENT_INSTANCES = 6;
+    static final String[] mMimeList = new String[] {
+            MediaFormat.MIMETYPE_VIDEO_AVC,
+            MediaFormat.MIMETYPE_VIDEO_HEVC,
+            MediaFormat.MIMETYPE_VIDEO_VP8,
+            MediaFormat.MIMETYPE_VIDEO_VP9,
+            MediaFormat.MIMETYPE_VIDEO_AV1
+    };
+    static Map<String, String> mTestFiles = new HashMap<>();
+    static {
+        mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x720_3mbps_30fps_avc.mp4");
+        mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1280x720_3mbps_30fps_hevc.mp4");
+        mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_1280x720_3mbps_30fps_vp8.webm");
+        mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1280x720_3mbps_30fps_vp9.webm");
+        mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1280x720_3mbps_30fps_av1.mp4");
+    }
+
+    String mMime;
+    String mTestFile;
+    final boolean mIsAsync;
+
+    double mMaxFrameRate;
+
+    @Before
+    public void isPerformanceClass() {
+        assumeTrue("Test requires performance class.", Utils.isPerfClass());
+    }
+
+    public MultiCodecPerfTestBase(String mime, String testFile, boolean isAsync) {
+        mMime = mime;
+        mTestFile = testFile;
+        mIsAsync = isAsync;
+    }
+
+    public static ArrayList<String> getHardwareCodecsFor720p(String mime, boolean isEncoder) {
+        MediaFormat fmt = MediaFormat.createVideoFormat(mime, 1280, 720);
+        fmt.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
+        ArrayList<MediaFormat> formatsList = new ArrayList<>();
+        formatsList.add(fmt);
+        return selectHardwareCodecs(mime, formatsList, null, isEncoder);
+    }
+
+    public int checkAndGetMaxSupportedInstancesFor720p(
+            ArrayList<Pair<String, String>> mimeCodecPairs) throws IOException {
+        int[] maxInstances = new int[mimeCodecPairs.size()];
+        int[] maxFrameRates = new int[mimeCodecPairs.size()];
+        int[] maxMacroBlockRates = new int[mimeCodecPairs.size()];
+        int loopCount = 0;
+        for (Pair<String, String> mimeCodecPair : mimeCodecPairs) {
+            MediaCodec codec = MediaCodec.createByCodecName(mimeCodecPair.second);
+            MediaCodecInfo.CodecCapabilities cap = codec.getCodecInfo()
+                    .getCapabilitiesForType(mimeCodecPair.first);
+            List<PerformancePoint> pps = cap.getVideoCapabilities().getSupportedPerformancePoints();
+            assertTrue(pps.size() > 0);
+            maxInstances[loopCount] = cap.getMaxSupportedInstances();
+            PerformancePoint PP720p = new PerformancePoint(1280, 720, 180);
+            maxMacroBlockRates[loopCount] = 0;
+            boolean supports720p180Performance = false;
+            for (PerformancePoint pp : pps) {
+                if(pp.covers(PP720p)) {
+                    supports720p180Performance = true;
+                    if (pp.getMaxMacroBlockRate() > maxMacroBlockRates[loopCount]) {
+                        maxMacroBlockRates[loopCount] = (int) pp.getMaxMacroBlockRate();
+                        maxFrameRates[loopCount] = pp.getMaxFrameRate();
+                    }
+                }
+            }
+            codec.release();
+            assertTrue("Codec " + mimeCodecPair.second + " doesn't support 720p 180 " +
+                    "performance point", supports720p180Performance);
+            loopCount++;
+        }
+        Arrays.sort(maxInstances);
+        Arrays.sort(maxFrameRates);
+        Arrays.sort(maxMacroBlockRates);
+        int minOfMaxInstances = maxInstances[0];
+        int minOfMaxFrameRates = maxFrameRates[0];
+        int minOfMaxMacroBlockRates = maxMacroBlockRates[0];
+        mMaxFrameRate = minOfMaxFrameRates;
+        // Calculate how many 720p 30fps max instances it can support from it's mMaxFrameRate
+        // amd maxMacroBlockRate. (720p is 3,600 macro blocks assuming 16x16 macroblocks)
+        return Math.min(minOfMaxInstances, Math.min((int) (minOfMaxFrameRates / 30.0),
+                (int) (minOfMaxMacroBlockRates / 3600.0 / 30)));
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java
new file mode 100644
index 0000000..6d6d003
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.util.Pair;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class MultiDecoderPairPerfTest extends MultiCodecPerfTestBase {
+    private static final String LOG_TAG = MultiDecoderPairPerfTest.class.getSimpleName();
+
+    private final Pair<String, String> mFirstPair;
+    private final Pair<String, String> mSecondPair;
+
+    public MultiDecoderPairPerfTest(Pair<String, String> firstPair, Pair<String, String> secondPair,
+            boolean isAsync) {
+        super(null, null, isAsync);
+        mFirstPair = firstPair;
+        mSecondPair = secondPair;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{1}_{2})")
+    public static Collection<Object[]> inputParams() {
+        // Prepares the params list with the supported Hardware decoders in the device
+        final List<Object[]> argsList = new ArrayList<>();
+        ArrayList<Pair<String, String>> mimeTypeDecoderPairs = new ArrayList<>();
+        for (String mime : mMimeList) {
+            ArrayList<String> listOfDecoders = getHardwareCodecsFor720p(mime, false);
+            for (String decoder : listOfDecoders) {
+                mimeTypeDecoderPairs.add(Pair.create(mime, decoder));
+            }
+        }
+        for (int i = 0; i < mimeTypeDecoderPairs.size(); i++) {
+            for (int j = i + 1; j < mimeTypeDecoderPairs.size(); j++) {
+                Pair<String, String> pair1 = mimeTypeDecoderPairs.get(i);
+                Pair<String, String> pair2 = mimeTypeDecoderPairs.get(j);
+                for (boolean isAsync : boolStates) {
+                    argsList.add(new Object[]{pair1, pair2, isAsync});
+                }
+            }
+        }
+        return argsList;
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void test720p() throws Exception {
+        ArrayList<Pair<String, String>> mimeDecoderPairs = new ArrayList<>();
+        mimeDecoderPairs.add(mFirstPair);
+        mimeDecoderPairs.add(mSecondPair);
+        int maxInstances = checkAndGetMaxSupportedInstancesFor720p(mimeDecoderPairs);
+        int secondPairInstances = maxInstances / 2;
+        int firstPairInstances = maxInstances - secondPairInstances;
+        ExecutorService pool = Executors.newFixedThreadPool(maxInstances);
+        List<Decode> testList = new ArrayList<>();
+        for (int i = 0; i < firstPairInstances; i++) {
+            testList.add(new Decode(mFirstPair.first, mTestFiles.get(mFirstPair.first),
+                    mFirstPair.second, mIsAsync));
+        }
+        for (int i = 0; i < secondPairInstances; i++) {
+            testList.add(new Decode(mSecondPair.first, mTestFiles.get(mSecondPair.first),
+                    mSecondPair.second, mIsAsync));
+        }
+        List<Future<Double>> resultList = pool.invokeAll(testList);
+        double achievedFrameRate = 0.0;
+        for (Future<Double> result : resultList) {
+            achievedFrameRate += result.get();
+        }
+        assertTrue("Unable to achieve the maxFrameRate supported. act/exp: " + achievedFrameRate
+                + "/" + mMaxFrameRate, achievedFrameRate >= mMaxFrameRate);
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java
new file mode 100644
index 0000000..dbaca7b
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.util.Pair;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class MultiDecoderPerfTest extends MultiCodecPerfTestBase {
+    private static final String LOG_TAG = MultiDecoderPerfTest.class.getSimpleName();
+
+    private final String mDecoderName;
+
+    public MultiDecoderPerfTest(String mimeType, String testFile, String decoderName,
+            boolean isAsync) {
+        super(mimeType, testFile, isAsync);
+        mDecoderName = decoderName;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{2}_{3})")
+    public static Collection<Object[]> inputParams() {
+        // Prepares the params list with the supported Hardware decoders in the device
+        final List<Object[]> argsList = new ArrayList<>();
+        for (String mime : mMimeList) {
+            ArrayList<String> listOfDecoders = getHardwareCodecsFor720p(mime, false);
+            for (String decoder : listOfDecoders) {
+                for (boolean isAsync : boolStates) {
+                    argsList.add(new Object[]{mime, mTestFiles.get(mime), decoder, isAsync});
+                }
+            }
+        }
+        return argsList;
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void test720p() throws Exception {
+        ArrayList<Pair<String, String>> mimeDecoderPairs = new ArrayList<>();
+        mimeDecoderPairs.add(Pair.create(mMime, mDecoderName));
+        int maxInstances = checkAndGetMaxSupportedInstancesFor720p(mimeDecoderPairs);
+        assertTrue("Decoder " + mDecoderName + " unable to support minimum concurrent " +
+                "instances. act/exp: " + maxInstances + "/" + REQUIRED_MIN_CONCURRENT_INSTANCES,
+                maxInstances >= REQUIRED_MIN_CONCURRENT_INSTANCES);
+        ExecutorService pool = Executors.newFixedThreadPool(maxInstances);
+        List<Decode> testList = new ArrayList<>();
+        for (int i = 0; i < maxInstances; i++) {
+            testList.add(new Decode(mMime, mTestFile, mDecoderName, mIsAsync));
+        }
+        List<Future<Double>> resultList = pool.invokeAll(testList);
+        double achievedFrameRate = 0.0;
+        for (Future<Double> result : resultList) {
+            achievedFrameRate += result.get();
+        }
+        assertTrue("Unable to achieve the maxFrameRate supported. act/exp: " + achievedFrameRate
+                + "/" + mMaxFrameRate, achievedFrameRate >= mMaxFrameRate);
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java
new file mode 100644
index 0000000..d732c82
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.util.Pair;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+@RunWith(Parameterized.class)
+public class MultiEncoderPairPerfTest extends MultiCodecPerfTestBase {
+    private static final String LOG_TAG = MultiEncoderPairPerfTest.class.getSimpleName();
+
+    private final Pair<String, String> mFirstPair;
+    private final Pair<String, String> mSecondPair;
+
+    public MultiEncoderPairPerfTest(Pair<String, String> firstPair, Pair<String, String> secondPair,
+            boolean isAsync) {
+        super(null, null, isAsync);
+        mFirstPair = firstPair;
+        mSecondPair = secondPair;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{1}_{2})")
+    public static Collection<Object[]> inputParams() {
+        // Prepares the params list with the supported Hardware encoders in the device
+        final List<Object[]> argsList = new ArrayList<>();
+        ArrayList<Pair<String, String>> mimeTypeEncoderPairs = new ArrayList<>();
+        for (String mime : mMimeList) {
+            ArrayList<String> listOfEncoders = getHardwareCodecsFor720p(mime, true);
+            for (String encoder : listOfEncoders) {
+                mimeTypeEncoderPairs.add(Pair.create(mime, encoder));
+            }
+        }
+        for (int i = 0; i < mimeTypeEncoderPairs.size(); i++) {
+            for (int j = i + 1; j < mimeTypeEncoderPairs.size(); j++) {
+                Pair<String, String> pair1 = mimeTypeEncoderPairs.get(i);
+                Pair<String, String> pair2 = mimeTypeEncoderPairs.get(j);
+                for (boolean isAsync : boolStates) {
+                    argsList.add(new Object[]{pair1, pair2, isAsync});
+                }
+            }
+        }
+        return argsList;
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void test720p() throws Exception {
+        ArrayList<Pair<String, String>> mimeEncoderPairs = new ArrayList<>();
+        mimeEncoderPairs.add(mFirstPair);
+        mimeEncoderPairs.add(mSecondPair);
+        int maxInstances = checkAndGetMaxSupportedInstancesFor720p(mimeEncoderPairs);
+        int secondPairInstances = maxInstances / 2;
+        int firstPairInstances = maxInstances - secondPairInstances;
+        ExecutorService pool = Executors.newFixedThreadPool(maxInstances);
+        List<Encode> testList = new ArrayList<>();
+        for (int i = 0; i < firstPairInstances; i++) {
+            testList.add(new Encode(mFirstPair.first, mFirstPair.second, mIsAsync));
+        }
+        for (int i = 0; i < secondPairInstances; i++) {
+            testList.add(new Encode(mSecondPair.first, mSecondPair.second, mIsAsync));
+        }
+        List<Future<Double>> resultList = pool.invokeAll(testList);
+        double achievedFrameRate = 0.0;
+        for (Future<Double> result : resultList) {
+            achievedFrameRate += result.get();
+        }
+        // Achieved frame rate is not compared as this test runs in byte buffer mode.
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java
new file mode 100644
index 0000000..8fb78b7
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.util.Pair;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class MultiEncoderPerfTest extends MultiCodecPerfTestBase {
+    private static final String LOG_TAG = MultiEncoderPerfTest.class.getSimpleName();
+
+    private final String mEncoderName;
+
+    public MultiEncoderPerfTest(String mimeType, String encoderName, boolean isAsync) {
+        super(mimeType, null, isAsync);
+        mEncoderName = encoderName;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{1}_{2})")
+    public static Collection<Object[]> inputParams() {
+        // Prepares the params list with the supported Hardware encoders in the device
+        final List<Object[]> argsList = new ArrayList<>();
+        for (String mime : mMimeList) {
+            ArrayList<String> listOfEncoders = getHardwareCodecsFor720p(mime, true);
+            for (String encoder : listOfEncoders) {
+                for (boolean isAsync : boolStates) {
+                    argsList.add(new Object[]{mime, encoder, isAsync});
+                }
+            }
+        }
+        return argsList;
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void test720p() throws Exception {
+        ArrayList<Pair<String, String>> mimeEncoderPairs = new ArrayList<>();
+        mimeEncoderPairs.add(Pair.create(mMime, mEncoderName));
+        int maxInstances = checkAndGetMaxSupportedInstancesFor720p(mimeEncoderPairs);
+        assertTrue("Encoder " + mEncoderName + " unable to support minimum concurrent " +
+                "instances. act/exp: " + maxInstances + "/" + REQUIRED_MIN_CONCURRENT_INSTANCES,
+                maxInstances >= REQUIRED_MIN_CONCURRENT_INSTANCES);
+        ExecutorService pool = Executors.newFixedThreadPool(maxInstances);
+        List<Encode> testList = new ArrayList<>();
+        for (int i = 0; i < maxInstances; i++) {
+            testList.add(new Encode(mMime, mEncoderName, mIsAsync));
+        }
+        List<Future<Double>> resultList = pool.invokeAll(testList);
+        double achievedFrameRate = 0.0;
+        for (Future<Double> result : resultList) {
+            achievedFrameRate += result.get();
+        }
+        // Achieved frame rate is not compared as this test runs in byte buffer mode.
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPairPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPairPerfTest.java
new file mode 100644
index 0000000..a5585d0
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPairPerfTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.util.Pair;
+import android.view.Surface;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class MultiTranscoderPairPerfTest extends MultiCodecPerfTestBase {
+    private static final String LOG_TAG = MultiTranscoderPairPerfTest.class.getSimpleName();
+
+    private final Pair<String, String> mDecoderPair;
+    private final Pair<String, String> mEncoderPair;
+
+    @Rule
+    public ActivityTestRule<TestActivity> mActivityRule =
+            new ActivityTestRule<>(TestActivity.class);
+
+
+    public MultiTranscoderPairPerfTest(Pair<String, String> decoderPair,
+            Pair<String, String> encoderPair, boolean isAsync) {
+        super(null, null, isAsync);
+        mDecoderPair = decoderPair;
+        mEncoderPair = encoderPair;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{1}_{2})")
+    public static Collection<Object[]> inputParams() {
+        // Prepares the params list with the supported Hardware decoders/encoders in the device
+        final List<Object[]> argsList = new ArrayList<>();
+        ArrayList<Pair<String, String>> mimeTypeDecoderPairs = new ArrayList<>();
+        ArrayList<Pair<String, String>> mimeTypeEncoderPairs = new ArrayList<>();
+        for (String mime : mMimeList) {
+            ArrayList<String> listOfDecoders = getHardwareCodecsFor720p(mime, false);
+            for (String decoder : listOfDecoders) {
+                mimeTypeDecoderPairs.add(Pair.create(mime, decoder));
+            }
+            ArrayList<String> listOfEncoders = getHardwareCodecsFor720p(mime, true);
+            for (String encoder : listOfEncoders) {
+                mimeTypeEncoderPairs.add(Pair.create(mime, encoder));
+            }
+        }
+        for (Pair<String, String> mimeTypeDecoderPair : mimeTypeDecoderPairs) {
+            for (Pair<String, String> mimeTypeEncoderPair : mimeTypeEncoderPairs) {
+                for (boolean isAsync : boolStates) {
+                    argsList.add(new Object[]{mimeTypeDecoderPair, mimeTypeEncoderPair, isAsync});
+                }
+            }
+        }
+        return argsList;
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void test720p() throws Exception {
+        ArrayList<Pair<String, String>> mimeCodecPairs = new ArrayList<>();
+        mimeCodecPairs.add(mDecoderPair);
+        mimeCodecPairs.add(mEncoderPair);
+        int maxInstances = checkAndGetMaxSupportedInstancesFor720p(mimeCodecPairs);
+        ExecutorService pool = Executors.newFixedThreadPool(maxInstances / 2 + maxInstances % 2);
+        List<Transcode> transcodeList = new ArrayList<>();
+        for (int i = 0; i < maxInstances / 2 ; i++) {
+            transcodeList.add(new Transcode(mEncoderPair.first, mTestFiles.get(mDecoderPair.first),
+                    mDecoderPair.second, mEncoderPair.second, mIsAsync));
+        }
+        double achievedFrameRate = 0.0;
+        if (maxInstances % 2 == 1) {
+            List<DecodeToSurface> decodeList = new ArrayList<>();
+            mActivityRule.getActivity().waitTillSurfaceIsCreated();
+            Surface surface = mActivityRule.getActivity().getSurface();
+            assertTrue("Surface created is null.", surface != null);
+            assertTrue("Surface created is invalid.", surface.isValid());
+            mActivityRule.getActivity().setScreenParams(1280, 720, true);
+            decodeList.add(new DecodeToSurface(mDecoderPair.first,
+                    mTestFiles.get(mDecoderPair.first), mDecoderPair.second, surface, mIsAsync));
+            List<Future<Double>> decodeResultList = pool.invokeAll(decodeList);
+            for (Future<Double> result : decodeResultList) {
+                achievedFrameRate += result.get();
+            }
+        }
+        List<Future<Double>> transcodeResultList = pool.invokeAll(transcodeList);
+        for (Future<Double> result : transcodeResultList) {
+            achievedFrameRate += result.get();
+        }
+        assertTrue("Unable to achieve the maxFrameRate supported. act/exp: " + achievedFrameRate
+                + "/" + mMaxFrameRate / 2, achievedFrameRate >= mMaxFrameRate / 2);
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java
new file mode 100644
index 0000000..b63e22d
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.util.Pair;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class MultiTranscoderPerfTest extends MultiCodecPerfTestBase {
+    private static final String LOG_TAG = MultiTranscoderPerfTest.class.getSimpleName();
+
+    private final String mDecoderName;
+    private final String mEncoderName;
+
+    public MultiTranscoderPerfTest(String mimeType, String testFile, String decoderName,
+            String encoderName, boolean isAsync) {
+        super(mimeType, testFile,isAsync);
+        mDecoderName = decoderName;
+        mEncoderName = encoderName;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{2}_{3}_{4})")
+    public static Collection<Object[]> inputParams() {
+        // Prepares the params list with the supported Hardware decoders/encoders in the device
+        final List<Object[]> argsList = new ArrayList<>();
+        for (String mime : mMimeList) {
+            ArrayList<String> listOfDecoders = getHardwareCodecsFor720p(mime, false);
+            ArrayList<String> listOfEncoders = getHardwareCodecsFor720p(mime, true);
+            for (String decoder : listOfDecoders) {
+                for (String encoder : listOfEncoders) {
+                    for (boolean isAsync : boolStates) {
+                        argsList.add(new Object[]{mime, mTestFiles.get(mime), decoder, encoder,
+                                isAsync});
+                    }
+                }
+            }
+        }
+        return argsList;
+    }
+
+    @LargeTest
+    @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void test720p() throws Exception {
+        ArrayList<Pair<String, String>> mimeCodecPairs = new ArrayList<>();
+        mimeCodecPairs.add(Pair.create(mMime, mDecoderName));
+        mimeCodecPairs.add(Pair.create(mMime, mEncoderName));
+        int maxInstances = checkAndGetMaxSupportedInstancesFor720p(mimeCodecPairs);
+        assertTrue("Decoder " + mDecoderName + " ,Encoder " + mEncoderName +
+                " unable to support minimum concurrent instances. act/exp: " + maxInstances + "/" +
+                (REQUIRED_MIN_CONCURRENT_INSTANCES / 2),
+                maxInstances >= (REQUIRED_MIN_CONCURRENT_INSTANCES / 2));
+        ExecutorService pool = Executors.newFixedThreadPool(maxInstances / 2);
+        List<Transcode> testList = new ArrayList<>();
+        for (int i = 0; i < maxInstances / 2; i++) {
+            testList.add(new Transcode(mMime, mTestFile, mDecoderName, mEncoderName, mIsAsync));
+        }
+        List<Future<Double>> resultList = pool.invokeAll(testList);
+        double achievedFrameRate = 0.0;
+        for (Future<Double> result : resultList) {
+            achievedFrameRate += result.get();
+        }
+        assertTrue("Unable to achieve the maxFrameRate supported. act/exp: " + achievedFrameRate
+                + "/" + mMaxFrameRate / 2, achievedFrameRate >= mMaxFrameRate / 2);
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
new file mode 100644
index 0000000..8e440b4
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.content.pm.PackageManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests the basic aspects of the media performance class.
+ */
+public class PerformanceClassTest {
+    private boolean isHandheld() {
+        // handheld nature is not exposed to package manager, for now
+        // we check for touchscreen and NOT watch and NOT tv
+        PackageManager pm =
+            InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+        return pm.hasSystemFeature(pm.FEATURE_TOUCHSCREEN)
+                && !pm.hasSystemFeature(pm.FEATURE_WATCH)
+                && !pm.hasSystemFeature(pm.FEATURE_TELEVISION)
+                && !pm.hasSystemFeature(pm.FEATURE_AUTOMOTIVE);
+    }
+
+    @SmallTest
+    @Test
+    public void testMediaPerformanceClassScope() throws Exception {
+        // if device is not of a performance class, we are done.
+        Assume.assumeTrue("not a device of a valid media performance class", Utils.isPerfClass());
+
+        if (Utils.isRPerfClass()
+                || Utils.isSPerfClass()) {
+            assertTrue("performance class is only defined for Handheld devices",
+                       isHandheld());
+        }
+    }
+}
+
diff --git a/tests/mediapc/src/android/mediapc/cts/TestActivity.java b/tests/mediapc/src/android/mediapc/cts/TestActivity.java
new file mode 100644
index 0000000..bd93df0
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/TestActivity.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class TestActivity extends Activity implements SurfaceHolder.Callback {
+    private static final String LOG_TAG = TestActivity.class.getSimpleName();
+    private SurfaceView mSurfaceView;
+    private SurfaceHolder mHolder;
+    private Surface mSurface;
+    private final Lock mLock = new ReentrantLock();
+    private final Condition mCondition = mLock.newCondition();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.media_decoder_surface_layout);
+        mSurfaceView = findViewById(R.id.surface);
+        mHolder = mSurfaceView.getHolder();
+        mHolder.addCallback(this);
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        Log.v(LOG_TAG, "surface created");
+        mLock.lock();
+        mSurface = mHolder.getSurface();
+        mLock.unlock();
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        Log.v(LOG_TAG, "surface changed " + format + " " + width + " " + height);
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        Log.v(LOG_TAG, "surface deleted");
+        mLock.lock();
+        mSurface = null;
+        mLock.unlock();
+    }
+
+    public void waitTillSurfaceIsCreated() throws InterruptedException {
+        final long mWaitTimeMs = 1000;
+        final int retries = 3;
+        mLock.lock();
+        final long start = SystemClock.elapsedRealtime();
+        while ((SystemClock.elapsedRealtime() - start) < (retries * mWaitTimeMs) &&
+                mSurface == null) {
+            mCondition.await(mWaitTimeMs, TimeUnit.MILLISECONDS);
+        }
+        mLock.unlock();
+        if (mSurface == null) {
+            throw new InterruptedException("Taking too long to attach a SurfaceView to a window.");
+        }
+    }
+
+    public Surface getSurface() {
+        return mSurface;
+    }
+
+    public void setScreenParams(int width, int height, boolean noStretch) {
+        ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
+        final DisplayMetrics dm = getResources().getDisplayMetrics();
+        if (noStretch && width <= dm.widthPixels && height <= dm.heightPixels) {
+            lp.width = width;
+            lp.height = height;
+        } else {
+            int a = dm.widthPixels * height / width;
+            if (a <= dm.heightPixels) {
+                lp.width = dm.widthPixels;
+                lp.height = a;
+            } else {
+                lp.width = dm.heightPixels * width / height;
+                lp.height = dm.heightPixels;
+            }
+        }
+        runOnUiThread(() -> mSurfaceView.setLayoutParams(lp));
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/Utils.java b/tests/mediapc/src/android/mediapc/cts/Utils.java
new file mode 100644
index 0000000..2912f5d
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/Utils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.Log;
+
+/**
+ * Test utilities.
+ */
+/* package private */ class Utils {
+    private static final int sPc = SystemProperties.getInt(
+                "ro.odm.build.media_performance_class", 0);
+
+    private static final String TAG = "PerformanceClassTestUtils";
+
+    static {
+        Log.d(TAG, "performance class is "  + sPc);
+    }
+
+    /**
+     * First defined media performance class.
+     */
+    private static final int FIRST_PERFORMANCE_CLASS = Build.VERSION_CODES.R;
+
+    public static boolean isRPerfClass() {
+        return sPc == Build.VERSION_CODES.R;
+    }
+
+    public static boolean isSPerfClass() {
+        return sPc == Build.VERSION_CODES.R + 1; /* TODO: make this S */
+    }
+
+    /**
+     * Latest defined media performance class.
+     */
+    /* TODO: make this S */
+    private static final int LAST_PERFORMANCE_CLASS = Build.VERSION_CODES.R + 1;
+
+    public static int getPerfClass() {
+        return sPc;
+    }
+
+    public static boolean isPerfClass() {
+        return sPc >= FIRST_PERFORMANCE_CLASS &&
+               sPc <= LAST_PERFORMANCE_CLASS;
+    }
+}
diff --git a/tests/mediapc/src/android/mediapc/cts/WorkDir.java b/tests/mediapc/src/android/mediapc/cts/WorkDir.java
new file mode 100644
index 0000000..7f93f43
--- /dev/null
+++ b/tests/mediapc/src/android/mediapc/cts/WorkDir.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediapc.cts;
+
+import android.os.Environment;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Assert;
+
+import java.io.File;
+
+class WorkDir {
+    private static final String MEDIA_PATH_INSTR_ARG_KEY = "media-path";
+    static private final File getTopDir() {
+        Assert.assertEquals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED);
+        return Environment.getExternalStorageDirectory();
+    }
+    static private final String getTopDirString() {
+        return (getTopDir().getAbsolutePath() + File.separator);
+    }
+    static final String getMediaDirString() {
+        android.os.Bundle bundle = InstrumentationRegistry.getArguments();
+        String mediaDirString = bundle.getString(MEDIA_PATH_INSTR_ARG_KEY);
+        if (mediaDirString != null) {
+            // user has specified the mediaDirString via instrumentation-arg
+            return mediaDirString + ((mediaDirString.endsWith("/")) ? "" : "/");
+        } else {
+            return (getTopDirString() + "test/CtsMediaPerformanceClassTestCases-1.0/");
+        }
+    }
+}
diff --git a/tests/netlegacy22.api/Android.bp b/tests/netlegacy22.api/Android.bp
new file mode 100644
index 0000000..b28f8a0
--- /dev/null
+++ b/tests/netlegacy22.api/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "CtsNetTestCasesLegacyApi22",
+    defaults: ["cts_defaults"],
+    srcs: ["src/**/*.java"],
+    platform_apis: true,
+    static_libs: [
+        "ctstestrunner-axt",
+        "compatibility-device-util-axt",
+    ],
+    libs: ["android.test.base"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+}
diff --git a/tests/netlegacy22.api/Android.mk b/tests/netlegacy22.api/Android.mk
deleted file mode 100644
index 53c910d..0000000
--- a/tests/netlegacy22.api/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := tests
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsNetTestCasesLegacyApi22
-
-LOCAL_SDK_VERSION := 22
-
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner-axt compatibility-device-util-axt
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts general-tests
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/netlegacy22.api/AndroidManifest.xml b/tests/netlegacy22.api/AndroidManifest.xml
index a9411cc..e062e14 100644
--- a/tests/netlegacy22.api/AndroidManifest.xml
+++ b/tests/netlegacy22.api/AndroidManifest.xml
@@ -18,6 +18,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="android.netlegacy22.api.cts">
 
+    <uses-sdk android:targetSdkVersion="22" />
+
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
diff --git a/tests/netlegacy22.api/OWNERS b/tests/netlegacy22.api/OWNERS
index 3fcbaf3..9b1555e 100644
--- a/tests/netlegacy22.api/OWNERS
+++ b/tests/netlegacy22.api/OWNERS
@@ -1,2 +1,3 @@
 # Bug component: 31808
-include ../tests/net/OWNERS
+set noparent
+include platform/packages/modules/Connectivity:/tests/cts/OWNERS
diff --git a/tests/netlegacy22.api/TEST_MAPPING b/tests/netlegacy22.api/TEST_MAPPING
new file mode 100644
index 0000000..38a8470
--- /dev/null
+++ b/tests/netlegacy22.api/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "CtsNetTestCasesLegacyApi22"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
index 4a8a2ad..b7880c7 100644
--- a/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
+++ b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
@@ -50,6 +50,7 @@
     private static final String HOST_ADDRESS1 = "192.0.2.1";
     private static final String HOST_ADDRESS2 = "192.0.2.2";
     private static final String HOST_ADDRESS3 = "192.0.2.3";
+    private static final String HOST_ADDRESS4 = "192.0.2.4";
 
     // These are correct as of API level 22, which is what we target here.
     private static final int APN_REQUEST_FAILED = 3;
@@ -163,8 +164,13 @@
             assertTrue("Couldn't requestRouteToHost using HIPRI.",
                     mCm.requestRouteToHost(TYPE_MOBILE_HIPRI, ipv4AddrToInt(HOST_ADDRESS1)));
 
+            assertTrue("Couldn't requestRouteToHostAddress using HIPRI.",
+                    mCm.requestRouteToHostAddress(TYPE_MOBILE_HIPRI,
+                            InetAddress.getByName(HOST_ADDRESS3)));
+
             checkSourceAddress(HOST_ADDRESS1, TYPE_MOBILE);
             checkSourceAddress(HOST_ADDRESS2, TYPE_WIFI);
+            checkSourceAddress(HOST_ADDRESS3, TYPE_MOBILE);
 
             // TODO check dns selection
 
@@ -282,7 +288,7 @@
 
             try {
                 assertTrue("Network type " + type,
-                        mCm.requestRouteToHost(type, ipv4AddrToInt(HOST_ADDRESS3)) == expectToWork);
+                        mCm.requestRouteToHost(type, ipv4AddrToInt(HOST_ADDRESS4)) == expectToWork);
             } catch (Exception e) {
                 Log.d(TAG, "got exception in requestRouteToHost for type " + type);
                 assertFalse("Exception received for type " + type, expectToWork);
@@ -291,6 +297,6 @@
             //TODO verify route table
         }
 
-        assertFalse(mCm.requestRouteToHost(-1, ipv4AddrToInt(HOST_ADDRESS1)));
+        assertFalse(mCm.requestRouteToHost(-1, ipv4AddrToInt(HOST_ADDRESS4)));
     }
 }
diff --git a/tests/openglperf2/Android.bp b/tests/openglperf2/Android.bp
new file mode 100644
index 0000000..ce83be2
--- /dev/null
+++ b/tests/openglperf2/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "CtsOpenGlPerf2TestCases",
+    defaults: ["cts_defaults"],
+    srcs: ["src/**/*.java"],
+    // Include both the 32 and 64 bit versions
+    compile_multilib: "both",
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+    ],
+    jni_libs: ["libctsopengl_jni"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    //LOCAL_MIN_SDK_VERSION := 16
+    sdk_version: "16",
+}
diff --git a/tests/openglperf2/Android.mk b/tests/openglperf2/Android.mk
deleted file mode 100644
index a6de6c8..0000000
--- a/tests/openglperf2/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := tests
-
-# Include both the 32 and 64 bit versions
-LOCAL_MULTILIB := both
-
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt ctstestrunner-axt
-
-LOCAL_JNI_SHARED_LIBRARIES := libctsopengl_jni
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsOpenGlPerf2TestCases
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts general-tests
-
-LOCAL_SDK_VERSION := 16
-#LOCAL_MIN_SDK_VERSION := 16
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/security/src/android/keystore/cts/Attestation.java b/tests/security/src/android/keystore/cts/Attestation.java
index e9ff0d2..ae2e29a 100644
--- a/tests/security/src/android/keystore/cts/Attestation.java
+++ b/tests/security/src/android/keystore/cts/Attestation.java
@@ -39,6 +39,16 @@
     public static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
     public static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
 
+    // Known KeyMaster/KeyMint versions. This is the version number
+    // which appear in the keymasterVersion field.
+    public static final int KM_VERSION_KEYMASTER_1 = 10;
+    public static final int KM_VERSION_KEYMASTER_1_1 = 11;
+    public static final int KM_VERSION_KEYMASTER_2 = 20;
+    public static final int KM_VERSION_KEYMASTER_3 = 30;
+    public static final int KM_VERSION_KEYMASTER_4 = 40;
+    public static final int KM_VERSION_KEYMASTER_4_1 = 41;
+    public static final int KM_VERSION_KEYMINT_1 = 100;
+
     int attestationVersion;
     int keymasterVersion;
     int keymasterSecurityLevel;
@@ -107,6 +117,7 @@
 
     public abstract RootOfTrust getRootOfTrust();
 
+    // Returns one of the KM_VERSION_* values define above.
     public int getKeymasterVersion() {
         return keymasterVersion;
     }
diff --git a/tests/signature/api-check/Android.bp b/tests/signature/api-check/Android.bp
index 3f686c2..5e7ff3b 100644
--- a/tests/signature/api-check/Android.bp
+++ b/tests/signature/api-check/Android.bp
@@ -70,12 +70,6 @@
     static_libs: ["cts-api-signature-test"],
 }
 
-// Access the hiddenapi-flags.csv file produced by the build.
-hiddenapi_flags {
-    name: "cts-hiddenapi-flags-csv",
-    filename: "hiddenapi-flags.csv",
-}
-
 filegroup {
     name: "cts-api-hiddenapi-filter-csv",
     srcs: [
@@ -88,7 +82,7 @@
     name: "hiddenapi-blocklist-check-defaults",
     defaults: ["signature-api-check-defaults"],
     java_resources: [
-        ":cts-hiddenapi-flags-csv",
+        ":platform-bootclasspath{hiddenapi-flags.csv}",
         ":cts-api-hiddenapi-filter-csv"
     ],
     jni_libs: [
diff --git a/tests/signature/api-check/shared-libs-api/Android.mk b/tests/signature/api-check/shared-libs-api/Android.mk
index 3128444..852c9db 100644
--- a/tests/signature/api-check/shared-libs-api/Android.mk
+++ b/tests/signature/api-check/shared-libs-api/Android.mk
@@ -71,6 +71,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := cts-api-signature-multilib-test
 
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 include $(LOCAL_PATH)/../build_signature_apk.mk
 
 LOCAL_JAVA_SDK_LIBRARIES :=
diff --git a/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java b/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java
index f74a027..4c0a31a 100644
--- a/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java
+++ b/tests/signature/api-check/shared-libs-api/src/android/signature/cts/api/SignatureMultiLibsTest.java
@@ -42,7 +42,7 @@
         runWithTestResultObserver(mResultObserver -> {
 
             ApiComplianceChecker complianceChecker =
-                    new ApiComplianceChecker(mResultObserver, classProvider);
+                    new ApiComplianceChecker(mResultObserver, mClassProvider);
 
             ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
 
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
index 1e96910..9707dd8 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/AbstractApiTest.java
@@ -26,17 +26,13 @@
 import android.signature.cts.VirtualPath.LocalFilePath;
 import android.signature.cts.VirtualPath.ResourcePath;
 import android.util.Log;
-import java.io.File;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
-import java.util.EnumSet;
 import java.util.stream.Stream;
 import java.util.zip.ZipFile;
 import repackaged.android.test.InstrumentationTestCase;
@@ -50,7 +46,7 @@
 
     private TestResultObserver mResultObserver;
 
-    ClassProvider classProvider;
+    ClassProvider mClassProvider;
 
     protected String getGlobalExemptions() {
         return Settings.Global.getString(
@@ -90,7 +86,7 @@
         // out known inaccessible classes.
         // Note that com.android.internal.R.* inner classes are also excluded as they are
         // not part of API though exist in the runtime.
-        classProvider = new ExcludingClassProvider(
+        mClassProvider = new ExcludingClassProvider(
                 new BootClassPathClassesProvider(),
                 name -> name != null && name.startsWith("com.android.internal.R."));
 
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java
index 2337ced..10907d0 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java
@@ -18,8 +18,6 @@
 
 import java.util.function.Predicate;
 
-import android.os.Bundle;
-import android.provider.Settings;
 import android.signature.cts.DexField;
 import android.signature.cts.DexMember;
 import android.signature.cts.DexMemberChecker;
@@ -127,8 +125,8 @@
                 }
 
             };
-            classProvider.getAllClasses().forEach(klass -> {
-                classProvider.getAllMembers(klass)
+            mClassProvider.getAllClasses().forEach(klass -> {
+                mClassProvider.getAllMembers(klass)
                         .filter(memberFilter)
                         .forEach(member -> {
                             DexMemberChecker.checkSingleMember(member, reflection, jni, observer);
diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java
index 440430d..2ef70ca 100644
--- a/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java
+++ b/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java
@@ -58,7 +58,7 @@
         runWithTestResultObserver(mResultObserver -> {
             Set<JDiffClassDescription> unexpectedClasses = loadUnexpectedClasses();
             for (JDiffClassDescription classDescription : unexpectedClasses) {
-                Class<?> unexpectedClass = findUnexpectedClass(classDescription, classProvider);
+                Class<?> unexpectedClass = findUnexpectedClass(classDescription, mClassProvider);
                 if (unexpectedClass != null) {
                     mResultObserver.notifyFailure(
                             FailureType.UNEXPECTED_CLASS,
@@ -68,7 +68,7 @@
             }
 
             ApiComplianceChecker complianceChecker =
-                    new ApiComplianceChecker(mResultObserver, classProvider);
+                    new ApiComplianceChecker(mResultObserver, mClassProvider);
 
             // Load classes from any API files that form the base which the expected APIs extend.
             loadBaseClasses(complianceChecker);
diff --git a/tests/signature/api-check/system-annotation/src/java/android/signature/cts/api/AnnotationTest.java b/tests/signature/api-check/system-annotation/src/java/android/signature/cts/api/AnnotationTest.java
index efefdd5..a3c46d5 100644
--- a/tests/signature/api-check/system-annotation/src/java/android/signature/cts/api/AnnotationTest.java
+++ b/tests/signature/api-check/system-annotation/src/java/android/signature/cts/api/AnnotationTest.java
@@ -27,6 +27,7 @@
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.function.Predicate;
 
 /**
  * Checks that parts of the device's API that are annotated (e.g. with android.annotation.SystemApi)
@@ -36,13 +37,22 @@
 
     private static final String TAG = AnnotationTest.class.getSimpleName();
 
-    private String[] expectedApiFiles;
-    private String annotationForExactMatch;
+    private String[] mExpectedApiFiles;
+    private String mAnnotationForExactMatch;
 
     @Override
     protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
-        expectedApiFiles = getCommaSeparatedList(instrumentationArgs, "expected-api-files");
-        annotationForExactMatch = instrumentationArgs.getString("annotation-for-exact-match");
+        mExpectedApiFiles = getCommaSeparatedList(instrumentationArgs, "expected-api-files");
+        mAnnotationForExactMatch = instrumentationArgs.getString("annotation-for-exact-match");
+    }
+
+    private Predicate<? super JDiffClassDescription> androidAutoClassesFilter() {
+        if (getInstrumentation().getContext().getPackageManager().hasSystemFeature(
+                "android.hardware.type.automotive")) {
+            return clz -> true;
+        } else {
+            return clz -> !clz.getAbsoluteClassName().startsWith("android.car.");
+        }
     }
 
     /**
@@ -50,44 +60,42 @@
      * android.annotation.SystemApi) match the API definition.
      */
     public void testAnnotation() {
-        if ("true".equals(PropertyUtil.getProperty("ro.treble.enabled")) &&
-                PropertyUtil.getFirstApiLevel() > Build.VERSION_CODES.O_MR1) {
-            AnnotationChecker.ResultFilter filter = new AnnotationChecker.ResultFilter() {
-                @Override
-                public boolean skip(Class<?> clazz) {
-                    return false;
-                }
+       AnnotationChecker.ResultFilter filter = new AnnotationChecker.ResultFilter() {
+            @Override
+            public boolean skip(Class<?> clazz) {
+                return false;
+            }
 
-                @Override
-                public boolean skip(Constructor<?> ctor) {
-                    return false;
-                }
+            @Override
+            public boolean skip(Constructor<?> ctor) {
+                return false;
+            }
 
-                @Override
-                public boolean skip(Method m) {
-                    return false;
-                }
+            @Override
+            public boolean skip(Method m) {
+                return false;
+            }
 
-                @Override
-                public boolean skip(Field f) {
-                    // The R.styleable class is not part of the API because it's annotated with
-                    // @doconly. But the class actually exists in the runtime classpath.  To avoid
-                    // the mismatch, skip the check for fields from the class.
-                    return "android.R$styleable".equals(f.getDeclaringClass().getName());
-                }
-            };
-            runWithTestResultObserver(resultObserver -> {
-                AnnotationChecker complianceChecker = new AnnotationChecker(resultObserver,
-                        classProvider, annotationForExactMatch, filter);
+            @Override
+            public boolean skip(Field f) {
+                // The R.styleable class is not part of the API because it's annotated with
+                // @doconly. But the class actually exists in the runtime classpath.  To avoid
+                // the mismatch, skip the check for fields from the class.
+                return "android.R$styleable".equals(f.getDeclaringClass().getName());
+            }
+        };
+        runWithTestResultObserver(resultObserver -> {
+            AnnotationChecker complianceChecker = new AnnotationChecker(resultObserver,
+                    mClassProvider, mAnnotationForExactMatch, filter);
 
-                ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
+            ApiDocumentParser apiDocumentParser = new ApiDocumentParser(TAG);
 
-                parseApiResourcesAsStream(apiDocumentParser, expectedApiFiles)
-                        .forEach(complianceChecker::checkSignatureCompliance);
+            parseApiResourcesAsStream(apiDocumentParser, mExpectedApiFiles)
+                    .filter(androidAutoClassesFilter())
+                    .forEach(complianceChecker::checkSignatureCompliance);
 
-                // After done parsing all expected API files, perform any deferred checks.
-                complianceChecker.checkDeferred();
-            });
-        }
+            // After done parsing all expected API files, perform any deferred checks.
+            complianceChecker.checkDeferred();
+        });
     }
 }
diff --git a/tests/signature/api-check/system-api/Android.mk b/tests/signature/api-check/system-api/Android.mk
index 6649ed8..ccb3c58 100644
--- a/tests/signature/api-check/system-api/Android.mk
+++ b/tests/signature/api-check/system-api/Android.mk
@@ -55,6 +55,8 @@
     system-current.api.gz \
     system-removed.api.gz \
 
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 include $(LOCAL_PATH)/../build_signature_apk.mk
 
 all_system_api_files :=
diff --git a/tests/signature/intent-check/DynamicConfig.xml b/tests/signature/intent-check/DynamicConfig.xml
index 4dc7b8a..7c33d1a 100644
--- a/tests/signature/intent-check/DynamicConfig.xml
+++ b/tests/signature/intent-check/DynamicConfig.xml
@@ -25,6 +25,8 @@
     Bug: 78574873 android.intent.action.EPHEMERAL_RESOLVER_SETTINGS
     Bug: 150153196 android.intent.action.LOAD_DATA (system in API 30)
     Bug: 150153196 android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY (system in API 30)
+    Bug: 187483828 android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD
+    Bug: 187483828 android.intent.action.ACTIVITY_RECOGNIZER
 -->
 <dynamicConfig>
     <entry key ="intent_whitelist">
@@ -38,5 +40,7 @@
       <value>android.intent.action.EPHEMERAL_RESOLVER_SETTINGS</value>
       <value>android.intent.action.LOAD_DATA</value>
       <value>android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY</value>
+      <value>android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD</value>
+      <value>android.intent.action.ACTIVITY_RECOGNIZER</value>
     </entry>
 </dynamicConfig>
diff --git a/tests/signature/lib/common/src/android/signature/cts/AnnotationChecker.java b/tests/signature/lib/common/src/android/signature/cts/AnnotationChecker.java
index 3f14dad..1567f16 100644
--- a/tests/signature/lib/common/src/android/signature/cts/AnnotationChecker.java
+++ b/tests/signature/lib/common/src/android/signature/cts/AnnotationChecker.java
@@ -27,7 +27,7 @@
 /**
  * Checks that the runtime representation of a class matches the API representation of a class.
  */
-public class AnnotationChecker extends AbstractApiChecker {
+public class AnnotationChecker extends ApiPresenceChecker {
 
     private final String annotationSpec;
 
@@ -39,7 +39,7 @@
     private final Map<String, Set<Field>> annotatedFieldsMap = new HashMap<>();
 
     /**
-     * @param annotationName name of the annotation class for the API type (e.g.
+     * @param annotationSpec name of the annotation class for the API type (e.g.
      *      android.annotation.SystemApi)
      */
     public AnnotationChecker(
@@ -82,7 +82,6 @@
         public boolean skip(Field f);
     }
 
-    @Override
     public void checkDeferred() {
         for (Class<?> clazz : annotatedClassesMap.values()) {
             if (filter != null && filter.skip(clazz)) continue;
@@ -117,17 +116,6 @@
     }
 
     @Override
-    protected boolean allowMissingClass(JDiffClassDescription classDescription) {
-        // A class that exist in the API document is not found in the runtime.
-        // This can happen for classes that are optional (e.g. classes for
-        // Android Auto). This, however, should not be considered as a test
-        // failure, because the purpose of this test is to ensure that every
-        // runtime classes found in the device have more annotations than
-        // the documented.
-        return true;
-    }
-
-    @Override
     protected boolean checkClass(JDiffClassDescription classDescription, Class<?> runtimeClass) {
         // remove the class from the set if found
         annotatedClassesMap.remove(runtimeClass.getName());
diff --git a/tests/signature/lib/common/src/android/signature/cts/ApiComplianceChecker.java b/tests/signature/lib/common/src/android/signature/cts/ApiComplianceChecker.java
index f1c812e..fe67157 100644
--- a/tests/signature/lib/common/src/android/signature/cts/ApiComplianceChecker.java
+++ b/tests/signature/lib/common/src/android/signature/cts/ApiComplianceChecker.java
@@ -29,7 +29,7 @@
 /**
  * Checks that the runtime representation of a class matches the API representation of a class.
  */
-public class ApiComplianceChecker extends AbstractApiChecker {
+public class ApiComplianceChecker extends ApiPresenceChecker {
 
     /**
      * A set of method signatures whose abstract modifier should be ignored.
@@ -73,7 +73,6 @@
         interfaceChecker = new InterfaceChecker(resultObserver, classProvider);
     }
 
-    @Override
     public void checkDeferred() {
         interfaceChecker.checkQueued();
     }
diff --git a/tests/signature/lib/common/src/android/signature/cts/AbstractApiChecker.java b/tests/signature/lib/common/src/android/signature/cts/ApiPresenceChecker.java
similarity index 86%
rename from tests/signature/lib/common/src/android/signature/cts/AbstractApiChecker.java
rename to tests/signature/lib/common/src/android/signature/cts/ApiPresenceChecker.java
index 03c4a85..fc4335c 100644
--- a/tests/signature/lib/common/src/android/signature/cts/AbstractApiChecker.java
+++ b/tests/signature/lib/common/src/android/signature/cts/ApiPresenceChecker.java
@@ -27,13 +27,13 @@
  * Base class for those that process a set of API definition files and perform some checking on
  * them.
  */
-public abstract class AbstractApiChecker {
+public class ApiPresenceChecker {
 
     final ResultObserver resultObserver;
 
     final ClassProvider classProvider;
 
-    AbstractApiChecker(ClassProvider classProvider, ResultObserver resultObserver) {
+    public ApiPresenceChecker(ClassProvider classProvider, ResultObserver resultObserver) {
         this.classProvider = classProvider;
         this.resultObserver = resultObserver;
     }
@@ -63,14 +63,10 @@
                     .findRequiredClass(classDescription, classProvider);
 
             if (runtimeClass == null) {
-                // No class found, notify the observer according to the class type,
-                // if missing a class isn't acceptable.
-                if (!allowMissingClass(classDescription)) {
-                    resultObserver.notifyFailure(FailureType.missing(classDescription),
-                            classDescription.getAbsoluteClassName(),
-                            "Classloader is unable to find " + classDescription
-                                    .getAbsoluteClassName());
-                }
+                resultObserver.notifyFailure(FailureType.missing(classDescription),
+                        classDescription.getAbsoluteClassName(),
+                        "Classloader is unable to find " + classDescription
+                                .getAbsoluteClassName());
                 return null;
             }
 
@@ -90,11 +86,6 @@
     }
 
     /**
-     * Perform any additional checks that can only be done after all api files have been processed.
-     */
-    public abstract void checkDeferred();
-
-    /**
      * Implement to provide custom check of the supplied class description.
      *
      * <p>This should not peform checks on the members, those will be done separately depending
@@ -104,21 +95,12 @@
      * @param runtimeClass the runtime class corresponding to the class description.
      * @return true if the checks passed and the members should now be checked.
      */
-    protected abstract boolean checkClass(JDiffClassDescription classDescription,
-            Class<?> runtimeClass);
-
-
-    /**
-     * Checks that a class that exists in the API xml file but that does not exist
-     * in the runtime is allowed or not.
-     *
-     * @param classDescription the class description that is missing.
-     * @return true if missing the class is acceptable.
-     */
-    protected boolean allowMissingClass(JDiffClassDescription classDescription) {
-        return false;
+    protected boolean checkClass(JDiffClassDescription classDescription,
+            Class<?> runtimeClass) {
+        return true;
     }
 
+
     /**
      * Checks all fields in test class for compliance with the API xml.
      *
@@ -175,9 +157,10 @@
         return fieldMap;
     }
 
-    protected abstract void checkField(JDiffClassDescription classDescription,
+    protected void checkField(JDiffClassDescription classDescription,
             Class<?> runtimeClass,
-            JDiffClassDescription.JDiffField fieldDescription, Field field);
+            JDiffClassDescription.JDiffField fieldDescription, Field field) {
+    }
 
 
     /**
@@ -217,9 +200,10 @@
         }
     }
 
-    protected abstract void checkConstructor(JDiffClassDescription classDescription,
+    protected void checkConstructor(JDiffClassDescription classDescription,
             Class<?> runtimeClass,
-            JDiffClassDescription.JDiffConstructor ctorDescription, Constructor<?> ctor);
+            JDiffClassDescription.JDiffConstructor ctorDescription, Constructor<?> ctor) {
+    }
 
     /**
      * Checks that the method found through reflection matches the
@@ -260,7 +244,8 @@
         }
     }
 
-    protected abstract void checkMethod(JDiffClassDescription classDescription,
+    protected void checkMethod(JDiffClassDescription classDescription,
             Class<?> runtimeClass,
-            JDiffClassDescription.JDiffMethod methodDescription, Method method);
+            JDiffClassDescription.JDiffMethod methodDescription, Method method) {
+    }
 }
diff --git a/tests/signature/tests/src/android/signature/cts/tests/AnnotationCheckerTest.java b/tests/signature/tests/src/android/signature/cts/tests/AnnotationCheckerTest.java
index a981be7..3e18522 100644
--- a/tests/signature/tests/src/android/signature/cts/tests/AnnotationCheckerTest.java
+++ b/tests/signature/tests/src/android/signature/cts/tests/AnnotationCheckerTest.java
@@ -21,8 +21,9 @@
 import android.signature.cts.FailureType;
 import android.signature.cts.JDiffClassDescription;
 import android.signature.cts.ResultObserver;
-import android.signature.cts.tests.data.ApiAnnotation;
+
 import java.lang.reflect.Modifier;
+import java.util.function.Consumer;
 
 import org.junit.Test;
 import org.junit.runners.JUnit4;
@@ -32,7 +33,7 @@
  * Test class for {@link android.signature.cts.AnnotationChecker}.
  */
 @RunWith(JUnit4.class)
-public class AnnotationCheckerTest extends AbstractApiCheckerTest<AnnotationChecker> {
+public class AnnotationCheckerTest extends ApiPresenceCheckerTest<AnnotationChecker> {
 
     @Override
     protected AnnotationChecker createChecker(ResultObserver resultObserver,
@@ -41,6 +42,18 @@
                 "@android.signature.cts.tests.data.ApiAnnotation()", null);
     }
 
+    @Override
+    void runWithApiChecker(ResultObserver resultObserver, Consumer<AnnotationChecker> consumer,
+            String... excludedRuntimeClasses) {
+        super.runWithApiChecker(
+                resultObserver,
+                checker -> {
+                    consumer.accept(checker);
+                    checker.checkDeferred();
+                },
+                excludedRuntimeClasses);
+    }
+
     private static void addConstructor(JDiffClassDescription clz, String... paramTypes) {
         JDiffClassDescription.JDiffConstructor constructor = new JDiffClassDescription.JDiffConstructor(
                 clz.getShortClassName(), Modifier.PUBLIC);
diff --git a/tests/signature/tests/src/android/signature/cts/tests/ApiComplianceCheckerTest.java b/tests/signature/tests/src/android/signature/cts/tests/ApiComplianceCheckerTest.java
index 405f5b2..c4c87f5 100644
--- a/tests/signature/tests/src/android/signature/cts/tests/ApiComplianceCheckerTest.java
+++ b/tests/signature/tests/src/android/signature/cts/tests/ApiComplianceCheckerTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.signature.cts.AnnotationChecker;
 import android.signature.cts.ApiComplianceChecker;
 import android.signature.cts.ClassProvider;
 import android.signature.cts.FailureType;
@@ -28,8 +29,8 @@
 import android.signature.cts.tests.data.NormalClass;
 import android.signature.cts.tests.data.NormalInterface;
 import java.lang.reflect.Modifier;
+import java.util.function.Consumer;
 
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runners.JUnit4;
 import org.junit.runner.RunWith;
@@ -38,7 +39,7 @@
  * Test class for JDiffClassDescription.
  */
 @RunWith(JUnit4.class)
-public class ApiComplianceCheckerTest extends AbstractApiCheckerTest<ApiComplianceChecker> {
+public class ApiComplianceCheckerTest extends ApiPresenceCheckerTest<ApiComplianceChecker> {
 
     @Override
     protected ApiComplianceChecker createChecker(ResultObserver resultObserver,
@@ -46,6 +47,18 @@
         return new ApiComplianceChecker(resultObserver, provider);
     }
 
+    @Override
+    void runWithApiChecker(
+            ResultObserver resultObserver, Consumer<ApiComplianceChecker> consumer, String... excludedRuntimeClasses) {
+        super.runWithApiChecker(
+                resultObserver,
+                checker -> {
+                    consumer.accept(checker);
+                    checker.checkDeferred();
+                },
+                excludedRuntimeClasses);
+    }
+
     @Test
     public void testNormalClassCompliance() {
         JDiffClassDescription clz = createClass(NormalClass.class.getSimpleName());
diff --git a/tests/signature/tests/src/android/signature/cts/tests/AbstractApiCheckerTest.java b/tests/signature/tests/src/android/signature/cts/tests/ApiPresenceCheckerTest.java
similarity index 95%
rename from tests/signature/tests/src/android/signature/cts/tests/AbstractApiCheckerTest.java
rename to tests/signature/tests/src/android/signature/cts/tests/ApiPresenceCheckerTest.java
index 901b424..719be91 100644
--- a/tests/signature/tests/src/android/signature/cts/tests/AbstractApiCheckerTest.java
+++ b/tests/signature/tests/src/android/signature/cts/tests/ApiPresenceCheckerTest.java
@@ -15,7 +15,7 @@
  */
 package android.signature.cts.tests;
 
-import android.signature.cts.AbstractApiChecker;
+import android.signature.cts.ApiPresenceChecker;
 import android.signature.cts.ClassProvider;
 import android.signature.cts.ExcludingClassProvider;
 import android.signature.cts.FailureType;
@@ -28,9 +28,9 @@
 import org.junit.Assert;
 
 /**
- * Base class for tests of implementations of {@link AbstractApiChecker}.
+ * Base class for tests of implementations of {@link ApiPresenceChecker}.
  */
-public abstract class AbstractApiCheckerTest<T extends AbstractApiChecker> {
+public abstract class ApiPresenceCheckerTest<T extends ApiPresenceChecker> {
 
     static final String VALUE = "VALUE";
 
@@ -79,7 +79,6 @@
         ClassProvider provider = createClassProvider(excludedRuntimeClasses);
         T checker = createChecker(resultObserver, provider);
         consumer.accept(checker);
-        checker.checkDeferred();
     }
 
     protected abstract T createChecker(ResultObserver resultObserver, ClassProvider provider);
diff --git a/tests/simplecpu/Android.bp b/tests/simplecpu/Android.bp
new file mode 100644
index 0000000..3400544
--- /dev/null
+++ b/tests/simplecpu/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "CtsSimpleCpuTestCases",
+    defaults: ["cts_defaults"],
+    srcs: ["src/**/*.java"],
+    // Include both the 32 and 64 bit versions
+    compile_multilib: "both",
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+    ],
+    jni_libs: ["libctscpu_jni"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+    sdk_version: "16",
+}
diff --git a/tests/simplecpu/Android.mk b/tests/simplecpu/Android.mk
deleted file mode 100644
index 7d1db20..0000000
--- a/tests/simplecpu/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := tests
-
-# Include both the 32 and 64 bit versions
-LOCAL_MULTILIB := both
-
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt ctstestrunner-axt
-
-LOCAL_JNI_SHARED_LIBRARIES := libctscpu_jni
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsSimpleCpuTestCases
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts general-tests
-
-LOCAL_SDK_VERSION := 16
-
-include $(BUILD_CTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/accounts/common/lint-baseline.xml b/tests/tests/accounts/common/lint-baseline.xml
new file mode 100644
index 0000000..6a95c6c
--- /dev/null
+++ b/tests/tests/accounts/common/lint-baseline.xml
@@ -0,0 +1,697 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountManager#get`"
+        errorLine1="        AccountManager am = AccountManager.get(context);"
+        errorLine2="                                           ~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="81"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountManager#getAuthenticatorTypes`"
+        errorLine1="        AuthenticatorDescription[] authenticators = am.getAuthenticatorTypes();"
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="82"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.AuthenticatorDescription#packageName`"
+        errorLine1="            if (a.packageName.equals(context.getPackageName())) {"
+        errorLine2="                ~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="88"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `new android.accounts.Account`"
+        errorLine1="                    Account account = new Account(name, a.type);"
+        errorLine2="                                      ~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="90"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.AuthenticatorDescription#type`"
+        errorLine1="                    Account account = new Account(name, a.type);"
+        errorLine2="                                                        ~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="90"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountManager#addAccountExplicitly`"
+        errorLine1="                    am.addAccountExplicitly(account, Fixtures.PREFIX_PASSWORD + name, null);"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="91"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountManager#get`"
+        errorLine1="        AccountManager am = AccountManager.get(context);"
+        errorLine2="                                           ~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="99"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountManager#getAuthenticatorTypes`"
+        errorLine1="        AuthenticatorDescription[] authenticators = am.getAuthenticatorTypes();"
+        errorLine2="                                                       ~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="100"
+            column="56"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.AuthenticatorDescription#packageName`"
+        errorLine1="            if (a.packageName.equals(context.getPackageName())) {"
+        errorLine2="                ~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="106"
+            column="17"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountManager#getAccountsByType`"
+        errorLine1="                Account[] accountsToRemove = am.getAccountsByType(a.type);"
+        errorLine2="                                                ~~~~~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="107"
+            column="49"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.AuthenticatorDescription#type`"
+        errorLine1="                Account[] accountsToRemove = am.getAccountsByType(a.type);"
+        errorLine2="                                                                  ~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="107"
+            column="67"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 22 (current min is 1): `android.accounts.AccountManager#removeAccountExplicitly`"
+        errorLine1="                    am.removeAccountExplicitly(account);"
+        errorLine2="                       ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/AuthenticatorContentProvider.java"
+            line="109"
+            column="24"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `Account` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="        out.writeParcelable(account, flags);"
+        errorLine2="                            ~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/tx/ConfirmCredentialsTx.java"
+            line="50"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `new android.accounts.Account`"
+        errorLine1="    public static final Account ACCOUNT_UNAFFILIATED_FIXTURE_SUCCESS = new Account("
+        errorLine2="                                                                       ~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/Fixtures.java"
+            line="53"
+            column="72"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `new android.accounts.Account`"
+        errorLine1="    public static final Account ACCOUNT_DEFAULT = new Account("
+        errorLine2="                                                  ~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/Fixtures.java"
+            line="57"
+            column="51"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `Account` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="        out.writeParcelable(account, flags);"
+        errorLine2="                            ~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/tx/GetAccountRemovalAllowedTx.java"
+            line="46"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `Account` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="        out.writeParcelable(account, flags);"
+        errorLine2="                            ~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/tx/GetAuthTokenTx.java"
+            line="54"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `Account` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="        out.writeParcelable(account, flags);"
+        errorLine2="                            ~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/tx/HasFeaturesTx.java"
+            line="57"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `Account` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="        out.writeParcelable(account, flags);"
+        errorLine2="                            ~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/tx/StartUpdateCredentialsSessionTx.java"
+            line="50"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 5 (current min is 1): `android.accounts.AbstractAccountAuthenticator`"
+        errorLine1="public class TestAccountAuthenticator extends AbstractAccountAuthenticator {"
+        errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="39"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `new android.accounts.AbstractAccountAuthenticator`"
+        errorLine1="        super(context);"
+        errorLine2="        ~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="47"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `AccountAuthenticatorResponse` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="            intent.putExtra(Fixtures.KEY_CALLBACK, response);"
+        errorLine2="                                                   ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="89"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountAuthenticatorResponse#onResult`"
+        errorLine1="                response.onResult(result);"
+        errorLine2="                         ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="101"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="        if (!mAccountType.equals(account.type)) {"
+        errorLine2="                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="111"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="        if (account.name.startsWith(Fixtures.PREFIX_NAME_SUCCESS)) {"
+        errorLine2="            ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="120"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);"
+        errorLine2="                                                              ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="123"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);"
+        errorLine2="                                                              ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="124"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="        } else if (account.name.startsWith(Fixtures.PREFIX_NAME_INTERVENE)) {"
+        errorLine2="                   ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="125"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);"
+        errorLine2="                                                                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="129"
+            column="82"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type);"
+        errorLine2="                                                                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="130"
+            column="82"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `AccountAuthenticatorResponse` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="            intent.putExtra(Fixtures.KEY_CALLBACK, response);"
+        errorLine2="                                                   ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="135"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountAuthenticatorResponse#onResult`"
+        errorLine1="                response.onResult(result);"
+        errorLine2="                         ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="147"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="        if (!mAccountType.equals(account.type)) {"
+        errorLine2="                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="158"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="        if (account.name.startsWith(Fixtures.PREFIX_NAME_SUCCESS)) {"
+        errorLine2="            ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="167"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);"
+        errorLine2="                                                              ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="174"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);"
+        errorLine2="                                                              ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="175"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="        } else if (account.name.startsWith(Fixtures.PREFIX_NAME_INTERVENE)) {"
+        errorLine2="                   ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="176"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);"
+        errorLine2="                                                                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="184"
+            column="82"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type);"
+        errorLine2="                                                                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="185"
+            column="82"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `AccountAuthenticatorResponse` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="            intent.putExtra(Fixtures.KEY_CALLBACK, response);"
+        errorLine2="                                                   ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="190"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountAuthenticatorResponse#onResult`"
+        errorLine1="                response.onResult(result);"
+        errorLine2="                         ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="203"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="        if (!mAccountType.equals(account.type)) {"
+        errorLine2="                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="222"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="        if (account.name.startsWith(Fixtures.PREFIX_NAME_SUCCESS)) {"
+        errorLine2="            ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="231"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="            result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);"
+        errorLine2="                                                              ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="233"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="            result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);"
+        errorLine2="                                                              ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="234"
+            column="63"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="        } else if (account.name.startsWith(Fixtures.PREFIX_NAME_INTERVENE)) {"
+        errorLine2="                   ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="235"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);"
+        errorLine2="                                                                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="238"
+            column="82"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="            eventualActivityResultData.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type);"
+        errorLine2="                                                                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="239"
+            column="82"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `AccountAuthenticatorResponse` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="            intent.putExtra(Fixtures.KEY_CALLBACK, response);"
+        errorLine2="                                                   ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="244"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountAuthenticatorResponse#onResult`"
+        errorLine1="                response.onResult(result);"
+        errorLine2="                         ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="256"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="        if (!mAccountType.equals(account.type)) {"
+        errorLine2="                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="266"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="        if (account.name.startsWith(Fixtures.PREFIX_NAME_SUCCESS)) {"
+        errorLine2="            ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="274"
+            column="13"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="        } else if (account.name.startsWith(Fixtures.PREFIX_NAME_INTERVENE)) {"
+        errorLine2="                   ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="277"
+            column="20"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `AccountAuthenticatorResponse` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="            intent.putExtra(Fixtures.KEY_CALLBACK, response);"
+        errorLine2="                                                   ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="284"
+            column="52"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountAuthenticatorResponse#onResult`"
+        errorLine1="                response.onResult(result);"
+        errorLine2="                         ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="295"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="        if (!mAccountType.equals(account.type)) {"
+        errorLine2="                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAccountAuthenticator.java"
+            line="365"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `android.accounts.AccountAuthenticatorResponse#onResult`"
+        errorLine1="            response.onResult(result.getExtras());"
+        errorLine2="                     ~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestAuthenticatorActivity.java"
+            line="33"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Class requires API level 5 (current min is 1): `android.accounts.AbstractAccountAuthenticator`"
+        errorLine1="public class TestDefaultAuthenticator extends AbstractAccountAuthenticator {"
+        errorLine2="                                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestDefaultAuthenticator.java"
+            line="34"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 5 (current min is 1): `new android.accounts.AbstractAccountAuthenticator`"
+        errorLine1="        super(context);"
+        errorLine2="        ~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestDefaultAuthenticator.java"
+            line="39"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="        if (!mAccountType.equals(account.type)) {"
+        errorLine2="                                 ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestDefaultAuthenticator.java"
+            line="108"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#name`"
+        errorLine1="        result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);"
+        errorLine2="                                                          ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestDefaultAuthenticator.java"
+            line="112"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Field requires API level 5 (current min is 1): `android.accounts.Account#type`"
+        errorLine1="        result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);"
+        errorLine2="                                                          ~~~~~~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/TestDefaultAuthenticator.java"
+            line="113"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Cast from `Account` to `Parcelable` requires API level 5 (current min is 1)"
+        errorLine1="        out.writeParcelable(account, flags);"
+        errorLine2="                            ~~~~~~~">
+        <location
+            file="cts/tests/tests/accounts/common/src/android/accounts/cts/common/tx/UpdateCredentialsTx.java"
+            line="54"
+            column="29"/>
+    </issue>
+
+</issues>
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index 92e351a..2b68ac1 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -57,6 +57,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseLongArray;
+import android.view.KeyEvent;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -199,7 +200,7 @@
     private void launchSubActivity(Class<? extends Activity> clazz) {
         final Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.setClassName(mTargetPackage, clazz.getName());
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
         mContext.startActivity(intent);
         mUiDevice.wait(Until.hasObject(By.clazz(clazz)), TIMEOUT);
     }
@@ -1073,7 +1074,11 @@
             SparseArray<AggrAllEventsData> baseAggr = getAggrEventData();
 
             // First test -- put device to sleep and make sure we see this event.
-            mUiDevice.sleep();
+            if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+              mUiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP);
+            } else {
+              mUiDevice.sleep();
+            }
 
             // Do we have one event, going in to non-interactive mode?
             events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 1);
diff --git a/tests/tests/appop/Android.bp b/tests/tests/appop/Android.bp
index 48f9647..3d8c1d5 100644
--- a/tests/tests/appop/Android.bp
+++ b/tests/tests/appop/Android.bp
@@ -32,6 +32,7 @@
     header_libs: ["jni_headers"],
     shared_libs: [
         "libbinder",
+        "libpermission",
         "libutils",
         "liblog",
     ],
@@ -79,6 +80,7 @@
         "libnetdutils",
         "libnetworkstatsfactorytestjni",
         "libpackagelistparser",
+        "libpermission",
         "libpcre2",
         "libprocessgroup",
         "libselinux",
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Bar.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Bar.aidl
index 99ba6b5..2aaf974 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Bar.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Bar.aidl
@@ -2,13 +2,14 @@
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ByteEnum.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ByteEnum.aidl
index 16c6e1b..944573e 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ByteEnum.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ByteEnum.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ExtendableParcelable.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ExtendableParcelable.aidl
index 6646ba0..dbbb82c 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ExtendableParcelable.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ExtendableParcelable.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/FixedSize.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/FixedSize.aidl
index e7a32f2..f4908e2 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/FixedSize.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/FixedSize.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl
index ddb6e8b..5e260ff 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/Foo.aidl
@@ -2,13 +2,14 @@
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/GenericBar.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/GenericBar.aidl
index 1d9103b..7cab936 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/GenericBar.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/GenericBar.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
@@ -16,7 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package test_package;
-parcelable GenericBar {
+parcelable GenericBar<T> {
   int a;
   test_package.GenericFoo<int,test_package.Bar,test_package.IntEnum> shouldBeGenericFoo;
 }
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/GenericFoo.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/GenericFoo.aidl
index 9bc86c1..8929c65 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/GenericFoo.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/GenericFoo.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
@@ -16,7 +32,7 @@
 // later when a module using the interface is updated, e.g., Mainline modules.
 
 package test_package;
-parcelable GenericFoo {
+parcelable GenericFoo<T, U, V> {
   int a;
   int b;
 }
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/IEmpty.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/IEmpty.aidl
index 5a27b19..1717e58 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/IEmpty.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/IEmpty.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl
index 7cd58e7..020c24f 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/ITest.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/IntEnum.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/IntEnum.aidl
index f889ec4..92ce575 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/IntEnum.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/IntEnum.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/LongEnum.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/LongEnum.aidl
index b03c85c..4156557 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/LongEnum.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/LongEnum.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/MyExt.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/MyExt.aidl
index 3525284..c054eeb 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/MyExt.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/MyExt.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/RegularPolygon.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/RegularPolygon.aidl
index 8fdd8c84b..3eab12e 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/RegularPolygon.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/RegularPolygon.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl
index e7fc95c..767b5ae 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_test_interface/current/test_package/SimpleUnion.aidl
@@ -2,13 +2,14 @@
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/tests/tests/bionic/Android.build.copy.libs.mk b/tests/tests/bionic/Android.build.copy.libs.mk
index 8900a75..628eeed 100644
--- a/tests/tests/bionic/Android.build.copy.libs.mk
+++ b/tests/tests/bionic/Android.build.copy.libs.mk
@@ -22,6 +22,12 @@
   elftls_dlopen_ie_error_helper/elftls_dlopen_ie_error_helper \
   exec_linker_helper/exec_linker_helper \
   exec_linker_helper_lib.so \
+  heap_tagging_async_helper/heap_tagging_async_helper \
+  heap_tagging_disabled_helper/heap_tagging_disabled_helper \
+  heap_tagging_static_sync_helper/heap_tagging_static_sync_helper \
+  heap_tagging_static_async_helper/heap_tagging_static_async_helper \
+  heap_tagging_static_disabled_helper/heap_tagging_static_disabled_helper \
+  heap_tagging_sync_helper/heap_tagging_sync_helper \
   inaccessible_libs/libtestshared.so \
   inaccessible_libs/libtestshared.so \
   ld_config_test_helper/ld_config_test_helper \
diff --git a/tests/tests/carrierapi/Android.bp b/tests/tests/carrierapi/Android.bp
index 5f5b61d..32b10e5 100644
--- a/tests/tests/carrierapi/Android.bp
+++ b/tests/tests/carrierapi/Android.bp
@@ -21,8 +21,8 @@
     defaults: ["cts_defaults"],
     static_libs: [
         "androidx.test.uiautomator_uiautomator",
-        "ctstestrunner-axt",
         "compatibility-device-util-axt",
+        "ctstestrunner-axt",
         "junit",
         "truth-prebuilt",
     ],
@@ -34,8 +34,8 @@
         "general-tests",
     ],
     libs: [
-        "android.test.runner",
         "android.test.base",
+        "android.test.runner",
     ],
     // This  APK must be signed to match the test SIM's cert whitelist.
     // While "testkey" is the default, there are different per-device testkeys, so
diff --git a/tests/tests/carrierapi/AndroidTest.xml b/tests/tests/carrierapi/AndroidTest.xml
index 8d1174b..9b6d4fe 100644
--- a/tests/tests/carrierapi/AndroidTest.xml
+++ b/tests/tests/carrierapi/AndroidTest.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Carrier APIs test cases">
-  <option name="test-suite-tag" value="cts" />
+    <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java
index 91ba3fc..59794c5 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java
@@ -15,22 +15,19 @@
  */
 package android.carrierapi.cts;
 
-import static junit.framework.TestCase.assertEquals;
+import static com.google.common.truth.Truth.assertWithMessage;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
 
 import android.content.ContentResolver;
 import android.content.ContentValues;
-import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.provider.Telephony.Carriers;
 import android.util.Log;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
@@ -42,17 +39,16 @@
 import java.util.Map;
 
 /**
- * Build, install and run the tests by running the commands below:
- *  make cts -j64
- *  cts-tradefed run cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.ApnDatabaseTest
+ * Unit tests for the APN database exposed by {@link Carriers}.
+ *
+ * <p>Test using `atest CtsCarrierApiTestCases:ApnDatabaseTest` or `make cts -j64 && cts-tradefed
+ * run cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.ApnDatabaseTest`
  */
 @RunWith(AndroidJUnit4.class)
-public class ApnDatabaseTest {
+public class ApnDatabaseTest extends BaseCarrierApiTest {
     private static final String TAG = "ApnDatabaseTest";
 
     private ContentResolver mContentResolver;
-    private PackageManager mPackageManager;
-    private boolean mHasCellular;
 
     private static final String NAME = "carrierName";
     private static final String APN = "apn";
@@ -72,25 +68,28 @@
     private static final String NETWORK_TYPE_BITMASK = "0";
     private static final String BEARER = "0";
 
-    private static final Map<String, String> APN_MAP = new HashMap<String,String>() {{
-        put(Carriers.NAME, NAME);
-        put(Carriers.APN, APN);
-        put(Carriers.PROXY, PROXY);
-        put(Carriers.PORT, PORT);
-        put(Carriers.MMSC, MMSC);
-        put(Carriers.MMSPROXY, MMSPROXY);
-        put(Carriers.MMSPORT, MMSPORT);
-        put(Carriers.NUMERIC, NUMERIC);
-        put(Carriers.USER, USER);
-        put(Carriers.PASSWORD, PASSWORD);
-        put(Carriers.AUTH_TYPE, AUTH_TYPE);
-        put(Carriers.TYPE, TYPE);
-        put(Carriers.PROTOCOL, PROTOCOL);
-        put(Carriers.ROAMING_PROTOCOL, ROAMING_PROTOCOL);
-        put(Carriers.CARRIER_ENABLED, CARRIER_ENABLED);
-        put(Carriers.NETWORK_TYPE_BITMASK, NETWORK_TYPE_BITMASK);
-        put(Carriers.BEARER, BEARER);
-    }};
+    private static final Map<String, String> APN_MAP =
+            new HashMap<String, String>() {
+                {
+                    put(Carriers.NAME, NAME);
+                    put(Carriers.APN, APN);
+                    put(Carriers.PROXY, PROXY);
+                    put(Carriers.PORT, PORT);
+                    put(Carriers.MMSC, MMSC);
+                    put(Carriers.MMSPROXY, MMSPROXY);
+                    put(Carriers.MMSPORT, MMSPORT);
+                    put(Carriers.NUMERIC, NUMERIC);
+                    put(Carriers.USER, USER);
+                    put(Carriers.PASSWORD, PASSWORD);
+                    put(Carriers.AUTH_TYPE, AUTH_TYPE);
+                    put(Carriers.TYPE, TYPE);
+                    put(Carriers.PROTOCOL, PROTOCOL);
+                    put(Carriers.ROAMING_PROTOCOL, ROAMING_PROTOCOL);
+                    put(Carriers.CARRIER_ENABLED, CARRIER_ENABLED);
+                    put(Carriers.NETWORK_TYPE_BITMASK, NETWORK_TYPE_BITMASK);
+                    put(Carriers.BEARER, BEARER);
+                }
+            };
 
     // Faked network type bitmask and its compatible bearer bitmask.
     private static final int NETWORK_TYPE_BITMASK_NUMBER = 1 << (13 - 1);
@@ -98,29 +97,16 @@
 
     @Before
     public void setUp() throws Exception {
-        mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
-        mPackageManager = InstrumentationRegistry.getContext().getPackageManager();
-        // Checks whether the cellular stack should be running on this device.
-        mHasCellular = mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
-        if (!mHasCellular) {
-            Log.e(TAG, "No cellular support, all tests will be skipped.");
-        }
-    }
-
-    private void failMessage() {
-        fail("This test requires a SIM card with carrier privilege rule on it.\n" +
-                "Visit https://source.android.com/devices/tech/config/uicc.html");
+        mContentResolver = getContext().getContentResolver();
     }
 
     /**
-     * Test inserting, querying, updating and deleting values in carriers table.
-     * Verify that the inserted values match the result of the query and are deleted.
+     * Test inserting, querying, updating and deleting values in carriers table. Verify that the
+     * inserted values match the result of the query and are deleted.
      */
     @Test
     public void testValidCase() {
-        if (!mHasCellular) return;
         Uri uri = Carriers.CONTENT_URI;
-        // CONTENT_URI = Uri.parse("content://telephony/carriers");
         // Create A set of column_name/value pairs to add to the database.
         ContentValues contentValues = makeDefaultContentValues();
 
@@ -128,69 +114,84 @@
             // Insert the value into database.
             Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
             Uri newUri = mContentResolver.insert(uri, contentValues);
-            assertNotNull("Failed to insert to table", newUri);
+            assertWithMessage("Failed to insert to table").that(newUri).isNotNull();
 
             // Get the values in table.
             final String selection = Carriers.NUMERIC + "=?";
-            String[] selectionArgs = { NUMERIC };
+            String[] selectionArgs = {NUMERIC};
             String[] apnProjection = APN_MAP.keySet().toArray(new String[APN_MAP.size()]);
-            Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(apnProjection)
-                    + "\ntestInsertCarriers selection: " + selection
-                    + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
-            Cursor cursor = mContentResolver.query(
-                    uri, apnProjection, selection, selectionArgs, null);
+            Log.d(
+                    TAG,
+                    "testInsertCarriers query projection: "
+                            + Arrays.toString(apnProjection)
+                            + "\ntestInsertCarriers selection: "
+                            + selection
+                            + "\ntestInsertCarriers selectionArgs: "
+                            + Arrays.toString(selectionArgs));
+            Cursor cursor =
+                    mContentResolver.query(uri, apnProjection, selection, selectionArgs, null);
 
             // Verify that the inserted value match the results of the query
-            assertNotNull("Failed to query the table", cursor);
-            assertEquals("Unexpected number of APNs returned by cursor",
-                    1, cursor.getCount());
+            assertWithMessage("Failed to query the table").that(cursor).isNotNull();
+            assertWithMessage("Unexpected number of APNs returned by cursor")
+                    .that(cursor.getCount())
+                    .isEqualTo(1);
             cursor.moveToFirst();
-            for (Map.Entry<String, String> entry: APN_MAP.entrySet()) {
-                assertEquals(
-                        "Unexpected value returned by cursor",
-                        cursor.getString(cursor.getColumnIndex(entry.getKey())), entry.getValue());
+            for (Map.Entry<String, String> entry : APN_MAP.entrySet()) {
+                assertWithMessage("Unexpected value returned by cursor")
+                        .that(cursor.getString(cursor.getColumnIndex(entry.getKey())))
+                        .isEqualTo(entry.getValue());
             }
 
             // update the apn
             final String newApn = "newapn";
             Log.d(TAG, "Update the APN field to: " + newApn);
             contentValues.put(Carriers.APN, newApn);
-            final int updateCount = mContentResolver.update(uri, contentValues, selection,
-                    selectionArgs);
-            assertEquals("Unexpected number of rows updated", 1, updateCount);
+            final int updateCount =
+                    mContentResolver.update(uri, contentValues, selection, selectionArgs);
+            assertWithMessage("Unexpected number of rows updated").that(updateCount).isEqualTo(1);
 
             // Verify the updated value
             cursor = mContentResolver.query(uri, apnProjection, selection, selectionArgs, null);
-            assertNotNull("Failed to query the table", cursor);
-            assertEquals("Unexpected number of APNs returned by cursor", 1, cursor.getCount());
+            assertWithMessage("Failed to query the table").that(cursor).isNotNull();
+            assertWithMessage("Unexpected number of APNs returned by cursor")
+                    .that(cursor.getCount())
+                    .isEqualTo(1);
             cursor.moveToFirst();
-            assertEquals("Unexpected value returned by cursor",
-                    cursor.getString(cursor.getColumnIndex(Carriers.APN)), newApn);
+            assertWithMessage("Unexpected value returned by cursor")
+                    .that(cursor.getString(cursor.getColumnIndex(Carriers.APN)))
+                    .isEqualTo(newApn);
 
             // delete test content
             final String selectionToDelete = Carriers.NUMERIC + "=?";
-            String[] selectionArgsToDelete = { NUMERIC };
-            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
-                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
-            int numRowsDeleted = mContentResolver.delete(
-                    uri, selectionToDelete, selectionArgsToDelete);
-            assertEquals("Unexpected number of rows deleted",1, numRowsDeleted);
+            String[] selectionArgsToDelete = {NUMERIC};
+            Log.d(
+                    TAG,
+                    "testInsertCarriers deleting selection: "
+                            + selectionToDelete
+                            + "testInsertCarriers selectionArgs: "
+                            + Arrays.toString(selectionArgs));
+            int numRowsDeleted =
+                    mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
+            assertWithMessage("Unexpected number of rows deleted")
+                    .that(numRowsDeleted)
+                    .isEqualTo(1);
 
             // verify that deleted values are gone
             cursor = mContentResolver.query(uri, apnProjection, selection, selectionArgs, null);
-            assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+            assertWithMessage("Unexpected number of rows deleted")
+                    .that(cursor.getCount())
+                    .isEqualTo(0);
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
     @Test
     public void testQueryConflictCase() {
-        if (!mHasCellular) return;
         String invalidColumn = "random";
         Uri uri = Carriers.CONTENT_URI;
-        // CONTENT_URI = Uri.parse("content://telephony/carriers");
-        // Create A set of column_name/value pairs to add to the database.
+        // Create a set of column_name/value pairs to add to the database.
         ContentValues contentValues = new ContentValues();
         contentValues.put(Carriers.NAME, NAME);
         contentValues.put(Carriers.APN, APN);
@@ -202,50 +203,57 @@
             // Insert the value into database.
             Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
             Uri newUri = mContentResolver.insert(uri, contentValues);
-            assertNotNull("Failed to insert to table", newUri);
+            assertWithMessage("Failed to insert to table").that(newUri).isNotNull();
 
             // Try to get the value with invalid selection
-            final String[] testProjection =
-                    {
-                            Carriers.NAME,
-                            Carriers.APN,
-                            Carriers.PORT,
-                            Carriers.PROTOCOL,
-                            Carriers.NUMERIC,
-                    };
+            final String[] testProjection = {
+                Carriers.NAME, Carriers.APN, Carriers.PORT, Carriers.PROTOCOL, Carriers.NUMERIC,
+            };
             final String selection = invalidColumn + "=?";
-            String[] selectionArgs = { invalidColumn };
-            Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
-                    + "\ntestInsertCarriers selection: " + selection
-                    + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
-            Cursor cursor = mContentResolver.query(
-                    uri, testProjection, selection, selectionArgs, null);
-            assertNull("Failed to query the table",cursor);
+            String[] selectionArgs = {invalidColumn};
+            Log.d(
+                    TAG,
+                    "testInsertCarriers query projection: "
+                            + Arrays.toString(testProjection)
+                            + "\ntestInsertCarriers selection: "
+                            + selection
+                            + "\ntestInsertCarriers selectionArgs: "
+                            + Arrays.toString(selectionArgs));
+            Cursor cursor =
+                    mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
+            assertWithMessage("Failed to query the table").that(cursor).isNull();
 
             // delete test content
             final String selectionToDelete = Carriers.NAME + "=?";
-            String[] selectionArgsToDelete = { NAME };
-            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
-                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
-            int numRowsDeleted = mContentResolver.delete(
-                    uri, selectionToDelete, selectionArgsToDelete);
-            assertEquals("Unexpected number of rows deleted", 1, numRowsDeleted);
+            String[] selectionArgsToDelete = {NAME};
+            Log.d(
+                    TAG,
+                    "testInsertCarriers deleting selection: "
+                            + selectionToDelete
+                            + "testInsertCarriers selectionArgs: "
+                            + Arrays.toString(selectionArgs));
+            int numRowsDeleted =
+                    mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
+            assertWithMessage("Unexpected number of rows deleted")
+                    .that(numRowsDeleted)
+                    .isEqualTo(1);
 
             // verify that deleted values are gone
-            cursor = mContentResolver.query(
-                    uri, testProjection, selectionToDelete, selectionArgsToDelete, null);
-            assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+            cursor =
+                    mContentResolver.query(
+                            uri, testProjection, selectionToDelete, selectionArgsToDelete, null);
+            assertWithMessage("Unexpected number of rows deleted")
+                    .that(cursor.getCount())
+                    .isEqualTo(0);
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
     @Test
     public void testUpdateConflictCase() {
-        if (!mHasCellular) return;
         Uri uri = Carriers.CONTENT_URI;
-        // CONTENT_URI = Uri.parse("content://telephony/carriers");
-        // Create A set of column_name/value pairs to add to the database.
+        // Create a set of column_name/value pairs to add to the database.
         ContentValues contentValues = new ContentValues();
         contentValues.put(Carriers.NAME, NAME);
         contentValues.put(Carriers.APN, APN);
@@ -257,63 +265,69 @@
             // Insert the value into database.
             Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
             Uri newUri = mContentResolver.insert(uri, contentValues);
-            assertNotNull("Failed to insert to table", newUri);
+            assertWithMessage("Failed to insert to table").that(newUri).isNotNull();
 
             // Try to get the value with invalid selection
-            final String[] testProjection =
-                    {
-                            Carriers.NAME,
-                            Carriers.APN,
-                            Carriers.PORT,
-                            Carriers.PROTOCOL,
-                            Carriers.NUMERIC,
-                    };
+            final String[] testProjection = {
+                Carriers.NAME, Carriers.APN, Carriers.PORT, Carriers.PROTOCOL, Carriers.NUMERIC,
+            };
             String selection = Carriers.NAME + "=?";
-            String[] selectionArgs = { NAME };
-            Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
-                    + "\ntestInsertCarriers selection: " + selection
-                    + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
-            Cursor cursor = mContentResolver.query(
-                    uri, testProjection, selection, selectionArgs, null);
-            assertEquals("Unexpected number of APNs returned by cursor",
-                    1, cursor.getCount());
+            String[] selectionArgs = {NAME};
+            Log.d(
+                    TAG,
+                    "testInsertCarriers query projection: "
+                            + Arrays.toString(testProjection)
+                            + "\ntestInsertCarriers selection: "
+                            + selection
+                            + "\ntestInsertCarriers selectionArgs: "
+                            + Arrays.toString(selectionArgs));
+            Cursor cursor =
+                    mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
+            assertWithMessage("Unexpected number of APNs returned by cursor")
+                    .that(cursor.getCount())
+                    .isEqualTo(1);
 
             // Update the table with invalid column
             String invalidColumn = "random";
             contentValues.put(invalidColumn, invalidColumn);
-            try {
-                mContentResolver.update(uri, contentValues, selection, selectionArgs);
-                fail();
-            } catch (SQLiteException e) {
-                // Expected: If there's no such a column, an exception will be thrown and the
-                // Activity Manager will kill this process shortly.
-            }
+            // Expected: If there's no such a column, an exception will be thrown and
+            // ActivityManager will kill this process shortly.
+            assertThrows(
+                    SQLiteException.class,
+                    () -> mContentResolver.update(uri, contentValues, selection, selectionArgs));
 
             // delete test content
             final String selectionToDelete = Carriers.NAME + "=?";
-            String[] selectionArgsToDelete = { NAME };
-            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
-                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
-            int numRowsDeleted = mContentResolver.delete(
-                    uri, selectionToDelete, selectionArgsToDelete);
-            assertEquals("Unexpected number of rows deleted", 1, numRowsDeleted);
+            String[] selectionArgsToDelete = {NAME};
+            Log.d(
+                    TAG,
+                    "testInsertCarriers deleting selection: "
+                            + selectionToDelete
+                            + "testInsertCarriers selectionArgs: "
+                            + Arrays.toString(selectionArgs));
+            int numRowsDeleted =
+                    mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
+            assertWithMessage("Unexpected number of rows deleted")
+                    .that(numRowsDeleted)
+                    .isEqualTo(1);
 
             // verify that deleted values are gone
-            cursor = mContentResolver.query(
-                    uri, testProjection, selectionToDelete, selectionArgsToDelete, null);
-            assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+            cursor =
+                    mContentResolver.query(
+                            uri, testProjection, selectionToDelete, selectionArgsToDelete, null);
+            assertWithMessage("Unexpected number of rows deleted")
+                    .that(cursor.getCount())
+                    .isEqualTo(0);
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
     @Test
     public void testDeleteConflictCase() {
-        if (!mHasCellular) return;
         String invalidColumn = "random";
         Uri uri = Carriers.CONTENT_URI;
-        // CONTENT_URI = Uri.parse("content://telephony/carriers");
-        // Create A set of column_name/value pairs to add to the database.
+        // Create a set of column_name/value pairs to add to the database.
         ContentValues contentValues = new ContentValues();
         contentValues.put(Carriers.NAME, NAME);
         contentValues.put(Carriers.APN, APN);
@@ -325,68 +339,84 @@
             // Insert the value into database.
             Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
             Uri newUri = mContentResolver.insert(uri, contentValues);
-            assertNotNull("Failed to insert to table", newUri);
+            assertWithMessage("Failed to insert to table").that(newUri).isNotNull();
 
             // Get the values in table.
-            final String[] testProjection =
-                    {
-                            Carriers.NAME,
-                            Carriers.APN,
-                            Carriers.PORT,
-                            Carriers.PROTOCOL,
-                            Carriers.NUMERIC,
-                    };
+            final String[] testProjection = {
+                Carriers.NAME, Carriers.APN, Carriers.PORT, Carriers.PROTOCOL, Carriers.NUMERIC,
+            };
             String selection = Carriers.NAME + "=?";
-            String[] selectionArgs = { NAME };
-            Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
-                    + "\ntestInsertCarriers selection: " + selection
-                    + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
-            Cursor cursor = mContentResolver.query(
-                    uri, testProjection, selection, selectionArgs, null);
-            assertEquals("Unexpected number of APNs returned by cursor", 1, cursor.getCount());
+            String[] selectionArgs = {NAME};
+            Log.d(
+                    TAG,
+                    "testInsertCarriers query projection: "
+                            + Arrays.toString(testProjection)
+                            + "\ntestInsertCarriers selection: "
+                            + selection
+                            + "\ntestInsertCarriers selectionArgs: "
+                            + Arrays.toString(selectionArgs));
+            Cursor cursor =
+                    mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
+            assertWithMessage("Unexpected number of APNs returned by cursor")
+                    .that(cursor.getCount())
+                    .isEqualTo(1);
 
             // try to delete with invalid selection
-            String selectionToDelete = invalidColumn + "=?";
-            String[] selectionArgsToDelete = { invalidColumn };
-            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
-                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+            String invalidSelectionToDelete = invalidColumn + "=?";
+            String[] invalidSelectionArgsToDelete = {invalidColumn};
+            Log.d(
+                    TAG,
+                    "testInsertCarriers deleting selection: "
+                            + invalidSelectionToDelete
+                            + "testInsertCarriers selectionArgs: "
+                            + Arrays.toString(selectionArgs));
 
-            try {
-                mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
-                fail();
-            } catch (SQLiteException e) {
-                // Expected: If there's no such a column, an exception will be thrown and the
-                // Activity Manager will kill this process shortly.
-            }
+            // Expected: If there's no such a column, an exception will be thrown and
+            // ActivityManager will kill this process shortly.
+            assertThrows(
+                    SQLiteException.class,
+                    () ->
+                            mContentResolver.delete(
+                                    uri, invalidSelectionToDelete, invalidSelectionArgsToDelete));
 
             // verify that deleted value is still there
             selection = Carriers.NAME + "=?";
             selectionArgs[0] = NAME;
             cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
-            assertEquals("Unexpected number of APNs returned by cursor", 1, cursor.getCount());
+            assertWithMessage("Unexpected number of APNs returned by cursor")
+                    .that(cursor.getCount())
+                    .isEqualTo(1);
 
             // delete test content
-            selectionToDelete = Carriers.NAME + "=?";
-            selectionArgsToDelete[0] = NAME;
-            Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
-                    + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
-            int numRowsDeleted = mContentResolver.delete(
-                    uri, selectionToDelete, selectionArgsToDelete);
-            assertEquals("Unexpected number of rows deleted", 1, numRowsDeleted);
+            String selectionToDelete = Carriers.NAME + "=?";
+            String[] selectionArgsToDelete = {NAME};
+            Log.d(
+                    TAG,
+                    "testInsertCarriers deleting selection: "
+                            + selectionToDelete
+                            + "testInsertCarriers selectionArgs: "
+                            + Arrays.toString(selectionArgs));
+            int numRowsDeleted =
+                    mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
+            assertWithMessage("Unexpected number of rows deleted")
+                    .that(numRowsDeleted)
+                    .isEqualTo(1);
 
             // verify that deleted values are gone
             cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
-            assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+            assertWithMessage("Unexpected number of rows deleted")
+                    .that(cursor.getCount())
+                    .isEqualTo(0);
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
     private ContentValues makeDefaultContentValues() {
         ContentValues contentValues = new ContentValues();
-        for (Map.Entry<String, String> entry: APN_MAP.entrySet()) {
+        for (Map.Entry<String, String> entry : APN_MAP.entrySet()) {
             contentValues.put(entry.getKey(), entry.getValue());
         }
         return contentValues;
     }
-}
\ No newline at end of file
+}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/BaseCarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/BaseCarrierApiTest.java
new file mode 100644
index 0000000..b0b6504
--- /dev/null
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/BaseCarrierApiTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.carrierapi.cts;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.FeatureUtil;
+
+import org.junit.Before;
+
+/**
+ * Common test base to ensure uniform preconditions checking. This class will check for:
+ *
+ * <ol>
+ *   <li>{@link android.content.pm.PackageManager#FEATURE_TELEPHONY}
+ *   <li>A SIM that grants us carrier privileges is currently active in the device
+ * </ol>
+ *
+ * Just inherit from this class when writing your test, then you are able to assume in the subclass
+ * {@code Before} method that preconditions have all passed. The setup and test methods will not be
+ * executed if preconditions are not met.
+ */
+public abstract class BaseCarrierApiTest {
+    protected static final String NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE =
+            "This test requires a SIM card with carrier privilege rules on it.\n"
+                    + "Visit https://source.android.com/devices/tech/config/uicc.html";
+
+    protected Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getTargetContext();
+    }
+
+    private boolean mPreconditionsSatisfied = false;
+
+    protected boolean werePreconditionsSatisfied() {
+        return mPreconditionsSatisfied;
+    }
+
+    /**
+     * Subclasses do NOT need to explicitly call or override this method. Per the JUnit docs, a
+     * superclass {@code Before} method always executes before a subclass {@code Before} method.
+     *
+     * <p>If preconditions fail, neither the subclass {@code Before} method(s) nor the actual {@code
+     * Test} method will execute, but {@code After} methods will still execute. If a subclass does
+     * work in an {@code After} method, then it should first check {@link
+     * #werePreconditionsSatisfied} and return early without doing any work if it's {@code false}.
+     */
+    @Before
+    public void ensurePreconditionsMet() {
+        mPreconditionsSatisfied = false;
+        // Bail out if no cellular support.
+        assumeTrue(
+                "No cellular support, CarrierAPI."
+                        + getClass().getSimpleName()
+                        + " cases will be skipped",
+                FeatureUtil.hasTelephony());
+        // We must run with carrier privileges.
+        assertWithMessage(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE)
+                .that(getContext().getSystemService(TelephonyManager.class).hasCarrierPrivileges())
+                .isTrue();
+        mPreconditionsSatisfied = true;
+    }
+}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/BugreportManagerTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/BugreportManagerTest.java
index 820c27a..f87bb3f 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/BugreportManagerTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/BugreportManagerTest.java
@@ -16,21 +16,19 @@
 
 package android.carrierapi.cts;
 
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
 
-import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.BugreportManager;
 import android.os.BugreportManager.BugreportCallback;
 import android.os.BugreportParams;
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.SystemUserOnly;
-import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -41,6 +39,8 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
+import com.android.compatibility.common.util.PollingCheck;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -64,11 +64,15 @@
  */
 @SystemUserOnly(reason = "BugreportManager requires calls to originate from the primary user")
 @RunWith(AndroidJUnit4.class)
-public class BugreportManagerTest {
+public class BugreportManagerTest extends BaseCarrierApiTest {
     private static final String TAG = "BugreportManagerTest";
 
+    // See BugreportManagerServiceImpl#BUGREPORT_SERVICE.
+    private static final String BUGREPORT_SERVICE = "bugreportd";
+
     private static final long BUGREPORT_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
     private static final long UIAUTOMATOR_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10);
+    private static final long ONEWAY_CALLBACK_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5);
     // This value is defined in dumpstate.cpp:TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS. Because the
     // consent dialog is so large and important, the user *must* be given at least 2 minutes to read
     // it before it times out.
@@ -78,7 +82,6 @@
 
     @Rule public TestName name = new TestName();
 
-    private TelephonyManager mTelephonyManager;
     private BugreportManager mBugreportManager;
     private File mBugreportFile;
     private ParcelFileDescriptor mBugreportFd;
@@ -87,20 +90,9 @@
 
     @Before
     public void setUp() throws Exception {
-        Context context = InstrumentationRegistry.getContext();
-        // Bail out if no cellular support.
-        assumeTrue(
-                "No cellular support, CarrierAPI.BugreportManagerTest cases will be skipped",
-                context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY));
-        // Fail the test if we don't have carrier privileges.
-        mTelephonyManager = context.getSystemService(TelephonyManager.class);
-        assertWithMessage(
-                        "This test requires a SIM card with carrier privilege rules on it.\n"
-                                + "Visit https://source.android.com/devices/tech/config/uicc.html")
-                .that(mTelephonyManager.hasCarrierPrivileges())
-                .isTrue();
-        mBugreportManager = context.getSystemService(BugreportManager.class);
+        mBugreportManager = getContext().getSystemService(BugreportManager.class);
 
+        killCurrentBugreportIfRunning();
         mBugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
         mBugreportFd = parcelFd(mBugreportFile);
         // Should never be written for anything a carrier app can trigger; several tests assert that
@@ -111,8 +103,11 @@
 
     @After
     public void tearDown() throws Exception {
+        if (!werePreconditionsSatisfied()) return;
+
         FileUtils.closeQuietly(mBugreportFd);
         FileUtils.closeQuietly(mScreenshotFd);
+        killCurrentBugreportIfRunning();
     }
 
     @Test
@@ -178,6 +173,13 @@
 
         // Attempting to start a second report immediately gets us a concurrency error.
         mBugreportManager.startConnectivityBugreport(bugreportFd2, Runnable::run, callback2);
+        // Since IDumpstateListener#onError is oneway, it's not guaranteed that binder has delivered
+        // the callback to us yet, even though BugreportManagerServiceImpl sends it before returning
+        // from #startBugreport.
+        PollingCheck.check(
+                "No terminal callback received for the second bugreport",
+                ONEWAY_CALLBACK_TIMEOUT_MILLIS,
+                callback2::isDone);
         assertThat(callback2.getErrorCode())
                 .isEqualTo(BugreportCallback.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
 
@@ -334,6 +336,14 @@
         }
     }
 
+    /**
+     * Kills the current bugreport if one is in progress to prevent failing test cases from
+     * cascading into other cases and causing flakes.
+     */
+    private static void killCurrentBugreportIfRunning() throws Exception {
+        runShellCommand("setprop ctl.stop " + BUGREPORT_SERVICE);
+    }
+
     /** Allow/deny the consent dialog to sharing bugreport data, or just check existence. */
     private enum ConsentReply {
         // Touch the positive button.
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index da46842..1f672db 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -22,9 +22,13 @@
 import static android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL;
 import static android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
+import static com.android.compatibility.common.util.UiccUtil.UiccCertificate.CTS_UICC_2021;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentProviderClient;
@@ -32,8 +36,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -55,15 +57,21 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
 import android.util.Base64;
 import android.util.Log;
 
-import com.android.compatibility.common.util.ShellIdentityUtils;
+import androidx.test.runner.AndroidJUnit4;
 
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.UiccUtil;
+
+import com.google.common.collect.Range;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -78,23 +86,27 @@
 
 import javax.annotation.Nonnull;
 
+/**
+ * Unit tests for various carrier-related APIs.
+ *
+ * <p>Test using `atest CtsCarrierApiTestCases:CarrierApiTest` or `make cts -j64 && cts-tradefed run
+ * cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.CarrierApiTest`
+ */
 // TODO(b/130187425): Split CarrierApiTest apart to have separate test classes for functionality
-public class CarrierApiTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class CarrierApiTest extends BaseCarrierApiTest {
     private static final String TAG = "CarrierApiTest";
+
     private TelephonyManager mTelephonyManager;
     private CarrierConfigManager mCarrierConfigManager;
-    private PackageManager mPackageManager;
     private SubscriptionManager mSubscriptionManager;
     private ContentProviderClient mVoicemailProvider;
     private ContentProviderClient mStatusProvider;
     private Uri mVoicemailContentUri;
     private Uri mStatusContentUri;
-    private boolean hasCellular;
     private String selfPackageName;
-    private String selfCertHash;
     private HandlerThread mListenerThread;
 
-    private static final String FiDevCert = "24EB92CBB156B280FA4E1429A6ECEEB6E5C1BFE4";
     // The minimum allocatable logical channel number, per TS 102 221 Section 11.1.17.1
     private static final int MIN_LOGICAL_CHANNEL = 1;
     // The maximum allocatable logical channel number in the standard range, per TS 102 221 Section
@@ -163,37 +175,26 @@
 
     private static final int DSDS_PHONE_COUNT = 2;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mPackageManager = getContext().getPackageManager();
-        mTelephonyManager = (TelephonyManager)
-                getContext().getSystemService(Context.TELEPHONY_SERVICE);
-        hasCellular = hasCellular();
-        if (!hasCellular) {
-            Log.e(TAG, "No cellular support, all tests will be skipped.");
-            return;
-        }
-
-        mCarrierConfigManager = (CarrierConfigManager)
-                getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        mSubscriptionManager = (SubscriptionManager)
-                getContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
-        selfPackageName = getContext().getPackageName();
-        selfCertHash = getCertHash(selfPackageName);
+    @Before
+    public void setUp() throws Exception {
+        Context context = getContext();
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
+        mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
+        selfPackageName = context.getPackageName();
         mVoicemailContentUri = VoicemailContract.Voicemails.buildSourceUri(selfPackageName);
-        mVoicemailProvider = getContext().getContentResolver()
-                .acquireContentProviderClient(mVoicemailContentUri);
+        mVoicemailProvider =
+                context.getContentResolver().acquireContentProviderClient(mVoicemailContentUri);
         mStatusContentUri = VoicemailContract.Status.buildSourceUri(selfPackageName);
-        mStatusProvider = getContext().getContentResolver()
-                .acquireContentProviderClient(mStatusContentUri);
+        mStatusProvider =
+                context.getContentResolver().acquireContentProviderClient(mStatusContentUri);
         mListenerThread = new HandlerThread("CarrierApiTest");
         mListenerThread.start();
     }
 
-    @Override
+    @After
     public void tearDown() throws Exception {
-        if (!hasCellular) return;
+        if (!werePreconditionsSatisfied()) return;
 
         mListenerThread.quit();
         try {
@@ -202,68 +203,34 @@
         } catch (Exception e) {
             Log.w(TAG, "Failed to clean up voicemail tables in tearDown", e);
         }
-        super.tearDown();
     }
 
-    /**
-     * Checks whether the cellular stack should be running on this device.
-     */
-    private boolean hasCellular() {
-        return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) &&
-                mTelephonyManager.getPhoneCount() > 0;
-    }
-
-    private boolean isSimCardPresent() {
-        return mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_ABSENT;
-    }
-
-    private String getCertHash(String pkgName) {
-        try {
-            PackageInfo pInfo = mPackageManager.getPackageInfo(pkgName,
-                    PackageManager.GET_SIGNATURES | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
-            MessageDigest md = MessageDigest.getInstance("SHA-1");
-            return bytesToHexString(md.digest(pInfo.signatures[0].toByteArray()));
-        } catch (PackageManager.NameNotFoundException ex) {
-            Log.e(TAG, pkgName + " not found", ex);
-        } catch (NoSuchAlgorithmException ex) {
-            Log.e(TAG, "Algorithm SHA1 is not found.");
-        }
-        return "";
-    }
-
-    private void failMessage() {
-        if (FiDevCert.equalsIgnoreCase(selfCertHash)) {
-            fail("This test requires a Project Fi SIM card.");
-        } else {
-            fail("This test requires a SIM card with carrier privilege rule on it.\n" +
-                 "Cert hash: " + selfCertHash + "\n" +
-                 "Visit https://source.android.com/devices/tech/config/uicc.html");
-        }
-    }
-
+    @Test
     public void testSimCardPresent() {
-        if (!hasCellular) return;
-        assertTrue("This test requires SIM card.", isSimCardPresent());
+        assertWithMessage("This test requires a SIM card")
+                .that(mTelephonyManager.getSimState())
+                .isNotEqualTo(TelephonyManager.SIM_STATE_ABSENT);
     }
 
+    @Test
     public void testHasCarrierPrivileges() {
-        if (!hasCellular) return;
-        if (!mTelephonyManager.hasCarrierPrivileges()) {
-            failMessage();
-        }
+        assertWithMessage(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE)
+                .that(mTelephonyManager.hasCarrierPrivileges())
+                .isTrue();
     }
 
     private static void assertUpdateAvailableNetworkSuccess(int value) {
-        assertEquals(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS, value);
+        assertThat(value).isEqualTo(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
     }
 
     private static void assertUpdateAvailableNetworkNoOpportunisticSubAvailable(int value) {
-        assertEquals(
-                TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE, value);
+        assertThat(value)
+                .isEqualTo(
+                        TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
     }
 
     private static void assertSetOpportunisticSubSuccess(int value) {
-        assertEquals(TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS, value);
+        assertThat(value).isEqualTo(TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS);
     }
 
     private int getFirstActivateCarrierPrivilegedSubscriptionId() {
@@ -272,8 +239,8 @@
                 mSubscriptionManager.getActiveSubscriptionInfoList();
         if (subscriptionInfos != null) {
             for (SubscriptionInfo info : subscriptionInfos) {
-                TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(
-                        info.getSubscriptionId());
+                TelephonyManager telephonyManager =
+                        mTelephonyManager.createForSubscriptionId(info.getSubscriptionId());
                 if (telephonyManager.hasCarrierPrivileges()) {
                     subId = info.getSubscriptionId();
                     return subId;
@@ -283,15 +250,12 @@
         return subId;
     }
 
+    @Test
     public void testUpdateAvailableNetworksWithCarrierPrivilege() {
-        if (!hasCellular) return;
-
         int subIdWithCarrierPrivilege = getFirstActivateCarrierPrivilegedSubscriptionId();
-        int activeSubscriptionInfoCount = ShellIdentityUtils.invokeMethodWithShellPermissions(
-                mSubscriptionManager, (tm) -> tm.getActiveSubscriptionInfoCount());
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
-            return;
-        }
+        int activeSubscriptionInfoCount =
+                ShellIdentityUtils.invokeMethodWithShellPermissions(
+                        mSubscriptionManager, (tm) -> tm.getActiveSubscriptionInfoCount());
         if (mTelephonyManager.getPhoneCount() == 1) {
             return;
         }
@@ -311,42 +275,52 @@
         List<String> mccMncs = new ArrayList<String>();
         List<Integer> bands = new ArrayList<Integer>();
         List<AvailableNetworkInfo> availableNetworkInfos = new ArrayList<AvailableNetworkInfo>();
-        Consumer<Integer> callbackSuccess =
-                CarrierApiTest::assertUpdateAvailableNetworkSuccess;
+        Consumer<Integer> callbackSuccess = CarrierApiTest::assertUpdateAvailableNetworkSuccess;
         Consumer<Integer> callbackNoOpportunisticSubAvailable =
                 CarrierApiTest::assertUpdateAvailableNetworkNoOpportunisticSubAvailable;
         Consumer<Integer> setOpCallbackSuccess = CarrierApiTest::assertSetOpportunisticSubSuccess;
-        if (subscriptionInfoList == null || subscriptionInfoList.size() == 0
+        if (subscriptionInfoList == null
+                || subscriptionInfoList.size() == 0
                 || !mSubscriptionManager.isActiveSubscriptionId(
                         subscriptionInfoList.get(0).getSubscriptionId())) {
             try {
-                AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(
-                        subIdWithCarrierPrivilege, AvailableNetworkInfo.PRIORITY_HIGH, mccMncs,
-                        bands);
+                AvailableNetworkInfo availableNetworkInfo =
+                        new AvailableNetworkInfo(
+                                subIdWithCarrierPrivilege,
+                                AvailableNetworkInfo.PRIORITY_HIGH,
+                                mccMncs,
+                                bands);
                 availableNetworkInfos.add(availableNetworkInfo);
                 // Call updateAvailableNetworks without opportunistic subscription.
                 // callbackNoOpportunisticSubAvailable is expected to be triggered
                 // and the return value will be checked against
                 // UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE
-                mTelephonyManager.updateAvailableNetworks(availableNetworkInfos,
-                        AsyncTask.SERIAL_EXECUTOR, callbackNoOpportunisticSubAvailable);
+                mTelephonyManager.updateAvailableNetworks(
+                        availableNetworkInfos,
+                        AsyncTask.SERIAL_EXECUTOR,
+                        callbackNoOpportunisticSubAvailable);
             } finally {
                 // clear all the operations at the end of test.
                 availableNetworkInfos.clear();
-                mTelephonyManager.updateAvailableNetworks(availableNetworkInfos,
-                        AsyncTask.SERIAL_EXECUTOR, callbackNoOpportunisticSubAvailable);
+                mTelephonyManager.updateAvailableNetworks(
+                        availableNetworkInfos,
+                        AsyncTask.SERIAL_EXECUTOR,
+                        callbackNoOpportunisticSubAvailable);
             }
         } else {
             // This is case of DSDS phone, one active opportunistic subscription and one
             // active primary subscription.
             int resultSubId;
             try {
-                AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(
-                        subscriptionInfoList.get(0).getSubscriptionId(),
-                        AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands);
+                AvailableNetworkInfo availableNetworkInfo =
+                        new AvailableNetworkInfo(
+                                subscriptionInfoList.get(0).getSubscriptionId(),
+                                AvailableNetworkInfo.PRIORITY_HIGH,
+                                mccMncs,
+                                bands);
                 availableNetworkInfos.add(availableNetworkInfo);
-                mTelephonyManager.updateAvailableNetworks(availableNetworkInfos,
-                        AsyncTask.SERIAL_EXECUTOR, callbackSuccess);
+                mTelephonyManager.updateAvailableNetworks(
+                        availableNetworkInfos, AsyncTask.SERIAL_EXECUTOR, callbackSuccess);
                 // wait for the data change to take effect
                 waitForMs(500);
                 // Call setPreferredData and reconfirm with getPreferred data
@@ -357,19 +331,21 @@
                 // wait for the data change to take effect
                 waitForMs(500);
                 resultSubId = mTelephonyManager.getPreferredOpportunisticDataSubscription();
-                assertEquals(preferSubId, resultSubId);
+                assertThat(resultSubId).isEqualTo(preferSubId);
             } finally {
                 // clear all the operations at the end of test.
                 availableNetworkInfos.clear();
-                mTelephonyManager.updateAvailableNetworks(availableNetworkInfos,
-                        AsyncTask.SERIAL_EXECUTOR, callbackSuccess);
+                mTelephonyManager.updateAvailableNetworks(
+                        availableNetworkInfos, AsyncTask.SERIAL_EXECUTOR, callbackSuccess);
                 waitForMs(500);
                 mTelephonyManager.setPreferredOpportunisticDataSubscription(
-                        SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false,
-                        AsyncTask.SERIAL_EXECUTOR, callbackSuccess);
+                        SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                        false,
+                        AsyncTask.SERIAL_EXECUTOR,
+                        callbackSuccess);
                 waitForMs(500);
                 resultSubId = mTelephonyManager.getPreferredOpportunisticDataSubscription();
-                assertEquals(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, resultSubId);
+                assertThat(resultSubId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
             }
         }
     }
@@ -382,107 +358,145 @@
         }
     }
 
+    @Test
     public void testGetIccAuthentication() {
         // EAP-SIM rand is 16 bytes.
         String base64Challenge = "ECcTqwuo6OfY8ddFRboD9WM=";
         String base64Challenge2 = "EMNxjsFrPCpm+KcgCmQGnwQ=";
-        if (!hasCellular) return;
+
         try {
-            assertNull("getIccAuthentication should return null for empty data.",
-                    mTelephonyManager.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
-                    TelephonyManager.AUTHTYPE_EAP_AKA, ""));
-            String response = mTelephonyManager.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
-                    TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge);
-            assertTrue("Response to EAP-SIM Challenge must not be Null.", response != null);
+            assertWithMessage("getIccAuthentication should return null for empty data.")
+                    .that(
+                            mTelephonyManager.getIccAuthentication(
+                                    TelephonyManager.APPTYPE_USIM,
+                                    TelephonyManager.AUTHTYPE_EAP_AKA,
+                                    ""))
+                    .isNull();
+            String response =
+                    mTelephonyManager.getIccAuthentication(
+                            TelephonyManager.APPTYPE_USIM,
+                            TelephonyManager.AUTHTYPE_EAP_SIM,
+                            base64Challenge);
+            assertWithMessage("Response to EAP-SIM Challenge must not be Null.")
+                    .that(response)
+                    .isNotNull();
             // response is base64 encoded. After decoding, the value should be:
             // 1 length byte + SRES(4 bytes) + 1 length byte + Kc(8 bytes)
             byte[] result = android.util.Base64.decode(response, android.util.Base64.DEFAULT);
-            assertTrue("Result length must be 14 bytes.", 14 == result.length);
-            String response2 = mTelephonyManager.getIccAuthentication(TelephonyManager.APPTYPE_USIM,
-                    TelephonyManager.AUTHTYPE_EAP_SIM, base64Challenge2);
-            assertTrue("Two responses must be different.", !response.equals(response2));
+            assertThat(result).hasLength(14);
+            String response2 =
+                    mTelephonyManager.getIccAuthentication(
+                            TelephonyManager.APPTYPE_USIM,
+                            TelephonyManager.AUTHTYPE_EAP_SIM,
+                            base64Challenge2);
+            assertWithMessage("Two responses must be different")
+                    .that(response)
+                    .isNotEqualTo(response2);
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
+    @Test
     @SystemUserOnly(reason = "b/177921545, broadcast sent only to primary user")
     public void testSendDialerSpecialCode() {
-        if (!hasCellular) return;
-        try {
-            IntentReceiver intentReceiver = new IntentReceiver();
-            final IntentFilter intentFilter = new IntentFilter();
-            intentFilter.addAction(Telephony.Sms.Intents.SECRET_CODE_ACTION);
-            intentFilter.addDataScheme("android_secret_code");
-            getContext().registerReceiver(intentReceiver, intentFilter);
+        IntentReceiver intentReceiver = new IntentReceiver();
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Telephony.Sms.Intents.SECRET_CODE_ACTION);
+        intentFilter.addDataScheme("android_secret_code");
 
+        Context context = getContext();
+        context.registerReceiver(intentReceiver, intentFilter);
+        try {
             mTelephonyManager.sendDialerSpecialCode("4636");
-            assertTrue("Did not receive expected Intent: " +
-                    Telephony.Sms.Intents.SECRET_CODE_ACTION,
-                    intentReceiver.waitForReceive());
+            assertWithMessage(
+                            "Did not receive expected Intent: "
+                                    + Telephony.Sms.Intents.SECRET_CODE_ACTION)
+                    .that(intentReceiver.waitForReceive())
+                    .isTrue();
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         } catch (InterruptedException e) {
             Log.d(TAG, "Broadcast receiver wait was interrupted.");
+        } finally {
+            context.unregisterReceiver(intentReceiver);
         }
     }
 
+    @Test
     public void testSubscriptionInfoListing() {
-        if (!hasCellular) return;
         try {
-            assertTrue("getActiveSubscriptionInfoCount() should be non-zero",
-                    mSubscriptionManager.getActiveSubscriptionInfoCount() > 0);
+            assertThat(mSubscriptionManager.getActiveSubscriptionInfoCount()).isGreaterThan(0);
             List<SubscriptionInfo> subInfoList =
                     mSubscriptionManager.getActiveSubscriptionInfoList();
-            assertNotNull("getActiveSubscriptionInfoList() returned null", subInfoList);
-            assertFalse("getActiveSubscriptionInfoList() returned an empty list",
-                    subInfoList.isEmpty());
+            assertWithMessage("getActiveSubscriptionInfoList() returned null")
+                    .that(subInfoList)
+                    .isNotNull();
+            assertWithMessage("getActiveSubscriptionInfoList() returned an empty list")
+                    .that(subInfoList)
+                    .isNotEmpty();
             for (SubscriptionInfo info : subInfoList) {
                 TelephonyManager tm =
                         mTelephonyManager.createForSubscriptionId(info.getSubscriptionId());
-                assertTrue("getActiveSubscriptionInfoList() returned an inaccessible subscription",
-                        tm.hasCarrierPrivileges());
+                assertWithMessage(
+                                "getActiveSubscriptionInfoList() returned an inaccessible"
+                                        + " subscription")
+                        .that(tm.hasCarrierPrivileges())
+                        .isTrue();
 
                 // Check other APIs to make sure they are accessible and return consistent info.
                 SubscriptionInfo infoForSlot =
                         mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(
                                 info.getSimSlotIndex());
-                assertNotNull("getActiveSubscriptionInfoForSimSlotIndex() returned null",
-                        infoForSlot);
-                assertEquals(
-                        "getActiveSubscriptionInfoForSimSlotIndex() returned inconsistent info",
-                        info.getSubscriptionId(), infoForSlot.getSubscriptionId());
+                assertWithMessage("getActiveSubscriptionInfoForSimSlotIndex() returned null")
+                        .that(infoForSlot)
+                        .isNotNull();
+                assertWithMessage(
+                                "getActiveSubscriptionInfoForSimSlotIndex() returned inconsistent"
+                                        + " info")
+                        .that(infoForSlot.getSubscriptionId())
+                        .isEqualTo(info.getSubscriptionId());
 
                 SubscriptionInfo infoForSubId =
                         mSubscriptionManager.getActiveSubscriptionInfo(info.getSubscriptionId());
-                assertNotNull("getActiveSubscriptionInfo() returned null", infoForSubId);
-                assertEquals("getActiveSubscriptionInfo() returned inconsistent info",
-                        info.getSubscriptionId(), infoForSubId.getSubscriptionId());
+                assertWithMessage("getActiveSubscriptionInfo() returned null")
+                        .that(infoForSubId)
+                        .isNotNull();
+                assertWithMessage("getActiveSubscriptionInfo() returned inconsistent info")
+                        .that(infoForSubId.getSubscriptionId())
+                        .isEqualTo(info.getSubscriptionId());
             }
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
+    @Test
     public void testCarrierConfigIsAccessible() {
-        if (!hasCellular) return;
         try {
             PersistableBundle bundle = mCarrierConfigManager.getConfig();
-            assertNotNull("CarrierConfigManager#getConfig() returned null", bundle);
-            assertFalse("CarrierConfigManager#getConfig() returned empty bundle", bundle.isEmpty());
+            assertWithMessage("CarrierConfigManager#getConfig() returned null")
+                    .that(bundle)
+                    .isNotNull();
+            assertWithMessage("CarrierConfigManager#getConfig() returned empty bundle")
+                    .that(bundle.isEmpty())
+                    .isFalse();
 
             int subId = SubscriptionManager.getDefaultSubscriptionId();
             bundle = mCarrierConfigManager.getConfigForSubId(subId);
-            assertNotNull("CarrierConfigManager#getConfigForSubId() returned null", bundle);
-            assertFalse("CarrierConfigManager#getConfigForSubId() returned empty bundle",
-                    bundle.isEmpty());
+            assertWithMessage("CarrierConfigManager#getConfigForSubId() returned null")
+                    .that(bundle)
+                    .isNotNull();
+            assertWithMessage("CarrierConfigManager#getConfigForSubId() returned empty bundle")
+                    .that(bundle.isEmpty())
+                    .isFalse();
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
+    @Test
     public void testTelephonyApisAreAccessible() {
-        if (!hasCellular) return;
         // The following methods may return any value depending on the state of the device. Simply
         // call them to make sure they do not throw any exceptions. Methods that return a device
         // identifier will be accessible to apps with carrier privileges in Q, but this may change
@@ -507,54 +521,65 @@
             mTelephonyManager.getManualNetworkSelectionPlmn();
             mTelephonyManager.setForbiddenPlmns(new ArrayList<String>());
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
+    @Test
     public void testVoicemailTableIsAccessible() throws Exception {
-        if (!hasCellular) return;
         ContentValues value = new ContentValues();
         value.put(VoicemailContract.Voicemails.NUMBER, "0123456789");
         value.put(VoicemailContract.Voicemails.SOURCE_PACKAGE, selfPackageName);
         try {
             Uri uri = mVoicemailProvider.insert(mVoicemailContentUri, value);
-            assertNotNull(uri);
-            Cursor cursor = mVoicemailProvider.query(uri,
-                    new String[] {
-                            VoicemailContract.Voicemails.NUMBER,
-                            VoicemailContract.Voicemails.SOURCE_PACKAGE
-                    }, null, null, null);
-            assertNotNull(cursor);
-            assertTrue(cursor.moveToFirst());
-            assertEquals("0123456789", cursor.getString(0));
-            assertEquals(selfPackageName, cursor.getString(1));
-            assertFalse(cursor.moveToNext());
+            assertThat(uri).isNotNull();
+            Cursor cursor =
+                    mVoicemailProvider.query(
+                            uri,
+                            new String[] {
+                                VoicemailContract.Voicemails.NUMBER,
+                                VoicemailContract.Voicemails.SOURCE_PACKAGE
+                            },
+                            null,
+                            null,
+                            null);
+            assertThat(cursor).isNotNull();
+            assertThat(cursor.moveToFirst()).isTrue();
+            assertThat(cursor.getString(0)).isEqualTo("0123456789");
+            assertThat(cursor.getString(1)).isEqualTo(selfPackageName);
+            assertThat(cursor.moveToNext()).isFalse();
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
+    @Test
     public void testVoicemailStatusTableIsAccessible() throws Exception {
-        if (!hasCellular) return;
         ContentValues value = new ContentValues();
-        value.put(VoicemailContract.Status.CONFIGURATION_STATE,
+        value.put(
+                VoicemailContract.Status.CONFIGURATION_STATE,
                 VoicemailContract.Status.CONFIGURATION_STATE_OK);
         value.put(VoicemailContract.Status.SOURCE_PACKAGE, selfPackageName);
         try {
             Uri uri = mStatusProvider.insert(mStatusContentUri, value);
-            assertNotNull(uri);
-            Cursor cursor = mVoicemailProvider.query(uri,
-                    new String[] {
-                            VoicemailContract.Status.CONFIGURATION_STATE,
-                            VoicemailContract.Status.SOURCE_PACKAGE
-                    }, null, null, null);
-            assertNotNull(cursor);
-            assertTrue(cursor.moveToFirst());
-            assertEquals(VoicemailContract.Status.CONFIGURATION_STATE_OK, cursor.getInt(0));
-            assertEquals(selfPackageName, cursor.getString(1));
-            assertFalse(cursor.moveToNext());
+            assertThat(uri).isNotNull();
+            Cursor cursor =
+                    mVoicemailProvider.query(
+                            uri,
+                            new String[] {
+                                VoicemailContract.Status.CONFIGURATION_STATE,
+                                VoicemailContract.Status.SOURCE_PACKAGE
+                            },
+                            null,
+                            null,
+                            null);
+            assertThat(cursor).isNotNull();
+            assertThat(cursor.moveToFirst()).isTrue();
+            assertThat(cursor.getInt(0)).isEqualTo(VoicemailContract.Status.CONFIGURATION_STATE_OK);
+            assertThat(cursor.getString(1)).isEqualTo(selfPackageName);
+            assertThat(cursor.moveToNext()).isFalse();
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
@@ -573,23 +598,23 @@
     static final int CARRIER_PRIVILEGE_LISTENERS =
             READ_PHONE_STATE_LISTENERS | READ_PRECISE_PHONE_STATE_LISTENERS;
 
+    @Test
     public void testGetManualNetworkSelectionPlmnPersisted() throws Exception {
-        if (!hasCellular) return;
         if (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) return;
 
         try {
             mTelephonyManager.setNetworkSelectionModeManual(
-                     TESTING_PLMN/* operatorNumeric */, true /* persistSelection */);
+                    TESTING_PLMN /* operatorNumeric */, true /* persistSelection */);
             String plmn = mTelephonyManager.getManualNetworkSelectionPlmn();
-            assertEquals(TESTING_PLMN, plmn);
+            assertThat(plmn).isEqualTo(TESTING_PLMN);
         } finally {
             mTelephonyManager.setNetworkSelectionModeAutomatic();
         }
     }
 
+    @Test
     public void testPhoneStateListener() throws Exception {
-        if (!hasCellular) return;
-        PhoneStateListener psl = new PhoneStateListener((Runnable r) -> { });
+        PhoneStateListener psl = new PhoneStateListener((Runnable r) -> {});
         try {
             mTelephonyManager.listen(psl, CARRIER_PRIVILEGE_LISTENERS);
         } finally {
@@ -597,60 +622,59 @@
         }
     }
 
+    @Test
     public void testIsManualNetworkSelectionAllowed() throws Exception {
-        if (!hasCellular) return;
         if (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) return;
 
         try {
-            assertTrue(mTelephonyManager.isManualNetworkSelectionAllowed());
+            assertThat(mTelephonyManager.isManualNetworkSelectionAllowed()).isTrue();
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
+    @Test
     public void testGetNetworkSelectionMode() throws Exception {
-        if (!hasCellular) return;
-
         try {
-            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
-                    (tm) -> tm.setNetworkSelectionModeAutomatic());
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                    mTelephonyManager, (tm) -> tm.setNetworkSelectionModeAutomatic());
             int networkMode = mTelephonyManager.getNetworkSelectionMode();
-            assertEquals(TelephonyManager.NETWORK_SELECTION_MODE_AUTO, networkMode);
+            assertThat(networkMode).isEqualTo(TelephonyManager.NETWORK_SELECTION_MODE_AUTO);
         } catch (SecurityException e) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
     }
 
+    @Test
     public void testSubscriptionInfoChangeListener() throws Exception {
-        if (!hasCellular) return;
         final AtomicReference<SecurityException> error = new AtomicReference<>();
         final CountDownLatch latch = new CountDownLatch(1);
-        new Handler(mListenerThread.getLooper()).post(() -> {
-            SubscriptionManager.OnSubscriptionsChangedListener listener =
-                    new SubscriptionManager.OnSubscriptionsChangedListener();
-            try {
-                mSubscriptionManager.addOnSubscriptionsChangedListener(listener);
-            } catch (SecurityException e) {
-                error.set(e);
-            } finally {
-                mSubscriptionManager.removeOnSubscriptionsChangedListener(listener);
-                latch.countDown();
-            }
-        });
-        assertTrue("Test timed out", latch.await(30L, TimeUnit.SECONDS));
+        new Handler(mListenerThread.getLooper())
+                .post(
+                        () -> {
+                            SubscriptionManager.OnSubscriptionsChangedListener listener =
+                                    new SubscriptionManager.OnSubscriptionsChangedListener();
+                            try {
+                                mSubscriptionManager.addOnSubscriptionsChangedListener(listener);
+                            } catch (SecurityException e) {
+                                error.set(e);
+                            } finally {
+                                mSubscriptionManager.removeOnSubscriptionsChangedListener(listener);
+                                latch.countDown();
+                            }
+                        });
+        assertWithMessage("Test timed out").that(latch.await(30L, TimeUnit.SECONDS)).isTrue();
         if (error.get() != null) {
-            failMessage();
+            fail(NO_CARRIER_PRIVILEGES_FAILURE_MESSAGE);
         }
-
     }
 
     /**
      * Test that it's possible to open logical channels to the ICC. This mirrors the Manage Channel
      * command described in TS 102 221 Section 11.1.17.
      */
+    @Test
     public void testIccOpenLogicalChannel() {
-        if (!hasCellular) return;
-
         // The AID here doesn't matter - we just need to open a valid connection. In this case, the
         // specified AID ("") opens a channel and selects the MF.
         IccOpenLogicalChannelResponse response = mTelephonyManager.iccOpenLogicalChannel("");
@@ -662,9 +686,8 @@
         }
     }
 
+    @Test
     public void testIccOpenLogicalChannelWithValidP2() {
-        if (!hasCellular) return;
-
         // {@link TelephonyManager#iccOpenLogicalChannel} sends a Manage Channel (open) APDU
         // followed by a Select APDU with the given AID and p2 values. See Open Mobile API
         // Specification v3.2 Section 6.2.7.h and TS 102 221 for details.
@@ -678,9 +701,8 @@
         }
     }
 
+    @Test
     public void testIccOpenLogicalChannelWithInvalidP2() {
-        if (!hasCellular) return;
-
         // Valid p2 values are defined in TS 102 221 Table 11.2. Per Table 11.2, 0xF0 should be
         // invalid. Any p2 values that produce non '9000'/'62xx'/'63xx' status words are treated as
         // an error and the channel is not opened. Due to compatibility issues with older devices,
@@ -690,8 +712,8 @@
             IccOpenLogicalChannelResponse response =
                     mTelephonyManager.iccOpenLogicalChannel("", p2);
             final int logicalChannel = response.getChannel();
-            assertEquals(INVALID_CHANNEL, logicalChannel);
-            assertNotEquals(STATUS_NO_ERROR, response.getStatus());
+            assertThat(logicalChannel).isEqualTo(INVALID_CHANNEL);
+            assertThat(response.getStatus()).isNotEqualTo(STATUS_NO_ERROR);
         }
     }
 
@@ -699,36 +721,32 @@
      * Test that it's possible to close logical channels to the ICC. This follows the Manage Channel
      * command described in TS 102 221 Section 11.1.17.
      */
+    @Test
     public void testIccCloseLogicalChannel() {
-        if (!hasCellular) return;
-
         // The directory here doesn't matter - we just need to open a valid connection that can
         // later be closed. In this case, the specified AID ("") opens a channel and selects the MF.
         IccOpenLogicalChannelResponse response = mTelephonyManager.iccOpenLogicalChannel("");
 
         // Check that the select command succeeded. This ensures that the logical channel is indeed
         // open.
-        assertArrayEquals(STATUS_NORMAL, response.getSelectResponse());
-        assertTrue(mTelephonyManager.iccCloseLogicalChannel(response.getChannel()));
+        assertThat(response.getSelectResponse()).isEqualTo(STATUS_NORMAL);
+        assertThat(mTelephonyManager.iccCloseLogicalChannel(response.getChannel())).isTrue();
 
         // Close opened channel twice.
-        assertFalse(mTelephonyManager.iccCloseLogicalChannel(response.getChannel()));
+        assertThat(mTelephonyManager.iccCloseLogicalChannel(response.getChannel())).isFalse();
 
         // Channel 0 is guaranteed to be always available and cannot be closed, per TS 102 221
         // Section 11.1.17
-        assertFalse(mTelephonyManager.iccCloseLogicalChannel(0));
+        assertThat(mTelephonyManager.iccCloseLogicalChannel(0)).isFalse();
     }
 
     /**
      * This test ensures that valid APDU instructions can be sent and processed by the ICC. To do
-     * so, APDUs are sent to:
-     * - get the status of the MF
-     * - select the Access Rule Reference (ARR) for the MF
-     * - get the FCP template response for the select
+     * so, APDUs are sent to: - get the status of the MF - select the Access Rule Reference (ARR)
+     * for the MF - get the FCP template response for the select
      */
+    @Test
     public void testIccTransmitApduLogicalChannel() {
-        if (!hasCellular) return;
-
         // An open LC is required for transmitting APDU commands. This opens an LC to the MF.
         IccOpenLogicalChannelResponse iccOpenLogicalChannelResponse =
                 mTelephonyManager.iccOpenLogicalChannel("");
@@ -741,7 +759,7 @@
             int cla = CLA_STATUS;
             int p1 = 0; // no indication of application status
             int p2 = 0; // same response parameters as the SELECT in the iccOpenLogicalChannel()
-                        // above
+            // above
             int p3 = 0; // length of 'data' payload
             String data = "";
             String response =
@@ -749,8 +767,8 @@
                             logicalChannel, cla, COMMAND_STATUS, p1, p2, p3, data);
             FcpTemplate fcpTemplate = FcpTemplate.parseFcpTemplate(response);
             // Check that the FCP Template's file ID matches the MF
-            assertTrue(containsFileId(fcpTemplate, MF_FILE_ID));
-            assertEquals(STATUS_NORMAL_STRING, fcpTemplate.getStatus());
+            assertThat(containsFileId(fcpTemplate, MF_FILE_ID)).isTrue();
+            assertThat(fcpTemplate.getStatus()).isEqualTo(STATUS_NORMAL_STRING);
 
             // Select the Access Rule Reference for the MF. Similar to the MF, this will exist
             // across all SIM cards. TS 102 221 Section 11.1.1
@@ -785,8 +803,8 @@
 
             fcpTemplate = FcpTemplate.parseFcpTemplate(response);
             // Check that the FCP Template's file ID matches the selected ARR
-            assertTrue(containsFileId(fcpTemplate, MF_ARR_FILE_ID));
-            assertEquals(STATUS_NORMAL_STRING, fcpTemplate.getStatus());
+            assertThat(containsFileId(fcpTemplate, MF_ARR_FILE_ID)).isTrue();
+            assertThat(fcpTemplate.getStatus()).isEqualTo(STATUS_NORMAL_STRING);
         } finally {
             mTelephonyManager.iccCloseLogicalChannel(logicalChannel);
         }
@@ -796,9 +814,8 @@
      * Tests several invalid APDU instructions over a logical channel and makes sure appropriate
      * errors are returned from the UICC.
      */
+    @Test
     public void testIccTransmitApduLogicalChannelWithInvalidInputs() {
-        if (!hasCellular) return;
-
         // An open LC is required for transmitting apdu commands. This opens an LC to the MF.
         IccOpenLogicalChannelResponse iccOpenLogicalChannelResponse =
                 mTelephonyManager.iccOpenLogicalChannel("");
@@ -810,13 +827,13 @@
             int cla = CLA_STATUS | logicalChannel;
             int p1 = 0xFF; // only '00', '01', and '02' are allowed
             int p2 = 0; // same response parameters as the SELECT in the iccOpenLogicalChannel()
-                        // above
+            // above
             int p3 = 0; // length of 'data' payload
             String data = "";
             String response =
                     mTelephonyManager.iccTransmitApduLogicalChannel(
                             logicalChannel, cla, COMMAND_STATUS, p1, p2, p3, data);
-            assertTrue(INVALID_PARAMETERS_STATUSES.contains(response));
+            assertThat(INVALID_PARAMETERS_STATUSES.contains(response)).isTrue();
 
             // Select a file that doesn't exist
             cla = CLA_SELECT;
@@ -827,7 +844,7 @@
             response =
                     mTelephonyManager.iccTransmitApduLogicalChannel(
                             logicalChannel, cla, COMMAND_SELECT, p1, p2, p3, data);
-            assertEquals(STATUS_FILE_NOT_FOUND, response);
+            assertThat(response).isEqualTo(STATUS_FILE_NOT_FOUND);
 
             // Manage channel with incorrect p1 parameter
             cla = CLA_MANAGE_CHANNEL | logicalChannel;
@@ -838,7 +855,7 @@
             response =
                     mTelephonyManager.iccTransmitApduLogicalChannel(
                             logicalChannel, cla, COMMAND_MANAGE_CHANNEL, p1, p2, p3, data);
-            assertTrue(isErrorResponse(response));
+            assertThat(isErrorResponse(response)).isTrue();
 
             // Use an incorrect class byte for Status apdu
             cla = 0xFF;
@@ -849,7 +866,7 @@
             response =
                     mTelephonyManager.iccTransmitApduLogicalChannel(
                             logicalChannel, cla, COMMAND_STATUS, p1, p2, p3, data);
-            assertEquals(STATUS_WRONG_CLASS, response);
+            assertThat(response).isEqualTo(STATUS_WRONG_CLASS);
 
             // Provide a data field that is longer than described for Select apdu
             cla = CLA_SELECT | logicalChannel;
@@ -860,7 +877,7 @@
             response =
                     mTelephonyManager.iccTransmitApduLogicalChannel(
                             logicalChannel, cla, COMMAND_SELECT, p1, p2, p3, data);
-            assertTrue(isErrorResponse(response));
+            assertThat(isErrorResponse(response)).isTrue();
 
             // Use an invalid instruction
             cla = 0;
@@ -872,7 +889,7 @@
             response =
                     mTelephonyManager.iccTransmitApduLogicalChannel(
                             logicalChannel, cla, invalidInstruction, p1, p2, p3, data);
-            assertTrue(isErrorResponse(response));
+            assertThat(isErrorResponse(response)).isTrue();
         } finally {
             mTelephonyManager.iccCloseLogicalChannel(logicalChannel);
         }
@@ -882,9 +899,8 @@
      * This test ensures that files can be read off the UICC. This helps to test the SIM booting
      * process, as it process involves several file-reads. The ICCID is one of the first files read.
      */
+    @Test
     public void testApduFileRead() {
-        if (!hasCellular) return;
-
         // Open a logical channel and select the MF.
         IccOpenLogicalChannelResponse iccOpenLogicalChannel =
                 mTelephonyManager.iccOpenLogicalChannel("");
@@ -898,7 +914,7 @@
             String response =
                     mTelephonyManager.iccTransmitApduLogicalChannel(
                             logicalChannel, CLA_SELECT, COMMAND_SELECT, p1, p2, p3, ICCID_FILE_ID);
-            assertEquals(STATUS_NORMAL_STRING, response);
+            assertThat(response).isEqualTo(STATUS_NORMAL_STRING);
 
             // Read the contents of the ICCID.
             p1 = 0; // 0-byte offset
@@ -907,27 +923,25 @@
             response =
                     mTelephonyManager.iccTransmitApduLogicalChannel(
                             logicalChannel, CLA_READ_BINARY, COMMAND_READ_BINARY, p1, p2, p3, "");
-            assertTrue(response.endsWith(STATUS_NORMAL_STRING));
+            assertThat(response).endsWith(STATUS_NORMAL_STRING);
         } finally {
             mTelephonyManager.iccCloseLogicalChannel(logicalChannel);
         }
     }
 
-    /**
-     * This test sends several valid APDU commands over the basic channel (channel 0).
-     */
+    /** This test sends several valid APDU commands over the basic channel (channel 0). */
+    @Test
     public void testIccTransmitApduBasicChannel() {
-        if (!hasCellular) return;
-
         // select the MF
         int cla = CLA_SELECT;
         int p1 = 0; // select EF by FID
         int p2 = 0x0C; // requesting FCP template
         int p3 = 2; // length of 'data' payload
         String data = MF_FILE_ID;
-        String response = mTelephonyManager
-            .iccTransmitApduBasicChannel(cla, COMMAND_SELECT, p1, p2, p3, data);
-        assertEquals(STATUS_NORMAL_STRING, response);
+        String response =
+                mTelephonyManager.iccTransmitApduBasicChannel(
+                        cla, COMMAND_SELECT, p1, p2, p3, data);
+        assertThat(response).isEqualTo(STATUS_NORMAL_STRING);
 
         // get the Status of the current file/directory
         cla = CLA_STATUS;
@@ -935,10 +949,11 @@
         p2 = 0; // same response parameters as the SELECT in the iccOpenLogicalChannel() above
         p3 = 0; // length of 'data' payload
         data = "";
-        response = mTelephonyManager
-            .iccTransmitApduBasicChannel(cla, COMMAND_STATUS, p1, p2, p3, data);
+        response =
+                mTelephonyManager.iccTransmitApduBasicChannel(
+                        cla, COMMAND_STATUS, p1, p2, p3, data);
         FcpTemplate fcpTemplate = FcpTemplate.parseFcpTemplate(response);
-        assertTrue(containsFileId(fcpTemplate, MF_FILE_ID));
+        assertThat(containsFileId(fcpTemplate, MF_FILE_ID)).isTrue();
 
         // Manually open a logical channel
         cla = CLA_MANAGE_CHANNEL;
@@ -946,11 +961,12 @@
         p2 = 0; // '00' for open command
         p3 = 0; // length of data payload
         data = "";
-        response = mTelephonyManager
-            .iccTransmitApduBasicChannel(cla, COMMAND_MANAGE_CHANNEL, p1, p2, p3, data);
+        response =
+                mTelephonyManager.iccTransmitApduBasicChannel(
+                        cla, COMMAND_MANAGE_CHANNEL, p1, p2, p3, data);
         // response is in the format | 1 byte: channel number | 2 bytes: status word |
         String responseStatus = response.substring(2);
-        assertEquals(STATUS_NORMAL_STRING, responseStatus);
+        assertThat(responseStatus).isEqualTo(STATUS_NORMAL_STRING);
 
         // Close the open channel
         byte[] responseBytes = hexStringToBytes(response);
@@ -960,18 +976,18 @@
         p2 = channel; // the channel to be closed
         p3 = 0; // length of data payload
         data = "";
-        response = mTelephonyManager
-            .iccTransmitApduBasicChannel(cla, COMMAND_MANAGE_CHANNEL, p1, p2, p3, data);
-        assertEquals(STATUS_NORMAL_STRING, response);
+        response =
+                mTelephonyManager.iccTransmitApduBasicChannel(
+                        cla, COMMAND_MANAGE_CHANNEL, p1, p2, p3, data);
+        assertThat(response).isEqualTo(STATUS_NORMAL_STRING);
     }
 
     /**
      * This test verifies that {@link TelephonyManager#setLine1NumberForDisplay(String, String)}
      * correctly sets the Line 1 alpha tag and number when called.
      */
+    @Test
     public void testLine1NumberForDisplay() {
-        if (!hasCellular) return;
-
         // Cache original alpha tag and number values.
         String originalAlphaTag = mTelephonyManager.getLine1AlphaTag();
         String originalNumber = mTelephonyManager.getLine1Number();
@@ -982,18 +998,18 @@
             String defaultAlphaTag = mTelephonyManager.getLine1AlphaTag();
             String defaultNumber = mTelephonyManager.getLine1Number();
 
-            assertTrue(mTelephonyManager.setLine1NumberForDisplay(ALPHA_TAG_A, NUMBER_A));
-            assertEquals(ALPHA_TAG_A, mTelephonyManager.getLine1AlphaTag());
-            assertEquals(NUMBER_A, mTelephonyManager.getLine1Number());
+            assertThat(mTelephonyManager.setLine1NumberForDisplay(ALPHA_TAG_A, NUMBER_A)).isTrue();
+            assertThat(mTelephonyManager.getLine1AlphaTag()).isEqualTo(ALPHA_TAG_A);
+            assertThat(mTelephonyManager.getLine1Number()).isEqualTo(NUMBER_A);
 
-            assertTrue(mTelephonyManager.setLine1NumberForDisplay(ALPHA_TAG_B, NUMBER_B));
-            assertEquals(ALPHA_TAG_B, mTelephonyManager.getLine1AlphaTag());
-            assertEquals(NUMBER_B, mTelephonyManager.getLine1Number());
+            assertThat(mTelephonyManager.setLine1NumberForDisplay(ALPHA_TAG_B, NUMBER_B)).isTrue();
+            assertThat(mTelephonyManager.getLine1AlphaTag()).isEqualTo(ALPHA_TAG_B);
+            assertThat(mTelephonyManager.getLine1Number()).isEqualTo(NUMBER_B);
 
             // null is used to clear the Line 1 alpha tag and number values.
-            assertTrue(mTelephonyManager.setLine1NumberForDisplay(null, null));
-            assertEquals(defaultAlphaTag, mTelephonyManager.getLine1AlphaTag());
-            assertEquals(defaultNumber, mTelephonyManager.getLine1Number());
+            assertThat(mTelephonyManager.setLine1NumberForDisplay(null, null)).isTrue();
+            assertThat(mTelephonyManager.getLine1AlphaTag()).isEqualTo(defaultAlphaTag);
+            assertThat(mTelephonyManager.getLine1Number()).isEqualTo(defaultNumber);
         } finally {
             // Reset original alpha tag and number values.
             mTelephonyManager.setLine1NumberForDisplay(originalAlphaTag, originalNumber);
@@ -1004,21 +1020,20 @@
      * This test verifies that {@link TelephonyManager#setVoiceMailNumber(String, String)} correctly
      * sets the VoiceMail alpha tag and number when called.
      */
+    @Test
     public void testVoiceMailNumber() {
-        if (!hasCellular) return;
-
         // Cache original alpha tag and number values.
         String originalAlphaTag = mTelephonyManager.getVoiceMailAlphaTag();
         String originalNumber = mTelephonyManager.getVoiceMailNumber();
 
         try {
-            assertTrue(mTelephonyManager.setVoiceMailNumber(ALPHA_TAG_A, NUMBER_A));
-            assertEquals(ALPHA_TAG_A, mTelephonyManager.getVoiceMailAlphaTag());
-            assertEquals(NUMBER_A, mTelephonyManager.getVoiceMailNumber());
+            assertThat(mTelephonyManager.setVoiceMailNumber(ALPHA_TAG_A, NUMBER_A)).isTrue();
+            assertThat(mTelephonyManager.getVoiceMailAlphaTag()).isEqualTo(ALPHA_TAG_A);
+            assertThat(mTelephonyManager.getVoiceMailNumber()).isEqualTo(NUMBER_A);
 
-            assertTrue(mTelephonyManager.setVoiceMailNumber(ALPHA_TAG_B, NUMBER_B));
-            assertEquals(ALPHA_TAG_B, mTelephonyManager.getVoiceMailAlphaTag());
-            assertEquals(NUMBER_B, mTelephonyManager.getVoiceMailNumber());
+            assertThat(mTelephonyManager.setVoiceMailNumber(ALPHA_TAG_B, NUMBER_B)).isTrue();
+            assertThat(mTelephonyManager.getVoiceMailAlphaTag()).isEqualTo(ALPHA_TAG_B);
+            assertThat(mTelephonyManager.getVoiceMailNumber()).isEqualTo(NUMBER_B);
         } finally {
             // Reset original alpha tag and number values.
             mTelephonyManager.setVoiceMailNumber(originalAlphaTag, originalNumber);
@@ -1029,12 +1044,11 @@
      * This test verifies that {@link SubscriptionManager#createSubscriptionGroup(List)} correctly
      * create a group with the given subscription id.
      *
-     * This also verifies that
-     * {@link SubscriptionManager#removeSubscriptionsFromGroup(List, ParcelUuid)} correctly remove
-     * the given subscription group.
+     * <p>This also verifies that {@link SubscriptionManager#removeSubscriptionsFromGroup(List,
+     * ParcelUuid)} correctly remove the given subscription group.
      */
+    @Test
     public void testCreateAndRemoveSubscriptionGroup() {
-        if (!hasCellular) return;
         // Set subscription group with current sub Id.
         int subId = SubscriptionManager.getDefaultSubscriptionId();
         List<Integer> subGroup = Arrays.asList(subId);
@@ -1044,19 +1058,20 @@
         List<SubscriptionInfo> infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
 
         try {
-            assertEquals(1, infoList.size());
-            assertEquals(uuid, infoList.get(0).getGroupUuid());
-            assertEquals(subId, infoList.get(0).getSubscriptionId());
+            assertThat(infoList).hasSize(1);
+            assertThat(infoList.get(0).getGroupUuid()).isEqualTo(uuid);
+            assertThat(infoList.get(0).getSubscriptionId()).isEqualTo(subId);
         } finally {
             // Verify that the given subGroup has been removed.
             mSubscriptionManager.removeSubscriptionsFromGroup(subGroup, uuid);
             infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
-            assertTrue(infoList.isEmpty());
+            assertThat(infoList).isEmpty();
         }
     }
 
+    @Test
     public void testAddSubscriptionToExistingGroupForMultipleSims() {
-        if (!hasCellular || mTelephonyManager.getPhoneCount() < DSDS_PHONE_COUNT) return;
+        if (mTelephonyManager.getPhoneCount() < DSDS_PHONE_COUNT) return;
 
         // Set subscription group with current sub Id.
         int subId = SubscriptionManager.getDefaultDataSubscriptionId();
@@ -1069,7 +1084,7 @@
                     mSubscriptionManager.getActiveSubscriptionInfoList();
 
             // Verify that the device has at least two active subscriptions.
-            assertTrue(activeSubInfos.size() >= DSDS_PHONE_COUNT);
+            assertThat(activeSubInfos.size()).isAtLeast(DSDS_PHONE_COUNT);
 
             List<Integer> activeSubGroup = getSubscriptionIdList(activeSubInfos);
             activeSubGroup.removeIf(id -> id == subId);
@@ -1079,23 +1094,21 @@
             List<Integer> infoList =
                     getSubscriptionIdList(mSubscriptionManager.getSubscriptionsInGroup(uuid));
             activeSubGroup.add(subId);
-            assertEquals(activeSubGroup.size(), infoList.size());
-            assertTrue(activeSubGroup.containsAll(infoList));
+            assertThat(infoList).hasSize(activeSubGroup.size());
+            assertThat(infoList).containsExactly(activeSubGroup);
         } finally {
             removeSubscriptionsFromGroup(uuid);
         }
     }
 
     /**
-     * This test verifies that
-     * {@link SubscriptionManager#addSubscriptionsIntoGroup(List, ParcelUuid)}} correctly add some
-     * additional subscriptions to the existing group.
+     * This test verifies that {@link SubscriptionManager#addSubscriptionsIntoGroup(List,
+     * ParcelUuid)}} correctly add some additional subscriptions to the existing group.
      *
-     * This test required the device has more than one subscription.
+     * <p>This test required the device has more than one subscription.
      */
+    @Test
     public void testAddSubscriptionToExistingGroupForEsim() {
-        if (!hasCellular) return;
-
         // Set subscription group with current sub Id.
         int subId = SubscriptionManager.getDefaultDataSubscriptionId();
         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) return;
@@ -1114,8 +1127,8 @@
                 List<Integer> infoList =
                         getSubscriptionIdList(mSubscriptionManager.getSubscriptionsInGroup(uuid));
                 accessibleSubGroup.add(subId);
-                assertEquals(accessibleSubGroup.size(), infoList.size());
-                assertTrue(accessibleSubGroup.containsAll(infoList));
+                assertThat(infoList).hasSize(accessibleSubGroup.size());
+                assertThat(infoList).containsExactly(accessibleSubGroup);
             }
         } finally {
             removeSubscriptionsFromGroup(uuid);
@@ -1126,9 +1139,8 @@
      * This test verifies that {@link SubscriptionManager#setOpportunistic(boolean, int)} correctly
      * set the opportunistic property of the given subscription.
      */
+    @Test
     public void testOpportunistic() {
-        if (!hasCellular) return;
-
         int subId = SubscriptionManager.getDefaultDataSubscriptionId();
         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) return;
         SubscriptionInfo info = mSubscriptionManager.getActiveSubscriptionInfo(subId);
@@ -1137,17 +1149,16 @@
 
         try {
             // Mark the given subscription as opportunistic subscription.
-            boolean successed = mSubscriptionManager.setOpportunistic(newOpportunistic, subId);
-            assertTrue(successed);
+            assertThat(mSubscriptionManager.setOpportunistic(newOpportunistic, subId)).isTrue();
 
             // Verify that the given subscription is opportunistic subscription.
             info = mSubscriptionManager.getActiveSubscriptionInfo(subId);
-            assertEquals(newOpportunistic, info.isOpportunistic());
+            assertThat(info.isOpportunistic()).isEqualTo(newOpportunistic);
         } finally {
             // Set back to original opportunistic property.
             mSubscriptionManager.setOpportunistic(oldOpportunistic, subId);
             info = mSubscriptionManager.getActiveSubscriptionInfo(subId);
-            assertEquals(oldOpportunistic, info.isOpportunistic());
+            assertThat(info.isOpportunistic()).isEqualTo(oldOpportunistic);
         }
     }
 
@@ -1156,9 +1167,8 @@
      * String)} correctly transmits iccIO commands to the UICC card. First, the MF is selected via a
      * SELECT apdu via the basic channel, then a STATUS AT-command is sent.
      */
+    @Test
     public void testIccExchangeSimIO() {
-        if (!hasCellular) return;
-
         // select the MF first. This makes sure the next STATUS AT-command returns a FCP template
         // for the right file.
         int cla = CLA_SELECT;
@@ -1166,65 +1176,71 @@
         int p2 = 0x0C; // requesting FCP template
         int p3 = 2; // length of 'data' payload
         String data = MF_FILE_ID;
-        String response = mTelephonyManager
-                .iccTransmitApduBasicChannel(cla, COMMAND_SELECT, p1, p2, p3, data);
-        assertEquals(STATUS_NORMAL_STRING, response);
+        String response =
+                mTelephonyManager.iccTransmitApduBasicChannel(
+                        cla, COMMAND_SELECT, p1, p2, p3, data);
+        assertThat(response).isEqualTo(STATUS_NORMAL_STRING);
 
         // The iccExchangeSimIO command implements the +CRSM command defined in TS 27.007 section
         // 8.18. A STATUS command is sent and the returned value will be an FCP template.
-        byte[] result = mTelephonyManager.iccExchangeSimIO(
-                0, // fileId: not required for STATUS
-                COMMAND_STATUS,  // command: STATUS
-                0, // p1: not required for STATUS
-                0, // p2: not required for STATUS
-                0, // p3: not required for STATUS
-                ""); // filePath: not required for STATUS
+        byte[] result =
+                mTelephonyManager.iccExchangeSimIO(
+                        0, // fileId: not required for STATUS
+                        COMMAND_STATUS, // command: STATUS
+                        0, // p1: not required for STATUS
+                        0, // p2: not required for STATUS
+                        0, // p3: not required for STATUS
+                        ""); // filePath: not required for STATUS
         String resultString = bytesToHexString(result);
         FcpTemplate fcpTemplate = FcpTemplate.parseFcpTemplate(resultString);
-        assertTrue(containsFileId(fcpTemplate, MF_FILE_ID));
-        assertEquals("iccExchangeSimIO returned non-normal Status byte: " + resultString,
-                STATUS_NORMAL_STRING, fcpTemplate.getStatus());
+        assertThat(containsFileId(fcpTemplate, MF_FILE_ID)).isTrue();
+        assertWithMessage("iccExchangeSimIO returned non-normal Status byte: %s", resultString)
+                .that(fcpTemplate.getStatus())
+                .isEqualTo(STATUS_NORMAL_STRING);
     }
 
     /**
      * This test checks that a STATUS apdu can be sent as an encapsulated envelope to the UICC via
      * {@link TelephonyManager#sendEnvelopeWithStatus(String)}.
      */
+    @Test
     public void testSendEnvelopeWithStatus() {
-        if (!hasCellular) return;
-
         // STATUS apdu as hex String
         String envelope =
                 CLA_STATUS_STRING
-                + COMMAND_STATUS_STRING
-                + "00" // p1: no indication of application status
-                + "00"; // p2: identical parameters to
+                        + COMMAND_STATUS_STRING
+                        + "00" // p1: no indication of application status
+                        + "00"; // p2: identical parameters to
         String response = mTelephonyManager.sendEnvelopeWithStatus(envelope);
 
         // TODO(b/137963715): add more specific assertions on response from TelMan#sendEnvelope
-        assertNotNull("sendEnvelopeWithStatus is null for envelope=" + envelope, response);
+        assertWithMessage("sendEnvelopeWithStatus is null for envelope=%s", envelope)
+                .that(response)
+                .isNotNull();
     }
 
     /**
      * This test checks that applications with carrier privilege can set/clear signal strength
-     * update request via
-     * {@link TelephonyManager#setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)} and
-     * {@link TelephonyManager#clearSignalStrengthUpdateRequest} without
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * update request via {@link
+     * TelephonyManager#setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)} and {@link
+     * TelephonyManager#clearSignalStrengthUpdateRequest} without {@link
+     * android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      */
+    @Test
     public void testSetClearSignalStrengthUpdateRequest() {
-        if (!hasCellular) return;
-
         final SignalStrengthUpdateRequest request =
                 new SignalStrengthUpdateRequest.Builder()
-                        .setSignalThresholdInfos(List.of(
-                                new SignalThresholdInfo.Builder()
-                                        .setRadioAccessNetworkType(
-                                                AccessNetworkConstants.AccessNetworkType.GERAN)
-                                        .setSignalMeasurementType(
-                                                SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
-                                        .setThresholds(new int[]{-113, -103, -97, -51})
-                                        .build()))
+                        .setSignalThresholdInfos(
+                                List.of(
+                                        new SignalThresholdInfo.Builder()
+                                                .setRadioAccessNetworkType(
+                                                        AccessNetworkConstants.AccessNetworkType
+                                                                .GERAN)
+                                                .setSignalMeasurementType(
+                                                        SignalThresholdInfo
+                                                                .SIGNAL_MEASUREMENT_TYPE_RSSI)
+                                                .setThresholds(new int[] {-113, -103, -97, -51})
+                                                .build()))
                         .setReportingRequestedWhileIdle(true)
                         .build();
         try {
@@ -1237,20 +1253,19 @@
     private void verifyValidIccOpenLogicalChannelResponse(IccOpenLogicalChannelResponse response) {
         // The assigned channel should be between the min and max allowed channel numbers
         int channel = response.getChannel();
-        assertTrue(MIN_LOGICAL_CHANNEL <= channel && channel <= MAX_LOGICAL_CHANNEL);
-        assertEquals(STATUS_NO_ERROR, response.getStatus());
-        assertArrayEquals(STATUS_NORMAL, response.getSelectResponse());
+        assertThat(channel).isIn(Range.closed(MIN_LOGICAL_CHANNEL, MAX_LOGICAL_CHANNEL));
+        assertThat(response.getStatus()).isEqualTo(STATUS_NO_ERROR);
+        assertThat(response.getSelectResponse()).isEqualTo(STATUS_NORMAL);
     }
 
     private void removeSubscriptionsFromGroup(ParcelUuid uuid) {
         List<SubscriptionInfo> infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
         if (!infoList.isEmpty()) {
             mSubscriptionManager.removeSubscriptionsFromGroup(
-                    getSubscriptionIdList(infoList),
-                    uuid);
+                    getSubscriptionIdList(infoList), uuid);
         }
         infoList = mSubscriptionManager.getSubscriptionsInGroup(uuid);
-        assertTrue(infoList.isEmpty());
+        assertThat(infoList).isEmpty();
     }
 
     private List<Integer> getSubscriptionIdList(List<SubscriptionInfo> subInfoList) {
@@ -1265,26 +1280,24 @@
      *
      * @param fcpTemplate The FCP Template to be checked.
      * @param fileId The file ID that is being searched for
-     *
      * @return true iff fcpTemplate contains fileId.
      */
     private boolean containsFileId(FcpTemplate fcpTemplate, String fileId) {
-        return fcpTemplate.getTlvs().stream().anyMatch(tlv ->
-                tlv.getTag() == FILE_IDENTIFIER && tlv.getValue().equals(fileId));
+        return fcpTemplate.getTlvs().stream()
+                .anyMatch(tlv -> tlv.getTag() == FILE_IDENTIFIER && tlv.getValue().equals(fileId));
     }
 
     /**
      * Returns true iff {@code response} indicates an error with the previous APDU.
      *
      * @param response The APDU response to be checked.
-     *
      * @return true iff the given response indicates an error occurred
      */
     private boolean isErrorResponse(@Nonnull String response) {
-        return !(STATUS_NORMAL_STRING.equals(response) ||
-            response.startsWith(STATUS_WARNING_A) ||
-            response.startsWith(STATUS_WARNING_B) ||
-            response.startsWith(STATUS_BYTES_REMAINING));
+        return !(STATUS_NORMAL_STRING.equals(response)
+                || response.startsWith(STATUS_WARNING_A)
+                || response.startsWith(STATUS_WARNING_B)
+                || response.startsWith(STATUS_BYTES_REMAINING));
     }
 
     private static class IntentReceiver extends BroadcastReceiver {
@@ -1300,8 +1313,11 @@
         }
     }
 
-    @Suppress
+    @Test
     public void testEapSimAuthentication() {
+        assumeTrue(
+                "testEapSimAuthentication requires a 2021 CTS UICC or newer",
+                UiccUtil.uiccHasCertificate(CTS_UICC_2021));
         // K: '000102030405060708090A0B0C0D0E0F', defined by TS 134 108#8.2
         // n: 128 (Bits to use for RES value)
         // Format: [Length][RAND]
@@ -1313,14 +1329,16 @@
                         TelephonyManager.AUTHTYPE_EAP_SIM,
                         base64Challenge);
         byte[] response = Base64.decode(base64Response, Base64.DEFAULT);
-        assertArrayEquals(
-                "Results for AUTHTYPE_EAP_SIM failed",
-                hexStringToBytes(EXPECTED_EAP_SIM_RESULT),
-                response);
+        assertWithMessage("Results for AUTHTYPE_EAP_SIM failed")
+                .that(response)
+                .isEqualTo(hexStringToBytes(EXPECTED_EAP_SIM_RESULT));
     }
 
-    @Suppress
+    @Test
     public void testEapAkaAuthentication() {
+        assumeTrue(
+                "testEapAkaAuthentication requires a 2021 CTS UICC or newer",
+                UiccUtil.uiccHasCertificate(CTS_UICC_2021));
         // K: '000102030405060708090A0B0C0D0E0F', defined by TS 134 108#8.2
         // n: 128 (Bits to use for RES value)
         // Format: [Length][Rand][Length][Autn]
@@ -1332,14 +1350,13 @@
                         TelephonyManager.AUTHTYPE_EAP_AKA,
                         base64Challenge);
 
-        assertNotNull("UICC returned null for EAP-AKA auth", base64Response);
+        assertWithMessage("UICC returned null for EAP-AKA auth").that(base64Response).isNotNull();
         byte[] response = Base64.decode(base64Response, Base64.NO_WRAP);
 
         // response may be formatted as: [DB][Length][RES][Length][CK][Length][IK][Length][Kc]
         byte[] akaResponse = Arrays.copyOfRange(response, 0, EAP_AKA_RESPONSE_LENGTH);
-        assertArrayEquals(
-                "Results for AUTHTYPE_EAP_AKA failed",
-                hexStringToBytes(EXPECTED_EAP_AKA_RESULT),
-                akaResponse);
+        assertWithMessage("Results for AUTHTYPE_EAP_AKA failed")
+                .that(akaResponse)
+                .isEqualTo(hexStringToBytes(EXPECTED_EAP_AKA_RESULT));
     }
 }
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/FcpTemplate.java b/tests/tests/carrierapi/src/android/carrierapi/cts/FcpTemplate.java
index a90c756..252b874 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/FcpTemplate.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/FcpTemplate.java
@@ -21,13 +21,14 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+
 import javax.annotation.Nonnull;
 
 /**
  * Class for representing a File Control Parameters (FCP) Template object. TS 101 220
  *
- * A correctly formatted FCP Template will be in the format:
- * | 1 byte: BER tag (0x62) | 1 byte: length of TLVs |...TLV objects...| 2 bytes: status |
+ * <p>A correctly formatted FCP Template will be in the format: | 1 byte: BER tag (0x62) | 1 byte:
+ * length of TLVs |...TLV objects...| 2 bytes: status |
  */
 public class FcpTemplate {
 
@@ -69,12 +70,11 @@
      * Parses and returns a FcpTemplate for the given {@code fcpResponse}
      *
      * @param fcpResponse The Hex String response for a given Status APDU command. Expected to be in
-     * the format: | 1 byte: BER tag | 1 byte: length of TLVs |...TLV objects...| 2 bytes: status |
-     *
+     *     the format: | 1 byte: BER tag | 1 byte: length of TLVs |...TLV objects...| 2 bytes:
+     *     status |
      * @return a FcpTemplate for the given hex String
-     *
      * @throws FcpTemplateParseException for non-FCP inputs or inputs of the wrong length (encoded
-     *                                   length does not match actual length)
+     *     length does not match actual length)
      */
     public static FcpTemplate parseFcpTemplate(@Nonnull String fcpResponse) {
         final List<Tlv> tlvObjects = new ArrayList<>();
@@ -98,17 +98,15 @@
             int tag = data[index++] & 0xFF;
             int length = data[index++] & 0xFF; // assumes that length is < 128 bytes.
             String value = fcpResponse.substring(index * 2, (index + length) * 2);
-            tlvObjects .add(new Tlv(tag, length, value));
+            tlvObjects.add(new Tlv(tag, length, value));
             index += length;
         }
 
         String status = fcpResponse.substring(fcpResponse.length() - 4);
-        return new FcpTemplate(tlvObjects , status);
+        return new FcpTemplate(tlvObjects, status);
     }
 
-    /**
-     * Represents a Tag-Length-Value object. TS 101 220 Section 2
-     */
+    /** Represents a Tag-Length-Value object. TS 101 220 Section 2 */
     public static class Tlv {
 
         private final int tag;
@@ -142,9 +140,7 @@
                 return false;
             }
             Tlv tlv = (Tlv) o;
-            return tag == tlv.tag &&
-                    length == tlv.length &&
-                    value.equals(tlv.value);
+            return tag == tlv.tag && length == tlv.length && value.equals(tlv.value);
         }
 
         @Override
@@ -159,7 +155,6 @@
     }
 
     private static final class FcpTemplateParseException extends RuntimeException {
-
         public FcpTemplateParseException(String message) {
             super(message);
         }
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/IccUtils.java b/tests/tests/carrierapi/src/android/carrierapi/cts/IccUtils.java
index 3409fe5..d36ca03 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/IccUtils.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/IccUtils.java
@@ -18,20 +18,18 @@
 
 import javax.annotation.Nonnull;
 
-/**
- * Utility class for converting between hex Strings and bitwise representations.
- */
+/** Utility class for converting between hex Strings and bitwise representations. */
 public class IccUtils {
 
     // A table mapping from a number to a hex character for fast encoding hex strings.
     private static final char[] HEX_CHARS = {
-            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
     };
 
     @Nonnull
     public static String bytesToHexString(byte[] bytes) {
         StringBuilder ret = new StringBuilder(2 * bytes.length);
-        for (int i = 0 ; i < bytes.length ; i++) {
+        for (int i = 0; i < bytes.length; i++) {
             int b;
             b = 0x0f & (bytes[i] >> 4);
             ret.append(HEX_CHARS[b]);
@@ -44,11 +42,8 @@
     /**
      * Converts a hex String to a byte array.
      *
-     * @param s A string of hexadecimal characters, must be an even number of
-     *          chars long
-     *
+     * @param s A string of hexadecimal characters, must be an even number of chars long
      * @return byte array representation
-     *
      * @throws RuntimeException on invalid format
      */
     public static byte[] hexStringToBytes(String s) {
@@ -58,10 +53,10 @@
 
         int sz = s.length();
 
-        ret = new byte[sz/2];
+        ret = new byte[sz / 2];
 
-        for (int i=0 ; i <sz ; i+=2) {
-            ret[i/2] = (byte) ((hexCharToInt(s.charAt(i)) << 4) | hexCharToInt(s.charAt(i+1)));
+        for (int i = 0; i < sz; i += 2) {
+            ret[i / 2] = (byte) ((hexCharToInt(s.charAt(i)) << 4) | hexCharToInt(s.charAt(i + 1)));
         }
 
         return ret;
@@ -71,12 +66,13 @@
      * Converts a hex char to its integer value
      *
      * @param c A single hexadecimal character. Must be in one of these ranges:
-     *          - '0' to '9', or
-     *          - 'a' to 'f', or
-     *          - 'A' to 'F'
+     *     <ul>
+     *       <li>'0' to '9'
+     *       <li>'a' to 'f'
+     *       <li>'A' to 'F'
+     *     </ul>
      *
      * @return the integer representation of {@code c}
-     *
      * @throws RuntimeException on invalid character
      */
     public static int hexCharToInt(char c) {
@@ -84,6 +80,6 @@
         if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
         if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
 
-        throw new RuntimeException ("invalid hex char '" + c + "'");
+        throw new RuntimeException("invalid hex char '" + c + "'");
     }
 }
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
index 017d7fa..82655e9 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
@@ -18,13 +18,12 @@
 import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import static org.junit.Assert.fail;
 
-import android.content.Context;
+import android.content.ContentResolver;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
@@ -64,15 +63,16 @@
 import java.util.stream.Collectors;
 
 /**
- * Build, install and run the tests by running the commands below:
- *  make cts -j64
- *  cts-tradefed run cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.NetworkScanApiTest
+ * Unit tests for {@link TelephonyManager}'s network scan APIs.
+ *
+ * <p>Test using `atest CtsCarrierApiTestCases:NetworkScanApiTest` or `make cts -j64 && cts-tradefed
+ * run cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.NetworkScanApiTest`
  */
 @RunWith(AndroidJUnit4.class)
-public class NetworkScanApiTest {
-    private TelephonyManager mTelephonyManager;
-    private PackageManager mPackageManager;
+public class NetworkScanApiTest extends BaseCarrierApiTest {
     private static final String TAG = "NetworkScanApiTest";
+
+    private TelephonyManager mTelephonyManager;
     private int mNetworkScanStatus;
     private static final int EVENT_NETWORK_SCAN_START = 100;
     private static final int EVENT_NETWORK_SCAN_RESULTS = 200;
@@ -103,18 +103,18 @@
     private static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3;
     private static final ArrayList<String> MCC_MNC = new ArrayList<>();
     private static final RadioAccessSpecifier[] RADIO_ACCESS_SPECIFIERS = {
-            new RadioAccessSpecifier(
-                    AccessNetworkConstants.AccessNetworkType.GERAN,
-                    null /* bands */,
-                    null /* channels */),
-            new RadioAccessSpecifier(
-                    AccessNetworkConstants.AccessNetworkType.EUTRAN,
-                    null /* bands */,
-                    null /* channels */),
-            new RadioAccessSpecifier(
-                    AccessNetworkConstants.AccessNetworkType.UTRAN,
-                    null /* bands */,
-                    null /* channels */)
+        new RadioAccessSpecifier(
+                AccessNetworkConstants.AccessNetworkType.GERAN,
+                null /* bands */,
+                null /* channels */),
+        new RadioAccessSpecifier(
+                AccessNetworkConstants.AccessNetworkType.EUTRAN,
+                null /* bands */,
+                null /* channels */),
+        new RadioAccessSpecifier(
+                AccessNetworkConstants.AccessNetworkType.UTRAN,
+                null /* bands */,
+                null /* channels */)
     };
 
     // Needed because NETWORK_SCAN_PERMISSION is a systemapi
@@ -122,20 +122,25 @@
 
     @Before
     public void setUp() throws Exception {
-        Context context = InstrumentationRegistry.getContext();
-        mTelephonyManager = (TelephonyManager)
-                context.getSystemService(Context.TELEPHONY_SERVICE);
-        mPackageManager = context.getPackageManager();
-        InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
-                context.getPackageName(), ACCESS_FINE_LOCATION);
-        InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
-                context.getPackageName(), ACCESS_BACKGROUND_LOCATION);
+        mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
+        String selfPackageName = getContext().getPackageName();
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .grantRuntimePermission(selfPackageName, ACCESS_FINE_LOCATION);
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .grantRuntimePermission(selfPackageName, ACCESS_BACKGROUND_LOCATION);
         mTestHandlerThread = new NetworkScanHandlerThread(TAG);
         mTestHandlerThread.start();
     }
 
     @After
     public void tearDown() throws Exception {
+        if (!werePreconditionsSatisfied()) return;
+
+        // Revoking runtime permissions makes ActivityManager kill our process, so we don't do it,
+        // as the test harness will eventually uninstall this APK after testing completes anyway, so
+        // we aren't really leaking anything long-term.
         mTestHandlerThread.quit();
     }
 
@@ -146,9 +151,7 @@
             } catch (InterruptedException ie) {
             }
 
-            if (!mReady) {
-                fail("NetworkScanApiTest failed to initialize");
-            }
+            assertWithMessage("NetworkScanApiTest failed to initialize").that(mReady).isTrue();
         }
     }
 
@@ -168,41 +171,45 @@
         @Override
         public void onLooperPrepared() {
             /* create a custom handler for the Handler Thread */
-            mHandler = new Handler(mTestHandlerThread.getLooper()) {
-                @Override
-                public void handleMessage(Message msg) {
-                    switch (msg.what) {
-                        case EVENT_NETWORK_SCAN_START:
-                            Log.d(TAG, "request network scan");
-                            boolean useShellIdentity = (Boolean) msg.obj;
-                            if (useShellIdentity) {
-                                InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                                        .adoptShellPermissionIdentity();
+            mHandler =
+                    new Handler(mTestHandlerThread.getLooper()) {
+                        @Override
+                        public void handleMessage(Message msg) {
+                            switch (msg.what) {
+                                case EVENT_NETWORK_SCAN_START:
+                                    Log.d(TAG, "request network scan");
+                                    boolean useShellIdentity = (Boolean) msg.obj;
+                                    if (useShellIdentity) {
+                                        InstrumentationRegistry.getInstrumentation()
+                                                .getUiAutomation()
+                                                .adoptShellPermissionIdentity();
+                                    }
+                                    try {
+                                        mNetworkScan =
+                                                mTelephonyManager.requestNetworkScan(
+                                                        mNetworkScanRequest,
+                                                        AsyncTask.SERIAL_EXECUTOR,
+                                                        mNetworkScanCallback);
+                                        if (mNetworkScan == null) {
+                                            mNetworkScanStatus = EVENT_SCAN_DENIED;
+                                            setReady(true);
+                                        }
+                                    } catch (SecurityException e) {
+                                        mNetworkScanStatus = EVENT_SCAN_DENIED;
+                                        setReady(true);
+                                    } finally {
+                                        if (useShellIdentity) {
+                                            InstrumentationRegistry.getInstrumentation()
+                                                    .getUiAutomation()
+                                                    .dropShellPermissionIdentity();
+                                        }
+                                    }
+                                    break;
+                                default:
+                                    Log.d(TAG, "Unknown Event " + msg.what);
                             }
-                            try {
-                                mNetworkScan = mTelephonyManager.requestNetworkScan(
-                                        mNetworkScanRequest,
-                                        AsyncTask.SERIAL_EXECUTOR,
-                                        mNetworkScanCallback);
-                                if (mNetworkScan == null) {
-                                    mNetworkScanStatus = EVENT_SCAN_DENIED;
-                                    setReady(true);
-                                }
-                            } catch (SecurityException e) {
-                                mNetworkScanStatus = EVENT_SCAN_DENIED;
-                                setReady(true);
-                            } finally {
-                                if (useShellIdentity) {
-                                    InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                                            .dropShellPermissionIdentity();
-                                }
-                            }
-                            break;
-                        default:
-                            Log.d(TAG, "Unknown Event " + msg.what);
-                    }
-                }
-            };
+                        }
+                    };
         }
     }
 
@@ -265,39 +272,35 @@
             Log.d(TAG, "lte channels" + lteChannels.toString());
             int ranLte = AccessNetworkConstants.AccessNetworkType.EUTRAN;
             radioAccessSpecifier.add(
-                    new RadioAccessSpecifier(ranLte, null /* bands */,
-                            lteChannels.stream().mapToInt(i->i).toArray()));
+                    new RadioAccessSpecifier(
+                            ranLte,
+                            null /* bands */,
+                            lteChannels.stream().mapToInt(i -> i).toArray()));
         }
         if (!wcdmaChannels.isEmpty()) {
             Log.d(TAG, "wcdma channels" + wcdmaChannels.toString());
             int ranWcdma = AccessNetworkConstants.AccessNetworkType.UTRAN;
             radioAccessSpecifier.add(
-                    new RadioAccessSpecifier(ranWcdma, null /* bands */,
-                            wcdmaChannels.stream().mapToInt(i->i).toArray()));
+                    new RadioAccessSpecifier(
+                            ranWcdma,
+                            null /* bands */,
+                            wcdmaChannels.stream().mapToInt(i -> i).toArray()));
         }
         if (!gsmChannels.isEmpty()) {
             Log.d(TAG, "gsm channels" + gsmChannels.toString());
             int ranGsm = AccessNetworkConstants.AccessNetworkType.GERAN;
             radioAccessSpecifier.add(
-                    new RadioAccessSpecifier(ranGsm, null /* bands */,
-                            gsmChannels.stream().mapToInt(i->i).toArray()));
+                    new RadioAccessSpecifier(
+                            ranGsm,
+                            null /* bands */,
+                            gsmChannels.stream().mapToInt(i -> i).toArray()));
         }
         return radioAccessSpecifier;
     }
 
-    /**
-     * Tests that the device properly requests a network scan.
-     */
+    /** Tests that the device properly requests a network scan. */
     @Test
     public void testRequestNetworkScan() {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
-            // Checks whether the cellular stack should be running on this device.
-            Log.e(TAG, "No cellular support, the test will be skipped.");
-            return;
-        }
-        if (!mTelephonyManager.hasCarrierPrivileges()) {
-            fail("This test requires a SIM card with carrier privilege rule on it.");
-        }
         boolean isLocationSwitchOn = getAndSetLocationSwitch(true);
         try {
             mNetworkScanRequest = buildNetworkScanRequest(true);
@@ -308,11 +311,16 @@
             waitUntilReady();
 
             Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
-            assertTrue("The final scan status is " + mNetworkScanStatus + " with error code "
-                            + mErrorCode + ", not ScanCompleted"
-                            + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
-                            + " ERROR_UNSUPPORTED",
-                    isScanStatusValid());
+            assertWithMessage(
+                            "The final scan status is "
+                                    + mNetworkScanStatus
+                                    + " with error code "
+                                    + mErrorCode
+                                    + ", not ScanCompleted"
+                                    + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
+                                    + " ERROR_UNSUPPORTED")
+                    .that(isScanStatusValid())
+                    .isTrue();
         } finally {
             getAndSetLocationSwitch(isLocationSwitchOn);
         }
@@ -328,40 +336,36 @@
         requestNetworkScanLocationOffHelper(true, true);
     }
 
-    public void requestNetworkScanLocationOffHelper(boolean includeBandsAndChannels,
-            boolean useSpecialScanPermission) {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
-            // Checks whether the cellular stack should be running on this device.
-            Log.e(TAG, "No cellular support, the test will be skipped.");
-            return;
-        }
-        if (!mTelephonyManager.hasCarrierPrivileges()) {
-            fail("This test requires a SIM card with carrier privilege rule on it.");
-        }
-
+    public void requestNetworkScanLocationOffHelper(
+            boolean includeBandsAndChannels, boolean useSpecialScanPermission) {
         mNetworkScanRequest = buildNetworkScanRequest(includeBandsAndChannels);
 
         boolean isLocationSwitchOn = getAndSetLocationSwitch(false);
         try {
             mNetworkScanCallback = new NetworkScanCallbackImpl();
-            Message startNetworkScan = mHandler.obtainMessage(EVENT_NETWORK_SCAN_START,
-                    useSpecialScanPermission);
+            Message startNetworkScan =
+                    mHandler.obtainMessage(EVENT_NETWORK_SCAN_START, useSpecialScanPermission);
             setReady(false);
             startNetworkScan.sendToTarget();
             waitUntilReady();
             if (includeBandsAndChannels) {
                 // If we included the bands when location is off, expect a security error and
                 // nothing else.
-                assertEquals(EVENT_SCAN_DENIED, mNetworkScanStatus);
+                assertThat(mNetworkScanStatus).isEqualTo(EVENT_SCAN_DENIED);
                 return;
             }
 
             Log.d(TAG, "mNetworkScanStatus: " + mNetworkScanStatus);
-            assertTrue("The final scan status is " + mNetworkScanStatus + " with error code "
-                            + mErrorCode + ", not ScanCompleted"
-                            + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
-                            + " ERROR_UNSUPPORTED",
-                    isScanStatusValid());
+            assertWithMessage(
+                            "The final scan status is "
+                                    + mNetworkScanStatus
+                                    + " with error code "
+                                    + mErrorCode
+                                    + ", not ScanCompleted"
+                                    + " or ScanError with an error code ERROR_MODEM_UNAVAILABLE or"
+                                    + " ERROR_UNSUPPORTED")
+                    .that(isScanStatusValid())
+                    .isTrue();
         } finally {
             getAndSetLocationSwitch(isLocationSwitchOn);
         }
@@ -376,32 +380,40 @@
             // Construct a NetworkScanRequest
             radioAccessSpecifier = getRadioAccessSpecifier(allCellInfo);
             if (!includeBandsAndChannels) {
-                radioAccessSpecifier = radioAccessSpecifier.stream().map(spec ->
-                    new RadioAccessSpecifier(spec.getRadioAccessNetwork(), null, null))
-                    .collect(Collectors.toList());
+                radioAccessSpecifier =
+                        radioAccessSpecifier.stream()
+                                .map(
+                                        spec ->
+                                                new RadioAccessSpecifier(
+                                                        spec.getRadioAccessNetwork(), null, null))
+                                .collect(Collectors.toList());
             }
         }
 
         Log.d(TAG, "number of radioAccessSpecifier: " + radioAccessSpecifier.size());
         if (radioAccessSpecifier.isEmpty()) {
             // Put in some arbitrary bands and channels so that we trip the location check if needed
-            int[] fakeBands = includeBandsAndChannels
-                    ? new int[] { AccessNetworkConstants.EutranBand.BAND_5 }
-                    : null;
-            int[] fakeChannels = includeBandsAndChannels ? new int[] { 2400 } : null;
+            int[] fakeBands =
+                    includeBandsAndChannels
+                            ? new int[] {AccessNetworkConstants.EutranBand.BAND_5}
+                            : null;
+            int[] fakeChannels = includeBandsAndChannels ? new int[] {2400} : null;
 
-            RadioAccessSpecifier gsm = new RadioAccessSpecifier(
-                    AccessNetworkConstants.AccessNetworkType.GERAN,
-                    null /* bands */,
-                    null /* channels */);
-            RadioAccessSpecifier lte = new RadioAccessSpecifier(
-                    AccessNetworkConstants.AccessNetworkType.EUTRAN,
-                    fakeBands /* bands */,
-                    fakeChannels /* channels */);
-            RadioAccessSpecifier wcdma = new RadioAccessSpecifier(
-                    AccessNetworkConstants.AccessNetworkType.UTRAN,
-                    null /* bands */,
-                    null /* channels */);
+            RadioAccessSpecifier gsm =
+                    new RadioAccessSpecifier(
+                            AccessNetworkConstants.AccessNetworkType.GERAN,
+                            null /* bands */,
+                            null /* channels */);
+            RadioAccessSpecifier lte =
+                    new RadioAccessSpecifier(
+                            AccessNetworkConstants.AccessNetworkType.EUTRAN,
+                            fakeBands /* bands */,
+                            fakeChannels /* channels */);
+            RadioAccessSpecifier wcdma =
+                    new RadioAccessSpecifier(
+                            AccessNetworkConstants.AccessNetworkType.UTRAN,
+                            null /* bands */,
+                            null /* channels */);
             radioAccessSpecifier.add(gsm);
             radioAccessSpecifier.add(lte);
             radioAccessSpecifier.add(wcdma);
@@ -416,7 +428,6 @@
                 true /*enable incremental results*/,
                 5 /* incremental results periodicity */,
                 null /* List of PLMN ids (MCC-MNC) */);
-
     }
 
     private List<CellInfo> getCellInfo() {
@@ -432,71 +443,87 @@
 
     @Test
     public void testNetworkScanPermission() {
-        PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        PackageManager pm = getContext().getPackageManager();
 
-        List<Integer> specialUids = Arrays.asList(Process.SYSTEM_UID,
-                Process.PHONE_UID, Process.SHELL_UID);
+        List<Integer> specialUids =
+                Arrays.asList(Process.SYSTEM_UID, Process.PHONE_UID, Process.SHELL_UID);
 
-        List<PackageInfo> holding = pm.getPackagesHoldingPermissions(
-                new String[] { NETWORK_SCAN_PERMISSION },
-                PackageManager.MATCH_DISABLED_COMPONENTS);
+        List<PackageInfo> holding =
+                pm.getPackagesHoldingPermissions(
+                        new String[] {NETWORK_SCAN_PERMISSION},
+                        PackageManager.MATCH_DISABLED_COMPONENTS);
 
-        List<Integer> nonSpecialPackages = holding.stream()
-                .map(pi -> {
-                    try {
-                        return pm.getPackageUid(pi.packageName, 0);
-                    } catch (PackageManager.NameNotFoundException e) {
-                        return Process.INVALID_UID;
-                    }
-                })
-                .filter(uid -> !specialUids.contains(UserHandle.getAppId(uid)))
-                .collect(Collectors.toList());
+        List<Integer> nonSpecialPackages =
+                holding.stream()
+                        .map(
+                                pi -> {
+                                    try {
+                                        return pm.getPackageUid(pi.packageName, 0);
+                                    } catch (PackageManager.NameNotFoundException e) {
+                                        return Process.INVALID_UID;
+                                    }
+                                })
+                        .filter(uid -> !specialUids.contains(UserHandle.getAppId(uid)))
+                        .collect(Collectors.toList());
 
-        if (nonSpecialPackages.size() > 1) {
-            fail("Only one app on the device is allowed to hold the NETWORK_SCAN permission.");
-        }
+        assertWithMessage(
+                        "Only one app on the device is allowed to hold the NETWORK_SCAN"
+                                + " permission.")
+                .that(nonSpecialPackages.size())
+                .isAtMost(1);
     }
 
     private boolean getAndSetLocationSwitch(boolean enabled) {
         CountDownLatch locationChangeLatch = new CountDownLatch(1);
-        ContentObserver settingsObserver = new ContentObserver(mHandler) {
-            @Override
-            public void onChange(boolean selfChange) {
-                locationChangeLatch.countDown();
-                super.onChange(selfChange);
-            }
-        };
+        ContentObserver settingsObserver =
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        locationChangeLatch.countDown();
+                        super.onChange(selfChange);
+                    }
+                };
 
-        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
                 .adoptShellPermissionIdentity();
+        ContentResolver contentResolver = getContext().getContentResolver();
         try {
-            int oldLocationMode = Settings.Secure.getInt(
-                    InstrumentationRegistry.getContext().getContentResolver(),
-                    Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
+            int oldLocationMode =
+                    Settings.Secure.getInt(
+                            contentResolver,
+                            Settings.Secure.LOCATION_MODE,
+                            Settings.Secure.LOCATION_MODE_OFF);
 
-            int locationMode = enabled ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
-                    : Settings.Secure.LOCATION_MODE_OFF;
+            int locationMode =
+                    enabled
+                            ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
+                            : Settings.Secure.LOCATION_MODE_OFF;
             if (locationMode != oldLocationMode) {
-                InstrumentationRegistry.getContext().getContentResolver().registerContentObserver(
+                contentResolver.registerContentObserver(
                         Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
-                        false, settingsObserver);
-                Settings.Secure.putInt(InstrumentationRegistry.getContext().getContentResolver(),
-                        Settings.Secure.LOCATION_MODE, locationMode);
+                        false,
+                        settingsObserver);
+                Settings.Secure.putInt(
+                        contentResolver, Settings.Secure.LOCATION_MODE, locationMode);
                 try {
-                    assertTrue(locationChangeLatch.await(LOCATION_SETTING_CHANGE_WAIT_MS,
-                            TimeUnit.MILLISECONDS));
+                    assertThat(
+                                    locationChangeLatch.await(
+                                            LOCATION_SETTING_CHANGE_WAIT_MS, TimeUnit.MILLISECONDS))
+                            .isTrue();
                 } catch (InterruptedException e) {
-                    Log.w(NetworkScanApiTest.class.getSimpleName(),
+                    Log.w(
+                            NetworkScanApiTest.class.getSimpleName(),
                             "Interrupted while waiting for location settings change. Test results"
-                            + " may not be accurate.");
+                                    + " may not be accurate.");
                 } finally {
-                    InstrumentationRegistry.getContext().getContentResolver()
-                            .unregisterContentObserver(settingsObserver);
+                    contentResolver.unregisterContentObserver(settingsObserver);
                 }
             }
             return oldLocationMode == Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
         } finally {
-            InstrumentationRegistry.getInstrumentation().getUiAutomation()
+            InstrumentationRegistry.getInstrumentation()
+                    .getUiAutomation()
                     .dropShellPermissionIdentity();
         }
     }
@@ -509,7 +536,7 @@
         }
         if ((mNetworkScanStatus == EVENT_NETWORK_SCAN_ERROR)
                 && ((mErrorCode == NetworkScan.ERROR_MODEM_UNAVAILABLE)
-                || (mErrorCode == NetworkScan.ERROR_UNSUPPORTED))) {
+                        || (mErrorCode == NetworkScan.ERROR_UNSUPPORTED))) {
             // Scan error but the error type is allowed.
             return true;
         }
@@ -523,130 +550,140 @@
         return mccMncs;
     }
 
-    /**
-     * To test its constructor and getters.
-     */
+    /** To test its constructor and getters. */
     @Test
-    public void testNetworkScanRequest_ConstructorAndGetters() {
-        NetworkScanRequest networkScanRequest = new NetworkScanRequest(
-                SCAN_TYPE,
-                RADIO_ACCESS_SPECIFIERS,
-                SEARCH_PERIODICITY_SEC,
-                MAX_SEARCH_TIME_SEC,
-                INCREMENTAL_RESULTS,
-                INCREMENTAL_RESULTS_PERIODICITY_SEC,
-                getPlmns());
+    public void testNetworkScanRequest_constructorAndGetters() {
+        NetworkScanRequest networkScanRequest =
+                new NetworkScanRequest(
+                        SCAN_TYPE,
+                        RADIO_ACCESS_SPECIFIERS,
+                        SEARCH_PERIODICITY_SEC,
+                        MAX_SEARCH_TIME_SEC,
+                        INCREMENTAL_RESULTS,
+                        INCREMENTAL_RESULTS_PERIODICITY_SEC,
+                        getPlmns());
 
-        assertEquals("getScanType() returns wrong value",
-                SCAN_TYPE, networkScanRequest.getScanType());
-        assertEquals("getSpecifiers() returns wrong value",
-                RADIO_ACCESS_SPECIFIERS, networkScanRequest.getSpecifiers());
-        assertEquals("getSearchPeriodicity() returns wrong value",
-                SEARCH_PERIODICITY_SEC, networkScanRequest.getSearchPeriodicity());
-        assertEquals("getMaxSearchTime() returns wrong value",
-                MAX_SEARCH_TIME_SEC, networkScanRequest.getMaxSearchTime());
-        assertEquals("getIncrementalResults() returns wrong value",
-                INCREMENTAL_RESULTS, networkScanRequest.getIncrementalResults());
-        assertEquals("getIncrementalResultsPeriodicity() returns wrong value",
-                INCREMENTAL_RESULTS_PERIODICITY_SEC,
-                networkScanRequest.getIncrementalResultsPeriodicity());
-        assertEquals("getPlmns() returns wrong value", getPlmns(), networkScanRequest.getPlmns());
-        assertEquals("describeContents() returns wrong value",
-                0, networkScanRequest.describeContents());
+        assertWithMessage("getScanType() returns wrong value")
+                .that(networkScanRequest.getScanType())
+                .isEqualTo(SCAN_TYPE);
+        assertWithMessage("getSpecifiers() returns wrong value")
+                .that(networkScanRequest.getSpecifiers())
+                .isEqualTo(RADIO_ACCESS_SPECIFIERS);
+        assertWithMessage("getSearchPeriodicity() returns wrong value")
+                .that(networkScanRequest.getSearchPeriodicity())
+                .isEqualTo(SEARCH_PERIODICITY_SEC);
+        assertWithMessage("getMaxSearchTime() returns wrong value")
+                .that(networkScanRequest.getMaxSearchTime())
+                .isEqualTo(MAX_SEARCH_TIME_SEC);
+        assertWithMessage("getIncrementalResults() returns wrong value")
+                .that(networkScanRequest.getIncrementalResults())
+                .isEqualTo(INCREMENTAL_RESULTS);
+        assertWithMessage("getIncrementalResultsPeriodicity() returns wrong value")
+                .that(networkScanRequest.getIncrementalResultsPeriodicity())
+                .isEqualTo(INCREMENTAL_RESULTS_PERIODICITY_SEC);
+        assertWithMessage("getPlmns() returns wrong value")
+                .that(networkScanRequest.getPlmns())
+                .isEqualTo(getPlmns());
+        assertWithMessage("describeContents() returns wrong value")
+                .that(networkScanRequest.describeContents())
+                .isEqualTo(0);
     }
 
-    /**
-     * To test its hashCode method.
-     */
+    /** To test its hashCode method. */
     @Test
-    public void testNetworkScanRequestParcel_Hashcode() {
-        NetworkScanRequest networkScanRequest1 = new NetworkScanRequest(
-                SCAN_TYPE,
-                RADIO_ACCESS_SPECIFIERS,
-                SEARCH_PERIODICITY_SEC,
-                MAX_SEARCH_TIME_SEC,
-                INCREMENTAL_RESULTS,
-                INCREMENTAL_RESULTS_PERIODICITY_SEC,
-                getPlmns());
+    public void testNetworkScanRequestParcel_hashCode() {
+        NetworkScanRequest networkScanRequest1 =
+                new NetworkScanRequest(
+                        SCAN_TYPE,
+                        RADIO_ACCESS_SPECIFIERS,
+                        SEARCH_PERIODICITY_SEC,
+                        MAX_SEARCH_TIME_SEC,
+                        INCREMENTAL_RESULTS,
+                        INCREMENTAL_RESULTS_PERIODICITY_SEC,
+                        getPlmns());
 
-        NetworkScanRequest networkScanRequest2 = new NetworkScanRequest(
-                SCAN_TYPE,
-                RADIO_ACCESS_SPECIFIERS,
-                SEARCH_PERIODICITY_SEC,
-                MAX_SEARCH_TIME_SEC,
-                INCREMENTAL_RESULTS,
-                INCREMENTAL_RESULTS_PERIODICITY_SEC,
-                getPlmns());
+        NetworkScanRequest networkScanRequest2 =
+                new NetworkScanRequest(
+                        SCAN_TYPE,
+                        RADIO_ACCESS_SPECIFIERS,
+                        SEARCH_PERIODICITY_SEC,
+                        MAX_SEARCH_TIME_SEC,
+                        INCREMENTAL_RESULTS,
+                        INCREMENTAL_RESULTS_PERIODICITY_SEC,
+                        getPlmns());
 
-        NetworkScanRequest networkScanRequest3 = new NetworkScanRequest(
-                SCAN_TYPE,
-                null,
-                SEARCH_PERIODICITY_SEC,
-                MAX_SEARCH_TIME_SEC,
-                false,
-                0,
-                getPlmns());
+        NetworkScanRequest networkScanRequest3 =
+                new NetworkScanRequest(
+                        SCAN_TYPE,
+                        null,
+                        SEARCH_PERIODICITY_SEC,
+                        MAX_SEARCH_TIME_SEC,
+                        false,
+                        0,
+                        getPlmns());
 
-        assertEquals("hashCode() returns different hash code for same objects",
-                networkScanRequest1.hashCode(), networkScanRequest2.hashCode());
-        assertNotSame("hashCode() returns same hash code for different objects",
-                networkScanRequest1.hashCode(), networkScanRequest3.hashCode());
+        assertWithMessage("hashCode() returns different hash code for same objects")
+                .that(networkScanRequest1.hashCode())
+                .isEqualTo(networkScanRequest2.hashCode());
+        assertWithMessage("hashCode() returns same hash code for different objects")
+                .that(networkScanRequest1.hashCode())
+                .isNotEqualTo(networkScanRequest3.hashCode());
     }
 
-    /**
-     * To test its comparision method.
-     */
+    /** To test its comparison method. */
     @Test
-    public void testNetworkScanRequestParcel_Equals() {
-        NetworkScanRequest networkScanRequest1 = new NetworkScanRequest(
-                SCAN_TYPE,
-                RADIO_ACCESS_SPECIFIERS,
-                SEARCH_PERIODICITY_SEC,
-                MAX_SEARCH_TIME_SEC,
-                INCREMENTAL_RESULTS,
-                INCREMENTAL_RESULTS_PERIODICITY_SEC,
-                getPlmns());
+    public void testNetworkScanRequestParcel_equals() {
+        NetworkScanRequest networkScanRequest1 =
+                new NetworkScanRequest(
+                        SCAN_TYPE,
+                        RADIO_ACCESS_SPECIFIERS,
+                        SEARCH_PERIODICITY_SEC,
+                        MAX_SEARCH_TIME_SEC,
+                        INCREMENTAL_RESULTS,
+                        INCREMENTAL_RESULTS_PERIODICITY_SEC,
+                        getPlmns());
 
-        NetworkScanRequest networkScanRequest2 = new NetworkScanRequest(
-                SCAN_TYPE,
-                RADIO_ACCESS_SPECIFIERS,
-                SEARCH_PERIODICITY_SEC,
-                MAX_SEARCH_TIME_SEC,
-                INCREMENTAL_RESULTS,
-                INCREMENTAL_RESULTS_PERIODICITY_SEC,
-                getPlmns());
+        NetworkScanRequest networkScanRequest2 =
+                new NetworkScanRequest(
+                        SCAN_TYPE,
+                        RADIO_ACCESS_SPECIFIERS,
+                        SEARCH_PERIODICITY_SEC,
+                        MAX_SEARCH_TIME_SEC,
+                        INCREMENTAL_RESULTS,
+                        INCREMENTAL_RESULTS_PERIODICITY_SEC,
+                        getPlmns());
 
-        assertTrue(networkScanRequest1.equals(networkScanRequest2));
+        assertThat(networkScanRequest1).isEqualTo(networkScanRequest2);
 
-        networkScanRequest2 = new NetworkScanRequest(
-                SCAN_TYPE,
-                RADIO_ACCESS_SPECIFIERS,
-                SEARCH_PERIODICITY_SEC,
-                MAX_SEARCH_TIME_SEC,
-                INCREMENTAL_RESULTS,
-                INCREMENTAL_RESULTS_PERIODICITY_SEC,
-                null /* List of PLMN ids (MCC-MNC) */);
-        assertFalse(networkScanRequest1.equals(networkScanRequest2));
+        networkScanRequest2 =
+                new NetworkScanRequest(
+                        SCAN_TYPE,
+                        RADIO_ACCESS_SPECIFIERS,
+                        SEARCH_PERIODICITY_SEC,
+                        MAX_SEARCH_TIME_SEC,
+                        INCREMENTAL_RESULTS,
+                        INCREMENTAL_RESULTS_PERIODICITY_SEC,
+                        null /* List of PLMN ids (MCC-MNC) */);
+        assertThat(networkScanRequest1).isNotEqualTo(networkScanRequest2);
     }
 
-    /**
-     * To test its writeToParcel and createFromParcel methods.
-     */
+    /** To test its writeToParcel and createFromParcel methods. */
     @Test
-    public void testNetworkScanRequestParcel_Parcel() {
-        NetworkScanRequest networkScanRequest = new NetworkScanRequest(
-                SCAN_TYPE,
-                null /* Radio Access Specifier */,
-                SEARCH_PERIODICITY_SEC,
-                MAX_SEARCH_TIME_SEC,
-                INCREMENTAL_RESULTS,
-                INCREMENTAL_RESULTS_PERIODICITY_SEC,
-                getPlmns());
+    public void testNetworkScanRequestParcel_parcel() {
+        NetworkScanRequest networkScanRequest =
+                new NetworkScanRequest(
+                        SCAN_TYPE,
+                        null /* Radio Access Specifier */,
+                        SEARCH_PERIODICITY_SEC,
+                        MAX_SEARCH_TIME_SEC,
+                        INCREMENTAL_RESULTS,
+                        INCREMENTAL_RESULTS_PERIODICITY_SEC,
+                        getPlmns());
 
         Parcel p = Parcel.obtain();
         networkScanRequest.writeToParcel(p, 0);
         p.setDataPosition(0);
         NetworkScanRequest newnsr = NetworkScanRequest.CREATOR.createFromParcel(p);
-        assertTrue(networkScanRequest.equals(newnsr));
+        assertThat(networkScanRequest).isEqualTo(newnsr);
     }
 }
diff --git a/tests/tests/content/OWNERS b/tests/tests/content/OWNERS
index 12c955a..737382c 100644
--- a/tests/tests/content/OWNERS
+++ b/tests/tests/content/OWNERS
@@ -3,3 +3,4 @@
 patb@google.com
 schfan@google.com
 alexbuy@google.com
+rtmitchell@google.com
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 8ae80df..03a638c 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -494,6 +494,9 @@
     }
 
     public void testPowerUsageSummarySettings() {
+        if(FeatureUtil.isWatch()){
+            return;
+        }
         if (isBatteryPresent()) {
             assertCanBeHandled(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
         }
@@ -517,6 +520,9 @@
     }
 
     public void testRequestSetAutofillServiceIntent() {
+        if (FeatureUtil.isWatch()) {
+            return;
+        }
         Intent intent = new Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE)
                 .setData(Uri.parse("package:android.content.cts"));
         assertCanBeHandled(intent);
diff --git a/tests/tests/content/src/android/content/cts/ClipboardManagerTest.java b/tests/tests/content/src/android/content/cts/ClipboardManagerTest.java
index 4d4c079..121999e 100644
--- a/tests/tests/content/src/android/content/cts/ClipboardManagerTest.java
+++ b/tests/tests/content/src/android/content/cts/ClipboardManagerTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.app.Activity;
 import android.content.ClipData;
@@ -31,6 +32,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.UiDevice;
@@ -49,13 +51,13 @@
 @RunWith(AndroidJUnit4.class)
 //@AppModeFull // TODO(Instant) Should clip board data be visible?
 public class ClipboardManagerTest {
-    private Context mContext;
+    private final Context mContext = InstrumentationRegistry.getTargetContext();
     private ClipboardManager mClipboardManager;
     private UiDevice mUiDevice;
 
     @Before
     public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
+        assumeTrue("Skipping Test: Wear-Os does not support ClipboardService", hasAutoFillFeature());
         mClipboardManager = mContext.getSystemService(ClipboardManager.class);
         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         mUiDevice.wakeUp();
@@ -365,4 +367,9 @@
             assertNull(item.getUri());
         }
     }
+
+    private boolean hasAutoFillFeature() {
+        return mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOFILL);
+    }
 }
diff --git a/tests/tests/content/src/android/content/cts/wm/OWNERS b/tests/tests/content/src/android/content/cts/wm/OWNERS
deleted file mode 100644
index 940ab87..0000000
--- a/tests/tests/content/src/android/content/cts/wm/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-include /tests/framework/base/windowmanager/OWNERS
-charlesccchen@google.com
diff --git a/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java b/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java
index 358a894..20752f1 100644
--- a/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/ApplicationInfoTest.java
@@ -30,11 +30,14 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNotNull;
 
 import android.content.Context;
 import android.content.cts.R;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Environment;
 import android.os.Parcel;
 import android.os.Process;
 import android.os.UserHandle;
@@ -44,6 +47,8 @@
 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;
@@ -275,4 +280,55 @@
 
         p.restoreAllowSquashing(prevSquashing);
     }
+
+    @Test
+    public void testIsProduct() throws Exception {
+        final String packageName = getPartitionFirstPackageName(
+                Environment.getProductDirectory().getAbsolutePath());
+        assertNotNull(packageName);
+
+        final PackageInfo info = getContext().getPackageManager().getPackageInfo(
+                packageName.trim(), 0 /* flags */);
+        assertTrue(info.applicationInfo.isProduct());
+    }
+
+    @Test
+    public void testIsVendor() throws Exception {
+        final String packageName = getPartitionFirstPackageName(
+                Environment.getVendorDirectory().getAbsolutePath());
+        assertNotNull(packageName);
+
+        final PackageInfo info = getContext().getPackageManager().getPackageInfo(
+                packageName.trim(), 0 /* flags */);
+        assertTrue(info.applicationInfo.isVendor());
+    }
+
+    @Test
+    public void testIsOem() throws Exception {
+        final String packageName = getPartitionFirstPackageName(
+                Environment.getOemDirectory().getAbsolutePath());
+        // Oem package may not exist in every builds like aosp.
+        assumeNotNull(packageName);
+
+        final PackageInfo info = getContext().getPackageManager().getPackageInfo(
+                packageName.trim(), 0 /* flags */);
+        assertTrue(info.applicationInfo.isOem());
+    }
+
+    private String getPartitionFirstPackageName(final String partition) throws Exception {
+        // List package with "-f" option which contains package direction, use that to distinguish
+        // package partition and find out target package.
+        final String output = SystemUtil.runShellCommand(
+                InstrumentationRegistry.getInstrumentation(), "pm list package -f -s");
+        final String[] packages = output.split("package:");
+        for (int i = 0; i < packages.length; i++) {
+            // Split package info to direction and name.
+            String[] info = packages[i].split("\\.apk=");
+            if (info.length != 2) continue; // Package info need include direction and name.
+            if (info[0] != null && info[0].startsWith(partition)) {
+                return info[1]; // Package name.
+            }
+        }
+        return null;
+    }
 }
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
index 75f7cc4..3db58e2 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
@@ -29,7 +29,6 @@
 import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.AppModeFull;
 import android.service.dataloader.DataLoaderService;
-import android.text.TextUtils;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
@@ -53,6 +52,9 @@
 import java.io.Reader;
 import java.util.Arrays;
 import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -233,28 +235,38 @@
 
     @LargeTest
     @Test
-    public void testInstallSysTrace() throws Exception {
-        // Async atrace dump uses less resources but requires periodic pulls.
-        // Overall timeout of 30secs in 100ms intervals should be enough.
-        final int atraceDumpIterations = 300;
-        final int atraceDumpDelayMs = 100;
+    public void testInstallSysTraceDebuggable() throws Exception {
+        doTestInstallSysTrace(TEST_APK);
+    }
 
-        final String expected = "|page_read:";
-        final ByteArrayOutputStream result = new ByteArrayOutputStream();
+    private boolean checkSysTraceForSubstring(String testApk, final String expected,
+            int atraceDumpIterations, int atraceDumpDelayMs) throws Exception {
+        return checkSysTrace(
+                atraceDumpIterations,
+                atraceDumpDelayMs,
+                () -> installPackage(testApk),
+                (stdout) -> stdout.contains(expected));
+    }
+
+    private boolean checkSysTrace(
+            int atraceDumpIterations,
+            int atraceDumpDelayMs,
+            final Callable<Void> installer,
+            final Function<String, Boolean> checker)
+            throws Exception {
+        final int beforeReadDelayMs = 1000;
+
+        final CompletableFuture<Boolean> result = new CompletableFuture<>();
         final Thread readFromProcess = new Thread(() -> {
             try {
                 executeShellCommand("atrace --async_start -b 1024 -c adb");
                 try {
                     for (int i = 0; i < atraceDumpIterations; ++i) {
-                        final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand(
-                                "atrace --async_dump");
-                        try (InputStream inputStream =
-                                     new ParcelFileDescriptor.AutoCloseInputStream(
-                                stdout)) {
-                            final String found = waitForSubstring(inputStream, expected);
-                            if (!TextUtils.isEmpty(found)) {
-                                result.write(found.getBytes());
-                                return;
+                        final String stdout = executeShellCommand("atrace --async_dump");
+                        try {
+                            if (checker.apply(stdout)) {
+                                result.complete(true);
+                                break;
                             }
                             Thread.currentThread().sleep(atraceDumpDelayMs);
                         } catch (InterruptedException ignored) {
@@ -269,13 +281,27 @@
         readFromProcess.start();
 
         for (int i = 0; i < 3; ++i) {
-            installPackage(TEST_APK);
+            installer.call();
             assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+            Thread.currentThread().sleep(beforeReadDelayMs);
             uninstallPackageSilently(TEST_APP_PACKAGE);
         }
 
         readFromProcess.join();
-        assertNotEquals(0, result.size());
+        return result.getNow(false);
+    }
+
+    private void doTestInstallSysTrace(String testApk) throws Exception {
+        // Async atrace dump uses less resources but requires periodic pulls.
+        // Overall timeout of 10secs in 100ms intervals should be enough.
+        final int atraceDumpIterations = 100;
+        final int atraceDumpDelayMs = 100;
+        final String expected = "|page_read:";
+
+        assertTrue(
+                "No page reads (" + expected + ") found in atrace dump",
+                checkSysTraceForSubstring(testApk, expected, atraceDumpIterations,
+                        atraceDumpDelayMs));
     }
 
     private boolean isAppInstalled(String packageName) throws IOException {
@@ -302,10 +328,11 @@
         return TEST_APK_PATH + baseName;
     }
 
-    private void installPackage(String baseName) throws IOException {
+    private Void installPackage(String baseName) throws IOException {
         File file = new File(createApkPath(baseName));
         assertEquals("Success\n",
                 executeShellCommand("pm install-incremental -t -g " + file.getPath()));
+        return null;
     }
 
     private String uninstallPackageSilently(String packageName) throws IOException {
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index 726e5de..5719872 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -16,6 +16,7 @@
 
 package android.content.pm.cts;
 
+import static android.Manifest.permission.INSTALL_TEST_ONLY_PACKAGE;
 import static android.content.pm.ApplicationInfo.FLAG_HAS_CODE;
 import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
@@ -108,6 +109,7 @@
     private static final int NUM_OF_ACTIVITIES_IN_MANIFEST = 12;
 
     private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
+    private static final String SHELL_PACKAGE_NAME = "com.android.shell";
 
     @Before
     public void setup() throws Exception {
@@ -990,4 +992,13 @@
         assertThat(packageInfo.signatures)
                 .asList().containsExactly((Object[]) pastSigningCertificates);
     }
+
+    @Test
+    public void testInstallTestOnlyPackagePermission_onlyGrantedToShell() {
+        List<PackageInfo> packages = mPackageManager.getPackagesHoldingPermissions(
+                new String[]{INSTALL_TEST_ONLY_PACKAGE}, /* flags= */ 0);
+
+        assertThat(packages).hasSize(1);
+        assertThat(packages.get(0).packageName).isEqualTo(SHELL_PACKAGE_NAME);
+    }
 }
diff --git a/tests/tests/content/src/android/content/wm/cts/OWNERS b/tests/tests/content/src/android/content/wm/cts/OWNERS
new file mode 100644
index 0000000..5ed5c56
--- /dev/null
+++ b/tests/tests/content/src/android/content/wm/cts/OWNERS
@@ -0,0 +1,2 @@
+include platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
+charlesccchen@google.com
diff --git a/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp b/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
index c9f557f53..8d50dfd 100644
--- a/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
+++ b/tests/tests/graphics/jni/android_graphics_cts_VulkanPreTransformCtsActivity.cpp
@@ -65,9 +65,10 @@
     for (uint32_t i = 0; i < 120; ++i) {
         ret = renderer.drawFrame();
         if (setPreTransform || preTransformHint == 0x1 /*VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR*/) {
-            ASSERT(ret == VK_TEST_SUCCESS, "Failed to draw frame");
+            ASSERT(ret == VK_TEST_SUCCESS, "Failed to draw frame(%u) ret(%d)", i, (int)ret);
         } else {
-            ASSERT(ret == VK_TEST_SUCCESS_SUBOPTIMAL, "Failed to draw suboptimal frame");
+            ASSERT(ret == VK_TEST_SUCCESS_SUBOPTIMAL, "Failed to draw suboptimal frame(%u) ret(%d)",
+                   i, (int)ret);
         }
     }
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index 0433205..ac3e111 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -2144,15 +2144,15 @@
         Debug.getMemoryInfo(end);
         assertNotEquals(0, start.getTotalPss());
         assertNotEquals(0, end.getTotalPss());
-        if (end.getTotalPss() - start.getTotalPss() > 2000 /* kB */) {
+        if (end.getTotalPss() - start.getTotalPss() > 5000 /* kB */) {
             runGcAndFinalizersSync();
             Debug.getMemoryInfo(end);
-            if (end.getTotalPss() - start.getTotalPss() > 4000 /* kB */) {
+            if (end.getTotalPss() - start.getTotalPss() > 7000 /* kB */) {
                 // Guarded by if so we don't continually generate garbage for the
                 // assertion string.
                 assertEquals("Memory leaked, iteration=" + iteration,
                         start.getTotalPss(), end.getTotalPss(),
-                        4000 /* kb */);
+                        7000 /* kb */);
             }
         }
     }
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java
index 14d3000..f14e60c 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanPreTransformTest.java
@@ -120,6 +120,7 @@
             return;
         }
         sActivity = mActivityRule.launchActivity(null);
+        SystemClock.sleep(5000);
         sActivity.testVulkanPreTransform(true);
         sActivity.finish();
         sActivity = null;
@@ -134,6 +135,7 @@
             return;
         }
         sActivity = mActivityRule.launchActivity(null);
+        SystemClock.sleep(5000);
         sActivity.testVulkanPreTransform(false);
         sActivity.finish();
         sActivity = null;
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java b/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java
index 028b18e..6a94327 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/InputCtsActivity.java
@@ -21,10 +21,13 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import java.util.function.Consumer;
+
 public class InputCtsActivity extends Activity {
     private static final String TAG = "InputCtsActivity";
 
     private InputCallback mInputCallback;
+    private Consumer<Boolean> mPointerCaptureCallback;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -63,7 +66,18 @@
         return true;
     }
 
+    @Override
+    public void onPointerCaptureChanged(boolean hasCapture) {
+        if (mPointerCaptureCallback != null) {
+            mPointerCaptureCallback.accept(hasCapture);
+        }
+    }
+
     public void setInputCallback(InputCallback callback) {
         mInputCallback = callback;
     }
+
+    public void setPointerCaptureCallback(Consumer<Boolean> callback) {
+        mPointerCaptureCallback = callback;
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
index 6239026..2113306 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
@@ -32,6 +32,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 
+import com.android.compatibility.common.util.PollingCheck;
 import com.android.cts.input.HidDevice;
 import com.android.cts.input.HidJsonParser;
 import com.android.cts.input.HidTestData;
@@ -43,6 +44,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -337,22 +339,50 @@
         }
     }
 
+    protected void requestFocusSync() {
+        mActivityRule.getActivity().runOnUiThread(() -> {
+            mDecorView.setFocusable(true);
+            mDecorView.setFocusableInTouchMode(true);
+            mDecorView.requestFocus();
+        });
+        PollingCheck.waitFor(mDecorView::hasFocus);
+    }
+
     protected class PointerCaptureSession implements AutoCloseable {
         protected PointerCaptureSession() {
-            requestPointerCaptureSync();
+            requestFocusSync();
+            ensurePointerCaptureState(true);
         }
 
         @Override
         public void close() {
-            releasePointerCaptureSync();
+            ensurePointerCaptureState(false);
         }
 
-        private void requestPointerCaptureSync() {
-            mInstrumentation.runOnMainSync(mDecorView::requestPointerCapture);
-        }
-
-        private void releasePointerCaptureSync() {
-            mInstrumentation.runOnMainSync(mDecorView::releasePointerCapture);
+        private void ensurePointerCaptureState(boolean enable) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            mActivityRule.getActivity().setPointerCaptureCallback(hasCapture -> {
+                if (enable == hasCapture) {
+                    latch.countDown();
+                }
+            });
+            mActivityRule.getActivity().runOnUiThread(enable ? mDecorView::requestPointerCapture
+                    : mDecorView::releasePointerCapture);
+            try {
+                if (!latch.await(60, TimeUnit.SECONDS)) {
+                    throw new IllegalStateException(
+                            "Did not receive callback after "
+                                    + (enable ? "enabling" : "disabling")
+                                    + " Pointer Capture.");
+                }
+            } catch (InterruptedException e) {
+                throw new IllegalStateException(
+                        "Interrupted while waiting for Pointer Capture state.");
+            } finally {
+                mActivityRule.getActivity().setPointerCaptureCallback(null);
+            }
+            assertEquals("The view's Pointer Capture state did not match.", enable,
+                    mDecorView.hasPointerCapture());
         }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/NintendoSwitchProTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/NintendoSwitchProTest.java
index c826b59..1e3c3b0 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/NintendoSwitchProTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/NintendoSwitchProTest.java
@@ -16,6 +16,10 @@
 
 package android.hardware.input.cts.tests;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assume.assumeFalse;
+
+import android.content.pm.PackageManager;
 import android.hardware.cts.R;
 import android.os.SystemClock;
 
@@ -49,11 +53,18 @@
 
     @Test
     public void testAllKeys() {
+        assumeFalse("Skipping test for wear devices", isWatch());
         testInputEvents(R.raw.nintendo_switchpro_keyeventtests);
     }
 
     @Test
     public void testAllMotions() {
+        assumeFalse("Skipping test for wear devices", isWatch());
         testInputEvents(R.raw.nintendo_switchpro_motioneventtests);
     }
+
+    static boolean isWatch() {
+        final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
 }
diff --git a/tests/tests/icu/CtsIcu4cTestCases.xml b/tests/tests/icu/CtsIcu4cTestCases.xml
index c216b3a..3ef1629 100644
--- a/tests/tests/icu/CtsIcu4cTestCases.xml
+++ b/tests/tests/icu/CtsIcu4cTestCases.xml
@@ -36,8 +36,8 @@
         <option name="push" value="icu4c_test_data.zip->/data/local/tmp/icu4c_test_data.zip" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="mkdir -p /data/local/tmp/test/testdata &amp;&amp; unzip -o -d /data/local/tmp/test/testdata /data/local/tmp/icu4c_test_data.zip" />
-        <option name="teardown-command" value="rm -r /data/local/tmp/test/testdata" />
+        <option name="run-command" value="unzip -o -d /data/local/tmp/ /data/local/tmp/icu4c_test_data.zip" />
+        <option name="teardown-command" value="rm -r /data/local/tmp/test /data/local/tmp/data" />
     </target_preparer>
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/icu/resources/android/icu/cts/expectations/icu-known-failures.txt b/tests/tests/icu/resources/android/icu/cts/expectations/icu-known-failures.txt
index e68fcd9..1b1535a 100644
--- a/tests/tests/icu/resources/android/icu/cts/expectations/icu-known-failures.txt
+++ b/tests/tests/icu/resources/android/icu/cts/expectations/icu-known-failures.txt
@@ -2,14 +2,6 @@
  * This file contains expectations for tests that are known to fail.
  */
 [
-// Uncomment this to exclude all tests and then add result: "SUCCESS" to any specific tests that
-// need to be run.
-/*
-{
-  description: "Exclude all tests",
-  name: "android.icu.dev.test"
-},
-*/
 {
   description: "Serialized forms have not been converted to use repackaged classes",
   name: "android.icu.dev.test.format.NumberFormatRegressionTest#TestSerialization",
@@ -19,22 +11,5 @@
   description: "Checks differences in DecimalFormat classes from ICU4J and JDK but on Android java.text.DecimalFormat is implemented in terms of ICU4J",
   name: "android.icu.dev.test.format.NumberFormatTest#TestDataDrivenJDK",
   bug: "27711713"
-},
-{
-  description: "Collation rules data has been removed from ICU4J data on Android",
-  names: [
-    "android.icu.dev.test.collator.CollationCreationMethodTest#TestRuleVsLocaleCreationMonkey",
-    "android.icu.dev.test.collator.CollationMiscTest#TestImport",
-    "android.icu.dev.test.collator.CollationMiscTest#TestImportWithType",
-    "android.icu.dev.test.collator.CollationMiscTest#TestUCARules",
-    "android.icu.dev.test.collator.CollationTest#TestDataDriven",
-    "android.icu.dev.test.collator.G7CollationTest#TestG7Data"
-  ],
-  bug: "27552651"
-},
-{
-  description: "Unknown Language != Unknown language",
-  name: "android.icu.dev.test.TestLocaleNamePackaging#testLanguageDisplayNames",
-  bug: "33447162"
 }
 ]
diff --git a/tests/tests/identity/TEST_MAPPING b/tests/tests/identity/TEST_MAPPING
new file mode 100644
index 0000000..87707a8
--- /dev/null
+++ b/tests/tests/identity/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsIdentityTestCases"
+    }
+  ]
+}
diff --git a/tests/tests/identity/src/android/security/identity/cts/UserAuthTest.java b/tests/tests/identity/src/android/security/identity/cts/UserAuthTest.java
index cc44f36..857ced0 100644
--- a/tests/tests/identity/src/android/security/identity/cts/UserAuthTest.java
+++ b/tests/tests/identity/src/android/security/identity/cts/UserAuthTest.java
@@ -118,6 +118,13 @@
             mLockCredential.gotoKeyguard();
             mLockCredential.enterAndConfirmLockCredential();
             launchHomeActivity();
+            Context appContext = InstrumentationRegistry.getTargetContext();
+            KeyguardManager keyguardManager = (KeyguardManager)appContext.
+                                              getSystemService(Context.KEYGUARD_SERVICE);
+            for (int i = 0; i < 5 && keyguardManager.isDeviceLocked(); i++) {
+                Log.w(TAG, "Wait for keyguardManager unlock device ...");
+                SystemClock.sleep(1000);
+            }
         }
 
         @Override
diff --git a/tests/tests/jni/libjnitest/Android.bp b/tests/tests/jni/libjnitest/Android.bp
index de15f35..5554d96 100644
--- a/tests/tests/jni/libjnitest/Android.bp
+++ b/tests/tests/jni/libjnitest/Android.bp
@@ -33,11 +33,11 @@
         "helper.c",
         "register.c",
     ],
+    header_libs: ["libnativehelper_header_only"],
     static_libs: ["libbase_ndk"],
     shared_libs: [
         "libdl",
         "liblog",
-        "libnativehelper_compat_libc++",
     ],
     sdk_version: "current",
     stl: "c++_static",
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_BasicLoaderTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_BasicLoaderTest.cpp
index 704cd4f..8fc8d4e 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_BasicLoaderTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_BasicLoaderTest.cpp
@@ -19,9 +19,11 @@
  */
 
 #include <dlfcn.h>
+#include <errno.h>
 #include <jni.h>
 #include <libgen.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -29,9 +31,8 @@
 
 #include <string>
 
-#include "nativehelper/JNIHelp.h"
-#include "nativehelper/ScopedLocalRef.h"
-#include "nativehelper/ScopedUtfChars.h"
+#include "nativehelper/scoped_local_ref.h"
+#include "nativehelper/scoped_utf_chars.h"
 
 static constexpr const char* kTestLibName = "libjni_test_dlclose.so";
 
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_InstanceNonce.c b/tests/tests/jni/libjnitest/android_jni_cts_InstanceNonce.c
index cb44b42a..597f03b 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_InstanceNonce.c
+++ b/tests/tests/jni/libjnitest/android_jni_cts_InstanceNonce.c
@@ -20,11 +20,12 @@
  */
 
 #include <jni.h>
-#include <nativehelper/JNIHelp.h>
 
 #include <stdbool.h>
 #include <string.h>
 
+#include "helper.h"
+
 // public native void nop();
 static void InstanceNonce_nop(JNIEnv *env, jobject this) {
     // This space intentionally left blank.
@@ -107,8 +108,7 @@
     }
 
     if (stringClass == NULL) {
-        jniThrowException(env, "java/lang/AssertionError",
-                "class String not found");
+        throwException(env, "java/lang/AssertionError", "class String not found");
         return NULL;
     }
 
@@ -236,8 +236,7 @@
     length = (*env)->GetStringUTFLength(env, v6);
 
     if (length != 3) {
-        jniThrowException(env, "java/lang/AssertionError",
-                "bad string length");
+        throwException(env, "java/lang/AssertionError", "bad string length");
         return false;
     }
 
@@ -252,8 +251,7 @@
 
     length = (*env)->GetArrayLength(env, v9);
     if (length != 2) {
-        jniThrowException(env, "java/lang/AssertionError",
-                "bad array length");
+        throwException(env, "java/lang/AssertionError", "bad array length");
         return false;
     }
 
@@ -347,7 +345,7 @@
 };
 
 int register_InstanceNonce(JNIEnv *env) {
-    return jniRegisterNativeMethods(
+    return registerJniMethods(
             env, "android/jni/cts/InstanceNonce",
             methods, sizeof(methods) / sizeof(JNINativeMethod));
 }
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_JniCTest.c b/tests/tests/jni/libjnitest/android_jni_cts_JniCTest.c
index cca5383..4b2e294 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_JniCTest.c
+++ b/tests/tests/jni/libjnitest/android_jni_cts_JniCTest.c
@@ -19,7 +19,6 @@
  */
 
 #include <jni.h>
-#include <nativehelper/JNIHelp.h>
 
 
 /*
@@ -42,7 +41,7 @@
 };
 
 int register_JniCTest(JNIEnv *env) {
-    return jniRegisterNativeMethods(
+    return registerJniMethods(
             env, "android/jni/cts/JniCTest",
             methods, sizeof(methods) / sizeof(JNINativeMethod));
 }
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_JniCppTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_JniCppTest.cpp
index b0937f41..df619be 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_JniCppTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_JniCppTest.cpp
@@ -19,7 +19,6 @@
  */
 
 #include <jni.h>
-#include <nativehelper/JNIHelp.h>
 
 
 /*
@@ -42,7 +41,7 @@
 };
 
 extern "C" int register_JniCppTest(JNIEnv *env) {
-    return jniRegisterNativeMethods(
+    return registerJniMethods(
             env, "android/jni/cts/JniCppTest",
             methods, sizeof(methods) / sizeof(JNINativeMethod));
 }
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp
index aa5651f..7aa54e2 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp
@@ -19,7 +19,6 @@
  */
 
 #include <jni.h>
-#include <nativehelper/JNIHelp.h>
 
 extern "C" JNIEXPORT jint JNICALL Java_android_jni_cts_ClassLoaderHelper_nativeGetHashCode(
         JNIEnv* env,
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index 2bd24b4..3128648 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -20,10 +20,12 @@
 
 #include <dirent.h>
 #include <dlfcn.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <jni.h>
 #include <libgen.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -37,9 +39,8 @@
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/scoped_local_ref.h>
+#include <nativehelper/scoped_utf_chars.h>
 
 #if defined(__LP64__)
 #define LIB_DIR "lib64"
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_StaticNonce.c b/tests/tests/jni/libjnitest/android_jni_cts_StaticNonce.c
index 4e330e5..7fb7f11 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_StaticNonce.c
+++ b/tests/tests/jni/libjnitest/android_jni_cts_StaticNonce.c
@@ -20,11 +20,12 @@
  */
 
 #include <jni.h>
-#include <nativehelper/JNIHelp.h>
 
 #include <stdbool.h>
 #include <string.h>
 
+#include "helper.h"
+
 // public static native void nop();
 static void StaticNonce_nop(JNIEnv *env, jclass clazz) {
     // This space intentionally left blank.
@@ -106,7 +107,7 @@
     }
 
     if (stringClass == NULL) {
-        jniThrowException(env, "java/lang/AssertionError",
+        throwException(env, "java/lang/AssertionError",
                 "class String not found");
         return NULL;
     }
@@ -149,7 +150,7 @@
     }
     
     if (id == NULL) {
-        jniThrowException(env, "java/lang/AssertionError",
+        throwException(env, "java/lang/AssertionError",
                 "constructor not found");
         return NULL;
     }
@@ -251,7 +252,7 @@
     length = (*env)->GetStringUTFLength(env, v6);
 
     if (length != 3) {
-        jniThrowException(env, "java/lang/AssertionError",
+        throwException(env, "java/lang/AssertionError",
                 "bad string length");
         return false;
     }
@@ -267,7 +268,7 @@
 
     length = (*env)->GetArrayLength(env, v9);
     if (length != 2) {
-        jniThrowException(env, "java/lang/AssertionError",
+        throwException(env, "java/lang/AssertionError",
                 "bad array length");
         return false;
     }
@@ -362,7 +363,7 @@
 };
 
 int register_StaticNonce(JNIEnv *env) {
-    return jniRegisterNativeMethods(
+    return registerJniMethods(
             env, "android/jni/cts/StaticNonce",
             methods, sizeof(methods) / sizeof(JNINativeMethod));
 }
diff --git a/tests/tests/jni/libjnitest/helper.c b/tests/tests/jni/libjnitest/helper.c
index 2281795..cf45911 100644
--- a/tests/tests/jni/libjnitest/helper.c
+++ b/tests/tests/jni/libjnitest/helper.c
@@ -87,3 +87,30 @@
 
     return result;
 }
+
+int registerJniMethods(JNIEnv* env, const char* className, const JNINativeMethod* methods,
+                       int numMethods)
+{
+    __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG,
+                        "Registering %s's %d native methods...", className, numMethods);
+    jclass clazz = (*env)->FindClass(env, className);
+    int result = (*env)->RegisterNatives(env, clazz, methods, numMethods);
+    (*env)->DeleteLocalRef(env, clazz);
+    if (result == 0) {
+        return 0;
+    }
+
+    __android_log_print(ANDROID_LOG_FATAL, LOG_TAG,
+                        "Failed to register JNI methods for %s: %d\n", className, result);
+    return result;
+}
+
+void throwException(JNIEnv* env, const char* className, const char* message) {
+    jclass clazz = (*env)->FindClass(env, className);
+    int result = (*env)->ThrowNew(env, clazz, message);
+    if (result != 0) {
+        __android_log_print(ANDROID_LOG_FATAL, LOG_TAG,
+                            "Failed to throw %s: d\n", className, result);
+
+    }
+}
diff --git a/tests/tests/jni/libjnitest/helper.h b/tests/tests/jni/libjnitest/helper.h
index f811055..69965fc 100644
--- a/tests/tests/jni/libjnitest/helper.h
+++ b/tests/tests/jni/libjnitest/helper.h
@@ -60,6 +60,26 @@
  */
 char *runJniTests(JNIEnv *env, ...);
 
+/**
+ * Register JNI native methods.
+ *
+ * @param env the JNI environment
+ * @param className the class registering native methods
+ * @param methods an array of methods
+ * @param numMethods the number of methods in the array
+ */
+int registerJniMethods(JNIEnv* env, const char* className, const JNINativeMethod* methods,
+                       int numMethods);
+
+/**
+ * Constructs an exception of the specified class name with the provided message.
+ *
+ * @param env the JNI environment
+ * @param className the class of the exception to construct
+ * @param message the message associated with the constructed exception
+ */
+void throwException(JNIEnv* env, const char* className, const char* message);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index 6cd2441..0cab14b 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -82,7 +82,8 @@
     private final static String[] PUBLIC_ART_LIBRARIES = {
         "libicui18n.so",
         "libicuuc.so",
-        "libnativehelper.so"
+        "libnativehelper.so",
+        "libsigchain.so"
     };
 
     // The grey-list.
diff --git a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
index c3a63f3..f1d7992 100644
--- a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
@@ -29,6 +29,8 @@
 
 import com.google.common.collect.ObjectArrays;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.security.AlgorithmParameters;
 import java.security.InvalidKeyException;
 import java.security.Key;
@@ -44,11 +46,14 @@
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Random;
 import java.util.Set;
 import java.util.TreeMap;
 
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.spec.GCMParameterSpec;
 import javax.crypto.spec.IvParameterSpec;
@@ -90,6 +95,13 @@
 
     private static String[] EXPECTED_ALGORITHMS = BASE_EXPECTED_ALGORITHMS;
 
+    // For tests of behavior largely unrelated to the selected algorithm, such as
+    // unlockedDeviceRequired
+    private static final String[] BASIC_ALGORITHMS = {
+            "AES/GCM/NoPadding",
+            "RSA/ECB/OAEPWithSHA-256AndMGF1Padding",
+    };
+
     static {
       if (TestUtils.supports3DES()) {
         EXPECTED_ALGORITHMS = ObjectArrays
@@ -281,6 +293,7 @@
 
         public void performDeviceUnlock() throws Exception {
             mLockCredential.gotoKeyguard();
+            SystemClock.sleep(200);
             mLockCredential.enterAndConfirmLockCredential();
             launchHomeActivity();
             KeyguardManager keyguardManager = (KeyguardManager)getContext().getSystemService(
@@ -483,6 +496,79 @@
         }
     }
 
+    /*
+     * This test performs a round trip en/decryption using Cipher*Streams.
+     */
+    public void testEncryptsAndDecryptsUsingCipherStreams()
+            throws Exception {
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        final byte[] originalPlaintext = new byte[1024];
+        new Random().nextBytes(originalPlaintext);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            for (ImportedKey key : importKatKeys(
+                    algorithm,
+                    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
+                    false)) {
+                try {
+                    Key encryptionKey = key.getKeystoreBackedEncryptionKey();
+                    byte[] plaintext = truncatePlaintextIfNecessary(
+                            algorithm, encryptionKey, originalPlaintext);
+                    if (plaintext == null) {
+                        // Key is too short to encrypt anything using this transformation
+                        continue;
+                    }
+                    Cipher cipher = Cipher.getInstance(algorithm, provider);
+                    cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
+                    final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+                    final CipherOutputStream cipherOutputStream =
+                            new CipherOutputStream(byteArrayOutputStream, cipher);
+
+                    cipherOutputStream.write(plaintext);
+                    cipherOutputStream.close();
+                    AlgorithmParameters params = cipher.getParameters();
+                    byte[] expectedPlaintext = plaintext;
+                    if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
+                        // RSA decryption without padding left-pads resulting plaintext with NUL
+                        // bytes to the length of RSA modulus.
+                        int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
+                        expectedPlaintext = TestUtils.leftPadWithZeroBytes(
+                                expectedPlaintext, modulusLengthBytes);
+                    }
+
+                    byte[] ciphertext = byteArrayOutputStream.toByteArray();
+                    assertNotNull(ciphertext);
+                    cipher = Cipher.getInstance(algorithm, provider);
+                    Key decryptionKey = key.getKeystoreBackedDecryptionKey();
+                    cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
+
+                    final ByteArrayInputStream byteArrayInputStream =
+                            new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+                    final CipherInputStream cipherInputStream =
+                            new CipherInputStream(byteArrayInputStream, cipher);
+                    byte[] actualPlaintext = new byte[plaintext.length * 2];
+                    int total = 0;
+                    int count = 0;
+                    while((count = cipherInputStream.read(actualPlaintext, total,
+                            actualPlaintext.length - total)) != -1) {
+                        total += count;
+                    }
+                    actualPlaintext = Arrays.copyOf(actualPlaintext, total);
+                    cipherInputStream.close();
+                    assertTrue("expected(" + expectedPlaintext.length + "): "
+                            + HexEncoding.encode(expectedPlaintext)
+                            + "\nactual(" + actualPlaintext.length + "): "
+                            + HexEncoding.encode(actualPlaintext),
+                            Arrays.equals(expectedPlaintext, actualPlaintext));
+                } catch (Throwable e) {
+                    throw new RuntimeException(
+                            "Failed for " + algorithm + " with key " + key.getAlias(),
+                            e);
+                }
+            }
+        }
+    }
 
     private boolean isDecryptValid(byte[] expectedPlaintext, byte[] ciphertext, Cipher cipher,
             AlgorithmParameters params, ImportedKey key) {
@@ -531,25 +617,37 @@
         }
 
         try (DeviceLockSession dl = new DeviceLockSession()) {
+            dl.performDeviceLock();
             KeyguardManager keyguardManager = (KeyguardManager)getContext()
                 .getSystemService(Context.KEYGUARD_SERVICE);
 
             Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
             assertNotNull(provider);
             final byte[] originalPlaintext = EmptyArray.BYTE;
-            for (String algorithm : EXPECTED_ALGORITHMS) {
-                // Normally we would test all combinations of algorithms and key sizes, but the
-                // semi-manual locking and unlocking this requires takes way too long if we try to
-                // go through all of those. Other tests check all the key sizes, so we don't need to
-                // duplicate all that work.
-                for (ImportedKey key : importKatKeys(
-                        algorithm,
-                        KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
-                        false, isUnlockedDeviceRequired, isUserAuthRequired)) {
+            // Normally we would test all combinations of algorithms and key sizes, but the
+            // semi-manual locking and unlocking this requires takes way too long if we try to
+            // go through all of those. Other tests check all the key sizes, so we don't need to
+            // duplicate all that work.
+            for (String algorithm : BASIC_ALGORITHMS) {
+                for (boolean createWhileLocked : new boolean[]{false, true}) {
                     try {
+                        if (createWhileLocked) {
+                            dl.performDeviceLock();
+                        } else {
+                            dl.performDeviceUnlock();
+                        }
+                        // Test just a single key in the collection
+                        ImportedKey key = importKatKeys(
+                                algorithm,
+                                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT,
+                                false, isUnlockedDeviceRequired,
+                                isUserAuthRequired).iterator().next();
+                        if (createWhileLocked) {
+                            dl.performDeviceUnlock();
+                        }
                         Key encryptionKey = key.getKeystoreBackedEncryptionKey();
                         byte[] plaintext = truncatePlaintextIfNecessary(
-                               algorithm, encryptionKey, originalPlaintext);
+                                algorithm, encryptionKey, originalPlaintext);
                         if (plaintext == null) {
                             // Key is too short to encrypt anything using this transformation
                             continue;
@@ -561,32 +659,39 @@
                         byte[] ciphertext = cipher.doFinal(plaintext);
                         byte[] expectedPlaintext = plaintext;
                         if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
-                            // RSA decryption without padding left-pads resulting plaintext with NUL
-                            // bytes to the length of RSA modulus.
-                            int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey) + 7) / 8;
+                            // RSA decryption without padding left-pads resulting plaintext
+                            // with NUL bytes to the length of RSA modulus.
+                            int modulusLengthBytes = (TestUtils.getKeySizeBits(encryptionKey)
+                                    + 7) / 8;
                             expectedPlaintext = TestUtils.leftPadWithZeroBytes(
-                                   expectedPlaintext, modulusLengthBytes);
+                                    expectedPlaintext, modulusLengthBytes);
                         }
 
                         dl.performDeviceLock();
 
                         // Attempt to decrypt the data with the device locked.
                         cipher = Cipher.getInstance(algorithm, provider);
-                        assertFalse(isDecryptValid(expectedPlaintext, ciphertext, cipher, params, key));
+                        assertFalse(
+                                isDecryptValid(expectedPlaintext, ciphertext, cipher, params,
+                                        key));
 
                         // Then attempt to decrypt the data with the device unlocked
                         // This should succeed
                         dl.performDeviceUnlock();
                         cipher = Cipher.getInstance(algorithm, provider);
-                        assertTrue(isDecryptValid(expectedPlaintext, ciphertext, cipher, params, key));
+                        assertTrue(isDecryptValid(expectedPlaintext, ciphertext, cipher, params,
+                                key));
+
+                        // Ensure a second decryption also succeeds
+                        cipher = Cipher.getInstance(algorithm, provider);
+                        assertTrue(isDecryptValid(expectedPlaintext, ciphertext, cipher, params,
+                                key));
                     } catch (Throwable e) {
                         throw new RuntimeException(
-                              "Failed for " + algorithm + " with key " + key.getAlias(),
-                               e);
+                                "Failed on createWhileLocked " + createWhileLocked + " for " +
+                                        algorithm,
+                                e);
                     }
-                    // We don't know the underlying type of this collection, so just break out of
-                    // the iterator loop.
-                    break;
                 }
             }
         }
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAgreementTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAgreementTest.java
index 85e24b3..c8d0eae 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAgreementTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAgreementTest.java
@@ -18,10 +18,12 @@
 
 import java.security.GeneralSecurityException;
 import java.security.InvalidKeyException;
+import java.security.KeyFactory;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
+import java.security.spec.InvalidKeySpecException;
 
 import javax.crypto.KeyAgreement;
 
@@ -29,8 +31,11 @@
 
 import org.junit.Assert;
 
+import android.content.Context;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyInfo;
+import androidx.test.InstrumentationRegistry;
 
 public class KeyAgreementTest extends TestCase {
     private static final String PRIVATE_KEY_ALIAS = "TemporaryPrivateKey";
@@ -92,6 +97,22 @@
         }
     }
 
+    public void testInit_withNonEcKey_fails() throws Exception {
+        KeyPairGenerator kpg =
+                KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
+        kpg.initialize(
+                new KeyGenParameterSpec.Builder("rsakey", KeyProperties.PURPOSE_AGREE_KEY).build());
+        KeyPair rsaKeyPair = kpg.genKeyPair();
+        KeyAgreement ka = getKeyStoreKeyAgreement();
+
+        try {
+            ka.init(rsaKeyPair.getPrivate());
+            fail("Initializing KeyAgreement with non-EC key should fail.");
+        } catch (InvalidKeyException ike) {
+            // Expected
+        }
+    }
+
     public void testDoPhase_withoutInitialization_fails() throws Exception {
         KeyAgreement ka = getKeyStoreKeyAgreement();
         try {
@@ -139,7 +160,31 @@
         kpg.initialize(
                 new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_AGREE_KEY).build());
 
-        return kpg.generateKeyPair();
+        KeyPair kp = kpg.generateKeyPair();
+
+        KeyFactory factory = KeyFactory.getInstance(kp.getPrivate().getAlgorithm(),
+                "AndroidKeyStore");
+        KeyInfo keyInfo = null;
+        try {
+            keyInfo = factory.getKeySpec(kp.getPrivate(), KeyInfo.class);
+        } catch (InvalidKeySpecException e) {
+            // Not an Android KeyStore key.
+            fail("Unable to get KeyInfo for created key.");
+        }
+
+        // ECDH is only implemented in Secure Hardware if KeyMint is available.
+        int level = keyInfo.getSecurityLevel();
+        Context context = InstrumentationRegistry.getTargetContext();
+        if (TestUtils.getFeatureVersionKeystore(context) >= Attestation.KM_VERSION_KEYMINT_1) {
+            Assert.assertTrue(
+                level == KeyProperties.SECURITY_LEVEL_TRUSTED_ENVIRONMENT ||
+                level == KeyProperties.SECURITY_LEVEL_UNKNOWN_SECURE);
+        } else {
+            Assert.assertEquals(keyInfo.getSecurityLevel(),
+                    KeyProperties.SECURITY_LEVEL_SOFTWARE);
+        }
+
+        return kp;
     }
 
     private static KeyPair generateEphemeralServerKeyPair() throws GeneralSecurityException {
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 596d391..0c022e3 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -54,6 +54,7 @@
 import static org.hamcrest.Matchers.greaterThanOrEqualTo;
 import static org.hamcrest.Matchers.hasItems;
 import static org.hamcrest.Matchers.lessThanOrEqualTo;
+import static org.junit.Assert.assertNotEquals;
 
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -299,6 +300,103 @@
         }
     }
 
+    public void testAttestationKmVersionMatchesFeatureVersion() throws Exception {
+        if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC))
+            return;
+
+        String keystoreAlias = "test_key";
+        Date now = new Date();
+        Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
+        Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
+        KeyGenParameterSpec.Builder builder =
+            new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+                    .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+                    .setAttestationChallenge(new byte[128])
+                    .setKeyValidityStart(now)
+                    .setKeyValidityForOriginationEnd(originationEnd)
+                    .setKeyValidityForConsumptionEnd(consumptionEnd);
+
+        generateKeyPair(KEY_ALGORITHM_EC, builder.build());
+
+        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+
+        try {
+            Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
+            verifyCertificateChain(certificates, false /* expectStrongBox */);
+            X509Certificate attestationCert = (X509Certificate) certificates[0];
+            Attestation attestation = Attestation.loadFromCertificate(attestationCert);
+            int kmVersionFromAttestation = attestation.keymasterVersion;
+            int keyStoreFeatureVersion = TestUtils.getFeatureVersionKeystore(getContext());
+
+            // Feature Version is required on devices launching with Android 12 (API Level
+            // 31) but may be reported on devices launching with an earlier version. If it's
+            // present, it must match what is reported in attestation.
+            int firstApiLevel = SystemProperties.getInt("ro.product.first_api_level", 0);
+            if (firstApiLevel >= 31) {
+                assertNotEquals(0, keyStoreFeatureVersion);
+            }
+            if (keyStoreFeatureVersion != 0) {
+                assertEquals(kmVersionFromAttestation, keyStoreFeatureVersion);
+            }
+        } finally {
+            keyStore.deleteEntry(keystoreAlias);
+        }
+    }
+
+    public void testAttestationKmVersionMatchesFeatureVersionStrongBox() throws Exception {
+        if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC))
+            return;
+
+        int keyStoreFeatureVersionStrongBox =
+                TestUtils.getFeatureVersionKeystoreStrongBox(getContext());
+
+        if (!TestUtils.hasStrongBox(getContext())) {
+            // If there's no StrongBox, ensure there's no feature version for it.
+            assertEquals(0, keyStoreFeatureVersionStrongBox);
+            return;
+        }
+
+        String keystoreAlias = "test_key";
+        Date now = new Date();
+        Date originationEnd = new Date(now.getTime() + ORIGINATION_TIME_OFFSET);
+        Date consumptionEnd = new Date(now.getTime() + CONSUMPTION_TIME_OFFSET);
+        KeyGenParameterSpec.Builder builder =
+            new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
+                    .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
+                    .setAttestationChallenge(new byte[128])
+                    .setKeyValidityStart(now)
+                    .setKeyValidityForOriginationEnd(originationEnd)
+                    .setKeyValidityForConsumptionEnd(consumptionEnd)
+                    .setIsStrongBoxBacked(true);
+
+        generateKeyPair(KEY_ALGORITHM_EC, builder.build());
+
+        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+
+        try {
+            Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
+            verifyCertificateChain(certificates, true /* expectStrongBox */);
+            X509Certificate attestationCert = (X509Certificate) certificates[0];
+            Attestation attestation = Attestation.loadFromCertificate(attestationCert);
+            int kmVersionFromAttestation = attestation.keymasterVersion;
+
+            // Feature Version is required on devices launching with Android 12 (API Level
+            // 31) but may be reported on devices launching with an earlier version. If it's
+            // present, it must match what is reported in attestation.
+            int firstApiLevel = SystemProperties.getInt("ro.product.first_api_level", 0);
+            if (firstApiLevel >= 31) {
+                assertNotEquals(0, keyStoreFeatureVersionStrongBox);
+            }
+            if (keyStoreFeatureVersionStrongBox != 0) {
+                assertEquals(kmVersionFromAttestation, keyStoreFeatureVersionStrongBox);
+            }
+        } finally {
+            keyStore.deleteEntry(keystoreAlias);
+        }
+    }
+
     public void testEcAttestation_KeyStoreExceptionWhenRequestingUniqueId() throws Exception {
         String keystoreAlias = "test_key";
         KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(keystoreAlias, PURPOSE_SIGN)
@@ -956,8 +1054,9 @@
 
     @SuppressWarnings("unchecked")
     private void checkAttestationSecurityLevelDependentParams(Attestation attestation) {
-        assertThat("Attestation version must be 1, 2, 3, 4 or 5", attestation.getAttestationVersion(),
-               either(is(1)).or(is(2)).or(is(3)).or(is(4)).or(is(5)));
+        assertThat("Attestation version must be 1, 2, 3, 4 or 100",
+                attestation.getAttestationVersion(),
+                either(is(1)).or(is(2)).or(is(3)).or(is(4)).or(is(100)));
 
         AuthorizationList teeEnforced = attestation.getTeeEnforced();
         AuthorizationList softwareEnforced = attestation.getSoftwareEnforced();
@@ -971,7 +1070,7 @@
                         attestation.getKeymasterSecurityLevel(),
                         is(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT));
                 assertThat(attestation.getKeymasterVersion(),
-                           either(is(2)).or(is(3)).or(is(4)).or(is(41)));
+                        either(is(2)).or(is(3)).or(is(4)).or(is(41)).or(is(100)));
 
                 checkRootOfTrust(attestation, false /* requireLocked */);
                 assertThat(teeEnforced.getOsVersion(), is(systemOsVersion));
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
index 431ffa5..6d9b33a 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
@@ -83,6 +83,7 @@
     }
 
     static final int[] AES_SUPPORTED_KEY_SIZES = new int[] {128, 192, 256};
+    static final int[] AES_STRONGBOX_SUPPORTED_KEY_SIZES = new int[] {128, 256};
     static final int[] DES_SUPPORTED_KEY_SIZES = new int[] {168};
 
     public void testAlgorithmList() {
@@ -277,13 +278,17 @@
                     }
                 }
                 rng.resetCounters();
-                if (TestUtils.contains(AES_SUPPORTED_KEY_SIZES, i)) {
+                if (TestUtils.contains(useStrongbox ?
+                        AES_STRONGBOX_SUPPORTED_KEY_SIZES : AES_SUPPORTED_KEY_SIZES, i)) {
                     keyGenerator.init(spec, rng);
                     SecretKey key = keyGenerator.generateKey();
                     assertEquals(i, TestUtils.getKeyInfo(key).getKeySize());
                     assertEquals((i + 7) / 8, rng.getOutputSizeBytes());
                 } else {
                     try {
+                        if (useStrongbox && (i == 192))
+                            throw new InvalidAlgorithmParameterException("Strongbox does not"
+                                    + " support key size 192.");
                         keyGenerator.init(spec, rng);
                         fail();
                     } catch (InvalidAlgorithmParameterException expected) {}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
index 7bdccb7..5eac878 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
@@ -882,7 +882,6 @@
                 .setCertificateSubject(certSubject)
                 .setCertificateNotBefore(certNotBefore)
                 .setCertificateNotAfter(certNotAfter)
-                .setUnlockedDeviceRequired(true)
                 .build());
         KeyPair keyPair = generator.generateKeyPair();
         assertGeneratedKeyPairAndSelfSignedCertificate(
@@ -952,7 +951,6 @@
                 .setCertificateSubject(certSubject)
                 .setCertificateNotBefore(certNotBefore)
                 .setCertificateNotAfter(certNotAfter)
-                .setUnlockedDeviceRequired(true)
                 .setIsStrongBoxBacked(true)
                 .build());
         KeyPair keyPair = generator.generateKeyPair();
@@ -1023,7 +1021,6 @@
                 .setCertificateSubject(certSubject)
                 .setCertificateNotBefore(certNotBefore)
                 .setCertificateNotAfter(certNotAfter)
-                .setUnlockedDeviceRequired(true)
                 .build());
         KeyPair keyPair = generator.generateKeyPair();
         assertGeneratedKeyPairAndSelfSignedCertificate(
@@ -1097,7 +1094,7 @@
                 KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
                         | KeyProperties.PURPOSE_ENCRYPT)
                 .setAlgorithmParameterSpec(
-                        new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F0))
+                        new RSAKeyGenParameterSpec(2048, RSAKeyGenParameterSpec.F4))
                 .setKeySize(2048)
                 .setDigests(KeyProperties.DIGEST_SHA256)
                 .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
@@ -1112,7 +1109,6 @@
                 .setCertificateSubject(certSubject)
                 .setCertificateNotBefore(certNotBefore)
                 .setCertificateNotAfter(certNotAfter)
-                .setUnlockedDeviceRequired(true)
                 .setIsStrongBoxBacked(true)
                 .build());
         KeyPair keyPair = generator.generateKeyPair();
@@ -1125,7 +1121,7 @@
                 certSerialNumber,
                 certNotBefore,
                 certNotAfter);
-        assertEquals(RSAKeyGenParameterSpec.F0,
+        assertEquals(RSAKeyGenParameterSpec.F4,
                 ((RSAPublicKey) keyPair.getPublic()).getPublicExponent());
         KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
         assertEquals(2048, keyInfo.getKeySize());
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyStoreTest.java
index ab9cec7..383e483 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyStoreTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyStoreTest.java
@@ -815,20 +815,15 @@
             try {
                 keyStore.setKeyEntry(null, null, null, null);
                 fail(keyStore.getType());
-            } catch (Exception e) {
-                if (e.getClass() != NullPointerException.class
-                    && e.getClass() != KeyStoreException.class) {
-                    throw e;
-                }
+            } catch (NullPointerException | KeyStoreException expected) {
+              // ignored
             }
+
             try {
                 keyStore.setKeyEntry(null, null, PASSWORD_KEY, null);
                 fail(keyStore.getType());
-            } catch (Exception e) {
-                if (e.getClass() != NullPointerException.class
-                    && e.getClass() != KeyStoreException.class) {
-                    throw e;
-                }
+            } catch (NullPointerException | KeyStoreException expected) {
+              // ignored
             }
             try {
                 keyStore.setKeyEntry(ALIAS_PRIVATE,
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
index 3e9d7d0..aeb3ad6 100644
--- a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.FeatureInfo;
 import android.os.SystemProperties;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyInfo;
@@ -82,6 +83,54 @@
 
     private TestUtils() {}
 
+    // Returns 0 if not implemented. Otherwise returns the feature version.
+    //
+    static int getFeatureVersionKeystore(Context appContext) {
+        PackageManager pm = appContext.getPackageManager();
+
+        int featureVersionFromPm = 0;
+        if (pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE)) {
+            FeatureInfo info = null;
+            FeatureInfo[] infos = pm.getSystemAvailableFeatures();
+            for (int n = 0; n < infos.length; n++) {
+                FeatureInfo i = infos[n];
+                if (i.name.equals(PackageManager.FEATURE_HARDWARE_KEYSTORE)) {
+                    info = i;
+                    break;
+                }
+            }
+            if (info != null) {
+                featureVersionFromPm = info.version;
+            }
+        }
+
+        return featureVersionFromPm;
+    }
+
+    // Returns 0 if not implemented. Otherwise returns the feature version.
+    //
+    static int getFeatureVersionKeystoreStrongBox(Context appContext) {
+        PackageManager pm = appContext.getPackageManager();
+
+        int featureVersionFromPm = 0;
+        if (pm.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE)) {
+            FeatureInfo info = null;
+            FeatureInfo[] infos = pm.getSystemAvailableFeatures();
+            for (int n = 0; n < infos.length; n++) {
+                FeatureInfo i = infos[n];
+                if (i.name.equals(PackageManager.FEATURE_STRONGBOX_KEYSTORE)) {
+                    info = i;
+                    break;
+                }
+            }
+            if (info != null) {
+                featureVersionFromPm = info.version;
+            }
+        }
+
+        return featureVersionFromPm;
+    }
+
     /**
      * Returns whether 3DES KeyStore tests should run on this device. 3DES support was added in
      * KeyMaster 4.0 and there should be no software fallback on earlier KeyMaster versions.
diff --git a/tests/tests/libnativehelper/OWNERS b/tests/tests/libnativehelper/OWNERS
index da3f4a8..8d9029d 100644
--- a/tests/tests/libnativehelper/OWNERS
+++ b/tests/tests/libnativehelper/OWNERS
@@ -1,5 +1,4 @@
 # Bug component: 86431
-enh@google.com
 mast@google.com
 ngeoffray@google.com
 oth@google.com
diff --git a/tests/tests/libnativehelper/jni/file_descriptor_ndk_test.cpp b/tests/tests/libnativehelper/jni/file_descriptor_ndk_test.cpp
index 6df63cf..459c359 100644
--- a/tests/tests/libnativehelper/jni/file_descriptor_ndk_test.cpp
+++ b/tests/tests/libnativehelper/jni/file_descriptor_ndk_test.cpp
@@ -23,13 +23,13 @@
     jobject jifd = AFileDescriptor_create(mEnv);
     ASSERT_NE(nullptr, jifd);
     ASSERT_EQ(JNI_FALSE, mEnv->ExceptionCheck());
-    ASSERT_EQ(kInvalidFd, AFileDescriptor_getFD(mEnv, jifd));
+    ASSERT_EQ(kInvalidFd, AFileDescriptor_getFd(mEnv, jifd));
 
     static const int kSubsequentUnixFds[] = { 0, -1, -999, -1, 0, 1812, -1, 1066 };
     for (int unixFd : kSubsequentUnixFds) {
-        AFileDescriptor_setFD(mEnv, jifd, unixFd);
+        AFileDescriptor_setFd(mEnv, jifd, unixFd);
         ASSERT_EQ(JNI_FALSE, mEnv->ExceptionCheck());
-        ASSERT_EQ(unixFd, AFileDescriptor_getFD(mEnv, jifd));
+        ASSERT_EQ(unixFd, AFileDescriptor_getFd(mEnv, jifd));
         ASSERT_EQ(JNI_FALSE, mEnv->ExceptionCheck());
     }
 }
diff --git a/tests/tests/media/res/raw/sample_mpegh_mha1.mp4 b/tests/tests/media/res/raw/sample_mpegh_mha1.mp4
new file mode 100644
index 0000000..23a211c
--- /dev/null
+++ b/tests/tests/media/res/raw/sample_mpegh_mha1.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/sample_mpegh_mhm1.mp4 b/tests/tests/media/res/raw/sample_mpegh_mhm1.mp4
new file mode 100644
index 0000000..25ea55b
--- /dev/null
+++ b/tests/tests/media/res/raw/sample_mpegh_mhm1.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioFormatTest.java b/tests/tests/media/src/android/media/cts/AudioFormatTest.java
index af5e572..e7b59a3 100644
--- a/tests/tests/media/src/android/media/cts/AudioFormatTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioFormatTest.java
@@ -182,7 +182,13 @@
             AudioFormat.ENCODING_AAC_LC,
             AudioFormat.ENCODING_AAC_HE_V1,
             AudioFormat.ENCODING_AAC_HE_V2,
-            AudioFormat.ENCODING_OPUS
+            AudioFormat.ENCODING_OPUS,
+            AudioFormat.ENCODING_MPEGH_BL_L3,
+            AudioFormat.ENCODING_MPEGH_BL_L4,
+            AudioFormat.ENCODING_MPEGH_LC_L3,
+            AudioFormat.ENCODING_MPEGH_LC_L4,
+            AudioFormat.ENCODING_DTS_UHD,
+            AudioFormat.ENCODING_DRA,
         };
         for (int encoding : encodings) {
             final AudioFormat format = new AudioFormat.Builder()
@@ -204,4 +210,65 @@
         assertEquals("Float AudioFormat has the wrong frame size",
             2 /* channels */ * 4 /* bytes per sample */, formatPcmFloat.getFrameSizeInBytes());
     }
+
+    /**
+     * Check whether the bits in a are all present in b.
+     *
+     * Used for channel position mask verification.
+     */
+    private boolean subsetOf(int a, int b) {
+        return Integer.bitCount(a ^ b) == Integer.bitCount(b) - Integer.bitCount(a);
+    }
+
+    /**
+     * Test case 8: Check validity of channel masks
+     */
+    public void testChannelMasks() throws Exception {
+        // Channel count check.
+        int[][] maskCount = new int[][] {
+                {AudioFormat.CHANNEL_OUT_MONO, 1},
+                {AudioFormat.CHANNEL_OUT_STEREO, 2},
+                {AudioFormat.CHANNEL_OUT_QUAD, 4},
+                {AudioFormat.CHANNEL_OUT_SURROUND, 4},
+                {AudioFormat.CHANNEL_OUT_5POINT1, 6},
+                {AudioFormat.CHANNEL_OUT_5POINT1POINT2, 8},
+                {AudioFormat.CHANNEL_OUT_7POINT1_SURROUND, 8},
+                {AudioFormat.CHANNEL_OUT_5POINT1POINT4, 10},
+                {AudioFormat.CHANNEL_OUT_7POINT1POINT2, 10},
+                {AudioFormat.CHANNEL_OUT_7POINT1POINT4, 12},
+                {AudioFormat.CHANNEL_OUT_13POINT_360RA, 13},
+                {AudioFormat.CHANNEL_OUT_22POINT2, 24},
+        };
+        for (int[] pair : maskCount) {
+            assertEquals("Mask " + Integer.toHexString(pair[0])
+                    + " should have " + pair[1] + " bits set",
+                    Integer.bitCount(pair[0]), pair[1]);
+        }
+
+        // Check channel position masks that are a subset of other masks.
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_MONO,
+                AudioFormat.CHANNEL_OUT_STEREO));
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_STEREO,
+                AudioFormat.CHANNEL_OUT_QUAD));
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_STEREO,
+                AudioFormat.CHANNEL_OUT_SURROUND));
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_QUAD,
+                AudioFormat.CHANNEL_OUT_5POINT1));
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_5POINT1,
+                AudioFormat.CHANNEL_OUT_5POINT1POINT2));
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_5POINT1,
+                AudioFormat.CHANNEL_OUT_5POINT1POINT4));
+        // Note CHANNEL_OUT_5POINT1POINT2 not a subset of CHANNEL_OUT_5POINT1POINT4
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_7POINT1_SURROUND,
+                AudioFormat.CHANNEL_OUT_7POINT1POINT2));
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_7POINT1_SURROUND,
+                AudioFormat.CHANNEL_OUT_7POINT1POINT4));
+        // Note CHANNEL_OUT_7POINT1POINT2 not a subset of CHANNEL_OUT_7POINT1POINT4
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_5POINT1POINT4,
+                AudioFormat.CHANNEL_OUT_7POINT1POINT4));
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_7POINT1POINT4,
+                AudioFormat.CHANNEL_OUT_22POINT2));
+        assertTrue(subsetOf(AudioFormat.CHANNEL_OUT_13POINT_360RA,
+                AudioFormat.CHANNEL_OUT_22POINT2));
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index e14a6c5..f2ae7b4 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -385,6 +385,30 @@
                 AudioFormat.ENCODING_PCM_16BIT);
     }
 
+    // Audit buffers can run out of space with high numbers of channels,
+    // so keep the sample rate low.
+    @Test
+    public void testAudioRecordAuditChannelIndex3() throws Exception {
+        doTest("audit_channel_index_3", true /*localRecord*/, true /*customHandler*/,
+                2 /*periodsPerSecond*/, 0 /*markerPeriodsPerSecond*/,
+                true /*useByteBuffer*/, false /*blocking*/,
+                true /*auditRecording*/, true /*isChannelIndex*/, 16000 /*TEST_SR*/,
+                (1 << 0) | (1 << 1) | (1 << 2)  /* 3 channels */,
+                AudioFormat.ENCODING_PCM_24BIT_PACKED);
+    }
+
+    // Audit buffers can run out of space with high numbers of channels,
+    // so keep the sample rate low.
+    @Test
+    public void testAudioRecordAuditChannelIndex1() throws Exception {
+        doTest("audit_channel_index_1", true /*localRecord*/, true /*customHandler*/,
+                2 /*periodsPerSecond*/, 0 /*markerPeriodsPerSecond*/,
+                true /*useByteBuffer*/, false /*blocking*/,
+                true /*auditRecording*/, true /*isChannelIndex*/, 24000 /*TEST_SR*/,
+                (1 << 0)  /* 1 channels */,
+                AudioFormat.ENCODING_PCM_32BIT);
+    }
+
     // Test AudioRecord.Builder to verify the observed configuration of an AudioRecord built with
     // an empty Builder matches the documentation / expected values
     @Test
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 372b583..1dec098 100755
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -32,6 +32,7 @@
 import android.media.AudioMetadata;
 import android.media.AudioMetadataReadMap;
 import android.media.AudioPresentation;
+import android.media.AudioSystem;
 import android.media.AudioTimestamp;
 import android.media.AudioTrack;
 import android.media.PlaybackParams;
@@ -1855,6 +1856,117 @@
         Thread.sleep(waitMsec); // wait for release to complete
     }
 
+    private void playOnceStreamByteBuffer(
+            String testName, double testFrequency, double testSweep,
+            int testStreamType, int testSampleRate, int testChannelMask, int testEncoding,
+            int testTransferMode, int testWriteMode,
+            boolean useChannelIndex, boolean useDirect) throws Exception {
+        AudioTrack track = null;
+        try {
+            AudioFormat.Builder afb = new AudioFormat.Builder()
+                    .setEncoding(testEncoding)
+                    .setSampleRate(testSampleRate);
+            if (useChannelIndex) {
+                afb.setChannelIndexMask(testChannelMask);
+            } else {
+                afb.setChannelMask(testChannelMask);
+            }
+            final AudioFormat format = afb.build();
+            final int frameSize = AudioHelper.frameSizeFromFormat(format);
+            final int frameCount =
+                    AudioHelper.frameCountFromMsec(300 /* ms */, format);
+            final int bufferSize = frameCount * frameSize;
+            final int bufferSamples = frameCount * format.getChannelCount();
+
+            track = new AudioTrack.Builder()
+                    .setAudioFormat(format)
+                    .setTransferMode(testTransferMode)
+                    .setBufferSizeInBytes(bufferSize)
+                    .build();
+
+            assertEquals(testName + ": state",
+                    AudioTrack.STATE_INITIALIZED, track.getState());
+            assertEquals(testName + ": sample rate",
+                    testSampleRate, track.getSampleRate());
+            assertEquals(testName + ": encoding",
+                    testEncoding, track.getAudioFormat());
+
+            ByteBuffer bb = useDirect
+                    ? ByteBuffer.allocateDirect(bufferSize)
+                    : ByteBuffer.allocate(bufferSize);
+            bb.order(java.nio.ByteOrder.nativeOrder());
+
+            final double sampleFrequency = testFrequency / format.getChannelCount();
+            switch (testEncoding) {
+                case AudioFormat.ENCODING_PCM_8BIT: {
+                    byte data[] = AudioHelper.createSoundDataInByteArray(
+                            bufferSamples, testSampleRate,
+                            sampleFrequency, testSweep);
+                    bb.put(data);
+                    bb.flip();
+                }
+                break;
+                case AudioFormat.ENCODING_PCM_16BIT: {
+                    short data[] = AudioHelper.createSoundDataInShortArray(
+                            bufferSamples, testSampleRate,
+                            sampleFrequency, testSweep);
+                    ShortBuffer sb = bb.asShortBuffer();
+                    sb.put(data);
+                    bb.limit(sb.limit() * 2);
+                }
+                break;
+                case AudioFormat.ENCODING_PCM_FLOAT: {
+                    float data[] = AudioHelper.createSoundDataInFloatArray(
+                            bufferSamples, testSampleRate,
+                            sampleFrequency, testSweep);
+                    FloatBuffer fb = bb.asFloatBuffer();
+                    fb.put(data);
+                    bb.limit(fb.limit() * 4);
+                }
+                break;
+            }
+            // start the AudioTrack
+            // This can be done before or after the first write.
+            // Current behavior for streaming tracks is that
+            // actual playback does not begin before the internal
+            // data buffer is completely full.
+            track.play();
+
+            // write data
+            final long startTime = System.currentTimeMillis();
+            final long maxDuration = frameCount * 1000 / testSampleRate + 1000;
+            for (int written = 0; written < bufferSize; ) {
+                // ret may return a short count if write
+                // is non blocking or even if write is blocking
+                // when a stop/pause/flush is issued from another thread.
+                final int kBatchFrames = 1000;
+                int ret = track.write(bb,
+                        Math.min(bufferSize - written, frameSize * kBatchFrames),
+                        testWriteMode);
+                // for non-blocking mode, this loop may spin quickly
+                assertTrue(testName + ": write error " + ret, ret >= 0);
+                assertTrue(testName + ": write timeout",
+                        (System.currentTimeMillis() - startTime) <= maxDuration);
+                written += ret;
+            }
+
+            // for streaming tracks, stop will allow the rest of the data to
+            // drain out, but we don't know how long to wait unless
+            // we check the position before stop. if we check position
+            // after we stop, we read 0.
+            final int position = track.getPlaybackHeadPosition();
+            final int remainingTimeMs = (int)((double)(frameCount - position)
+                    * 1000 / testSampleRate);
+            track.stop();
+            Thread.sleep(remainingTimeMs);
+            Thread.sleep(WAIT_MSEC);
+        } finally {
+            if (track != null) {
+                track.release();
+            }
+        }
+    }
+
     @Test
     public void testPlayStreamByteBuffer() throws Exception {
         // constants for test
@@ -1876,7 +1988,7 @@
         };
         final int TEST_MODE = AudioTrack.MODE_STREAM;
         final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
-        final float TEST_SWEEP = 0; // sine wave only
+        final double TEST_SWEEP = 0; // sine wave only
 
         for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
             double frequency = 800; // frequency changes for each test
@@ -1884,76 +1996,14 @@
                 for (int TEST_CONF : TEST_CONF_ARRAY) {
                     for (int TEST_WRITE_MODE : TEST_WRITE_MODE_ARRAY) {
                         for (int useDirect = 0; useDirect < 2; ++useDirect) {
-                            // -------- initialization --------------
-                            int minBufferSize = AudioTrack.getMinBufferSize(TEST_SR,
-                                    TEST_CONF, TEST_FORMAT); // in bytes
-                            int bufferSize = 12 * minBufferSize;
-                            int bufferSamples = bufferSize
-                                    / AudioFormat.getBytesPerSample(TEST_FORMAT);
+                            playOnceStreamByteBuffer(TEST_NAME, frequency, TEST_SWEEP,
+                                    TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
+                                    TEST_MODE, TEST_WRITE_MODE,
+                                    false /* useChannelIndex */, useDirect != 0);
 
-                            // create audio track and confirm settings
-                            AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR,
-                                    TEST_CONF, TEST_FORMAT, minBufferSize, TEST_MODE);
-                            assertEquals(TEST_NAME + ": state",
-                                    AudioTrack.STATE_INITIALIZED, track.getState());
-                            assertEquals(TEST_NAME + ": sample rate",
-                                    TEST_SR, track.getSampleRate());
-                            assertEquals(TEST_NAME + ": channel mask",
-                                    TEST_CONF, track.getChannelConfiguration());
-                            assertEquals(TEST_NAME + ": encoding",
-                                    TEST_FORMAT, track.getAudioFormat());
-
-                            ByteBuffer bb = (useDirect == 1)
-                                    ? ByteBuffer.allocateDirect(bufferSize)
-                                            : ByteBuffer.allocate(bufferSize);
-                            bb.order(java.nio.ByteOrder.nativeOrder());
-
-                            // -------- test --------------
-                            switch (TEST_FORMAT) {
-                                case AudioFormat.ENCODING_PCM_8BIT: {
-                                    byte data[] = AudioHelper.createSoundDataInByteArray(
-                                            bufferSamples, TEST_SR,
-                                            frequency, TEST_SWEEP);
-                                    bb.put(data);
-                                    bb.flip();
-                                } break;
-                                case AudioFormat.ENCODING_PCM_16BIT: {
-                                    short data[] = AudioHelper.createSoundDataInShortArray(
-                                            bufferSamples, TEST_SR,
-                                            frequency, TEST_SWEEP);
-                                    ShortBuffer sb = bb.asShortBuffer();
-                                    sb.put(data);
-                                    bb.limit(sb.limit() * 2);
-                                } break;
-                                case AudioFormat.ENCODING_PCM_FLOAT: {
-                                    float data[] = AudioHelper.createSoundDataInFloatArray(
-                                            bufferSamples, TEST_SR,
-                                            frequency, TEST_SWEEP);
-                                    FloatBuffer fb = bb.asFloatBuffer();
-                                    fb.put(data);
-                                    bb.limit(fb.limit() * 4);
-                                } break;
-                            }
-
-                            boolean hasPlayed = false;
-                            int written = 0;
-                            while (written < bufferSize) {
-                                int ret = track.write(bb,
-                                        Math.min(bufferSize - written, minBufferSize),
-                                        TEST_WRITE_MODE);
-                                assertTrue(TEST_NAME, ret >= 0);
-                                written += ret;
-                                if (!hasPlayed) {
-                                    track.play();
-                                    hasPlayed = true;
-                                }
-                            }
-
-                            track.stop();
-                            Thread.sleep(WAIT_MSEC);
-                            // -------- tear down --------------
-                            track.release();
-                            frequency += 200; // increment test tone frequency
+                            // add a gap to make tones distinct
+                            Thread.sleep(100 /* millis */);
+                            frequency += 30; // increment test tone frequency
                         }
                     }
                 }
@@ -1991,7 +2041,9 @@
                 AudioTrack.WRITE_BLOCKING,
                 AudioTrack.WRITE_NON_BLOCKING,
         };
-        final float TEST_SWEEP = 0;
+        final double TEST_SWEEP = 0;
+        final int TEST_MODE = AudioTrack.MODE_STREAM;
+        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
 
         for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
             for (int TEST_CONF : TEST_CONF_ARRAY) {
@@ -1999,93 +2051,14 @@
                 for (int TEST_SR : TEST_SR_ARRAY) {
                     for (int TEST_WRITE_MODE : TEST_WRITE_MODE_ARRAY) {
                         for (int useDirect = 0; useDirect < 2; ++useDirect) {
-                            AudioFormat format = new AudioFormat.Builder()
-                                    .setEncoding(TEST_FORMAT)
-                                    .setSampleRate(TEST_SR)
-                                    .setChannelIndexMask(TEST_CONF)
-                                    .build();
-                            AudioTrack track = new AudioTrack.Builder()
-                                    .setAudioFormat(format)
-                                    .build();
-                            assertEquals(TEST_NAME,
-                                    AudioTrack.STATE_INITIALIZED, track.getState());
+                            playOnceStreamByteBuffer(TEST_NAME, frequency, TEST_SWEEP,
+                                    TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
+                                    TEST_MODE, TEST_WRITE_MODE,
+                                    true /* useChannelIndex */, useDirect != 0);
 
-                            // create the byte buffer and fill with test data
-                            final int frameSize = AudioHelper.frameSizeFromFormat(format);
-                            final int frameCount =
-                                    AudioHelper.frameCountFromMsec(300 /* ms */, format);
-                            final int bufferSize = frameCount * frameSize;
-                            final int bufferSamples = frameCount * format.getChannelCount();
-                            ByteBuffer bb = (useDirect == 1)
-                                    ? ByteBuffer.allocateDirect(bufferSize)
-                                            : ByteBuffer.allocate(bufferSize);
-                            bb.order(java.nio.ByteOrder.nativeOrder());
-
-                            switch (TEST_FORMAT) {
-                            case AudioFormat.ENCODING_PCM_8BIT: {
-                                byte data[] = AudioHelper.createSoundDataInByteArray(
-                                        bufferSamples, TEST_SR,
-                                        frequency, TEST_SWEEP);
-                                bb.put(data);
-                                bb.flip();
-                            } break;
-                            case AudioFormat.ENCODING_PCM_16BIT: {
-                                short data[] = AudioHelper.createSoundDataInShortArray(
-                                        bufferSamples, TEST_SR,
-                                        frequency, TEST_SWEEP);
-                                ShortBuffer sb = bb.asShortBuffer();
-                                sb.put(data);
-                                bb.limit(sb.limit() * 2);
-                            } break;
-                            case AudioFormat.ENCODING_PCM_FLOAT: {
-                                float data[] = AudioHelper.createSoundDataInFloatArray(
-                                        bufferSamples, TEST_SR,
-                                        frequency, TEST_SWEEP);
-                                FloatBuffer fb = bb.asFloatBuffer();
-                                fb.put(data);
-                                bb.limit(fb.limit() * 4);
-                            } break;
-                            }
-
-                            // start the AudioTrack
-                            // This can be done before or after the first write.
-                            // Current behavior for streaming tracks is that
-                            // actual playback does not begin before the internal
-                            // data buffer is completely full.
-                            track.play();
-
-                            // write data
-                            final long startTime = System.currentTimeMillis();
-                            final long maxDuration = frameCount * 1000 / TEST_SR + 1000;
-                            for (int written = 0; written < bufferSize; ) {
-                                // ret may return a short count if write
-                                // is non blocking or even if write is blocking
-                                // when a stop/pause/flush is issued from another thread.
-                                final int kBatchFrames = 1000;
-                                int ret = track.write(bb,
-                                        Math.min(bufferSize - written, frameSize * kBatchFrames),
-                                        TEST_WRITE_MODE);
-                                // for non-blocking mode, this loop may spin quickly
-                                assertTrue(TEST_NAME + ": write error " + ret, ret >= 0);
-                                assertTrue(TEST_NAME + ": write timeout",
-                                        (System.currentTimeMillis() - startTime) <= maxDuration);
-                                written += ret;
-                            }
-
-                            // for streaming tracks, stop will allow the rest of the data to
-                            // drain out, but we don't know how long to wait unless
-                            // we check the position before stop. if we check position
-                            // after we stop, we read 0.
-                            final int position = track.getPlaybackHeadPosition();
-                            final int remainingTimeMs = (int)((double)(frameCount - position)
-                                    * 1000 / TEST_SR);
-                            track.stop();
-                            Thread.sleep(remainingTimeMs);
-                            // tear down
-                            track.release();
                             // add a gap to make tones distinct
                             Thread.sleep(100 /* millis */);
-                            frequency += 200; // increment test tone frequency
+                            frequency += 30; // increment test tone frequency
                         }
                     }
                 }
@@ -2911,6 +2884,334 @@
             audioTrack.getAudioDescriptionMixLeveldB(), 0.f /*delta*/);
     }
 
+    /*
+     * The following helpers and tests are used to test setting
+     * and getting the start threshold in frames.
+     *
+     * See Android CDD 5.6 [C-1-2] Cold output latency
+     */
+    private static final int START_THRESHOLD_SLEEP_MILLIS = 500;
+
+    /**
+     * Helper test that validates setting the start threshold.
+     *
+     * @param track
+     * @param startThresholdInFrames
+     * @throws Exception
+     */
+    private static void validateSetStartThresholdInFrames(
+            AudioTrack track, int startThresholdInFrames) throws Exception {
+        assertEquals(startThresholdInFrames,
+                track.setStartThresholdInFrames(startThresholdInFrames));
+        assertEquals(startThresholdInFrames,
+                track.getStartThresholdInFrames());
+    }
+
+    /**
+     * Helper that tests that the head position eventually equals expectedFrames.
+     *
+     * Exponential backoff to ~ 2 x START_THRESHOLD_SLEEP_MILLIS
+     *
+     * @param track
+     * @param expectedFrames
+     * @param message
+     * @throws Exception
+     */
+    private static void validatePlaybackHeadPosition(
+            AudioTrack track, int expectedFrames, String message) throws Exception {
+        int cumulativeMillis = 0;
+        int playbackHeadPosition = 0;
+        for (double testMillis = START_THRESHOLD_SLEEP_MILLIS * 0.125;
+             testMillis <= START_THRESHOLD_SLEEP_MILLIS;  // this is exact for IEEE binary double
+             testMillis *= 2.) {
+            Thread.sleep((int)testMillis);
+            playbackHeadPosition = track.getPlaybackHeadPosition();
+            if (playbackHeadPosition == expectedFrames) return;
+            cumulativeMillis += (int)testMillis;
+        }
+        fail(message + ": expected track playbackHeadPosition: " + expectedFrames
+                + " actual playbackHeadPosition: " + playbackHeadPosition
+                + " wait time: " + cumulativeMillis + "ms");
+    }
+
+    /**
+     * Helper test that sets the start threshold to frames, and validates
+     * writing exactly frames amount of data is needed to start the
+     * track streaming.
+     *
+     * @param track
+     * @param frames
+     * @throws Exception
+     */
+    private static void validateWriteStartsStream(
+            AudioTrack track, int frames) throws Exception {
+        assertEquals(1, track.getChannelCount()); // must be MONO
+        final short[] data = new short[frames];
+
+        // The track must be idle/underrun or the test will fail.
+        int expectedFrames = track.getPlaybackHeadPosition();
+
+        // Set our threshold to frames.
+        validateSetStartThresholdInFrames(track, frames);
+
+        Thread.sleep(START_THRESHOLD_SLEEP_MILLIS);
+        assertEquals("Changing start threshold doesn't start if it is larger than buffer data",
+                expectedFrames, track.getPlaybackHeadPosition());
+
+        // Write a small amount of data, this isn't enough to start the track.
+        final int PARTIAL_WRITE_IN_FRAMES = frames - 1;
+        track.write(data, 0 /* offsetInShorts */, PARTIAL_WRITE_IN_FRAMES);
+
+        // Ensure the track hasn't started.
+        Thread.sleep(START_THRESHOLD_SLEEP_MILLIS);
+        assertEquals("Track needs enough frames to start",
+                expectedFrames, track.getPlaybackHeadPosition());
+
+        // Write exactly threshold frames out, this should kick the playback off.
+        track.write(data, 0 /* offsetInShorts */, data.length - PARTIAL_WRITE_IN_FRAMES);
+
+        // Verify that we have processed the data now.
+        expectedFrames += frames;
+        Thread.sleep(frames * 1000L / track.getSampleRate());  // accommodate for #frames.
+        validatePlaybackHeadPosition(track, expectedFrames,
+                "Writing buffer data to start threshold should start streaming");
+    }
+
+    /**
+     * Helper that tests reducing the start threshold to frames will start track
+     * streaming when frames of data are written to it.  (Presumes the
+     * previous start threshold was greater than frames).
+     *
+     * @param track
+     * @param frames
+     * @throws Exception
+     */
+    private static void validateSetStartThresholdStartsStream(
+            AudioTrack track, int frames) throws Exception {
+        assertTrue(track.getStartThresholdInFrames() > frames);
+        assertEquals(1, track.getChannelCount()); // must be MONO
+        final short[] data = new short[frames];
+
+        // The track must be idle/underrun or the test will fail.
+        int expectedFrames = track.getPlaybackHeadPosition();
+
+        // This write is too small for now.
+        track.write(data, 0 /* offsetInShorts */, data.length);
+
+        Thread.sleep(START_THRESHOLD_SLEEP_MILLIS);
+        assertEquals("Track needs enough frames to start",
+                expectedFrames, track.getPlaybackHeadPosition());
+
+        // Reduce our start threshold.  This should start streaming.
+        validateSetStartThresholdInFrames(track, frames);
+
+        // Verify that we have processed the data now.
+        expectedFrames += frames;
+        Thread.sleep(frames * 1000L / track.getSampleRate());  // accommodate for #frames.
+        validatePlaybackHeadPosition(track, expectedFrames,
+                "Changing start threshold to buffer data level should start streaming");
+    }
+
+    // Start threshold levels that we check.
+    private enum ThresholdLevel { LOW, MEDIUM, HIGH };
+    @Test
+    public void testStartThresholdInFrames() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        for (ThresholdLevel level : new ThresholdLevel[] {
+                ThresholdLevel.LOW, ThresholdLevel.MEDIUM, ThresholdLevel.HIGH}) {
+            AudioTrack audioTrack = null;
+            try {
+                // Build our audiotrack
+                audioTrack = new AudioTrack.Builder()
+                        .setAudioFormat(new AudioFormat.Builder()
+                                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                                .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
+                                .build())
+                        .build();
+
+                // Initially the start threshold must be the same as the buffer size in frames.
+                final int bufferSizeInFrames = audioTrack.getBufferSizeInFrames();
+                assertEquals("At start, getBufferSizeInFrames should equal getStartThresholdInFrames",
+                        bufferSizeInFrames,
+                        audioTrack.getStartThresholdInFrames());
+
+                final int TARGET_THRESHOLD_IN_FRAMES;  // threshold level to verify
+                switch (level) {
+                    default:
+                    case LOW:
+                        TARGET_THRESHOLD_IN_FRAMES = 2;
+                        break;
+                    case MEDIUM:
+                        TARGET_THRESHOLD_IN_FRAMES = bufferSizeInFrames / 2;
+                        break;
+                    case HIGH:
+                        TARGET_THRESHOLD_IN_FRAMES = bufferSizeInFrames - 1;
+                        break;
+                }
+
+                // Skip extreme cases that don't need testing.
+                if (TARGET_THRESHOLD_IN_FRAMES < 2
+                        || TARGET_THRESHOLD_IN_FRAMES >= bufferSizeInFrames) continue;
+
+                // Start the AudioTrack. Now the track is waiting for data.
+                audioTrack.play();
+
+                validateWriteStartsStream(audioTrack, TARGET_THRESHOLD_IN_FRAMES);
+
+                // Try a condition that requires buffers to be filled again.
+                if (false) {
+                    // Only a deep underrun when the track becomes inactive requires a refill.
+                    // Disabled as this is dependent on underlying MixerThread timeouts.
+                    Thread.sleep(5000 /* millis */);
+                } else {
+                    // Flushing will require a refill (this does not require timing).
+                    audioTrack.pause();
+                    audioTrack.flush();
+                    audioTrack.play();
+                }
+
+                // Check that reducing to a smaller threshold will start the track streaming.
+                validateSetStartThresholdStartsStream(audioTrack, TARGET_THRESHOLD_IN_FRAMES - 1);
+            } finally {
+                if (audioTrack != null) {
+                    audioTrack.release();
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testStartThresholdInFramesExceptions() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+        AudioTrack audioTrack = null;
+        try {
+            // Build our audiotrack
+            audioTrack = new AudioTrack.Builder()
+                    .setAudioFormat(new AudioFormat.Builder()
+                            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                            .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
+                            .build())
+                    .build();
+
+            // Test setting invalid start threshold.
+            final AudioTrack track = audioTrack; // make final for lambda
+            assertThrows(IllegalArgumentException.class, () -> {
+                track.setStartThresholdInFrames(-1 /* startThresholdInFrames */);
+            });
+        } finally {
+            if (audioTrack != null) {
+                audioTrack.release();
+            }
+        }
+        // If we're here audioTrack should be non-null but released,
+        // so calls should return an IllegalStateException.
+        final AudioTrack track = audioTrack; // make final for lambda
+        assertThrows(IllegalStateException.class, () -> {
+            track.getStartThresholdInFrames();
+        });
+        assertThrows(IllegalStateException.class, () -> {
+            track.setStartThresholdInFrames(1 /* setStartThresholdInFrames */);
+        });
+    }
+
+    /**
+     * Tests height channel masks and higher channel counts
+     * used in immersive AudioTrack streaming.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testImmersiveStreaming() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        final String TEST_NAME = "testImmersiveStreaming";
+        final int TEST_FORMAT_ARRAY[] = {
+            AudioFormat.ENCODING_PCM_16BIT,
+            AudioFormat.ENCODING_PCM_FLOAT,
+        };
+        final int TEST_SR_ARRAY[] = {
+            48000,  // do not set too high - costly in memory.
+        };
+        final int TEST_CONF_ARRAY[] = {
+            AudioFormat.CHANNEL_OUT_5POINT1POINT2, // 8 ch (includes height channels vs 7.1).
+            AudioFormat.CHANNEL_OUT_7POINT1POINT4, // 12 ch
+            AudioFormat.CHANNEL_OUT_22POINT2,      // 24 ch
+        };
+
+        final int TEST_MODE = AudioTrack.MODE_STREAM;
+        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+        final float TEST_SWEEP = 0; // sine wave only
+        final boolean TEST_IS_LOW_RAM_DEVICE = false;
+        for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
+            double frequency = 400; // Note: frequency changes for each test
+            for (int TEST_SR : TEST_SR_ARRAY) {
+                for (int TEST_CONF : TEST_CONF_ARRAY) {
+                    if (AudioFormat.channelCountFromOutChannelMask(TEST_CONF)
+                            > AudioSystem.OUT_CHANNEL_COUNT_MAX) {
+                        continue; // Skip if the channel count exceeds framework capabilities.
+                    }
+                    playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
+                            TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, frequency, TEST_SR, TEST_CONF,
+                            WAIT_MSEC);
+                    frequency += 50; // increment test tone frequency
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testImmersiveChannelIndex() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
+
+        final String TEST_NAME = "testImmersiveChannelIndex";
+        final int TEST_FORMAT_ARRAY[] = {
+                AudioFormat.ENCODING_PCM_FLOAT,
+        };
+        final int TEST_SR_ARRAY[] = {
+                48000,  // do not set too high - costly in memory.
+        };
+        final int MAX_CHANNEL_BIT = 1 << (AudioSystem.FCC_24 - 1); // highest allowed channel.
+        final int TEST_CONF_ARRAY[] = {
+                (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1,
+                MAX_CHANNEL_BIT,      // likely silent - no physical device on top channel.
+                MAX_CHANNEL_BIT | 1,  // first channel will likely have physical device.
+        };
+        final int TEST_WRITE_MODE_ARRAY[] = {
+                AudioTrack.WRITE_BLOCKING,
+                AudioTrack.WRITE_NON_BLOCKING,
+        };
+        final double TEST_SWEEP = 0;
+        final int TEST_TRANSFER_MODE = AudioTrack.MODE_STREAM;
+        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+
+        double frequency = 200; // frequency changes for each test
+        for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
+            for (int TEST_CONF : TEST_CONF_ARRAY) {
+                for (int TEST_SR : TEST_SR_ARRAY) {
+                    for (int TEST_WRITE_MODE : TEST_WRITE_MODE_ARRAY) {
+                        for (int useDirect = 0; useDirect < 2; ++useDirect) {
+                            playOnceStreamByteBuffer(
+                                    TEST_NAME, frequency, TEST_SWEEP,
+                                    TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
+                                    TEST_TRANSFER_MODE, TEST_WRITE_MODE,
+                                    true /* useChannelIndex */, useDirect != 0);
+                            frequency += 30; // increment test tone frequency
+                        }
+                    }
+                }
+            }
+        }
+    }
+
 /* Do not run in JB-MR1. will be re-opened in the next platform release.
     public void testResourceLeakage() throws Exception {
         final int BUFFER_SIZE = 600 * 1024;
diff --git a/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java b/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
index 0803da3..4bada72 100644
--- a/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
+++ b/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
@@ -60,7 +60,19 @@
     private static final String KEY_ALLOW_FRAME_DROP = "allow-frame-drop";
 
     // movie length, in frames
-    private static final int NUM_FRAMES = 30;               // two seconds of video
+    private static final int NUM_FRAMES = FRAME_RATE * 3;   // three seconds of video
+
+    // since encoders are lossy, we treat the first N frames differently, with a different
+    // tolerance, than the remainder of the clip.  The # of such frames is
+    // INITIAL_TOLERANCE_FRAME_LIMIT, the tolerance within that window is defined by
+    // INITIAL_TOLERANCE and the tolerance afterwards is defined by TOLERANCE
+    private final int INITIAL_TOLERANCE_FRAME_LIMIT = FRAME_RATE * 2;
+
+    // allowed error between input and output
+    private static final int TOLERANCE = 8;
+
+    // allowed error between input and output for initial INITIAL_TOLERANCE_FRAME_LIMIT frames
+    private static final int INITIAL_TOLERANCE = 10;
 
     private static final int TEST_R0 = 0;                   // dull green background
     private static final int TEST_G0 = 136;
@@ -733,9 +745,10 @@
                                 info.presentationTimeUs);
                         surface.awaitNewImage();
                         surface.drawImage();
-                        if (!checkSurfaceFrame(checkIndex++)) {
+                        if (!checkSurfaceFrame(checkIndex)) {
                             badFrames++;
                         }
+                        checkIndex++;
                     }
                 }
             }
@@ -752,7 +765,8 @@
     private boolean checkSurfaceFrame(int frameIndex) {
         ByteBuffer pixelBuf = ByteBuffer.allocateDirect(4); // TODO - reuse this
         boolean frameFailed = false;
-
+        // Choose the appropriate initial/regular tolerance
+        int maxDelta = frameIndex < INITIAL_TOLERANCE_FRAME_LIMIT ? INITIAL_TOLERANCE : TOLERANCE;
         for (int i = 0; i < 8; i++) {
             // Note the coordinates are inverted on the Y-axis in GL.
             int x, y;
@@ -782,12 +796,12 @@
                 expG = TEST_B0;
                 expB = TEST_G0;
             }
-            if (!isColorClose(r, expR) ||
-                    !isColorClose(g, expG) ||
-                    !isColorClose(b, expB)) {
+            if (!isColorClose(r, expR, maxDelta) ||
+                    !isColorClose(g, expG, maxDelta) ||
+                    !isColorClose(b, expB, maxDelta)) {
                 Log.w(TAG, "Bad frame " + frameIndex + " (rect=" + i + ": rgb=" + r +
                         "," + g + "," + b + " vs. expected " + expR + "," + expG +
-                        "," + expB + ")");
+                        "," + expB + ") for allowed error of " + maxDelta);
                 frameFailed = true;
             }
         }
@@ -799,13 +813,12 @@
      * Returns true if the actual color value is close to the expected color value.  Updates
      * mLargestColorDelta.
      */
-    boolean isColorClose(int actual, int expected) {
-        final int MAX_DELTA = 8;
+    boolean isColorClose(int actual, int expected, int maxDelta) {
         int delta = Math.abs(actual - expected);
         if (delta > mLargestColorDelta) {
             mLargestColorDelta = delta;
         }
-        return (delta <= MAX_DELTA);
+        return (delta <= maxDelta);
     }
 
     /**
diff --git a/tests/tests/media/src/android/media/cts/DecoderConformanceTest.java b/tests/tests/media/src/android/media/cts/DecoderConformanceTest.java
index 258fd7a..7a5acbe 100644
--- a/tests/tests/media/src/android/media/cts/DecoderConformanceTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderConformanceTest.java
@@ -189,6 +189,7 @@
                     if (stat == Status.PASS) {
                         pass = true;
                     } else if (stat == Status.SKIP) {
+                        release();
                         continue;
                     }
                 } catch (Exception e) {
diff --git a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
index 5009f58..b82019a 100755
--- a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
@@ -28,6 +28,7 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.compatibility.common.util.MediaUtils;
@@ -54,6 +55,8 @@
 @Presubmit
 @SmallTest
 @RequiresDevice
+// TODO: b/186001256
+@FlakyTest
 public class EncodeDecodeTest extends AndroidTestCase {
     private static final String TAG = "EncodeDecodeTest";
     private static final boolean VERBOSE = false;           // lots of logging
diff --git a/tests/tests/media/src/android/media/cts/HeifWriterTest.java b/tests/tests/media/src/android/media/cts/HeifWriterTest.java
index 7f1c0d6..40d6128 100644
--- a/tests/tests/media/src/android/media/cts/HeifWriterTest.java
+++ b/tests/tests/media/src/android/media/cts/HeifWriterTest.java
@@ -52,6 +52,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.heifwriter.HeifWriter;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.compatibility.common.util.ApiLevelUtil;
@@ -171,21 +172,26 @@
         doTestForVariousNumberImages(builder);
     }
 
+    // TODO: b/186001256
+    @FlakyTest
     public void testInputSurface_NoGrid_NoHandler() throws Throwable {
         TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, false, false);
         doTestForVariousNumberImages(builder);
     }
 
+    @FlakyTest
     public void testInputSurface_Grid_NoHandler() throws Throwable {
         TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, true, false);
         doTestForVariousNumberImages(builder);
     }
 
+    @FlakyTest
     public void testInputSurface_NoGrid_Handler() throws Throwable {
         TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, false, true);
         doTestForVariousNumberImages(builder);
     }
 
+    @FlakyTest
     public void testInputSurface_Grid_Handler() throws Throwable {
         TestConfig.Builder builder = new TestConfig.Builder(INPUT_MODE_SURFACE, true, true);
         doTestForVariousNumberImages(builder);
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
index ff61af0..dcd592c 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -44,6 +44,7 @@
 import android.util.Log;
 import android.view.Surface;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import com.android.compatibility.common.util.MediaUtils;
@@ -393,11 +394,16 @@
     public void testGoogVP8ImageReader()    { swirlTest(googVP8(),    MODE_IMAGEREADER); }
     public void testGoogVP9ImageReader()    { swirlTest(googVP9(),    MODE_IMAGEREADER); }
 
+    // TODO: b/186001256
+    @FlakyTest
     public void testOtherH265ImageReader()  { swirlTest(otherH265(),  MODE_IMAGEREADER); }
+    @FlakyTest
     public void testOtherH264ImageReader()  { swirlTest(otherH264(),  MODE_IMAGEREADER); }
     public void testOtherH263ImageReader()  { swirlTest(otherH263(),  MODE_IMAGEREADER); }
     public void testOtherMpeg4ImageReader() { swirlTest(otherMpeg4(), MODE_IMAGEREADER); }
+    @FlakyTest
     public void testOtherVP8ImageReader()   { swirlTest(otherVP8(),   MODE_IMAGEREADER); }
+    @FlakyTest
     public void testOtherVP9ImageReader()   { swirlTest(otherVP9(),   MODE_IMAGEREADER); }
 
     /**
diff --git a/tests/tests/media/src/android/media/cts/MediaCasTest.java b/tests/tests/media/src/android/media/cts/MediaCasTest.java
index a3b9176..268c34d 100644
--- a/tests/tests/media/src/android/media/cts/MediaCasTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCasTest.java
@@ -28,6 +28,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.platform.test.annotations.AppModeFull;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresDevice;
 import android.test.AndroidTestCase;
@@ -51,6 +52,7 @@
 @Presubmit
 @SmallTest
 @RequiresDevice
+@AppModeFull(reason = "TODO: evaluate and port to instant")
 public class MediaCasTest extends AndroidTestCase {
     private static final String TAG = "MediaCasTest";
 
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 1176dd8..e764ac5 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -125,6 +125,8 @@
     private static final int DECODING_TIMEOUT_MS = 10000;
 
     private static boolean mIsAtLeastR = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
+    private static boolean mIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
+
     /**
      * Tests:
      * <br> Exceptions for MediaCodec factory methods
@@ -382,6 +384,38 @@
             fail("stop should not return MediaCodec.CodecException on wrong state");
         } catch (IllegalStateException e) { // expected
         }
+        try {
+            codec.getSupportedVendorParameters();
+            fail("getSupportedVendorParameters should throw IllegalStateException" +
+                    " when in Uninitialized state");
+        } catch (IllegalStateException e) { // expected
+        } catch (Exception e) {
+            fail("unexpected exception: " + e.toString());
+        }
+        try {
+            codec.getParameterDescriptor("");
+            fail("getParameterDescriptor should throw IllegalStateException" +
+                    " when in Uninitialized state");
+        } catch (IllegalStateException e) { // expected
+        } catch (Exception e) {
+            fail("unexpected exception: " + e.toString());
+        }
+        try {
+            codec.subscribeToVendorParameters(List.of(""));
+            fail("subscribeToVendorParameters should throw IllegalStateException" +
+                    " when in Uninitialized state");
+        } catch (IllegalStateException e) { // expected
+        } catch (Exception e) {
+            fail("unexpected exception: " + e.toString());
+        }
+        try {
+            codec.unsubscribeFromVendorParameters(List.of(""));
+            fail("unsubscribeFromVendorParameters should throw IllegalStateException" +
+                    " when in Uninitialized state");
+        } catch (IllegalStateException e) { // expected
+        } catch (Exception e) {
+            fail("unexpected exception: " + e.toString());
+        }
 
         if (mIsAtLeastR) {
             // recreate
@@ -2781,4 +2815,82 @@
             }
         }
     }
+
+    public void testVendorParameters() {
+        if (!MediaUtils.check(mIsAtLeastS, "test needs Android 12")) {
+            return;
+        }
+        MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        for (MediaCodecInfo info : mcl.getCodecInfos()) {
+            if (info.isAlias()) {
+                continue;
+            }
+            MediaCodec codec = null;
+            try {
+                codec = MediaCodec.createByCodecName(info.getName());
+                List<String> vendorParams = codec.getSupportedVendorParameters();
+                if (VERBOSE) {
+                    Log.d(TAG, "vendor params supported by " + info.getName() + ": " +
+                            vendorParams.toString());
+                }
+                for (String name : vendorParams) {
+                    MediaCodec.ParameterDescriptor desc = codec.getParameterDescriptor(name);
+                    assertNotNull(name + " is in the list of supported parameters, so the codec" +
+                            " should be able to describe it.", desc);
+                    assertEquals("name differs from the name in the descriptor",
+                            name, desc.getName());
+                    assertTrue("type in the descriptor cannot be TYPE_NULL",
+                            MediaFormat.TYPE_NULL != desc.getType());
+                    if (VERBOSE) {
+                        Log.d(TAG, name + " is of type " + desc.getType());
+                    }
+                }
+                codec.subscribeToVendorParameters(vendorParams);
+
+                // Build a MediaFormat that makes sense to the codec.
+                String type = info.getSupportedTypes()[0];
+                MediaFormat format = null;
+                CodecCapabilities caps = info.getCapabilitiesForType(type);
+                AudioCapabilities audioCaps = caps.getAudioCapabilities();
+                VideoCapabilities videoCaps = caps.getVideoCapabilities();
+                if (audioCaps != null) {
+                    format = MediaFormat.createAudioFormat(
+                            type,
+                            audioCaps.getMaxInputChannelCount(),
+                            audioCaps.getSupportedSampleRateRanges()[0].getLower());
+                    if (info.isEncoder()) {
+                        format.setInteger(MediaFormat.KEY_BIT_RATE, AUDIO_BIT_RATE);
+                    }
+                } else if (videoCaps != null) {
+                    int width = videoCaps.getSupportedWidths().getLower();
+                    int height = videoCaps.getSupportedHeightsFor(width).getLower();
+                    format = MediaFormat.createVideoFormat(type, width, height);
+                    if (info.isEncoder()) {
+                        format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
+                        format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
+                        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+                        format.setInteger(
+                                MediaFormat.KEY_COLOR_FORMAT,
+                                CodecCapabilities.COLOR_FormatYUV420Flexible);
+                    }
+                } else {
+                    Log.i(TAG, info.getName() + " is in neither audio nor video domain; skipped");
+                    codec.release();
+                    continue;
+                }
+                codec.configure(
+                        format, null, null,
+                        info.isEncoder() ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0);
+                codec.start();
+                codec.unsubscribeFromVendorParameters(vendorParams);
+                codec.stop();
+            } catch (Exception e) {
+                throw new RuntimeException("codec name: " + info.getName(), e);
+            } finally {
+                if (codec != null) {
+                    codec.release();
+                }
+            }
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
index 48e9810..fe9c30d 100644
--- a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
@@ -73,6 +73,7 @@
     private static final UUID UUID_WIDEVINE = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
     private static final UUID UUID_PLAYREADY = new UUID(0x9A04F07998404286L, 0xAB92E65BE0885F95L);
     private static boolean mIsAtLeastR = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
+    private static final boolean IS_AT_LEAST_S = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
 
     static final String mInpPrefix = WorkDir.getMediaDirString();
     protected MediaExtractor mExtractor;
@@ -429,6 +430,53 @@
         assertEquals("video/av01", mimeType);
     }
 
+    //MPEG-H 3D Audio single stream (mha1)
+    public void testMpegh3dAudioMediaExtractorMha1() throws Exception {
+        // TODO(b/186267251) move file to cloud storage.
+        AssetFileDescriptor afd = getContext().getResources()
+            .openRawResourceFd(R.raw.sample_mpegh_mha1);
+        mExtractor.setDataSource(afd);
+        assertEquals(1, mExtractor.getTrackCount());
+
+        // The following values below require API Build.VERSION_CODES.S
+        if (!MediaUtils.check(IS_AT_LEAST_S, "test needs Android 12")) return;
+
+        MediaFormat trackFormat = mExtractor.getTrackFormat(0);
+        final String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
+        assertEquals(MediaFormat.MIMETYPE_AUDIO_MPEGH_MHA1, mimeType);
+
+        final int hpli = trackFormat.getInteger(MediaFormat.KEY_MPEGH_PROFILE_LEVEL_INDICATION);
+        assertEquals(0x0D, hpli);
+
+        final int hrcl = trackFormat.getInteger(MediaFormat.KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT);
+        assertEquals(0x13, hrcl);
+    }
+
+    //MPEG-H 3D Audio single stream encapsulated in MHAS (mhm1)
+    public void testMpegh3dAudioMediaExtractorMhm1() throws Exception {
+        // TODO(b/186267251) move file to cloud storage.
+        AssetFileDescriptor afd = getContext().getResources()
+            .openRawResourceFd(R.raw.sample_mpegh_mhm1);
+        mExtractor.setDataSource(afd);
+        assertEquals(1, mExtractor.getTrackCount());
+
+        // The following values below require API Build.VERSION_CODES.S
+        if (!MediaUtils.check(IS_AT_LEAST_S, "test needs Android 12")) return;
+
+        MediaFormat trackFormat = mExtractor.getTrackFormat(0);
+        final String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
+        assertEquals(MediaFormat.MIMETYPE_AUDIO_MPEGH_MHM1, mimeType);
+
+        final int hpli = trackFormat.getInteger(MediaFormat.KEY_MPEGH_PROFILE_LEVEL_INDICATION);
+        assertEquals(0x0D, hpli);
+
+        final int hrcl = trackFormat.getInteger(MediaFormat.KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT);
+        assertEquals(0x13, hrcl);
+
+        final ByteBuffer hcos = trackFormat.getByteBuffer(MediaFormat.KEY_MPEGH_COMPATIBLE_SETS);
+        assertEquals(0x12, hcos.get());
+    }
+
     public void testGetDrmInitData() throws Exception {
         if (!MediaUtils.check(mIsAtLeastR, "test needs Android 11")) return;
         setDataSource("psshtest.mp4");
diff --git a/tests/tests/media/src/android/media/cts/MediaFormatTest.java b/tests/tests/media/src/android/media/cts/MediaFormatTest.java
index 2beae2b..18a7add 100644
--- a/tests/tests/media/src/android/media/cts/MediaFormatTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaFormatTest.java
@@ -16,16 +16,30 @@
 
 package android.media.cts;
 
+import android.annotation.NonNull;
 import android.media.MediaFormat;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+
+import java.lang.reflect.Field;
 import java.nio.ByteBuffer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
 public class MediaFormatTest extends AndroidTestCase {
+    private static final String TAG = "MediaFormatTest";
     private static ByteBuffer defaultByteBuffer = ByteBuffer.allocateDirect(16);
 
     private void assertGetByteBuffersThrowClassCastException(
@@ -635,4 +649,183 @@
             assertEquals(1, other.getFeatures().size());
         }
     }
+
+    /**
+     * Check MediaFormat key name and string value consistency.
+     *
+     * The canonical key reads something like this:
+     * KEY_SOMETHING_HERE = "something-here";
+     *
+     * An exclusion list allows arbitrary keys as needed.
+     *
+     * This test uses introspection to find the key fields.
+     *
+     * @throws Exception
+     */
+    public void testKeyConsistency() throws Exception {
+        // Legacy MediaFormat keys inconsistent with the canonical format.
+        final Set<String> exclusions = Stream.of(
+            // <aac-drc-[cut-level]>
+            "KEY_AAC_DRC_ATTENUATION_FACTOR",
+            // <aac-drc-boost-[level]>
+            "KEY_AAC_DRC_BOOST_FACTOR",
+            // <aac-[target-ref]-level>
+            "KEY_AAC_DRC_TARGET_REFERENCE_LEVEL",
+            // <...c-max-output-channel[_]count>
+            "KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT",
+            // <bit[]rate>
+            "KEY_BIT_RATE",
+            // <create-input-[buffers]-suspended>
+            "KEY_CREATE_INPUT_SURFACE_SUSPENDED",
+            // <duration[u]s>
+            "KEY_DURATION",
+            // <grid-col[]s>
+            "KEY_GRID_COLUMNS",
+            // <h[w]-av-sync-id>
+            "KEY_HARDWARE_AV_SYNC_ID",
+            // <max-bit[]rate>
+            "KEY_MAX_BIT_RATE",
+            // <max-b[]frames>
+            "KEY_MAX_B_FRAMES",
+            // <[sar]-height>
+            "KEY_PIXEL_ASPECT_RATIO_HEIGHT",
+            // <[sar]-width>
+            "KEY_PIXEL_ASPECT_RATIO_WIDTH",
+            // <prepend-[sps-pps-to-idr]-frames>
+            "KEY_PREPEND_HEADER_TO_SYNC_FRAMES",
+            // <...h-blank-buffers-on-s[hutdown]>
+            "KEY_PUSH_BLANK_BUFFERS_ON_STOP",
+            // <rotation[-degrees]>
+            "KEY_ROTATION",
+            // <t[s-schema]>
+            "KEY_TEMPORAL_LAYERING"
+            ).collect(Collectors.toCollection(HashSet::new));
+
+        ArrayList<String> failures = new ArrayList<>();
+        final Field[] fields = MediaFormat.class.getFields();
+        for (Field field : fields) {
+            final String key = field.getName();
+            if (!key.startsWith("KEY_")) continue;
+            if (exclusions.contains(key)) continue;
+
+            if (!key.equals(key.toUpperCase())) {
+                failures.add("Key field " + key + " must be upper case");
+            }
+            final String value = (String)field.get(null);
+            assertEquals("String value " + value + " must be lower case",
+                    value.toLowerCase(), value);
+
+            // What do we expect the key should look like for the value?
+            final String checkKey = "KEY_" + value.toUpperCase().replace('-', '_');
+            if (!checkKey.equals(key)) {
+                failures.add("Key field " + key + " should represent value " + value
+                        + " expected(" + checkKey + ")");
+            }
+        }
+        // There may be special vendor keys that are public.
+        // Log failures but don't fail test.
+        logFailures("testKeyConsistency", failures);
+    }
+
+    /**
+     * Check MediaFormat mime type field name and string value consistency.
+     *
+     * The typical mime type field reads as follows:
+     * MIMETYPE_CATEGORY_ANYCASE_HERE = "category/anYCaSE[-.+]HeRE";
+     *
+     * See here for the Internet Assigned Numbers Authority (IANA) list of media mime types:
+     * https://www.iana.org/assignments/media-types/media-types.xhtml
+     *
+     * An exclusion list allows arbitrary keys as needed.
+     *
+     * This test uses introspection to find the mime type fields.
+     *
+     * @throws Exception
+     */
+    public void testMimeTypeConsistency() throws Exception {
+        // Legacy inconsistent mime types with the exception
+        final Set<String> exclusions = Stream.of(
+                // <audio/[mp4a-latm]>
+                "MIMETYPE_AUDIO_AAC",
+                // <audio/[3gpp]>
+                "MIMETYPE_AUDIO_AMR_NB",
+                // audio/mhm1
+                "MIMETYPE_AUDIO_MPEGH_MHM1",
+                // audio/mha1
+                "MIMETYPE_AUDIO_MPEGH_MHA1",
+                // <audio/[]gsm>
+                "MIMETYPE_AUDIO_MSGSM",
+                // <image/[vnd.android.]heic>
+                "MIMETYPE_IMAGE_ANDROID_HEIC",
+                // <[application/x-]subrip>
+                "MIMETYPE_TEXT_SUBRIP",
+                // <video/av[0]1>
+                "MIMETYPE_VIDEO_AV1",
+                // <video/[3gpp]>
+                "MIMETYPE_VIDEO_H263",
+                // <video/mp[4v-es]>
+                "MIMETYPE_VIDEO_MPEG4",
+                // <video/[x-vnd.on2.]vp8>
+                "MIMETYPE_VIDEO_VP8",
+                // <video/[x-vnd.on2.]vp9>
+                "MIMETYPE_VIDEO_VP9"
+        ).collect(Collectors.toCollection(HashSet::new));
+
+        ArrayList<String> failures = new ArrayList<>();
+        final Field[] fields = MediaFormat.class.getFields();
+        for (Field field : fields) {
+            final String mimeType = field.getName();
+
+            if (!mimeType.startsWith("MIMETYPE_")) continue;
+            if (exclusions.contains(mimeType)) continue;
+
+            if (!mimeType.equals(mimeType.toUpperCase())) {
+                failures.add("mimeType field " + mimeType + " must be upper case");
+                continue;
+            }
+            final String value = (String)field.get(null);
+
+            // What do we expect the mime type field should be for the value?
+            final String checkMime = "MIMETYPE_"
+                    + value.toUpperCase().replace('/', '_').replace('-', '_')
+                            .replace('.', '_').replace('+', '_');
+            if (!mimeType.equals(checkMime)) {
+                failures.add("Mime type " + mimeType
+                        + " should represent value " + value
+                        + " expected(" + checkMime + ")");
+            }
+        }
+        // There may be special vendor keys that are public.
+        // Log failures but don't fail test.
+        logFailures("testMimeTypeConsistency", failures);
+    }
+
+    private static final String REPORT_LOG_NAME = "CtsMediaTestCases";
+    private static final int REPORT_SUMMARY_MAX_KEY_LEN = 240;
+
+    /**
+     * Log failures on atest, but don't raise an exception or fail CTS.
+     *
+     * This part is tricky:
+     * 1) We create a device report log so it is visible on the host.
+     * 2) We also write to logcat.
+     */
+    private static void logFailures(@NonNull String logName, @NonNull List<String> failures) {
+        if (failures.size() > 0) {
+            DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, logName);
+            StringBuilder sb = new StringBuilder("FAILED ON: ");
+            int i = 0;
+            for (String failure : failures) {
+                Log.w(TAG, failure);
+                log.addValue("failure_" + i++, failure, ResultType.NEUTRAL, ResultUnit.NONE);
+                sb.append("[" + failure + "] ");
+            }
+            if (sb.length() > REPORT_SUMMARY_MAX_KEY_LEN) {
+                sb.setLength(REPORT_SUMMARY_MAX_KEY_LEN);
+            }
+            log.setSummary(sb.toString(), failures.size(),
+                    ResultType.LOWER_BETTER, ResultUnit.COUNT);
+            log.submit(InstrumentationRegistry.getInstrumentation());
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaStubActivity.java b/tests/tests/media/src/android/media/cts/MediaStubActivity.java
index 48dde25..fc5d459 100644
--- a/tests/tests/media/src/android/media/cts/MediaStubActivity.java
+++ b/tests/tests/media/src/android/media/cts/MediaStubActivity.java
@@ -22,6 +22,7 @@
 import android.util.Log;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
+import android.view.WindowManager;
 
 public class MediaStubActivity extends Activity {
     private static final String TAG = "MediaStubActivity";
@@ -31,6 +32,11 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        setTurnScreenOn(true);
+        setShowWhenLocked(true);
+
         setContentView(R.layout.mediaplayer);
 
         SurfaceView surfaceV = (SurfaceView)findViewById(R.id.surface);
diff --git a/tests/tests/media/src/android/media/cts/MediaStubActivity2.java b/tests/tests/media/src/android/media/cts/MediaStubActivity2.java
index d16c841..8b9a109 100644
--- a/tests/tests/media/src/android/media/cts/MediaStubActivity2.java
+++ b/tests/tests/media/src/android/media/cts/MediaStubActivity2.java
@@ -23,6 +23,7 @@
 import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.TextureView.SurfaceTextureListener;
+import android.view.WindowManager;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
@@ -36,6 +37,11 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        setTurnScreenOn(true);
+        setShowWhenLocked(true);
+
         setContentView(R.layout.mediacodecplayer);
 
         SurfaceView surfaceV = (SurfaceView)findViewById(R.id.surface);
diff --git a/tests/tests/media/src/android/media/cts/SoundPoolTest.java b/tests/tests/media/src/android/media/cts/SoundPoolTest.java
index e4df4ac..c345816 100644
--- a/tests/tests/media/src/android/media/cts/SoundPoolTest.java
+++ b/tests/tests/media/src/android/media/cts/SoundPoolTest.java
@@ -16,26 +16,32 @@
 
 package android.media.cts;
 
-import android.media.cts.R;
-
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.SoundPool;
+import android.media.cts.R;
 import android.platform.test.annotations.AppModeFull;
-import android.test.AndroidTestCase;
-
+import androidx.test.InstrumentationRegistry;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.util.Arrays;
-import java.util.concurrent.atomic.AtomicInteger;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @AppModeFull(reason = "TODO: evaluate and port to instant")
-abstract class SoundPoolTest extends AndroidTestCase {
+@RunWith(JUnitParamsRunner.class)
+abstract class SoundPoolTest {
 
     private static final int SOUNDPOOL_STREAMS = 4;
     private static final int PRIORITY = 1;
@@ -71,20 +77,22 @@
         return sounds;
     }
 
+    private static Context getContext() {
+        return InstrumentationRegistry.getInstrumentation().getTargetContext();
+    }
+
     protected AudioAttributes getAudioAttributes() {
         return new AudioAttributes.Builder()
                 .setLegacyStreamType(AudioManager.STREAM_MUSIC).build();
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mFile = new File(mContext.getFilesDir(), getFileName());
+    @Before
+    public void setUp() throws Exception {
+        mFile = new File(getContext().getFilesDir(), getFileName());
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
+    @After
+    public void tearDown() throws Exception {
         if (mFile.exists()) {
             mFile.delete();
         }
@@ -95,15 +103,16 @@
         }
     }
 
+    @Test
     public void testLoad() throws Exception {
         mSoundPool = new SoundPool.Builder().setMaxStreams(SOUNDPOOL_STREAMS)
                 .setAudioAttributes(getAudioAttributes()).build();
-        int sampleId1 = mSoundPool.load(mContext, getSoundA(), PRIORITY);
+        int sampleId1 = mSoundPool.load(getContext(), getSoundA(), PRIORITY);
         waitUntilLoaded(sampleId1);
         // should return true, but returns false
         mSoundPool.unload(sampleId1);
 
-        AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(getSoundCs());
+        AssetFileDescriptor afd = getContext().getResources().openRawResourceFd(getSoundCs());
         int sampleId2;
         sampleId2 = mSoundPool.load(afd, PRIORITY);
         waitUntilLoaded(sampleId2);
@@ -129,7 +138,7 @@
         FileOutputStream fOutput = null;
         try {
             fOutput = new FileOutputStream(f);
-            InputStream is = mContext.getResources().openRawResource(getSoundA());
+            InputStream is = getContext().getResources().openRawResource(getSoundA());
             byte[] buffer = new byte[1024];
             int length = is.read(buffer);
             while (length != -1) {
@@ -144,8 +153,14 @@
         }
     }
 
-    public void testSoundPoolOp() throws Exception {
-        mSoundPool = new SoundPool.Builder().setMaxStreams(SOUNDPOOL_STREAMS)
+    /**
+     * Parameterized tests consider 1, 2, 4 streams in the SoundPool.
+     */
+
+    @Test
+    @Parameters({"1", "2", "4"})
+    public void testSoundPoolOp(int streamCount) throws Exception {
+        mSoundPool = new SoundPool.Builder().setMaxStreams(streamCount)
                 .setAudioAttributes(getAudioAttributes()).build();
         int sampleID = loadSampleSync(getSoundA(), PRIORITY);
 
@@ -187,8 +202,10 @@
         mSoundPool.unload(sampleID);
     }
 
-    public void testMultiSound() throws Exception {
-        mSoundPool = new SoundPool.Builder().setMaxStreams(SOUNDPOOL_STREAMS)
+    @Test
+    @Parameters({"1", "2", "4"})
+    public void testMultiSound(int streamCount) throws Exception {
+        mSoundPool = new SoundPool.Builder().setMaxStreams(streamCount)
                 .setAudioAttributes(getAudioAttributes()).build();
         int sampleID1 = loadSampleSync(getSoundA(), PRIORITY);
         int sampleID2 = loadSampleSync(getSoundCs(), PRIORITY);
@@ -217,8 +234,10 @@
         mSoundPool = null;
     }
 
-    public void testLoadMore() throws Exception {
-        mSoundPool = new SoundPool.Builder().setMaxStreams(SOUNDPOOL_STREAMS)
+    @Test
+    @Parameters({"1", "2", "4"})
+    public void testLoadMore(int streamCount) throws Exception {
+        mSoundPool = new SoundPool.Builder().setMaxStreams(streamCount)
                 .setAudioAttributes(getAudioAttributes()).build();
         int[] sounds = getSounds();
         int[] soundIds = new int[sounds.length];
@@ -241,6 +260,7 @@
         mSoundPool.release();
     }
 
+    @Test
     public void testAutoPauseResume() throws Exception {
         // The number of possible SoundPool streams simultaneously active is limited by
         // track resources. Generally this is no greater than 32, but the actual
@@ -283,7 +303,7 @@
             // initiate loading
             final int[] soundIds = new int[TEST_STREAMS];
             for (int i = 0; i < soundIds.length; i++) {
-                soundIds[i] = soundPool.load(mContext, sounds[i % sounds.length], PRIORITY);
+                soundIds[i] = soundPool.load(getContext(), sounds[i % sounds.length], PRIORITY);
             }
 
             // wait for all sounds to load,
@@ -346,7 +366,7 @@
      * @throws InterruptedException
      */
     private int loadSampleSync(int sampleId, int prio) throws InterruptedException {
-        int sample = mSoundPool.load(mContext, sampleId, prio);
+        int sample = mSoundPool.load(getContext(), sampleId, prio);
         waitUntilLoaded(sample);
         return sample;
     }
diff --git a/tests/tests/mediaparser/AndroidTest.xml b/tests/tests/mediaparser/AndroidTest.xml
index f3d66cd..e670a80 100644
--- a/tests/tests/mediaparser/AndroidTest.xml
+++ b/tests/tests/mediaparser/AndroidTest.xml
@@ -19,6 +19,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.media.apex" />
     <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk30ModuleController" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/tests/mediaparser/TEST_MAPPING b/tests/tests/mediaparser/TEST_MAPPING
index 3d21914..b871ae5 100644
--- a/tests/tests/mediaparser/TEST_MAPPING
+++ b/tests/tests/mediaparser/TEST_MAPPING
@@ -1,4 +1,9 @@
 {
+  "mainline-presubmit": [
+    {
+      "name": "CtsMediaParserTestCases[com.google.android.media.apex]"
+    }
+  ],
   "presubmit": [
     {
       "name": "CtsMediaParserTestCases"
diff --git a/tests/tests/mediastress/OWNERS b/tests/tests/mediastress/OWNERS
new file mode 100644
index 0000000..eb1063e
--- /dev/null
+++ b/tests/tests/mediastress/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1344
+include ../../media/OWNERS
diff --git a/tests/tests/nativemedia/aaudio/Android.mk b/tests/tests/nativemedia/aaudio/Android.mk
index ed3814d..8f099f5 100644
--- a/tests/tests/nativemedia/aaudio/Android.mk
+++ b/tests/tests/nativemedia/aaudio/Android.mk
@@ -17,6 +17,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsNativeMediaAAudioTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 # Include both the 32 and 64 bit versions
 LOCAL_MULTILIB := both
diff --git a/tests/tests/nativemidi/Android.mk b/tests/tests/nativemidi/Android.mk
index 8c628bc..8b91117 100755
--- a/tests/tests/nativemidi/Android.mk
+++ b/tests/tests/nativemidi/Android.mk
@@ -36,6 +36,8 @@
 
 # Must match the package name in CtsTestCaseList.mk
 LOCAL_PACKAGE_NAME := CtsNativeMidiTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MULTILIB := both
 
 LOCAL_SDK_VERSION := current
diff --git a/tests/tests/neuralnetworks/Android.mk b/tests/tests/neuralnetworks/Android.mk
index 019672c..ff5317a 100644
--- a/tests/tests/neuralnetworks/Android.mk
+++ b/tests/tests/neuralnetworks/Android.mk
@@ -38,6 +38,7 @@
 
 LOCAL_SDK_VERSION := current
 LOCAL_NDK_STL_VARIANT := c++_static
+LOCAL_MIN_SDK_VERSION := 30
 
 include $(BUILD_CTS_EXECUTABLE)
 
diff --git a/tests/tests/neuralnetworks/AndroidManifest.xml b/tests/tests/neuralnetworks/AndroidManifest.xml
new file mode 100644
index 0000000..de2efbd
--- /dev/null
+++ b/tests/tests/neuralnetworks/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.neuralnetworks">
+    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="31" />
+</manifest>
+
diff --git a/tests/tests/neuralnetworks/AndroidTest.xml b/tests/tests/neuralnetworks/AndroidTest.xml
index 6ec4752..b89d563 100644
--- a/tests/tests/neuralnetworks/AndroidTest.xml
+++ b/tests/tests/neuralnetworks/AndroidTest.xml
@@ -19,6 +19,7 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.neuralnetworks.apex" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsNNAPITestCases->/data/local/tmp/CtsNNAPITestCases" />
diff --git a/tests/tests/neuralnetworks/benchmark/Android.mk b/tests/tests/neuralnetworks/benchmark/Android.mk
index dd69c5e..dfa3131 100644
--- a/tests/tests/neuralnetworks/benchmark/Android.mk
+++ b/tests/tests/neuralnetworks/benchmark/Android.mk
@@ -17,6 +17,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsNNAPIBenchmarkTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 # Don't include this package in any target
 LOCAL_MODULE_TAGS := optional
@@ -36,6 +38,6 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_ASSET_DIR := test/mlts/models/assets
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 30
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/neuralnetworks/benchmark/src/com/android/nn/benchmark/cts/NNAccuracyTest.java b/tests/tests/neuralnetworks/benchmark/src/com/android/nn/benchmark/cts/NNAccuracyTest.java
index 2c860ad..c6ad0dc 100644
--- a/tests/tests/neuralnetworks/benchmark/src/com/android/nn/benchmark/cts/NNAccuracyTest.java
+++ b/tests/tests/neuralnetworks/benchmark/src/com/android/nn/benchmark/cts/NNAccuracyTest.java
@@ -93,23 +93,28 @@
     @Test
     @LargeTest
     public void testNNAPI() throws BenchmarkException, IOException {
-        if (!NNTestBase.hasAccelerator()) {  // Skip.
-            return;
-        }
+        List<String> accelerators = new ArrayList<>();
+        NNTestBase.getAcceleratorNames(accelerators);
+        for (String accelerator : accelerators) {
+            if (accelerator.equals("nnapi-reference")) {  // Skip.
+                continue;
+            }
 
-        try (NNTestBase test = mModel.createNNTestBase(/*useNNAPI=*/true,
-                    /*enableIntermediateTensorsDump=*/false)) {
-            test.setupModel(mActivity);
-            Pair<List<InferenceInOutSequence>, List<InferenceResult>> inferenceResults =
-                    test.runBenchmarkCompleteInputSet(/*setRepeat=*/1, /*timeoutSec=*/3600);
-            BenchmarkResult benchmarkResult =
-                    BenchmarkResult.fromInferenceResults(
-                            mModel.mModelName,
-                            BenchmarkResult.BACKEND_TFLITE_NNAPI,
-                            inferenceResults.first,
-                            inferenceResults.second,
-                            test.getEvaluator());
-            assertFalse(benchmarkResult.hasValidationErrors());
+            try (NNTestBase test = mModel.createNNTestBase(/*useNNAPI=*/true,
+                        /*enableIntermediateTensorsDump=*/false)) {
+                test.setNNApiDeviceName(accelerator);
+                test.setupModel(mActivity);
+                Pair<List<InferenceInOutSequence>, List<InferenceResult>> inferenceResults =
+                        test.runBenchmarkCompleteInputSet(/*setRepeat=*/1, /*timeoutSec=*/3600);
+                BenchmarkResult benchmarkResult =
+                        BenchmarkResult.fromInferenceResults(
+                                mModel.mModelName,
+                                BenchmarkResult.BACKEND_TFLITE_NNAPI,
+                                inferenceResults.first,
+                                inferenceResults.second,
+                                test.getEvaluator());
+                assertFalse(benchmarkResult.hasValidationErrors());
+            }
         }
     }
 }
diff --git a/tests/tests/neuralnetworks/tflite_delegate/Android.mk b/tests/tests/neuralnetworks/tflite_delegate/Android.mk
index 0fbc8e1..eb6f2c88 100644
--- a/tests/tests/neuralnetworks/tflite_delegate/Android.mk
+++ b/tests/tests/neuralnetworks/tflite_delegate/Android.mk
@@ -23,6 +23,7 @@
     tensorflow/lite/delegates/nnapi/nnapi_delegate_test.cc \
     tensorflow/lite/kernels/test_util.cc \
     tensorflow/core/platform/default/logging.cc \
+    tensorflow/core/platform/default/env_time.cc \
     tensorflow/lite/kernels/acceleration_test_util.cc \
     tensorflow/lite/kernels/acceleration_test_util_internal.cc \
     tensorflow/lite/delegates/nnapi/acceleration_test_list.cc \
@@ -31,6 +32,7 @@
 
 LOCAL_C_INCLUDES += external/flatbuffers/include
 LOCAL_C_INCLUDES += external/tensorflow
+LOCAL_C_INCLUDES += external/ruy
 
 LOCAL_CFLAGS :=  \
     -DPLATFORM_POSIX_ANDROID \
@@ -44,7 +46,7 @@
 
 LOCAL_SHARED_LIBRARIES := libandroid liblog libneuralnetworks
 LOCAL_STATIC_LIBRARIES := libgtest_ndk_c++ libgmock_ndk libtflite_static
-LOCAL_HEADER_LIBRARIES := libeigen gemmlowp_headers
+LOCAL_HEADER_LIBRARIES := libeigen gemmlowp_headers libtflite_schema_headers
 LOCAL_SDK_VERSION := current
 LOCAL_NDK_STL_VARIANT := c++_static
 include $(BUILD_STATIC_LIBRARY)
diff --git a/tests/tests/nfc/src/android/nfc/cts/NfcPreferredPaymentTest.java b/tests/tests/nfc/src/android/nfc/cts/NfcPreferredPaymentTest.java
index c55edc4..193b029 100644
--- a/tests/tests/nfc/src/android/nfc/cts/NfcPreferredPaymentTest.java
+++ b/tests/tests/nfc/src/android/nfc/cts/NfcPreferredPaymentTest.java
@@ -116,4 +116,33 @@
         }
     }
 
+    /** Tests getSelectionModeForCategory API
+     *  CardEmulation.CATEGORY_PAYMENT */
+    @Test
+    public void testGetSelectionModeForCategoryPayment() {
+        try {
+            int mode = mCardEmulation.getSelectionModeForCategory(CardEmulation.CATEGORY_PAYMENT);
+            Log.i(mTag, "getSelectionModeForCategory for Payment: " + mode);
+
+            assertTrue("Retrieve incorrect SelectionMode for Payment",
+                    CardEmulation.SELECTION_MODE_PREFER_DEFAULT == mode);
+        } catch (Exception e) {
+            fail("Unexpected Exception " + e);
+        }
+    }
+
+    /** Tests getSelectionModeForCategory API
+     *  CardEmulation.CATEGORY_OTHER */
+    @Test
+    public void testGetSelectionModeForCategoryOther() {
+        try {
+            int mode = mCardEmulation.getSelectionModeForCategory(CardEmulation.CATEGORY_OTHER);
+            Log.i(mTag, "getSelectionModeForCategory for Other: " + mode);
+
+            assertTrue("Retrieve incorrect SelectionMode for Other",
+                    CardEmulation.SELECTION_MODE_ASK_IF_CONFLICT == mode);
+        } catch (Exception e) {
+            fail("Unexpected Exception " + e);
+        }
+    }
 }
diff --git a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
index f5606de..3f93fb5 100644
--- a/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy27/src/android/app/notification/legacy/cts/LegacyNotificationManagerTest.java
@@ -35,6 +35,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.UiAutomation;
+import android.content.pm.PackageManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -238,21 +239,28 @@
 
     @Test
     public void testResetListenerHints_singleListener() throws Exception {
-        toggleListenerAccess(TestNotificationListener.getId(),
-                InstrumentationRegistry.getInstrumentation(), true);
-        Thread.sleep(500); // wait for listener to be allowed
+      int conditionValue = 0;
+      int condition = NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
 
-        mListener = TestNotificationListener.getInstance();
-        Assert.assertNotNull(mListener);
+      toggleListenerAccess(
+          TestNotificationListener.getId(), InstrumentationRegistry.getInstrumentation(), true);
+      Thread.sleep(500); // wait for listener to be allowed
 
-        mListener.requestListenerHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
+      mListener = TestNotificationListener.getInstance();
+      Assert.assertNotNull(mListener);
+      /*In case of wear os we have some default disable listener registered*/
+      if (isWatch()) {
+        condition |= NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
+        conditionValue = mListener.getCurrentListenerHints();
+      }
 
-        assertEquals(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS,
-                mListener.getCurrentListenerHints());
+      mListener.requestListenerHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
 
-        mListener.clearRequestedListenerHints();
+      assertEquals(condition, mListener.getCurrentListenerHints());
 
-        assertEquals(0, mListener.getCurrentListenerHints());
+      mListener.clearRequestedListenerHints();
+
+      assertEquals(conditionValue, mListener.getCurrentListenerHints());
     }
 
     @Test
@@ -278,8 +286,11 @@
                 mListener.getCurrentListenerHints());
 
         mSecondaryListener.clearRequestedListenerHints();
-        assertEquals(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS,
-                mSecondaryListener.getCurrentListenerHints());
+        /*In case of wear os we have some default disable listener registered*/
+        if (!isWatch()) {
+          assertEquals(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS,
+              mSecondaryListener.getCurrentListenerHints());
+        }
     }
 
     @Test
@@ -385,4 +396,8 @@
             uiAutomation.destroy();
         }
     }
+
+    private boolean isWatch() {
+      return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
 }
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
index 4318f7f..e83928a 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationAssistantServiceTest.java
@@ -23,6 +23,7 @@
 
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
 
 import android.app.Instrumentation;
 import android.app.Notification;
@@ -71,7 +72,7 @@
     final String TAG = "NotAsstServiceTest";
     final String NOTIFICATION_CHANNEL_ID = "NotificationAssistantServiceTest";
     final int ICON_ID = android.R.drawable.sym_def_app_icon;
-    final long SLEEP_TIME = 500; // milliseconds
+    final long SLEEP_TIME = 1000; // milliseconds
 
     private TestNotificationAssistant mNotificationAssistantService;
     private TestNotificationListener mNotificationListenerService;
@@ -80,6 +81,10 @@
     private Context mContext;
     private UiAutomation mUi;
 
+    private boolean isWatch() {
+      return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
+
     @Before
     public void setUp() throws IOException {
         mUi = InstrumentationRegistry.getInstrumentation().getUiAutomation();
@@ -557,6 +562,7 @@
         if (isTelevision()) {
             return;
         }
+        assumeFalse("Status bar service not supported", isWatch());
         setUpListeners();
         turnScreenOn();
         mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
@@ -588,6 +594,7 @@
         if (isTelevision()) {
             return;
         }
+        assumeFalse("Status bar service not supported", isWatch());
         setUpListeners();
         turnScreenOn();
         mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
@@ -613,6 +620,7 @@
         if (isTelevision()) {
             return;
         }
+        assumeFalse("Status bar service not supported", isWatch());
         setUpListeners();
         turnScreenOn();
         mUi.adoptShellPermissionIdentity("android.permission.EXPAND_STATUS_BAR");
diff --git a/tests/tests/os/CtsOsTestCases.xml b/tests/tests/os/CtsOsTestCases.xml
index 1d4b393..e80d897 100644
--- a/tests/tests/os/CtsOsTestCases.xml
+++ b/tests/tests/os/CtsOsTestCases.xml
@@ -23,16 +23,6 @@
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsOsTestCases.apk" />
     </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="force-install-mode" value="FULL"/>
-        <option name="test-file-name" value="CtsMockInputMethod.apk" />
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
-        <option name="force-skip-system-props" value="true" />
-        <option name="screen-always-on" value="on" />
-    </target_preparer>
-
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.os.cts" />
         <option name="runtime-hint" value="3m15s" />
diff --git a/tests/tests/os/OWNERS b/tests/tests/os/OWNERS
index 3429892..c748b28 100644
--- a/tests/tests/os/OWNERS
+++ b/tests/tests/os/OWNERS
@@ -1,4 +1,5 @@
-per-file *AutoRevoke* = eugenesusla@google.com
-per-file *Companion* = eugenesusla@google.com
-per-file *AndroidManifest.xml = eugenesusla@google.com
-per-file *CtsOsTestCases.xml = eugenesusla@google.com
+per-file *AutoRevoke* = file:platform/frameworks/base:/core/java/android/permission/OWNERS
+per-file *AppHibernation* = file:platform/frameworks/base:/core/java/android/permission/OWNERS
+per-file *Companion* = file:platform/frameworks/base:/core/java/android/permission/OWNERS
+per-file *AndroidManifest.xml = file:platform/frameworks/base:/core/java/android/permission/OWNERS
+per-file *CtsOsTestCases.xml = file:platform/frameworks/base:/core/java/android/permission/OWNERS
diff --git a/tests/tests/os/UffdGc/Android.bp b/tests/tests/os/UffdGc/Android.bp
new file mode 100644
index 0000000..8ef4d9a
--- /dev/null
+++ b/tests/tests/os/UffdGc/Android.bp
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "CtsUffdGcTestCases",
+    defaults: ["cts_defaults"],
+    compile_multilib: "both",
+    srcs: ["src/**/*.java"],
+    jni_libs: ["libuserfaultfdtest"],
+    sdk_version: "current",
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "ctstestrunner-axt",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+}
+
+cc_test_library {
+    name: "libuserfaultfdtest",
+    defaults: ["libctsos_jni_defaults"],
+    srcs: ["jni/android_os_cts_uffdgc_UserfaultfdTest.cc"],
+    stl: "c++_static",
+    sdk_version: "current",
+}
diff --git a/tests/tests/os/UffdGc/AndroidManifest.xml b/tests/tests/os/UffdGc/AndroidManifest.xml
new file mode 100644
index 0000000..8d7f12e
--- /dev/null
+++ b/tests/tests/os/UffdGc/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Copyright (C) 2021 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.os.cts.uffdgc">
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.os.cts.uffdgc"
+                     android:label="CTS tests of userfaultfd-based GC">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/os/UffdGc/AndroidTest.xml b/tests/tests/os/UffdGc/AndroidTest.xml
new file mode 100644
index 0000000..48e9869
--- /dev/null
+++ b/tests/tests/os/UffdGc/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs CTS tests of userfaultfd-based GC.">
+    <option name="config-descriptor:metadata" key="component" value="art" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <option name="test-suite-tag" value="cts" />
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController">
+        <option name="min-api-level" value="31" />
+    </object>
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsUffdGcTestCases.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.os.cts.uffdgc" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/tests/tests/os/UffdGc/OWNERS b/tests/tests/os/UffdGc/OWNERS
new file mode 100644
index 0000000..37794e7
--- /dev/null
+++ b/tests/tests/os/UffdGc/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 44922
+lokeshgidra@google.com
diff --git a/tests/tests/os/UffdGc/jni/android_os_cts_uffdgc_UserfaultfdTest.cc b/tests/tests/os/UffdGc/jni/android_os_cts_uffdgc_UserfaultfdTest.cc
new file mode 100644
index 0000000..d540753
--- /dev/null
+++ b/tests/tests/os/UffdGc/jni/android_os_cts_uffdgc_UserfaultfdTest.cc
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+#include "jni.h"
+
+#include <cstring>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/userfaultfd.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <syscall.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+static int page_sz = 0;
+static bool perform_tests = true;
+
+static void* userfault_handler_thread(void* arg) {
+  int uffd = reinterpret_cast<intptr_t>(arg);
+  struct uffd_msg msg;
+  struct uffdio_copy uffdio_copy;
+  ssize_t nread;
+  int ret = 0;
+  char* page = static_cast<char*>(mmap(nullptr, page_sz, PROT_READ | PROT_WRITE,
+                                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
+  if (page == MAP_FAILED) {
+    ret = errno;
+    goto out;
+  }
+  std::memset(&msg, '\0', sizeof msg);
+  std::memset(&uffdio_copy, '\0', sizeof uffdio_copy);
+  std::memset(page, 'a', page_sz);
+
+  // Loop, handling incoming events on the userfaultfd file descriptor
+  do {
+    // poll on uffd waiting for an event
+    struct pollfd pollfd;
+    int nready;
+    pollfd.fd = uffd;
+    pollfd.events = POLLIN;
+    nready = poll(&pollfd, 1, -1);
+    if (nready == -1) {
+      ret = errno;
+      break;
+    }
+
+    /* Read an event from the userfaultfd */
+    nread = read(uffd, &msg, sizeof(msg));
+    // EOF on userfaultfd
+    if (nread == 0) {
+       ret = -1;
+       break;
+    }
+
+    if (nread == -1) {
+      ret = errno;
+      break;
+    }
+
+    // We expect only one kind of event; verify that assumption
+    if (msg.event != UFFD_EVENT_PAGEFAULT) {
+      ret = -1;
+      break;
+    }
+    uffdio_copy.src = reinterpret_cast<size_t>(page);
+
+    // Align fault address to page boundary
+    uffdio_copy.dst = msg.arg.pagefault.address & ~(page_sz - 1);
+    uffdio_copy.len = page_sz;
+    uffdio_copy.mode = 0;  // Wake-up thread thread waiting for page-fault resolution
+    uffdio_copy.copy = 0;  // Used by kernel to return how many bytes copied
+    if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) < 0) {
+      ret = errno;
+      break;
+    }
+  } while (false);
+  munmap(page, page_sz);
+out:
+  pthread_exit(reinterpret_cast<void*>(ret));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_android_os_cts_uffdgc_UserfaultfdTest_setUpUserfaultfd(JNIEnv*) {
+#if defined(__linux__)
+  static constexpr int kRequiredMajor = 5;
+  static constexpr int kRequiredMinor = 4;
+
+  int major, minor;
+  struct utsname uts;
+  if (uname(&uts) != 0 ||
+      strcmp(uts.sysname, "Linux") != 0 ||
+      sscanf(uts.release, "%d.%d", &major, &minor) != 2 ||
+      (major < kRequiredMajor || (major == kRequiredMajor && minor < kRequiredMinor))) {
+    perform_tests = false;
+  }
+#else
+  perform_tests = false;
+#endif
+  page_sz = sysconf(_SC_PAGE_SIZE);
+}
+
+extern "C"
+JNIEXPORT jint JNICALL Java_android_os_cts_uffdgc_UserfaultfdTest_performKernelSpaceUffd(JNIEnv*) {
+  if (!perform_tests) {
+    return 0;
+  }
+  int ret = 0, write_fd = 0;
+  void* addr = nullptr;
+  pthread_t thr;  // ID of thread that handles page faults
+  struct uffdio_register uffdio_register;
+  int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY);
+  if (uffd < 0) {
+    ret = errno;
+    goto out;
+  }
+
+  write_fd = memfd_create("ReadFrom", MFD_CLOEXEC);
+  if (write_fd == -1) {
+    ret = errno;
+    goto out_close_uffd;
+  }
+  if (ftruncate(write_fd, page_sz) == -1) {
+    ret = errno;
+    goto out_close_both;
+  }
+
+  /* Create a private anonymous mapping. The memory will be
+   * demand-zero paged--that is, not yet allocated. When we
+   * actually touch the memory, it will be allocated via
+   * the userfaultfd.
+   */
+  addr = mmap(nullptr, page_sz, PROT_READ | PROT_WRITE,
+              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (addr == MAP_FAILED) {
+    ret = errno;
+    goto out_close_both;
+  }
+
+  struct uffdio_api api;
+  std::memset(&api, '\0', sizeof api);
+  api.api = UFFD_API;
+  if (ioctl(uffd, UFFDIO_API, &api) < 0) {
+    ret = errno;
+    goto out_unmap;
+  }
+
+  /* Register the memory range of the mapping we just created for
+   * handling by the userfaultfd object. In mode, we request to track
+   * missing pages (i.e., pages that have not yet been faulted in).
+   */
+  std::memset(&uffdio_register, '\0', sizeof uffdio_register);
+  uffdio_register.range.start = reinterpret_cast<size_t>(addr);
+  uffdio_register.range.len = page_sz;
+  uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+  if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) < 0) {
+    ret = errno;
+    goto out_unmap;
+  }
+
+  // Create a thread that will process the userfaultfd events
+  ret = pthread_create(&thr, nullptr, userfault_handler_thread, reinterpret_cast<void*>(uffd));
+  if (ret != 0) {
+    goto out_unmap;
+  }
+
+  // The write is expected to fail as the buffer starting at addr doesn't have a
+  // page pinned, and when the syscall will try to read from it, will trigger a
+  // userfault. But only user-mode faults are allowed.
+  if (write(write_fd, addr, page_sz) >= 0) {
+    ret = -1;
+  } else {
+    ret = errno;
+  }
+
+  // Invoke a userfault so that the handler thread then exit normally.
+  static_cast<char*>(addr)[42] = 'a';
+
+  pthread_join(thr, nullptr);
+out_unmap:
+  munmap(addr, page_sz);
+out_close_both:
+  close(write_fd);
+out_close_uffd:
+  close(uffd);
+out:
+  return ret;
+}
+
+// Invoking userfaultfd without USER_MODE_ONLY by a process without CAP_SYS_PTRACE
+// should not be permitted.
+extern "C" JNIEXPORT jint JNICALL Java_android_os_cts_uffdgc_UserfaultfdTest_performUffdWithoutUserModeOnly(JNIEnv*) {
+  if (!perform_tests) {
+    return 0;
+  }
+  int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
+  if (uffd < 0) {
+    return errno;
+  } else {
+    close(uffd);
+  }
+  return 0;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_android_os_cts_uffdgc_UserfaultfdTest_performMremapDontUnmap(JNIEnv*) {
+  int ret = 0;
+  if (!perform_tests) {
+    return 0;
+  }
+  void* old = mmap(nullptr, page_sz, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+  if (old == MAP_FAILED ||
+      mremap(old, page_sz, page_sz, MREMAP_MAYMOVE | MREMAP_DONTUNMAP, nullptr) == MAP_FAILED) {
+    ret = errno;
+  }
+  return ret;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_android_os_cts_uffdgc_UserfaultfdTest_performMinorUffd(JNIEnv*) {
+  int ret = 0;
+  if (!perform_tests) {
+    return 0;
+  }
+  int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY);
+  if (uffd < 0) {
+    ret = errno;
+    goto out;
+  }
+  struct uffdio_api api;
+  std::memset(&api, '\0', sizeof api);
+  api.api = UFFD_API;
+  // TODO: Remove the following define once its definition is available in
+  // linux/userfaultfd.h header file.
+#ifndef UFFD_FEATURE_MINOR_SHMEM
+#define UFFD_FEATURE_MINOR_SHMEM (1 << 10)
+#endif
+  api.features = UFFD_FEATURE_MINOR_SHMEM;
+  if (ioctl(uffd, UFFDIO_API, &api) < 0) {
+    ret = errno;
+  }
+  close(uffd);
+out:
+  return ret;
+}
diff --git a/tests/tests/os/UffdGc/src/android/os/cts/uffdgc/UserfaultfdTest.java b/tests/tests/os/UffdGc/src/android/os/cts/uffdgc/UserfaultfdTest.java
new file mode 100644
index 0000000..e83357c
--- /dev/null
+++ b/tests/tests/os/UffdGc/src/android/os/cts/uffdgc/UserfaultfdTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.os.cts.uffdgc;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.runner.AndroidJUnit4;
+
+@RunWith(AndroidJUnit4.class)
+public final class UserfaultfdTest {
+
+  static {
+      System.loadLibrary("userfaultfdtest");
+  }
+
+  @Before
+  public void setUp() {
+      setUpUserfaultfd();
+  }
+
+  // Test if a userfault from kernel-space fails or not. It is
+  // not allowed for unprivileged processes.
+  @Test
+  public void kernelSpaceUserfault() {
+    // Expect return value to be EFAULT (14).
+    assertEquals(14, performKernelSpaceUffd());
+  }
+
+  // Test if a userfault file descriptor can be obtained without
+  // UFFD_USER_MODE_ONLY flag. Unprivileged processes must specify
+  // this flag to forbid kernel-space userfaults.
+  @Test
+  public void nonUserModeOnlyUserfaultfd() {
+    // Expect return value to be EPERM (1).
+    assertEquals(1, performUffdWithoutUserModeOnly());
+  }
+
+  // Test if mremap syscall on a non-anonymous shared mapping
+  // using MREMAP_DONTUNMAP flag works.
+  @Test
+  public void mremapDontUnmap() {
+    assertEquals(0, performMremapDontUnmap());
+  }
+
+  // Test if userfaultfd works for minor-faults on shmem.
+  @Test
+  public void minorUserfaultfd() {
+    assertEquals(0, performMinorUffd());
+  }
+
+  private native void setUpUserfaultfd();
+  private native int performKernelSpaceUffd();
+  private native int performUffdWithoutUserModeOnly();
+  private native int performMremapDontUnmap();
+  private native int performMinorUffd();
+}
diff --git a/tests/tests/os/assets/platform_versions.txt b/tests/tests/os/assets/platform_versions.txt
index 3762249..f53859b 100644
--- a/tests/tests/os/assets/platform_versions.txt
+++ b/tests/tests/os/assets/platform_versions.txt
@@ -1 +1,2 @@
 S
+T
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index 44a5444..c77b52a 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -30,7 +30,10 @@
 import android.provider.DeviceConfig
 import android.support.test.uiautomator.By
 import android.support.test.uiautomator.BySelector
+import android.support.test.uiautomator.UiDevice
 import android.support.test.uiautomator.UiObject2
+import android.support.test.uiautomator.UiScrollable
+import android.support.test.uiautomator.UiSelector
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.Switch
 import androidx.test.InstrumentationRegistry
@@ -76,6 +79,7 @@
 
     private val context: Context = InstrumentationRegistry.getTargetContext()
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
 
     private val mPermissionControllerResources: Resources = context.createPackageContext(
             context.packageManager.permissionControllerPackageName, 0).resources
@@ -244,7 +248,10 @@
                 // Setup
                 goToPermissions()
                 click("Calendar")
-                click("Allow")
+                // Wear OS uses a switch and does not display a dialog
+                if (!hasFeatureWatch()) {
+                    click("Allow")
+                }
                 Thread.sleep(500)
                 goBack()
                 goBack()
@@ -387,11 +394,17 @@
                 .addFlags(FLAG_ACTIVITY_NEW_TASK))
 
         waitForIdle()
+
         click("Permissions")
     }
 
     private fun click(label: String) {
-        waitFindNode(hasTextThat(containsStringIgnoringCase(label))).click()
+        waitFindObject(byTextIgnoreCase(label)).click()
+        waitForIdle()
+    }
+
+    private fun hasFeatureWatch(): Boolean {
+        return context.packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
     }
 
     private fun assertWhitelistState(state: Boolean) {
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
index b0a1a5e..efe3c12 100644
--- a/tests/tests/os/src/android/os/cts/BuildTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -348,6 +348,7 @@
     public void testIsSecureUserBuild() throws IOException {
         assertEquals("Must be a user build", "user", Build.TYPE);
         assertProperty("Must be a non-debuggable build", RO_DEBUGGABLE, "0");
+        assertFalse("Must be a non-debuggable build", Build.isDebuggable());
         assertProperty("Must be a secure build", RO_SECURE, "1");
     }
 
diff --git a/tests/tests/os/src/android/os/cts/SimpleTestActivity.java b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
index 22c706fe..3acbce6 100644
--- a/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
+++ b/tests/tests/os/src/android/os/cts/SimpleTestActivity.java
@@ -16,30 +16,6 @@
 
 package android.os.cts;
 
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
-
 import android.app.Activity;
-import android.os.Bundle;
-import android.widget.EditText;
-import android.widget.LinearLayout;
 
-public class SimpleTestActivity extends Activity {
-    private EditText mEditText;
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        mEditText = new EditText(this);
-        final LinearLayout layout = new LinearLayout(this);
-        layout.setOrientation(LinearLayout.VERTICAL);
-        layout.addView(mEditText);
-        setContentView(layout);
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
-        mEditText.requestFocus();
-    }
-}
\ No newline at end of file
+public class SimpleTestActivity extends Activity {}
\ No newline at end of file
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index 562fc5e..f0358bd 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -17,15 +17,9 @@
 package android.os.cts;
 
 import static android.content.Context.WINDOW_SERVICE;
-import static android.content.pm.PackageManager.FEATURE_INPUT_METHODS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
-import static com.android.cts.mockime.ImeEventStreamTestUtils.clearAllEvents;
-import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
-import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
-import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
-
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -38,9 +32,7 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
-import android.content.res.Configuration;
 import android.hardware.display.DisplayManager;
-import android.inputmethodservice.InputMethodService;
 import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.IBinder;
@@ -69,16 +61,10 @@
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
 
-import androidx.annotation.IntDef;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.cts.mockime.ImeEvent;
-import com.android.cts.mockime.ImeEventStream;
-import com.android.cts.mockime.ImeSettings;
-import com.android.cts.mockime.MockImeSession;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -90,8 +76,6 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.net.HttpURLConnection;
 import java.net.Socket;
 import java.net.URL;
@@ -110,52 +94,10 @@
 public class StrictModeTest {
     private static final String TAG = "StrictModeTest";
     private static final String REMOTE_SERVICE_ACTION = "android.app.REMOTESERVICE";
-    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10); // 10 seconds
-    private static final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(2);
 
     private StrictMode.ThreadPolicy mThreadPolicy;
     private StrictMode.VmPolicy mVmPolicy;
 
-    // TODO(b/160143006): re-enable IMS part test.
-    private static final boolean DISABLE_VERIFY_IMS = false;
-
-    /**
-     * Verify mode to verifying if APIs violates incorrect context violation.
-     *
-     * @see #VERIFY_MODE_GET_DISPLAY
-     * @see #VERIFY_MODE_GET_WINDOW_MANAGER
-     * @see #VERIFY_MODE_GET_VIEW_CONFIGURATION
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, value = {
-            VERIFY_MODE_GET_DISPLAY,
-            VERIFY_MODE_GET_WINDOW_MANAGER,
-            VERIFY_MODE_GET_VIEW_CONFIGURATION,
-    })
-    private @interface VerifyMode {}
-
-    /**
-     * Verifies if {@link Context#getDisplay} from {@link InputMethodService} and context created
-     * from {@link InputMethodService#createConfigurationContext(Configuration)} violates
-     * incorrect context violation.
-     */
-    private static final int VERIFY_MODE_GET_DISPLAY = 1;
-    /**
-     * Verifies if get {@link android.view.WindowManager} from {@link InputMethodService} and
-     * context created from {@link InputMethodService#createConfigurationContext(Configuration)}
-     * violates incorrect context violation.
-     *
-     * @see Context#getSystemService(String)
-     * @see Context#getSystemService(Class)
-     */
-    private static final int VERIFY_MODE_GET_WINDOW_MANAGER = 2;
-    /**
-     * Verifies if passing {@link InputMethodService} and context created
-     * from {@link InputMethodService#createConfigurationContext(Configuration)} to
-     * {@link android.view.ViewConfiguration#get(Context)} violates incorrect context violation.
-     */
-    private static final int VERIFY_MODE_GET_VIEW_CONFIGURATION = 3;
-
     private Context getContext() {
         return ApplicationProvider.getApplicationContext();
     }
@@ -707,9 +649,6 @@
         final Activity activity = InstrumentationRegistry.getInstrumentation()
                 .startActivitySync(intent);
         assertNoViolation(() -> activity.getSystemService(WINDOW_SERVICE));
-
-        // TODO(b/159593676): move the logic to CtsInputMethodTestCases
-        verifyIms(VERIFY_MODE_GET_WINDOW_MANAGER);
     }
 
     @Test
@@ -737,8 +676,6 @@
                 .startActivitySync(intent);
         assertNoViolation(() -> activity.getDisplay());
 
-        // TODO(b/159593676): move the logic to CtsInputMethodTestCases
-        verifyIms(VERIFY_MODE_GET_DISPLAY);
         try {
             getContext().getApplicationContext().getDisplay();
         } catch (UnsupportedOperationException e) {
@@ -776,57 +713,6 @@
         final Activity activity = InstrumentationRegistry.getInstrumentation()
                 .startActivitySync(intent);
         assertNoViolation(() -> ViewConfiguration.get(activity));
-
-        // TODO(b/159593676): move the logic to CtsInputMethodTestCases
-        verifyIms(VERIFY_MODE_GET_VIEW_CONFIGURATION);
-    }
-
-    // TODO(b/159593676): move the logic to CtsInputMethodTestCases
-    /**
-     * Verify if APIs violates incorrect context violations by {@code mode}.
-     *
-     * @see VerifyMode
-     */
-    private void verifyIms(@VerifyMode int mode) throws Exception {
-        // If devices do not support installable IMEs, finish the test gracefully. We don't use
-        // assumeTrue here because we do pass some cases, so showing "pass" instead of "skip" makes
-        // sense here.
-        // TODO(b/160143006): re-enable IMS part test.
-        if (!supportsInstallableIme() || DISABLE_VERIFY_IMS) {
-            return;
-        }
-
-        try (final MockImeSession imeSession = MockImeSession.create(getContext(),
-                InstrumentationRegistry.getInstrumentation().getUiAutomation(),
-                new ImeSettings.Builder().setStrictModeEnabled(true))) {
-            final ImeEventStream stream = imeSession.openEventStream();
-            expectEvent(stream, event -> "onStartInput".equals(event.getEventName()), TIMEOUT);
-            final ImeEventStream forkedStream = clearAllEvents(stream, "onStrictModeViolated");
-            final ImeEvent imeEvent;
-            switch (mode) {
-                case VERIFY_MODE_GET_DISPLAY:
-                    imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetDisplay(),
-                            TIMEOUT);
-                    break;
-                case VERIFY_MODE_GET_WINDOW_MANAGER:
-                    imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetWindowManager(),
-                            TIMEOUT);
-                    break;
-                case VERIFY_MODE_GET_VIEW_CONFIGURATION:
-                    imeEvent = expectCommand(forkedStream,
-                            imeSession.callVerifyGetViewConfiguration(), TIMEOUT);
-                    break;
-                default:
-                    imeEvent = null;
-            }
-            assertTrue(imeEvent.getReturnBooleanValue());
-            notExpectEvent(stream, event -> "onStrictModeViolated".equals(event.getEventName()),
-                    NOT_EXPECT_TIMEOUT);
-        }
-    }
-
-    private boolean supportsInstallableIme() {
-        return getContext().getPackageManager().hasSystemFeature(FEATURE_INPUT_METHODS);
     }
 
     private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
diff --git a/tests/tests/packageinstaller/adminpackageinstaller/OWNERS b/tests/tests/packageinstaller/adminpackageinstaller/OWNERS
index 0ab0d04..7455e48 100644
--- a/tests/tests/packageinstaller/adminpackageinstaller/OWNERS
+++ b/tests/tests/packageinstaller/adminpackageinstaller/OWNERS
@@ -1,3 +1,4 @@
 # Bug component: 36137
 toddke@google.com
 sunnygoyal@google.com
+patb@google.com
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/ExcludeWatch.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/ExcludeWatch.kt
new file mode 100644
index 0000000..20cba14
--- /dev/null
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/ExcludeWatch.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.packageinstaller.install.cts
+
+import android.content.pm.PackageManager
+import org.junit.Assume.assumeFalse
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+class ExcludeWatch(
+    val motivation: String,
+    val packageManager: PackageManager
+) : TestRule {
+
+    override fun apply(stmt: Statement?, desc: Description?): Statement {
+        return FeatureStatement()
+    }
+
+    inner class FeatureStatement : Statement() {
+        override fun evaluate() {
+            assumeFalse(
+                motivation,
+                packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/ExternalSourcesTestAppOpAllowed.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/ExternalSourcesTestAppOpAllowed.kt
index 3e7cd4a..bdb4b20 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/ExternalSourcesTestAppOpAllowed.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/ExternalSourcesTestAppOpAllowed.kt
@@ -75,11 +75,13 @@
 
     @Test
     fun allowedSourceTestViaIntent() {
+        assumeNotWatch()
         allowedSourceTest { startInstallationViaIntent() }
     }
 
     @Test
     fun allowedSourceTestViaSession() {
+        assumeNotWatch()
         allowedSourceTest { startInstallationViaSession() }
     }
 
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallSourceInfoTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallSourceInfoTest.kt
index 42fddd8..5396efc 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallSourceInfoTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/InstallSourceInfoTest.kt
@@ -40,6 +40,8 @@
 
     @Test
     fun installViaIntent() {
+        assumeNotWatch()
+
         val packageInstallerPackageName = getPackageInstallerPackageName()
 
         val installation = startInstallationViaIntent()
@@ -56,6 +58,8 @@
 
     @Test
     fun InstallViaSession() {
+        assumeNotWatch()
+
         startInstallationViaSession()
         clickInstallerUIButton(INSTALL_BUTTON_ID)
 
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
index 33a8966..2220893 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/IntentTest.kt
@@ -49,6 +49,8 @@
      */
     @Test
     fun confirmInstallation() {
+        assumeNotWatch()
+
         val installation = startInstallationViaIntent()
         clickInstallerUIButton(INSTALL_BUTTON_ID)
 
@@ -63,6 +65,8 @@
      */
     @Test
     fun cancelInstallation() {
+        assumeNotWatch()
+
         val installation = startInstallationViaIntent()
         clickInstallerUIButton(CANCEL_BUTTON_ID)
 
@@ -76,6 +80,8 @@
      */
     @Test
     fun reinstallViaPackageUri() {
+        assumeNotWatch()
+
         // Regular install
         confirmInstallation()
 
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
index 45eb038..3480142 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt
@@ -40,6 +40,8 @@
 import com.android.compatibility.common.util.FutureResultActivity
 import org.junit.After
 import org.junit.Assert
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Rule
 import java.io.File
@@ -215,4 +217,16 @@
     fun uninstallTestPackage() {
         uiDevice.executeShellCommand("pm uninstall $TEST_APK_PACKAGE_NAME")
     }
+
+    fun assumeWatch() {
+        assumeTrue("Test only valid for watch", hasFeatureWatch())
+    }
+
+    fun assumeNotWatch() {
+        assumeFalse("Installing APKs not supported on watch", hasFeatureWatch())
+    }
+
+    private fun hasFeatureWatch(): Boolean {
+        return pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
+    }
 }
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
index 69096f8..87711e9 100644
--- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/SessionTest.kt
@@ -27,6 +27,7 @@
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import java.util.concurrent.TimeUnit
@@ -40,6 +41,9 @@
     private val context = InstrumentationRegistry.getTargetContext()
     private val pm = context.packageManager
 
+    @get:Rule
+    val excludeWatch = ExcludeWatch("Installing APKs not supported on watch", pm)
+
     /**
      * Check that we can install an app via a package-installer session
      */
diff --git a/tests/tests/packageinstaller/nopermission/src/android.packageinstaller.nopermission.cts/NoPermissionTests.kt b/tests/tests/packageinstaller/nopermission/src/android.packageinstaller.nopermission.cts/NoPermissionTests.kt
index 024d1ad..235f589 100644
--- a/tests/tests/packageinstaller/nopermission/src/android.packageinstaller.nopermission.cts/NoPermissionTests.kt
+++ b/tests/tests/packageinstaller/nopermission/src/android.packageinstaller.nopermission.cts/NoPermissionTests.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import android.content.pm.PackageManager;
 import android.content.pm.PackageInstaller
 import android.content.pm.PackageInstaller.EXTRA_STATUS
 import android.content.pm.PackageInstaller.STATUS_FAILURE_INVALID
@@ -38,6 +39,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.junit.runners.model.Statement
+import org.junit.runner.Description
+import org.junit.Rule
+import org.junit.rules.TestRule
+import org.junit.AssumptionViolatedException
+
 import java.io.File
 import java.lang.IllegalArgumentException
 
@@ -62,6 +69,7 @@
     private var packageName = context.packageName
     private var apkFile = File(context.filesDir, TEST_APK_NAME)
     private var uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    private var isWatch: Boolean = pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
 
     private val receiver = object : BroadcastReceiver() {
         override fun onReceive(context: Context, intent: Intent) {
@@ -75,6 +83,21 @@
         }
     }
 
+    @get:Rule
+    val testRule: TestRule = object :TestRule {
+        override fun apply(base: Statement, description: Description?)
+                = NewStatement(base)
+        inner class NewStatement(private val base: Statement) : Statement() {
+            @Throws(Throwable::class)
+            override fun evaluate() {
+                if (isWatch) {
+                    throw AssumptionViolatedException("Install/uninstall feature is not supported on WearOs");
+                }
+                base.evaluate()
+            }
+        }
+    }
+
     @Before
     fun wakeUpScreen() {
         if (!uiDevice.isScreenOn) {
diff --git a/tests/tests/packageinstaller/tapjacking/src/android/packageinstaller/tapjacking/cts/TapjackingTest.java b/tests/tests/packageinstaller/tapjacking/src/android/packageinstaller/tapjacking/cts/TapjackingTest.java
index e5fda9b..5aa9c5d 100644
--- a/tests/tests/packageinstaller/tapjacking/src/android/packageinstaller/tapjacking/cts/TapjackingTest.java
+++ b/tests/tests/packageinstaller/tapjacking/src/android/packageinstaller/tapjacking/cts/TapjackingTest.java
@@ -23,6 +23,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.platform.test.annotations.AppModeFull;
 import android.support.test.uiautomator.By;
@@ -40,6 +41,11 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.junit.AssumptionViolatedException;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
 
 import java.io.IOException;
 
@@ -57,13 +63,16 @@
 
     private static final long WAIT_FOR_UI_TIMEOUT = 5000;
 
-    private Context mContext;
+    private Context mContext = InstrumentationRegistry.getTargetContext();
     private String mPackageName;
     private UiDevice mUiDevice;
+    boolean isWatch = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+
+    @Rule
+    public final RequiredRule mRequiredRule = new RequiredRule(isWatch);
 
     @Before
     public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
         mPackageName = mContext.getPackageName();
         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         if (!mUiDevice.isScreenOn()) {
@@ -114,4 +123,24 @@
     public void tearDown() throws Exception {
         mUiDevice.pressHome();
     }
+
+    private static final class RequiredRule implements TestRule {
+        boolean mIsWatch;
+        RequiredRule(boolean isWatch) {
+            mIsWatch = isWatch;
+        }
+        @Override
+        public Statement apply(Statement base, Description description) {
+            return new Statement() {
+
+                @Override
+                public void evaluate() throws Throwable {
+                    if (mIsWatch) {
+                        throw new AssumptionViolatedException("Install/uninstall feature is not supported on WearOs");
+                    }
+                    base.evaluate();
+                }
+            };
+        }
+    }
 }
diff --git a/tests/tests/permission/src/android/permission/cts/NfcPermissionTest.java b/tests/tests/permission/src/android/permission/cts/NfcPermissionTest.java
new file mode 100644
index 0000000..c2eae01
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/NfcPermissionTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.concurrent.Executor;
+
+@RunWith(JUnit4.class)
+public final class NfcPermissionTest {
+
+    private NfcAdapter mNfcAdapter;
+
+    private boolean supportsHardware() {
+        final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+    }
+
+    @Before
+    public void setUp() {
+        assumeTrue(supportsHardware());
+        mNfcAdapter = NfcAdapter.getDefaultAdapter(InstrumentationRegistry.getTargetContext());
+    }
+
+    /**
+     * Verifies that isControllerAlwaysOnSupported() requires Permission.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+     */
+    @Test
+    @AppModeFull
+    public void testIsControllerAlwaysOnSupported() {
+        try {
+            mNfcAdapter.isControllerAlwaysOnSupported();
+            fail("mNfcAdapter.isControllerAlwaysOnSupported() did not throw SecurityException"
+                    + " as expected");
+        } catch (SecurityException se) {
+            // Expected Exception
+        }
+    }
+
+    /**
+     * Verifies that isControllerAlwaysOn() requires Permission.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+     */
+    @Test
+    @AppModeFull
+    public void testIsControllerAlwaysOn() {
+        try {
+            mNfcAdapter.isControllerAlwaysOn();
+            fail("mNfcAdapter.isControllerAlwaysOn() did not throw SecurityException"
+                    + " as expected");
+        } catch (SecurityException se) {
+            // Expected Exception
+        }
+    }
+
+    /**
+     * Verifies that setControllerAlwaysOn(true) requires Permission.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+     */
+    @Test
+    @AppModeFull
+    public void testSetControllerAlwaysOnTrue() {
+        try {
+            mNfcAdapter.setControllerAlwaysOn(true);
+            fail("mNfcAdapter.setControllerAlwaysOn(true) did not throw SecurityException"
+                    + " as expected");
+        } catch (SecurityException se) {
+            // Expected Exception
+        }
+    }
+
+    /**
+     * Verifies that setControllerAlwaysOn(false) requires Permission.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+     */
+    @Test
+    @AppModeFull
+    public void testSetControllerAlwaysOnFalse() {
+        try {
+            mNfcAdapter.setControllerAlwaysOn(false);
+            fail("mNfcAdapter.setControllerAlwaysOn(true) did not throw SecurityException"
+                    + " as expected");
+        } catch (SecurityException se) {
+            // Expected Exception
+        }
+    }
+
+    /**
+     * Verifies that registerControllerAlwaysOnListener() requires Permission.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+     */
+    @Test
+    @AppModeFull
+    public void testRegisterControllerAlwaysOnListener() {
+        try {
+            mNfcAdapter.registerControllerAlwaysOnListener(
+                    new SynchronousExecutor(), new AlwaysOnStateListener());
+            fail("mNfcAdapter.registerControllerAlwaysOnListener did not throw"
+                    + "SecurityException as expected");
+        } catch (SecurityException se) {
+            // Expected Exception
+        }
+    }
+
+    /**
+     * Verifies that unregisterControllerAlwaysOnListener() requires Permission.
+     * <p>
+     * Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
+     */
+    @Test
+    @AppModeFull
+    public void testUnregisterControllerAlwaysOnListener() {
+        try {
+            mNfcAdapter.unregisterControllerAlwaysOnListener(new AlwaysOnStateListener());
+            fail("mNfcAdapter.unregisterControllerAlwaysOnListener did not throw"
+                    + "SecurityException as expected");
+        } catch (SecurityException se) {
+            // Expected Exception
+        }
+    }
+
+    private class SynchronousExecutor implements Executor {
+        public void execute(Runnable r) {
+            r.run();
+        }
+    }
+
+    private class AlwaysOnStateListener implements ControllerAlwaysOnListener {
+        @Override
+        public void onControllerAlwaysOnChanged(boolean isEnabled) {
+            // Do nothing
+        }
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
index b837976..1c834cece 100644
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionTest.java
@@ -270,22 +270,6 @@
      * If a permission was granted before the split happens, the new permission should inherit the
      * granted state.
      *
-     * This is a duplicate of {@link #inheritGrantedPermissionState} but for the storage permission
-     */
-    @Test
-    public void inheritGrantedPermissionStateStorage() throws Exception {
-        install(APK_STORAGE_29);
-        grantPermission(APP_PKG, READ_EXTERNAL_STORAGE);
-
-        install(APK_STORAGE_28);
-
-        assertPermissionGranted(ACCESS_MEDIA_LOCATION);
-    }
-
-    /**
-     * If a permission was granted before the split happens, the new permission should inherit the
-     * granted state.
-     *
      * <p>App using a shared uid
      */
     @Test
diff --git a/tests/tests/permission2/res/raw/OWNERS b/tests/tests/permission2/res/raw/OWNERS
index a67ff98..04895ff 100644
--- a/tests/tests/permission2/res/raw/OWNERS
+++ b/tests/tests/permission2/res/raw/OWNERS
@@ -2,6 +2,7 @@
 cbrubaker@google.com
 hackbod@google.com
 toddke@google.com
+patb@google.com
 yamasani@google.com
 michaelwr@google.com
 narayan@google.com
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index a887a26..8695968 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -325,7 +325,7 @@
     <protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_SEND_MULTIPLE" />
     <protected-broadcast android:name="com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER" />
 
-    <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
+    <protected-broadcast android:name="android.net.action.CLEAR_DNS_CACHE" />
     <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
 
     <protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
@@ -559,8 +559,6 @@
 
     <protected-broadcast android:name="android.intent.action.DYNAMIC_SENSOR_CHANGED" />
 
-    <protected-broadcast android:name="android.intent.action.ACTION_RADIO_OFF" />
-
     <protected-broadcast android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
     <protected-broadcast android:name="android.accounts.action.ACCOUNT_REMOVED" />
     <protected-broadcast android:name="android.accounts.action.VISIBLE_ACCOUNTS_CHANGED" />
@@ -1862,6 +1860,12 @@
         android:label="@string/permlab_preferredPaymentInfo"
         android:protectionLevel="normal" />
 
+    <!-- @SystemApi Allows access to set NFC controller always on states.
+         <p>Protection level: signature|privileged
+         @hide -->
+    <permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
          @hide -->
     <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION"
@@ -2517,10 +2521,6 @@
     <permission android:name="android.permission.CREATE_USERS"
         android:protectionLevel="signature" />
 
-    <!-- @TestApi @hide Allows an application to query user info for all users on the device. -->
-    <permission android:name="android.permission.QUERY_USERS"
-                android:protectionLevel="signature" />
-
     <!-- @hide Allows an application to set the profile owners and the device owner.
          This permission is not available to third party applications.-->
     <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
@@ -2528,6 +2528,16 @@
         android:label="@string/permlab_manageProfileAndDeviceOwners"
         android:description="@string/permdesc_manageProfileAndDeviceOwners" />
 
+    <!-- @TestApi @hide Allows an application to reset the record of previous system update freeze
+         periods. -->
+    <permission android:name="android.permission.CLEAR_FREEZE_PERIOD"
+                android:protectionLevel="signature" />
+
+    <!-- @TestApi @hide Allows an application to force available DevicePolicyManager logs to
+         DPC. -->
+    <permission android:name="android.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS"
+                android:protectionLevel="signature" />
+
     <!-- Allows an application to get full detailed information about
          recently running tasks, with full fidelity to the real state.
          @hide -->
@@ -3742,6 +3752,13 @@
     <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @TestApi Allows a testOnly application to get installed.
+        <p>Not for use by third-party applications.
+        @hide
+    -->
+    <permission android:name="android.permission.INSTALL_TEST_ONLY_PACKAGE"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi @TestApi Allows an application to clear user data.
          <p>Not for use by third-party applications
          @hide
@@ -4062,6 +4079,11 @@
     <permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @TestApi Allows an application to query audio related state.
+         @hide -->
+    <permission android:name="android.permission.QUERY_AUDIO_STATE"
+                android:protectionLevel="signature" />
+
     <!-- Allows an application to modify what effects are applied to all audio
          (matching certain criteria) from any application.
          <p>Not for use by third-party applications.</p>
@@ -4536,6 +4558,11 @@
     <permission android:name="android.permission.SET_INITIAL_LOCK"
         android:protectionLevel="signature|setup"/>
 
+    <!-- @TestApi Allows applications to set and verify lockscreen credentials.
+        @hide -->
+    <permission android:name="android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"
+                android:protectionLevel="signature"/>
+
     <!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
     <permission android:name="android.permission.MANAGE_FINGERPRINT"
         android:protectionLevel="signature|privileged" />
@@ -5081,9 +5108,16 @@
     <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
                 android:protectionLevel="signature|privileged" />
     <!-- Allows an app to override compat change config.
+         This permission only allows to override config on debuggable builds or test-apks and is
+         therefore a less powerful version of OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD.
          @hide  <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
                 android:protectionLevel="signature|privileged" />
+    <!-- @SystemApi Allows an app to override compat change config on release builds.
+        Only ChangeIds that are annotated as @Overridable can be overridden on release builds.
+        @hide -->
+    <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"
+                android:protectionLevel="signature|privileged" />
 
     <!-- Allows input events to be monitored. Very dangerous!  @hide -->
     <permission android:name="android.permission.MONITOR_INPUT"
@@ -5129,6 +5163,10 @@
     <permission android:name="android.permission.INPUT_CONSUMER"
                 android:protectionLevel="signature" />
 
+    <!-- @hide @SystemApi Allows the holder to manage app hibernation states for packages -->
+    <permission android:name="android.permission.MANAGE_APP_HIBERNATION"
+                android:protectionLevel="signature|installer" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/tests/tests/permission3/res/values-watch/strings.xml b/tests/tests/permission3/res/values-watch/strings.xml
new file mode 100755
index 0000000..5a7c933
--- /dev/null
+++ b/tests/tests/permission3/res/values-watch/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <string name="deny">Deny anyway</string>
+</resources>
diff --git a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
index e842917..9a23af5 100644
--- a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
+++ b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
@@ -87,6 +87,8 @@
                 PackageManager.MATCH_DEFAULT_ONLY).activityInfo.packageName;
 
         assumeFalse("Skipping test: Auto does not support provider android.settings.panel", isCar());
+        assumeFalse(
+            "Skipping test: Watch does not support provider android.settings.panel", isWatch());
     }
 
     @After
@@ -315,4 +317,8 @@
         PackageManager pm = mContext.getPackageManager();
         return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
     }
+
+    private boolean isWatch() {
+      return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
 }
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
index 11a0e66..2416fd3 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
@@ -118,10 +118,8 @@
         Log.d(TAG, "Using volume " + mVolumeName);
         mExternalImages = MediaStore.Images.Media.getContentUri(mVolumeName);
 
-        final Resources res = mContext.getResources();
-        final Configuration config = res.getConfiguration();
-        mLargestDimension = (int) (Math.max(config.screenWidthDp, config.screenHeightDp)
-                * res.getDisplayMetrics().density);
+        final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
+        mLargestDimension = Math.max(metrics.widthPixels, metrics.heightPixels);
     }
 
     private void prepareImages() throws Exception {
diff --git a/tests/tests/renderscript/Android.mk b/tests/tests/renderscript/Android.mk
index bf121c2..20fe73e 100644
--- a/tests/tests/renderscript/Android.mk
+++ b/tests/tests/renderscript/Android.mk
@@ -18,6 +18,8 @@
 
 # Replace "Example" with your name.
 LOCAL_PACKAGE_NAME := CtsRenderscriptTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 # Don't include this package in any target.
 LOCAL_MODULE_TAGS := optional
diff --git a/tests/tests/renderscriptlegacy/Android.bp b/tests/tests/renderscriptlegacy/Android.bp
new file mode 100644
index 0000000..4f20661
--- /dev/null
+++ b/tests/tests/renderscriptlegacy/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "CtsRenderscriptLegacyTestCases",
+    defaults: ["cts_defaults"],
+    static_libs: ["ctstestrunner-axt"],
+    srcs: ["src/**/*.java"],
+    sdk_version: "19",
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+}
diff --git a/tests/tests/renderscriptlegacy/Android.mk b/tests/tests/renderscriptlegacy/Android.mk
deleted file mode 100644
index 98df8e9..0000000
--- a/tests/tests/renderscriptlegacy/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_PACKAGE_NAME := CtsRenderscriptLegacyTestCases
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := optional
-
-# When built, explicitly put it in the data partition.
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner-axt
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_SDK_VERSION := 19
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts general-tests
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/resourcesloader/OWNERS b/tests/tests/resourcesloader/OWNERS
index d2cb9ad..57228c7 100644
--- a/tests/tests/resourcesloader/OWNERS
+++ b/tests/tests/resourcesloader/OWNERS
@@ -1,4 +1,5 @@
  # Bug component: 568761
 rtmitchell@google.com
 chiuwinson@google.com
-toddke@google.com
\ No newline at end of file
+toddke@google.com
+patb@google.com
diff --git a/tests/tests/resourcesloader/src/android/content/res/loader/cts/ResourcesLoaderTestBase.kt b/tests/tests/resourcesloader/src/android/content/res/loader/cts/ResourcesLoaderTestBase.kt
index 1295fe1..84ed5a7c 100644
--- a/tests/tests/resourcesloader/src/android/content/res/loader/cts/ResourcesLoaderTestBase.kt
+++ b/tests/tests/resourcesloader/src/android/content/res/loader/cts/ResourcesLoaderTestBase.kt
@@ -46,7 +46,7 @@
     companion object {
         /** Converts the map to a stable JSON string representation. */
         fun mapToString(m: Map<String, String>): String {
-            return JSONObject(ArrayMap<String, String>().apply { putAll(m) }).toString()
+            return JSONObject(ArrayMap<Any?, Any?>().apply { putAll(m) }).toString()
         }
 
         /** Creates a lambda that runs multiple resources queries and concatenates the results. */
diff --git a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
index 00ddc34..39cc25b 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -316,14 +316,6 @@
     }
 
     @Test
-    public void requestEmptyRoleThenDeniedAutomatically() throws Exception {
-        requestRole("");
-        Pair<Integer, Intent> result = waitForResult();
-
-        assertThat(result.first).isEqualTo(Activity.RESULT_CANCELED);
-    }
-
-    @Test
     public void requestInvalidRoleThenDeniedAutomatically() throws Exception {
         requestRole("invalid");
         Pair<Integer, Intent> result = waitForResult();
@@ -378,7 +370,7 @@
 
     @Nullable
     private UiObject2 findDontAskAgainCheck(boolean expected) throws UiObjectNotFoundException {
-        BySelector selector = By.text("Don\u2019t ask again");
+        BySelector selector = By.res("com.android.permissioncontroller:id/dont_ask_again");
         return expected
                 ? waitFindObject(selector)
                 : waitFindObjectOrNull(selector, UNEXPECTED_TIMEOUT_MILLIS);
diff --git a/tests/tests/rsblas/Android.mk b/tests/tests/rsblas/Android.mk
index 6b18c5d..ffe4944 100644
--- a/tests/tests/rsblas/Android.mk
+++ b/tests/tests/rsblas/Android.mk
@@ -17,6 +17,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CtsRsBlasTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 # Don't include this package in any target.
 LOCAL_MODULE_TAGS := optional
diff --git a/tests/tests/rscpp/Android.mk b/tests/tests/rscpp/Android.mk
index 79e8a2c..8231e41 100644
--- a/tests/tests/rscpp/Android.mk
+++ b/tests/tests/rscpp/Android.mk
@@ -18,6 +18,8 @@
 
 # Replace "Example" with your name.
 LOCAL_PACKAGE_NAME := CtsRsCppTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 # Don't include this package in any target.
 LOCAL_MODULE_TAGS := optional
diff --git a/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp b/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp
index ad089a2..0cc60e8 100644
--- a/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp
+++ b/tests/tests/security/native/verified_boot/VerifiedBootTest.cpp
@@ -57,6 +57,12 @@
       continue;
     }
 
+    if (android::base::EqualsIgnoreCase(entry.fs_type, "emmc")) {
+      GTEST_LOG_(INFO) << entry.mount_point << " has emmc fs_type, skipping"
+          << " hashtree algorithm verification";
+      continue;
+    }
+
     GTEST_LOG_(ERROR) << "partition enabled verity " << entry.mount_point;
 
     // The verity sysprop use "system" as the partition name in the system as
diff --git a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
index 496270f..799b755 100644
--- a/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
+++ b/tests/tests/selinux/common/src/android/security/SELinuxTargetSdkTestBase.java
@@ -62,21 +62,13 @@
         }
     }
 
-    protected static void checkNetlinkRouteGetlink(boolean expectAllowed) throws IOException {
-        if (!expectAllowed) {
-            assertEquals(
-                    "RTM_GETLINK is not allowed on a netlink route sockets. Verify that the"
-                        + " following patch has been applied to your kernel: "
-                        + "https://android-review.googlesource.com/q/I7b44ce60ad98f858c412722d41b9842f8577151f",
-                    13,
-                    checkNetlinkRouteGetlink());
-        } else {
-            assertEquals(
-                    "RTM_GETLINK should be allowed netlink route sockets for apps with "
-                            + "targetSdkVersion <= Q",
-                    -1,
-                    checkNetlinkRouteGetlink());
-        }
+    protected static void noNetlinkRouteGetlink() throws IOException {
+        assertEquals(
+                "RTM_GETLINK is not allowed on netlink route sockets. Verify that the"
+                    + " following patch has been applied to your kernel: "
+                    + "https://android-review.googlesource.com/q/I7b44ce60ad98f858c412722d41b9842f8577151f",
+                13,
+                checkNetlinkRouteGetlink());
     }
 
     protected static void noNetlinkRouteBind() throws IOException {
@@ -164,10 +156,18 @@
     }
 
     /**
+     * Verify that apps are not able to see MAC addresses of ethernet devices.
+     */
+    protected static void checkNetworkInterfaceHardwareAddress_returnsNull() throws Exception {
+        assertNotNull(NetworkInterface.getNetworkInterfaces());
+        for (NetworkInterface nif : Collections.list(NetworkInterface.getNetworkInterfaces())) {
+            assertNull(nif.getHardwareAddress());
+        }
+    }
+
+    /**
      * Verify that apps having targetSdkVersion <= 29 get an anonymized MAC
      * address (02:00:00:00:00:00) instead of a null MAC for ethernet interfaces.
-     * The counterpart of this test (testing for targetSdkVersion > 29) is
-     * {@link libcore.java.net.NetworkInterfaceTest#testGetHardwareAddress_returnsNull()}.
      */
     protected static void checkNetworkInterface_returnsAnonymizedHardwareAddresses()
         throws Exception {
diff --git a/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
index 1098a6f..d642e45 100644
--- a/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxEphemeral/src/android/security/SELinuxTargetSdkTest.java
@@ -79,10 +79,14 @@
     }
 
     public void testNoNetlinkRouteGetlink() throws IOException {
-        checkNetlinkRouteGetlink(false);
+        noNetlinkRouteGetlink();
     }
 
     public void testNoNetlinkRouteBind() throws IOException {
         noNetlinkRouteBind();
     }
+
+    public void testNetworkInterface() throws Exception {
+        checkNetworkInterfaceHardwareAddress_returnsNull();
+    }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
index 5e2f75d..e3f2cf6 100644
--- a/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk27/src/android/security/SELinuxTargetSdkTest.java
@@ -68,4 +68,12 @@
     public void testNetworkInterface() throws Exception {
         checkNetworkInterface_returnsAnonymizedHardwareAddresses();
     }
+
+    public void testNoNetlinkRouteGetlink() throws IOException {
+        noNetlinkRouteGetlink();
+    }
+
+    public void testNoNetlinkRouteBind() throws IOException {
+        noNetlinkRouteBind();
+    }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
index 29b68db..8f2aea5 100644
--- a/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk28/src/android/security/SELinuxTargetSdkTest.java
@@ -68,4 +68,12 @@
     public void testNetworkInterface() throws Exception {
         checkNetworkInterface_returnsAnonymizedHardwareAddresses();
     }
+
+    public void testNoNetlinkRouteGetlink() throws IOException {
+        noNetlinkRouteGetlink();
+    }
+
+    public void testNoNetlinkRouteBind() throws IOException {
+        noNetlinkRouteBind();
+    }
 }
diff --git a/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
index c1d93dc..3429f58 100644
--- a/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdk29/src/android/security/SELinuxTargetSdkTest.java
@@ -42,9 +42,8 @@
             checkDex2oatAccess(false);
         }
     }
-
-    public void testNetlinkRouteGetlinkSucceeds() throws IOException {
-        checkNetlinkRouteGetlink(true);
+    public void testNoNetlinkRouteGetlink() throws IOException {
+        noNetlinkRouteGetlink();
     }
 
     public void testNoNetlinkRouteBind() throws IOException {
diff --git a/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java b/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
index 4de03b0..859864e 100644
--- a/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
+++ b/tests/tests/selinux/selinuxTargetSdkCurrent/src/android/security/SELinuxTargetSdkTest.java
@@ -34,7 +34,7 @@
     }
 
     public void testNoNetlinkRouteGetlink() throws IOException {
-        checkNetlinkRouteGetlink(false);
+        noNetlinkRouteGetlink();
     }
 
     public void testNoNetlinkRouteBind() throws IOException {
diff --git a/tests/tests/settings/src/android/settings/cts/WifiSliceTest.java b/tests/tests/settings/src/android/settings/cts/WifiSliceTest.java
index 26d78a5..78461ce 100644
--- a/tests/tests/settings/src/android/settings/cts/WifiSliceTest.java
+++ b/tests/tests/settings/src/android/settings/cts/WifiSliceTest.java
@@ -74,6 +74,8 @@
   public void setUp() throws Exception {
     assumeFalse("Skipping test: Auto does not support provider android.settings.slices", isCar());
     assumeFalse("Skipping test: TV does not support provider android.settings.slices", isTv());
+    assumeFalse(
+        "Skipping test: Watch does not support provider android.settings.slices", isWatch());
     mWifiSlice = mSliceManager.bindSlice(WIFI_SLICE_URI, Collections.emptySet());
     mAssistant = Secure.getString(mContext.getContentResolver(), ASSISTANT);
   }
@@ -170,6 +172,10 @@
             && pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
   }
 
+  private boolean isWatch() {
+    return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+  }
+
   private boolean isWifiEnabled() {
     final WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
     return wifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED
diff --git a/tests/tests/shortcutmanager/OWNERS b/tests/tests/shortcutmanager/OWNERS
index 557370d..2f325bb 100644
--- a/tests/tests/shortcutmanager/OWNERS
+++ b/tests/tests/shortcutmanager/OWNERS
@@ -1,3 +1,5 @@
 # Bug component: 166829
+sunnygoyal@google.com
+pinyaoting@google.com
 omakoto@google.com
 yamasani@google.com
diff --git a/tests/tests/simphonebookprovider/AndroidTest.xml b/tests/tests/simphonebookprovider/AndroidTest.xml
index 0d7df7a..e21c163 100644
--- a/tests/tests/simphonebookprovider/AndroidTest.xml
+++ b/tests/tests/simphonebookprovider/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="test-suite-tag" value="cts" />
 
     <option name="config-descriptor:metadata" key="component" value="framework" />
+    <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
     <!-- Instant apps can't access the system providers. -->
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
diff --git a/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/RemovableSims.java b/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/RemovableSims.java
index 1d32f3a..27a3389 100644
--- a/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/RemovableSims.java
+++ b/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/RemovableSims.java
@@ -74,7 +74,10 @@
         Map<Integer, List<SubscriptionInfo>> subscriptionsByCardId = allSubscriptions.stream()
                 .collect(Collectors.groupingBy(SubscriptionInfo::getCardId));
         for (UiccCardInfo cardInfo : uiccCards) {
-            if (!cardInfo.isRemovable() || cardInfo.isEuicc()) {
+            // On GSI builds the eUICC won't be loaded but its card info will still be returned
+            // and so it will have UNINITIALIZED_CARD_ID permanently.
+            if (!cardInfo.isRemovable() || cardInfo.isEuicc() ||
+                    cardInfo.getCardId() == TelephonyManager.UNINITIALIZED_CARD_ID) {
                 continue;
             }
             mRemovableSimSlotCount++;
diff --git a/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookContract_ContentNotificationsTest.java b/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookContract_ContentNotificationsTest.java
index 87648b1..bee9449 100644
--- a/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookContract_ContentNotificationsTest.java
+++ b/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookContract_ContentNotificationsTest.java
@@ -20,6 +20,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.hamcrest.Matchers.oneOf;
 import static org.junit.Assume.assumeThat;
 
 import static java.util.concurrent.TimeUnit.SECONDS;
@@ -38,7 +39,6 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.util.Log;
 
 import androidx.annotation.Nullable;
 import androidx.test.core.app.ApplicationProvider;
@@ -186,11 +186,16 @@
                 Manifest.permission.MODIFY_PHONE_STATE, Manifest.permission.READ_PHONE_STATE);
 
         int result = resultFuture.get(30, SECONDS);
-        if (result != TelephonyManager.SET_SIM_POWER_STATE_ALREADY_IN_STATE &&
-                result != TelephonyManager.SET_SIM_POWER_STATE_SUCCESS) {
-            Log.w(TAG, "setSimPowerStateForSlot failed for slot=" + slotIndex + " result="
-                    + result);
-        }
+        assumeThat("setSimPowerStateForSlot failed for slot=" + slotIndex,
+                result, oneOf(
+                        TelephonyManager.SET_SIM_POWER_STATE_ALREADY_IN_STATE,
+                        TelephonyManager.SET_SIM_POWER_STATE_SUCCESS));
+        int simState = SystemUtil.runWithShellPermissionIdentity(() ->
+                        telephonyManager.getSimState(slotIndex),
+                Manifest.permission.READ_PHONE_STATE);
+        // This doesn't work on Cuttlefish so confirm the SIM was actually powered off.
+        assumeThat(simState, Matchers.not(oneOf(
+                TelephonyManager.SIM_STATE_PRESENT, TelephonyManager.SIM_STATE_READY)));
     }
 
     private static class RecordingContentObserver extends ContentObserver {
diff --git a/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookContract_SimRecordsNoSimTest.java b/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookContract_SimRecordsNoSimTest.java
index 55fdd41..4078816 100644
--- a/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookContract_SimRecordsNoSimTest.java
+++ b/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookContract_SimRecordsNoSimTest.java
@@ -24,6 +24,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assume.assumeThat;
+
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -33,6 +35,7 @@
 import android.provider.SimPhonebookContract;
 import android.provider.SimPhonebookContract.ElementaryFiles;
 import android.provider.SimPhonebookContract.SimRecords;
+import android.telephony.TelephonyManager;
 
 import androidx.annotation.NonNull;
 import androidx.test.core.app.ApplicationProvider;
@@ -151,9 +154,14 @@
         values.put(SimRecords.NAME, "Name");
         values.put(SimRecords.PHONE_NUMBER, "8005550101");
 
-        assertThrows(UnsupportedOperationException.class,
-                () -> mResolver.insert(
-                        SimRecords.getContentUri(MISSING_SIM_SUBSCRIPTION_ID, EF_FDN), values));
+        // Depending on the context where the test is run the test app may have carrier privileges
+        // e.g. omitting this will cause a failure when running on cuttlefish if it is launched
+        // with --modem_simulator_sim_type=2
+        if (!mContext.getSystemService(TelephonyManager.class).hasCarrierPrivileges()) {
+            assertThrows(UnsupportedOperationException.class,
+                    () -> mResolver.insert(
+                            SimRecords.getContentUri(MISSING_SIM_SUBSCRIPTION_ID, EF_FDN), values));
+        }
         assertThrows(UnsupportedOperationException.class,
                 () -> mResolver.insert(
                         SimRecords.getContentUri(MISSING_SIM_SUBSCRIPTION_ID, EF_SDN), values));
diff --git a/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookRequirementsRule.java b/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookRequirementsRule.java
index e53694b..a7773b5 100644
--- a/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookRequirementsRule.java
+++ b/tests/tests/simphonebookprovider/src/android/provider/cts/simphonebook/SimPhonebookRequirementsRule.java
@@ -22,6 +22,7 @@
 import static org.junit.Assume.assumeThat;
 
 import android.content.Context;
+import android.telephony.TelephonyManager;
 
 import androidx.test.core.app.ApplicationProvider;
 
@@ -42,6 +43,13 @@
     @Override
     protected void before() {
         Context context = ApplicationProvider.getApplicationContext();
+        TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
+
+        // Skip the test if the device doesn't appear to have any multi-SIM capability. The checks
+        // that follow this one are a bit flaky on devices that have an eSIM but don't support
+        // DSDS or DSDA (e.g. crosshatch and blueline).
+        assumeThat("not enough SIMs",
+                telephonyManager.getSupportedModemCount(), greaterThanOrEqualTo(mMinimumSimCount));
         RemovableSims removableSims = new RemovableSims(context);
 
         assumeThat(removableSims.getRemovableSimSlotCount(),
diff --git a/tests/tests/simpleperf/Android.mk b/tests/tests/simpleperf/Android.mk
index 351ff22..23e9245 100644
--- a/tests/tests/simpleperf/Android.mk
+++ b/tests/tests/simpleperf/Android.mk
@@ -21,7 +21,7 @@
   libsimpleperf_etm_decoder \
   libbacktrace \
   libunwindstack \
-  libdexfile_external_static \
+  libdexfile_static \
   libziparchive \
   libz \
   libgtest \
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index b2c2539..149b0a8 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.media.AudioManager;
@@ -171,14 +172,19 @@
             return;
         }
 
-        // Assume we start in normal mode at the start of all Telecom tests.
-        // A failure to leave car mode in any of the tests would cause subsequent test failures,
-        // but this failure should not affect other tests.
+        // Assume we start in normal mode at the start of all Telecom tests; a failure to leave car
+        // mode in any of the tests would cause subsequent test failures.
+        // For Watch, UI_MODE shouldn't be normal mode.
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
         if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
             mUiModeManager.disableCarMode(0);
         }
-        assertUiMode(Configuration.UI_MODE_TYPE_NORMAL);
+
+        if  (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+             assertUiMode(Configuration.UI_MODE_TYPE_WATCH);
+        } else {
+             assertUiMode(Configuration.UI_MODE_TYPE_NORMAL);
+        }
 
         mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
diff --git a/tests/tests/telecom/src/android/telecom/cts/BluetoothCallQualityReportTest.java b/tests/tests/telecom/src/android/telecom/cts/BluetoothCallQualityReportTest.java
new file mode 100644
index 0000000..dc40407
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/BluetoothCallQualityReportTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom.cts;
+
+import android.telecom.BluetoothCallQualityReport;
+
+public class BluetoothCallQualityReportTest extends BaseTelecomTestWithMockServices {
+    /**
+     * Test passing of BT call quality report to CDS.
+     */
+    public void testBluetoothCallQualityReport() {
+        long timestamp = System.currentTimeMillis();
+        int rssi = 1;
+        int snr = 2;
+        int retransmissionCount = 3;
+        int packetsNotReceiveCount = 4;
+        int negativeAcknowledgementCount = 5;
+
+        BluetoothCallQualityReport report = new BluetoothCallQualityReport.Builder()
+                .setSentTimestampMillis(timestamp)
+                .setChoppyVoice(true)
+                .setRssiDbm(rssi)
+                .setSnrDb(snr)
+                .setRetransmittedPacketsCount(retransmissionCount)
+                .setPacketsNotReceivedCount(packetsNotReceiveCount)
+                .setNegativeAcknowledgementCount(negativeAcknowledgementCount)
+                .build();
+
+        assertEquals(timestamp, report.getSentTimestampMillis());
+        assertEquals(true, report.isChoppyVoice());
+        assertEquals(rssi, report.getRssiDbm());
+        assertEquals(snr, report.getSnrDb());
+        assertEquals(retransmissionCount, report.getRetransmittedPacketsCount());
+        assertEquals(packetsNotReceiveCount, report.getPacketsNotReceivedCount());
+        assertEquals(negativeAcknowledgementCount, report.getNegativeAcknowledgementCount());
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
index 0e51b37..c7fe892 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.res.Configuration;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -48,6 +49,10 @@
             return;
         }
 
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return;
+        }
+
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .adoptShellPermissionIdentity("android.permission.ENTER_CAR_MODE_PRIORITIZED",
                         "android.permission.CONTROL_INCALL_EXPERIENCE");
@@ -69,6 +74,10 @@
             return;
         }
 
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return;
+        }
+
         if (mCarModeIncallServiceControlOne != null) {
             mCarModeIncallServiceControlOne.reset();
         }
@@ -91,6 +100,10 @@
             return;
         }
 
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return;
+        }
+
         enableAndVerifyCarMode(mCarModeIncallServiceControlOne, 1000);
         disableAndVerifyCarMode(mCarModeIncallServiceControlOne, Configuration.UI_MODE_TYPE_NORMAL);
     }
@@ -104,6 +117,10 @@
             return;
         }
 
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return;
+        }
+
         enableAndVerifyCarMode(mCarModeIncallServiceControlOne, 1000);
 
         // Place a call and verify we bound to the Car Mode InCallService
@@ -126,6 +143,10 @@
             return;
         }
 
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return;
+        }
+
         enableAndVerifyCarMode(mCarModeIncallServiceControlOne, 1000);
         enableAndVerifyCarMode(mCarModeIncallServiceControlTwo, 999);
 
@@ -152,6 +173,10 @@
             return;
         }
 
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return;
+        }
+
         // Place a call and verify it went to the default dialer
         placeAndVerifyCall();
         verifyConnectionForOutgoingCall();
@@ -173,6 +198,10 @@
             return;
         }
 
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return;
+        }
+
         // Place a call and verify it went to the default dialer
         placeAndVerifyCall();
         verifyConnectionForOutgoingCall();
@@ -209,6 +238,10 @@
             return;
         }
 
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return;
+        }
+
         // Place a call and verify it went to the default dialer
         placeAndVerifyCall();
         verifyConnectionForOutgoingCall();
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
index 89eb5a6..68ad014 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Intent;
+import android.os.IBinder;
 import android.telecom.Conference;
 import android.telecom.Connection;
 import android.telecom.ConnectionRequest;
@@ -58,8 +59,8 @@
     private static boolean sIsBound = false;
     private static CountDownLatch sServiceUnBoundLatch = new CountDownLatch(1);
 
-    public CtsConnectionService() throws Exception {
-        super();
+    @Override
+    public void onBindClient(Intent intent) {
         sTelecomConnectionService = this;
         sIsBound = true;
     }
@@ -77,7 +78,6 @@
 
     public static void tearDown() {
         synchronized(sLock) {
-            sTelecomConnectionService = null;
             sConnectionService = null;
         }
     }
@@ -185,14 +185,24 @@
 
     public static void addConferenceToTelecom(Conference conference) {
         synchronized(sLock) {
-            sTelecomConnectionService.addConference(conference);
+            if (sTelecomConnectionService != null) {
+                sTelecomConnectionService.addConference(conference);
+            } else {
+                Log.e(LOG_TAG, "addConferenceToTelecom called when"
+                        + " sTelecomConnectionService null!");
+            }
         }
     }
 
     public static void addExistingConnectionToTelecom(
             PhoneAccountHandle phoneAccountHandle, Connection connection) {
         synchronized(sLock) {
-            sTelecomConnectionService.addExistingConnection(phoneAccountHandle, connection);
+            if (sTelecomConnectionService != null) {
+                sTelecomConnectionService.addExistingConnection(phoneAccountHandle, connection);
+            } else {
+                Log.e(LOG_TAG, "addExistingConnectionToTelecom called when"
+                        + " sTelecomConnectionService null!");
+            }
         }
     }
 
@@ -209,8 +219,14 @@
             PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
         synchronized(sLock) {
-            return sTelecomConnectionService.createRemoteOutgoingConnection(
-                    connectionManagerPhoneAccount, request);
+            if (sTelecomConnectionService != null) {
+                return sTelecomConnectionService.createRemoteOutgoingConnection(
+                        connectionManagerPhoneAccount, request);
+            } else {
+                Log.e(LOG_TAG, "createRemoteOutgoingConnectionToTelecom called when"
+                        + " sTelecomConnectionService null!");
+                return null;
+            }
         }
     }
 
@@ -218,8 +234,14 @@
             PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
         synchronized(sLock) {
-            return sTelecomConnectionService.createRemoteIncomingConnection(
-                    connectionManagerPhoneAccount, request);
+            if (sTelecomConnectionService != null) {
+                return sTelecomConnectionService.createRemoteIncomingConnection(
+                        connectionManagerPhoneAccount, request);
+            } else {
+                Log.e(LOG_TAG, "createRemoteIncomingConnectionToTelecom called when"
+                        + " sTelecomConnectionService null!");
+                return null;
+            }
         }
     }
 
@@ -227,8 +249,14 @@
             PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
         synchronized (sLock) {
-            return sTelecomConnectionService.createRemoteIncomingConference(
-                    connectionManagerPhoneAccount, request);
+            if (sTelecomConnectionService != null) {
+                return sTelecomConnectionService.createRemoteIncomingConference(
+                        connectionManagerPhoneAccount, request);
+            } else {
+                Log.e(LOG_TAG, "createRemoteIncomingConferenceToTelecom called when"
+                        + " sTelecomConnectionService null!");
+                return null;
+            }
         }
     }
 
@@ -237,8 +265,14 @@
             PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
         synchronized (sLock) {
-            return sTelecomConnectionService.createRemoteOutgoingConference(
-                    connectionManagerPhoneAccount, request);
+            if (sTelecomConnectionService != null) {
+                return sTelecomConnectionService.createRemoteOutgoingConference(
+                        connectionManagerPhoneAccount, request);
+            } else {
+                Log.e(LOG_TAG, "createRemoteOutgoingConferenceToTelecom called when"
+                        + " sTelecomConnectionService null!");
+                return null;
+            }
         }
     }
 
@@ -275,6 +309,7 @@
         sServiceUnBoundLatch.countDown();
         sIsBound = false;
         sConnectionService = null;
+        sTelecomConnectionService = null;
         return super.onUnbind(intent);
     }
 
diff --git a/tests/tests/telephony/current/Android.bp b/tests/tests/telephony/current/Android.bp
index 1876bc7..014d0a4 100644
--- a/tests/tests/telephony/current/Android.bp
+++ b/tests/tests/telephony/current/Android.bp
@@ -30,7 +30,9 @@
         "src/android/telephony/ims/cts/TestSipDelegate.java",
         "src/android/telephony/ims/cts/TestSipDelegateConnection.java",
         "src/android/telephony/ims/cts/ImsUtils.java",
-	"src/android/telephony/ims/cts/TestAcsClient.java",
+        "src/android/telephony/ims/cts/SipDialogAttributes.java",
+        "src/android/telephony/ims/cts/SipMessageUtils.java",
+	    "src/android/telephony/ims/cts/TestAcsClient.java",
     ],
     path: "src/",
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
index 0cf33b1..bf13b0c 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -573,7 +573,7 @@
         assertTrue("getPci() out of range [0, 1007], pci = " + pci, 0 <= pci && pci <= 1007);
 
         int tac = nr.getTac();
-        assertTrue("getTac() out of range [0, 65536], tac = " + tac, 0 <= tac && tac <= 65536);
+        assertTrue("getTac() out of range [0, 16777215], tac = " + tac, 0 <= tac && tac <= 16777215);
 
         int nrArfcn = nr.getNrarfcn();
         assertTrue("getNrarfcn() out of range [0, 3279165], nrarfcn = " + nrArfcn,
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/LteVopsSupportInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/LteVopsSupportInfoTest.java
deleted file mode 100644
index 1832fad..0000000
--- a/tests/tests/telephony/current/src/android/telephony/cts/LteVopsSupportInfoTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.cts;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.os.Parcel;
-import android.telephony.LteVopsSupportInfo;
-
-import org.junit.Test;
-
-public class LteVopsSupportInfoTest {
-
-    @Test
-    public void testLteVopsSupportInfo() {
-        LteVopsSupportInfo lteVops =
-                new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
-                LteVopsSupportInfo.LTE_STATUS_SUPPORTED);
-        assertEquals(0, lteVops.describeContents());
-        assertEquals(LteVopsSupportInfo.LTE_STATUS_SUPPORTED, lteVops.getVopsSupport());
-        assertEquals(LteVopsSupportInfo.LTE_STATUS_SUPPORTED, lteVops.getEmcBearerSupport());
-
-        Parcel lteVopsParcel = Parcel.obtain();
-        lteVops.writeToParcel(lteVopsParcel, 0);
-        lteVopsParcel.setDataPosition(0);
-        LteVopsSupportInfo checkLteVops =
-                LteVopsSupportInfo.CREATOR.createFromParcel(lteVopsParcel);
-        assertTrue(lteVops.equals(checkLteVops));
-    }
-}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
index 60b0de2..f10b27c 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/MmsTest.java
@@ -148,15 +148,12 @@
             synchronized(mLock) {
                 final long startTime = SystemClock.elapsedRealtime();
                 long waitTime = timeout;
-                while (waitTime > 0) {
+                while (!mDone && waitTime > 0) {
                     try {
                         mLock.wait(waitTime);
                     } catch (InterruptedException e) {
                         // Ignore
                     }
-                    if (mDone) {
-                        break;
-                    }
                     waitTime = timeout - (SystemClock.elapsedRealtime() - startTime);
                 }
                 Log.i(TAG, "Wait for sent: done=" + mDone + ", success=" + mSuccess);
@@ -183,15 +180,6 @@
         }
 
         Log.i(TAG, "testSendMmsMessage");
-        // Prime the MmsService so that MMS config is loaded
-        final SmsManager smsManager = SmsManager.getDefault();
-        smsManager.getCarrierConfigValues();
-        // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
-        try {
-            Thread.sleep(1000);
-        } catch (InterruptedException e) {
-            // Ignore
-        }
 
         final Context context = getContext();
         // Register sent receiver
@@ -213,7 +201,7 @@
         // Send
         final PendingIntent pendingIntent = PendingIntent.getBroadcast(
                 context, 0, new Intent(ACTION_MMS_SENT), 0);
-        smsManager.sendMultimediaMessage(context,
+        SmsManager.getDefault().sendMultimediaMessage(context,
                 contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
         assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT));
         sendFile.delete();
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java
index cc534f7..eaa6b7d 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/PhysicalChannelConfigTest.java
@@ -36,6 +36,8 @@
     private static final int CONNECTION_STATUS = PhysicalChannelConfig.CONNECTION_PRIMARY_SERVING;
     private static final int CELL_BANDWIDTH = 12345;
     private static final int CHANNEL_NUMBER = 1234;
+    private static final int DOWNLINK_FREQUENCY = 11100;
+    private static final int UPLINK_FREQUENCY = 11100;
     private static final int FREQUENCY_RANGE = 1;
     private static final int PHYSICAL_CELL_ID = 502;
     private static final int PHYSICAL_INVALID_CELL_ID = 1008;
@@ -168,4 +170,32 @@
         assertThat(mPhysicalChannelConfig.getFrequencyRange()).isEqualTo(
                 ServiceState.FREQUENCY_RANGE_LOW);
     }
+
+    private void setupNrPhysicalChannelConfig() {
+        mPhysicalChannelConfig = new PhysicalChannelConfig.Builder()
+                .setPhysicalCellId(PHYSICAL_CELL_ID)
+                .setNetworkType(NETWORK_TYPE_NR)
+                .setCellConnectionStatus(CONNECTION_STATUS)
+                .setCellBandwidthDownlinkKhz(CELL_BANDWIDTH)
+                .setCellBandwidthUplinkKhz(CELL_BANDWIDTH)
+                .setContextIds(CONTEXT_IDS)
+                .setDownlinkChannelNumber(2220)
+                .setUplinkChannelNumber(2220)
+                .setBand(BAND)
+                .build();
+    }
+
+    @Test
+    public void testUplinkFrequencyKhz() {
+        setupNrPhysicalChannelConfig();
+
+        assertEquals(UPLINK_FREQUENCY, mPhysicalChannelConfig.getUplinkFrequencyKhz());
+    }
+
+    @Test
+    public void testDownlinkFrequencyKhz() {
+        setupNrPhysicalChannelConfig();
+
+        assertEquals(DOWNLINK_FREQUENCY, mPhysicalChannelConfig.getDownlinkFrequencyKhz());
+    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/ServiceStateTest.java b/tests/tests/telephony/current/src/android/telephony/cts/ServiceStateTest.java
index 3ec048b..60b9106 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/ServiceStateTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/ServiceStateTest.java
@@ -29,7 +29,10 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
+import android.content.Context;
+import android.os.Build;
 import android.os.Parcel;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.LteVopsSupportInfo;
@@ -37,10 +40,14 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class ServiceStateTest {
     private static final String OPERATOR_ALPHA_LONG = "CtsOperatorLong";
@@ -150,12 +157,38 @@
         assertEquals(DUPLEX_MODE_TDD, serviceState.getDuplexMode());
     }
 
+    private static Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
     @Test
     public void testToString() {
         assertNotNull(serviceState.toString());
     }
 
     @Test
+    public void testNrStateRedacted() {
+        final TelephonyManager tm = getContext().getSystemService(TelephonyManager.class);
+
+        // Verify that NR State is not leaked in user builds.
+        if (!Build.IS_DEBUGGABLE) {
+            final String sss = tm.getServiceState().toString();
+            // The string leaked in previous releases is "nrState=<val>"; test that there is
+            // no matching or highly similar string leak, such as:
+            // nrState=NONE
+            // nrState=0
+            // mNrState=RESTRICTED
+            // NRSTATE=NOT_RESTRICTED
+            // nrState = CONNECTED
+            // etc.
+            Pattern p = Pattern.compile("nrState\\s*=\\s*[a-zA-Z0-9_]+", Pattern.CASE_INSENSITIVE);
+            Matcher m = p.matcher(sss);
+            // Need to use if (find) fail to ensure that the start and end are populated
+            if (m.find()) fail("Found nrState reported as: " + sss.substring(m.start(), m.end()));
+        }
+    }
+
+    @Test
     public void testCopyConstructor() {
         ServiceState serviceState = getServiceStateWithOperatorName("name", "numeric");
         assertEquals(serviceState, new ServiceState(serviceState));
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
index 05a749e..468d0a5 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsManagerTest.java
@@ -482,6 +482,7 @@
 
     @Test
     public void testContentProviderAccessRestriction() throws Exception {
+        if (!mTelephonyManager.isSmsCapable()) return;
         Uri dummySmsUri = null;
         Context context = getInstrumentation().getContext();
         ContentResolver contentResolver = context.getContentResolver();
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
old mode 100644
new mode 100755
index 78561b5..e9cf4ce
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -32,6 +32,7 @@
 import static org.junit.Assert.fail;
 
 import android.annotation.Nullable;
+import android.app.UiAutomation;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
@@ -39,6 +40,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.net.Uri;
 import android.os.Looper;
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
@@ -47,6 +49,11 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsRcsManager;
+import android.telephony.ims.RcsUceAdapter;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -61,6 +68,8 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.time.Period;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
@@ -79,7 +88,13 @@
 
 public class SubscriptionManagerTest {
     private static final String TAG = "SubscriptionManagerTest";
+    private static final String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
     private SubscriptionManager mSm;
+    private static final List<Uri> CONTACTS = new ArrayList<>();
+    static {
+        CONTACTS.add(Uri.fromParts("tel", "+16505551212", null));
+        CONTACTS.add(Uri.fromParts("tel", "+16505552323", null));
+    }
 
     private int mSubId;
     private String mPackageName;
@@ -751,6 +766,8 @@
                 // not treating this as test failure as it may be due to UX confirmation or may not
                 // be supported
                 Log.e(TAG, "setSubscriptionEnabled() did not complete");
+                executeWithShellPermissionAndDefault(false, mSm,
+                    (sm) -> sm.setSubscriptionEnabled(mSubId, enabled));
                 return;
             }
 
@@ -824,6 +841,153 @@
         setPreferredDataSubId(preferredSubId);
     }
 
+    @Test
+    public void testRestoreAllSimSpecificSettingsFromBackup() throws Exception {
+        if (!isSupported()) return;
+
+        int activeDataSubId = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
+                (sm) -> sm.getActiveDataSubscriptionId());
+        assertNotEquals(activeDataSubId, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        SubscriptionInfo activeSubInfo = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
+                (sm) -> sm.getActiveSubscriptionInfo(activeDataSubId));
+        String isoCountryCode = activeSubInfo.getCountryIso();
+
+        byte[] backupData = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
+                (sm) -> sm.getAllSimSpecificSettingsForBackup());
+        assertTrue(backupData.length > 0);
+
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
+        bundle.putBoolean(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL, false);
+        overrideCarrierConfig(bundle, activeDataSubId);
+
+        // Get the original ims values.
+        ImsManager imsManager = InstrumentationRegistry.getContext().getSystemService(
+                ImsManager.class);
+        ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(activeDataSubId);
+        boolean isVolteVtEnabledOriginal = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mMmTelManager, (m) -> m.isAdvancedCallingSettingEnabled());
+        boolean isVtImsEnabledOriginal = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mMmTelManager, (m) -> m.isVtSettingEnabled());
+        boolean isVoWiFiSettingEnabledOriginal =
+                ShellIdentityUtils.invokeMethodWithShellPermissions(
+                        mMmTelManager, (m) -> m.isVoWiFiSettingEnabled());
+        int voWifiModeOriginal = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mMmTelManager, (m) -> m.getVoWiFiModeSetting());
+        int voWiFiRoamingModeOriginal = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mMmTelManager, (m) -> m.getVoWiFiRoamingModeSetting());
+
+        // Get the original RcsUce values.
+        ImsRcsManager imsRcsManager = imsManager.getImsRcsManager(activeDataSubId);
+        RcsUceAdapter rcsUceAdapter = imsRcsManager.getUceAdapter();
+        boolean isImsRcsUceEnabledOriginal =
+                ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
+                rcsUceAdapter, (a) -> a.isUceSettingEnabled(), ImsException.class,
+                android.Manifest.permission.READ_PHONE_STATE);
+
+        //Change values in DB.
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
+                (m) -> m.setAdvancedCallingSettingEnabled(!isVolteVtEnabledOriginal));
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
+                (m) -> m.setVtSettingEnabled(!isVtImsEnabledOriginal));
+        ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
+                rcsUceAdapter, (a) -> a.setUceSettingEnabled(!isImsRcsUceEnabledOriginal),
+                ImsException.class);
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
+                (m) -> m.setVoWiFiSettingEnabled(!isVoWiFiSettingEnabledOriginal));
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
+                (m) -> m.setVoWiFiModeSetting((voWifiModeOriginal + 1) % 3));
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
+                (m) -> m.setVoWiFiRoamingModeSetting((voWiFiRoamingModeOriginal + 1) % 3));
+
+        // Restore back to original values.
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
+                (sm) -> sm.restoreAllSimSpecificSettingsFromBackup(backupData));
+
+        // Get ims values to verify with.
+        boolean isVolteVtEnabledAfterRestore = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mMmTelManager, (m) -> m.isAdvancedCallingSettingEnabled());
+        boolean isVtImsEnabledAfterRestore = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mMmTelManager, (m) -> m.isVtSettingEnabled());
+        boolean isVoWiFiSettingEnabledAfterRestore =
+                ShellIdentityUtils.invokeMethodWithShellPermissions(
+                        mMmTelManager, (m) -> m.isVoWiFiSettingEnabled());
+        int voWifiModeAfterRestore = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mMmTelManager, (m) -> m.getVoWiFiModeSetting());
+        int voWiFiRoamingModeAfterRestore = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mMmTelManager, (m) -> m.getVoWiFiRoamingModeSetting());
+        // Get RcsUce values to verify with.
+        boolean isImsRcsUceEnabledAfterRestore =
+                ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
+                        rcsUceAdapter, (a) -> a.isUceSettingEnabled(), ImsException.class,
+                        android.Manifest.permission.READ_PHONE_STATE);
+
+        assertEquals(isVolteVtEnabledOriginal, isVolteVtEnabledAfterRestore);
+        if (isoCountryCode == null || isoCountryCode.equals("us") || isoCountryCode.equals("ca")) {
+            assertEquals(!isVoWiFiSettingEnabledOriginal, isVoWiFiSettingEnabledAfterRestore);
+        } else {
+            assertEquals(isVoWiFiSettingEnabledOriginal, isVoWiFiSettingEnabledAfterRestore);
+        }
+        assertEquals(voWifiModeOriginal, voWifiModeAfterRestore);
+        assertEquals(voWiFiRoamingModeOriginal, voWiFiRoamingModeAfterRestore);
+        assertEquals(isVtImsEnabledOriginal, isVtImsEnabledAfterRestore);
+        assertEquals(isImsRcsUceEnabledOriginal, isImsRcsUceEnabledAfterRestore);
+
+        // restore original carrier config.
+        overrideCarrierConfig(null, activeDataSubId);
+
+
+        try {
+            // Check api call will fail without proper permissions.
+            mSm.restoreAllSimSpecificSettingsFromBackup(backupData);
+            fail("SecurityException expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testSetAndGetD2DStatusSharing() {
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        uiAutomation.adoptShellPermissionIdentity(MODIFY_PHONE_STATE);
+        int originalD2DStatusSharing = mSm.getDeviceToDeviceStatusSharing(mSubId);
+        mSm.setDeviceToDeviceStatusSharing(SubscriptionManager.D2D_SHARING_ALL_CONTACTS, mSubId);
+        assertEquals(SubscriptionManager.D2D_SHARING_ALL_CONTACTS,
+                mSm.getDeviceToDeviceStatusSharing(mSubId));
+        mSm.setDeviceToDeviceStatusSharing(SubscriptionManager.D2D_SHARING_ALL, mSubId);
+        assertEquals(SubscriptionManager.D2D_SHARING_ALL,
+                mSm.getDeviceToDeviceStatusSharing(mSubId));
+        mSm.setDeviceToDeviceStatusSharing(originalD2DStatusSharing, mSubId);
+        uiAutomation.dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void testSetAndGetD2DSharingContacts() {
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        uiAutomation.adoptShellPermissionIdentity(MODIFY_PHONE_STATE);
+        List<Uri> originalD2DSharingContacts = mSm.getDeviceToDeviceStatusSharingContacts(mSubId);
+        mSm.setDeviceToDeviceStatusSharingContacts(CONTACTS, mSubId);
+        assertEquals(CONTACTS, mSm.getDeviceToDeviceStatusSharingContacts(mSubId));
+        mSm.setDeviceToDeviceStatusSharingContacts(originalD2DSharingContacts, mSubId);
+        uiAutomation.dropShellPermissionIdentity();
+    }
+
+    @Nullable
+    private PersistableBundle getBundleFromBackupData(byte[] data) {
+        try (ByteArrayInputStream bis = new ByteArrayInputStream(data)) {
+            return PersistableBundle.readFromStream(bis);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    private void overrideCarrierConfig(PersistableBundle bundle, int subId) throws Exception {
+        CarrierConfigManager carrierConfigManager = InstrumentationRegistry.getContext()
+                .getSystemService(CarrierConfigManager.class);
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(carrierConfigManager,
+                (m) -> m.overrideConfig(subId, bundle));
+    }
+
     private void setPreferredDataSubId(int subId) {
         final LinkedBlockingQueue<Integer> resultQueue = new LinkedBlockingQueue<>(1);
         Executor executor = (command)-> command.run();
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java
index 071f331..fbd2a12 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java
@@ -38,6 +38,7 @@
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
+import android.telephony.LinkCapacityEstimate;
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
@@ -94,10 +95,14 @@
     private boolean mOnTelephonyDisplayInfoChanged;
     private boolean mOnPhysicalChannelConfigCalled;
     private boolean mOnDataEnabledChangedCalled;
+    private boolean mOnLinkCapacityEstimateChangedCalled;
     @RadioPowerState
     private int mRadioPowerState;
     @SimActivationState
     private int mVoiceActivationState;
+    private boolean mOnAllowedNetworkTypesChangedCalled;
+    private int mAllowedNetworkTypeReason;
+    private long mAllowedNetworkTypeValue;
     private BarringInfo mBarringInfo;
     private PreciseDataConnectionState mPreciseDataConnectionState;
     private PreciseCallState mPreciseCallState;
@@ -1311,4 +1316,109 @@
         // Test unregister
         unRegisterTelephonyCallback(mOnDataEnabledChangedCalled, mDataEnabledCallback);
     }
+
+    private AllowedNetworkTypesListener mAllowedNetworkTypesCallback;
+
+    private class AllowedNetworkTypesListener extends TelephonyCallback
+            implements TelephonyCallback.AllowedNetworkTypesListener {
+        @Override
+        public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
+            synchronized (mLock) {
+                mAllowedNetworkTypeReason = reason;
+                mAllowedNetworkTypeValue = allowedNetworkType;
+                mOnAllowedNetworkTypesChangedCalled = true;
+
+                mLock.notify();
+            }
+        }
+    }
+
+    @Test
+    public void testOnAllowedNetworkTypesChangedByRegisterPhoneStateListener() throws Throwable {
+        assertFalse(mOnAllowedNetworkTypesChangedCalled);
+
+        mHandler.post(() -> {
+            mAllowedNetworkTypesCallback = new AllowedNetworkTypesListener();
+            registerTelephonyCallbackWithPermission(mAllowedNetworkTypesCallback);
+        });
+
+        long originalAllowedNetworkTypeUser = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTelephonyManager, (tm) -> {
+                    return tm.getAllowedNetworkTypesForReason(
+                            TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER);
+                }
+        );
+
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                mTelephonyManager,
+                (tm) -> tm.setAllowedNetworkTypesForReason(
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
+                        TelephonyManager.NETWORK_TYPE_BITMASK_NR));
+
+        synchronized (mLock) {
+            if (!mOnAllowedNetworkTypesChangedCalled) {
+                mLock.wait(WAIT_TIME);
+            }
+        }
+
+        long allowedNetworkTypeUser = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTelephonyManager, (tm) -> {
+                    return tm.getAllowedNetworkTypesForReason(
+                            TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER);
+                }
+        );
+
+        assertEquals(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, mAllowedNetworkTypeReason);
+        assertEquals(allowedNetworkTypeUser, mAllowedNetworkTypeValue);
+        // Test unregister
+        unRegisterTelephonyCallback(mOnAllowedNetworkTypesChangedCalled,
+                mAllowedNetworkTypesCallback);
+
+        // Recover the allowed network type user settings.
+        ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                mTelephonyManager,
+                (tm) -> tm.setAllowedNetworkTypesForReason(
+                        TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
+                        originalAllowedNetworkTypeUser));
+    }
+
+    private LinkCapacityEstimateChangedListener mLinkCapacityEstimateChangedListener;
+
+    private class LinkCapacityEstimateChangedListener extends TelephonyCallback
+            implements TelephonyCallback.LinkCapacityEstimateChangedListener {
+        @Override
+        public void onLinkCapacityEstimateChanged(
+                List<LinkCapacityEstimate> linkCapacityEstimateList) {
+            synchronized (mLock) {
+                mOnLinkCapacityEstimateChangedCalled = true;
+                mLock.notify();
+            }
+        }
+    }
+
+    @Test
+    public void testOnLinkCapacityEstimateChangedByRegisterPhoneStateListener() throws Throwable {
+        if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+            Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+            return;
+        }
+
+        assertFalse(mOnLinkCapacityEstimateChangedCalled);
+        mHandler.post(() -> {
+            mLinkCapacityEstimateChangedListener = new LinkCapacityEstimateChangedListener();
+            registerTelephonyCallbackWithPermission(mLinkCapacityEstimateChangedListener);
+        });
+
+        synchronized (mLock) {
+            while (!mOnLinkCapacityEstimateChangedCalled) {
+                mLock.wait(WAIT_TIME);
+            }
+        }
+        assertTrue(mOnLinkCapacityEstimateChangedCalled);
+
+        // Test unregister
+        unRegisterTelephonyCallback(mOnLinkCapacityEstimateChangedCalled,
+                mLinkCapacityEstimateChangedListener);
+    }
+
 }
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 dbb9cc9..6295762 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -56,7 +56,6 @@
 import android.telephony.CallAttributes;
 import android.telephony.CallForwardingInfo;
 import android.telephony.CallQuality;
-import android.telephony.CarrierBandwidth;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellIdentity;
 import android.telephony.CellIdentityLte;
@@ -184,6 +183,11 @@
     private static final int MAX_FPLMN_NUM = 100;
     private static final int MIN_FPLMN_NUM = 3;
 
+    private static final String THERMAL_MITIGATION_COMMAND_BASE = "cmd phone thermal-mitigation ";
+    private static final String ALLOW_PACKAGE_SUBCOMMAND = "allow-package ";
+    private static final String DISALLOW_PACKAGE_SUBCOMMAND = "disallow-package ";
+    private static final String TELEPHONY_CTS_PACKAGE = "android.telephony.cts";
+
     private static final String TEST_FORWARD_NUMBER = "54321";
     private static final String TESTING_PLMN = "12345";
 
@@ -282,6 +286,12 @@
         if (mIsAllowedNetworkTypeChanged) {
             recoverAllowedNetworkType();
         }
+
+        StringBuilder cmdBuilder = new StringBuilder();
+        cmdBuilder.append(THERMAL_MITIGATION_COMMAND_BASE).append(DISALLOW_PACKAGE_SUBCOMMAND)
+                .append(TELEPHONY_CTS_PACKAGE);
+        TelephonyUtils.executeShellCommand(InstrumentationRegistry.getInstrumentation(),
+                cmdBuilder.toString());
     }
 
     private void saveAllowedNetworkTypesForAllReasons() {
@@ -307,12 +317,20 @@
                             TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER);
                 }
         );
+        long allowedNetworkTypesEnable2g = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTelephonyManager, (tm) -> {
+                    return tm.getAllowedNetworkTypesForReason(
+                            TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
+                }
+        );
         mAllowedNetworkTypesList.put(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER,
                 allowedNetworkTypesUser);
         mAllowedNetworkTypesList.put(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_POWER,
                 allowedNetworkTypesPower);
         mAllowedNetworkTypesList.put(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER,
                 allowedNetworkTypesCarrier);
+        mAllowedNetworkTypesList.put(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G,
+                allowedNetworkTypesEnable2g);
     }
 
     private void recoverAllowedNetworkType() {
@@ -2846,6 +2864,8 @@
         long allowedNetworkTypes3 = TelephonyManager.NETWORK_TYPE_BITMASK_NR
                 | TelephonyManager.NETWORK_TYPE_BITMASK_LTE
                 | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
+        long allowedNetworkTypes4 = TelephonyManager.NETWORK_TYPE_LTE
+                | TelephonyManager.NETWORK_TYPE_EVDO_B;
 
         try {
             mIsAllowedNetworkTypeChanged = true;
@@ -2865,6 +2885,11 @@
                     (tm) -> tm.setAllowedNetworkTypesForReason(
                             TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER,
                             allowedNetworkTypes3));
+            ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
+                    mTelephonyManager,
+                    (tm) -> tm.setAllowedNetworkTypesForReason(
+                            TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G,
+                            allowedNetworkTypes4));
             long deviceAllowedNetworkTypes1 = ShellIdentityUtils.invokeMethodWithShellPermissions(
                     mTelephonyManager, (tm) -> {
                         return tm.getAllowedNetworkTypesForReason(
@@ -2883,9 +2908,16 @@
                                 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER);
                     }
             );
+            long deviceAllowedNetworkTypes4 = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTelephonyManager, (tm) -> {
+                        return tm.getAllowedNetworkTypesForReason(
+                                TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G);
+                    }
+            );
             assertEquals(allowedNetworkTypes1, deviceAllowedNetworkTypes1);
             assertEquals(allowedNetworkTypes2, deviceAllowedNetworkTypes2);
             assertEquals(allowedNetworkTypes3, deviceAllowedNetworkTypes3);
+            assertEquals(allowedNetworkTypes4, deviceAllowedNetworkTypes4);
         } catch (SecurityException se) {
             fail("testSetAllowedNetworkTypes: SecurityException not expected");
         }
@@ -3379,23 +3411,6 @@
     }
 
     @Test
-    public void testGetCarrierBandwidth() {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
-            return;
-        }
-        CarrierBandwidth bandwidth =
-                ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
-                        (tm) -> tm.getCarrierBandwidth());
-        if (mRadioVersion >= RADIO_HAL_VERSION_1_6) {
-            assertTrue(bandwidth != null);
-            assertTrue(bandwidth.getPrimaryDownlinkCapacityKbps()
-                    != CarrierBandwidth.INVALID);
-            assertTrue(bandwidth.getPrimaryUplinkCapacityKbps()
-                    != CarrierBandwidth.INVALID);
-        }
-    }
-
-    @Test
     public void testSetSignalStrengthUpdateRequest_nullRequest() {
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             Log.d(TAG, "skipping test on device without FEATURE_TELEPHONY present");
@@ -3720,15 +3735,27 @@
     }
 
     @Test
-    public void testSendThermalMitigationRequest() {
+    public void testSendThermalMitigationRequest() throws Exception {
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
             return;
         }
+
+        StringBuilder cmdBuilder = new StringBuilder();
+        cmdBuilder.append(THERMAL_MITIGATION_COMMAND_BASE).append(ALLOW_PACKAGE_SUBCOMMAND)
+                .append(TELEPHONY_CTS_PACKAGE);
+        TelephonyUtils.executeShellCommand(InstrumentationRegistry.getInstrumentation(),
+                cmdBuilder.toString());
+
         long arbitraryCompletionWindowSecs = 1L;
 
+        boolean isDataThrottlingSupported = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTelephonyManager, (tm) -> tm.isRadioInterfaceCapabilitySupported(
+                        TelephonyManager.CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING));
 
-        // Test a proper data throttling thermal mitigation request.
-        int thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions(
+        int thermalMitigationResult = -1;
+        if (isDataThrottlingSupported) {
+            // Test a proper data throttling thermal mitigation request.
+            thermalMitigationResult = ShellIdentityUtils.invokeMethodWithShellPermissions(
                 mTelephonyManager, (tm) -> tm.sendThermalMitigationRequest(
                         new ThermalMitigationRequest.Builder()
                                 .setThermalMitigationAction(ThermalMitigationRequest
@@ -3739,8 +3766,7 @@
                                         .setCompletionDurationMillis(arbitraryCompletionWindowSecs)
                                         .build())
                                 .build()));
-        // Only verify the result for supported devices on IRadio 1.6+
-        if (mRadioVersion >= RADIO_HAL_VERSION_1_6) {
+
             assertEquals(thermalMitigationResult,
                     TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS);
         }
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/VopsSupportInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/VopsSupportInfoTest.java
new file mode 100644
index 0000000..431da5e
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/VopsSupportInfoTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.telephony.LteVopsSupportInfo;
+import android.telephony.NrVopsSupportInfo;
+import android.telephony.VopsSupportInfo;
+
+import org.junit.Test;
+
+public class VopsSupportInfoTest {
+
+    private static final int[] NR_VOPS_VALUE = {
+        NrVopsSupportInfo.NR_STATUS_VOPS_NOT_SUPPORTED,
+        NrVopsSupportInfo.NR_STATUS_VOPS_3GPP_SUPPORTED,
+        NrVopsSupportInfo.NR_STATUS_VOPS_NON_3GPP_SUPPORTED};
+    private static final int[] NR_EMC_VALUE = {
+        NrVopsSupportInfo.NR_STATUS_EMC_NOT_SUPPORTED,
+        NrVopsSupportInfo.NR_STATUS_EMC_5GCN_ONLY,
+        NrVopsSupportInfo.NR_STATUS_EMC_EUTRA_5GCN_ONLY,
+        NrVopsSupportInfo.NR_STATUS_EMC_NR_EUTRA_5GCN};
+    private static final int[] NR_EMF_VALUE = {
+        NrVopsSupportInfo.NR_STATUS_EMF_NOT_SUPPORTED,
+        NrVopsSupportInfo.NR_STATUS_EMF_5GCN_ONLY,
+        NrVopsSupportInfo.NR_STATUS_EMF_EUTRA_5GCN_ONLY,
+        NrVopsSupportInfo.NR_STATUS_EMF_NR_EUTRA_5GCN};
+
+    @Test
+    public void testLteVopsSupportInfoApi() {
+        VopsSupportInfo vops = new LteVopsSupportInfo(
+                LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+                LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED);
+
+        assertEquals(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+                ((LteVopsSupportInfo) vops).getVopsSupport());
+        assertEquals(LteVopsSupportInfo.LTE_STATUS_NOT_SUPPORTED,
+                ((LteVopsSupportInfo) vops).getEmcBearerSupport());
+        assertFalse(vops.isVopsSupported());
+        assertFalse(vops.isEmergencyServiceSupported());
+        assertFalse(vops.isEmergencyServiceFallbackSupported());
+
+        vops = new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+                LteVopsSupportInfo.LTE_STATUS_SUPPORTED);
+
+        assertEquals(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+                ((LteVopsSupportInfo) vops).getVopsSupport());
+        assertEquals(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+                ((LteVopsSupportInfo) vops).getEmcBearerSupport());
+        assertTrue(vops.isVopsSupported());
+        assertTrue(vops.isEmergencyServiceSupported());
+        assertFalse(vops.isEmergencyServiceFallbackSupported());
+    }
+
+    @Test
+    public void testLteVopsSupportInfoParcel() {
+        LteVopsSupportInfo vops = new LteVopsSupportInfo(LteVopsSupportInfo.LTE_STATUS_SUPPORTED,
+                LteVopsSupportInfo.LTE_STATUS_SUPPORTED);
+
+        Parcel vopsParcel = Parcel.obtain();
+        vops.writeToParcel(vopsParcel, 0);
+        vopsParcel.setDataPosition(0);
+        LteVopsSupportInfo checkLteVops =
+                LteVopsSupportInfo.CREATOR.createFromParcel(vopsParcel);
+
+        assertEquals(0, vops.describeContents());
+        assertEquals(vops, checkLteVops);
+    }
+
+    @Test
+    public void testNrVopsSupportInfoApi() {
+        for (int i = 0; i < NR_VOPS_VALUE.length; i++) {
+            for (int j = 0; j < NR_EMC_VALUE.length; j++) {
+                for (int k = 0; k < NR_EMF_VALUE.length; k++) {
+                    VopsSupportInfo vops = new NrVopsSupportInfo(NR_VOPS_VALUE[i],
+                            NR_EMC_VALUE[j], NR_EMF_VALUE[k]);
+
+                    assertEquals(NR_VOPS_VALUE[i], ((NrVopsSupportInfo) vops).getVopsSupport());
+                    assertEquals(NR_EMC_VALUE[j], ((NrVopsSupportInfo) vops).getEmcSupport());
+                    assertEquals(NR_EMF_VALUE[k], ((NrVopsSupportInfo) vops).getEmfSupport());
+                    assertEquals(isVopsSupportedByNrVopsInfo(NR_VOPS_VALUE[i]),
+                            vops.isVopsSupported());
+                    assertEquals(isEmcSupportedByNrVopsInfo(NR_EMC_VALUE[j]),
+                            vops.isEmergencyServiceSupported());
+                    assertEquals(isEmfSupportedByNrVopsInfo(NR_EMF_VALUE[k]),
+                            vops.isEmergencyServiceFallbackSupported());
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testNrVopsSupportInfoParcel() {
+        NrVopsSupportInfo vops = new NrVopsSupportInfo(
+                NrVopsSupportInfo.NR_STATUS_VOPS_3GPP_SUPPORTED,
+                NrVopsSupportInfo.NR_STATUS_EMC_NR_EUTRA_5GCN,
+                NrVopsSupportInfo.NR_STATUS_EMF_NR_EUTRA_5GCN);
+
+        Parcel vopsParcel = Parcel.obtain();
+        vops.writeToParcel(vopsParcel, 0);
+        vopsParcel.setDataPosition(0);
+        NrVopsSupportInfo checkNrVops =
+                NrVopsSupportInfo.CREATOR.createFromParcel(vopsParcel);
+
+        assertEquals(0, vops.describeContents());
+        assertEquals(vops, checkNrVops);
+    }
+
+    private boolean isVopsSupportedByNrVopsInfo(int value) {
+        return value != NrVopsSupportInfo.NR_STATUS_VOPS_NOT_SUPPORTED;
+    }
+
+    private boolean isEmcSupportedByNrVopsInfo(int value) {
+        return value != NrVopsSupportInfo.NR_STATUS_EMC_NOT_SUPPORTED;
+    }
+
+    private boolean isEmfSupportedByNrVopsInfo(int value) {
+        return value != NrVopsSupportInfo.NR_STATUS_EMF_NOT_SUPPORTED;
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/gba/cts/GbaServiceTest.java b/tests/tests/telephony/current/src/android/telephony/gba/cts/GbaServiceTest.java
index 64a1538..c5baf84 100644
--- a/tests/tests/telephony/current/src/android/telephony/gba/cts/GbaServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/gba/cts/GbaServiceTest.java
@@ -134,6 +134,7 @@
                       TelephonyManager.BootstrapAuthenticationCallback() {
                 @Override
                 public void onKeysAvailable(byte[] gbaKey, String btId) {
+                    super.onKeysAvailable(gbaKey, btId);
                     assertNotNull(gbaKey);
                     assertNotNull(btId);
                     assertArrayEquals(key, gbaKey);
@@ -146,6 +147,7 @@
 
                 @Override
                 public void onAuthenticationFailure(int reason) {
+                    super.onAuthenticationFailure(reason);
                     synchronized (isSuccess) {
                         isFail.set(true);
                         isSuccess.notify();
diff --git a/tests/tests/telephony/current/src/android/telephony/gba/cts/UaSecurityProtocolIdentifierTest.java b/tests/tests/telephony/current/src/android/telephony/gba/cts/UaSecurityProtocolIdentifierTest.java
index ae1b0fd..6cfd87e 100644
--- a/tests/tests/telephony/current/src/android/telephony/gba/cts/UaSecurityProtocolIdentifierTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/gba/cts/UaSecurityProtocolIdentifierTest.java
@@ -176,6 +176,16 @@
         }
     }
 
+    @Test
+    public void testUaSecurityProtocolIdentifierBuilder() {
+        UaSecurityProtocolIdentifier sp = testCreate3GppSpId(
+                PROTO_3GPP_TLS_ID[0], TLS_CS_ID_SUPPORTED[0], false);
+        UaSecurityProtocolIdentifier.Builder builder =
+                new UaSecurityProtocolIdentifier.Builder(sp);
+
+        assertTrue(sp.equals(builder.build()));
+    }
+
     private UaSecurityProtocolIdentifier testCreate3GppSpId(
             Integer id, Integer cs, boolean nullExpected) {
         boolean isFail = false;
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/EabControllerTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/EabControllerTest.java
index 622745b..5e0bcb0 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/EabControllerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/EabControllerTest.java
@@ -178,12 +178,12 @@
         }
         sServiceConnector = null;
 
+        overrideCarrierConfig(null);
+
         if (sReceiver != null) {
             InstrumentationRegistry.getInstrumentation().getContext().unregisterReceiver(sReceiver);
             sReceiver = null;
         }
-
-        overrideCarrierConfig(null);
     }
 
     @Before
@@ -539,7 +539,7 @@
         assertNotNull("Service capabilities should not be null!", capabilities);
 
         // Verify timestamp
-        assertNotNull("Timestamp should not be null!", presenceTuple.getTimestamp());
+        assertNotNull("Timestamp should not be null!", presenceTuple.getTime());
 
         // Verify service id
         assertEquals("service_id_01", presenceTuple.getServiceId());
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsRegistrationAttributesTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsRegistrationAttributesTest.java
index 55a8d0c..85b6e6d 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsRegistrationAttributesTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsRegistrationAttributesTest.java
@@ -60,6 +60,17 @@
                 attr.getTransportType());
         assertNotNull(attr.getFeatureTags());
         assertEquals(0, attr.getFeatureTags().size());
+
+        // NR
+        attr = new ImsRegistrationAttributes.Builder(
+                ImsRegistrationImplBase.REGISTRATION_TECH_NR).build();
+        assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_NR,
+                attr.getRegistrationTechnology());
+        assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN,
+                attr.getTransportType());
+        assertEquals(0, attr.getAttributeFlags());
+        assertNotNull(attr.getFeatureTags());
+        assertEquals(0, attr.getFeatureTags().size());
     }
 
     @Test
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceConnector.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceConnector.java
index 9b84dad..e2764f0 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceConnector.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceConnector.java
@@ -74,6 +74,10 @@
     private static final String COMMAND_REMOVE_EAB_CONTACT = "uce remove-eab-contact ";
     private static final String COMMAND_GET_UCE_ENABLED = "uce get-device-enabled";
     private static final String COMMAND_SET_UCE_ENABLED = "uce set-device-enabled ";
+    private static final String COMMAND_REMOVE_UCE_REQUEST_DISALLOWED_STATUS =
+            "uce remove-request-disallowed-status ";
+    private static final String COMMAND_SET_CAPABILITY_REQUEST_TIMEOUT =
+            "uce set-capabilities-request-timeout ";
     private static final String COMMAND_SET_TEST_MODE_ENABLED = "src set-test-enabled ";
 
     private class TestCarrierServiceConnection implements ServiceConnection {
@@ -619,4 +623,18 @@
         TelephonyUtils.executeShellCommand(mInstrumentation, COMMAND_BASE
                 + COMMAND_SET_TEST_MODE_ENABLED  + (enabled ? "true" : "false"));
     }
+
+    void setCapabilitiesRequestTimeout(int slotId, long timeoutAfterMs) throws Exception {
+        StringBuilder cmdBuilder = new StringBuilder();
+        cmdBuilder.append(COMMAND_BASE).append(COMMAND_SET_CAPABILITY_REQUEST_TIMEOUT)
+                .append(COMMAND_SLOT_IDENTIFIER).append(slotId).append(" ").append(timeoutAfterMs);
+        TelephonyUtils.executeShellCommand(mInstrumentation, cmdBuilder.toString());
+    }
+
+    void removeUceRequestDisallowedStatus(int slotId) throws Exception {
+        StringBuilder cmdBuilder = new StringBuilder();
+        cmdBuilder.append(COMMAND_BASE).append(COMMAND_REMOVE_UCE_REQUEST_DISALLOWED_STATUS)
+                .append(COMMAND_SLOT_IDENTIFIER).append(slotId);
+        TelephonyUtils.executeShellCommand(mInstrumentation, cmdBuilder.toString());
+    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
index 829ddeb..6e0795b 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
@@ -110,30 +110,84 @@
     private static final int TEST_CONFIG_VALUE_INT = 0xDEADBEEF;
     private static final String TEST_CONFIG_VALUE_STRING = "DEADBEEF";
 
-    private static final String TEST_RCS_CONFIG = "<RCSConfig>\n"
-            + "\t<rcsVolteSingleRegistration>1</rcsVolteSingleRegistration>\n"
-            + "\t<SERVICES>\n"
-            + "\t\t<SupportedRCSProfileVersions>UP_2.0</SupportedRCSProfileVersions>\n"
-            + "\t\t<ChatAuth>1</ChatAuth>\n"
-            + "\t\t<GroupChatAuth>1</GroupChatAuth>\n"
-            + "\t\t<ftAuth>1</ftAuth>\n"
-            + "\t\t<standaloneMsgAuth>1</standaloneMsgAuth>\n"
-            + "\t\t<geolocPushAuth>1<geolocPushAuth>\n"
-            + "\t\t<Ext>\n"
-            + "\t\t\t<DataOff>\n"
-            + "\t\t\t\t<rcsMessagingDataOff>1</rcsMessagingDataOff>\n"
-            + "\t\t\t\t<fileTransferDataOff>1</fileTransferDataOff>\n"
-            + "\t\t\t\t<mmsDataOff>1</mmsDataOff>\n"
-            + "\t\t\t\t<syncDataOff>1</syncDataOff>\n"
-            + "\t\t\t</DataOff>\n"
-            + "\t\t</Ext>\n"
-            + "\t</SERVICES>\n"
-            + "</RCSConfig>";
+    private static final String TEST_RCS_CONFIG_DEFAULT = "<?xml version=\"1.0\"?>\n"
+            + "<wap-provisioningdoc version=\"1.1\">\n"
+            + "\t<characteristic type=\"APPLICATION\">\n"
+            + "\t\t<parm name=\"AppID\" value=\"urn:oma:mo:ext-3gpp-ims:1.0\"/>\n"
+            + "\t\t<characteristic type=\"3GPP_IMS\">\n"
+            + "\t\t\t<parm name=\"AppID\" value=\"ap2001\"/>\n"
+            + "\t\t\t<parm name=\"Name\" value=\"RCS IMS Settings\"/>\n"
+            + "\t\t\t<characteristic type=\"Ext\">\n"
+            + "\t\t\t\t<characteristic type=\"GSMA\">\n"
+            + "\t\t\t\t\t<parm name=\"AppRef\" value=\"IMS-Setting\"/>\n"
+            + "\t\t\t\t\t<parm name=\"rcsVolteSingleRegistration\" value=\"1\"/>\n"
+            + "\t\t\t\t</characteristic>\n"
+            + "\t\t\t</characteristic>\n"
+            + "\t\t</characteristic>\n"
+            + "\t\t<characteristic type=\"SERVICES\">\n"
+            + "\t\t\t<parm name=\"SupportedRCSProfileVersions\" value=\"UP2.3\"/>\n"
+            + "\t\t\t<parm name=\"ChatAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"GroupChatAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"ftAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"standaloneMsgAuth\" value=\"1\"/>\n"
+            + "\t\t\t<parm name=\"geolocPushAuth\" value=\"1\"/>\n"
+            + "\t\t\t<characteristic type=\"Ext\">\n"
+            + "\t\t\t\t<characteristic type=\"DataOff\">\n"
+            + "\t\t\t\t\t<parm name=\"rcsMessagingDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<parm name=\"fileTransferDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<parm name=\"mmsDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<parm name=\"syncDataOff\" value=\"1\"/>\n"
+            + "\t\t\t\t\t<characteristic type=\"Ext\"/>\n"
+            + "\t\t\t\t</characteristic>\n"
+            + "\t\t\t</characteristic>\n"
+            + "\t\t</characteristic>\n"
+            + "\t</characteristic>\n"
+            + "</wap-provisioningdoc>\n";
+
+    private static final String TEST_RCS_CONFIG_SINGLE_REGISTRATION_DISABLED =
+            "<?xml version=\"1.0\"?>\n"
+            + "<wap-provisioningdoc version=\"1.1\">\n"
+            + "\t<characteristic type=\"APPLICATION\">\n"
+            + "\t\t<parm name=\"AppID\" value=\"urn:oma:mo:ext-3gpp-ims:1.0\"/>\n"
+            + "\t\t<characteristic type=\"3GPP_IMS\">\n"
+            + "\t\t\t<parm name=\"AppID\" value=\"ap2001\"/>\n"
+            + "\t\t\t<parm name=\"Name\" value=\"RCS IMS Settings\"/>\n"
+            + "\t\t\t<characteristic type=\"Ext\">\n"
+            + "\t\t\t\t<characteristic type=\"GSMA\">\n"
+            + "\t\t\t\t\t<parm name=\"AppRef\" value=\"IMS-Setting\"/>\n"
+            + "\t\t\t\t\t<parm name=\"rcsVolteSingleRegistration\" value=\"0\"/>\n"
+            + "\t\t\t\t</characteristic>\n"
+            + "\t\t\t</characteristic>\n"
+            + "\t\t</characteristic>\n"
+            + "\t</characteristic>\n"
+            + "</wap-provisioningdoc>\n";
+    private static final String TEST_RCS_PRE_CONFIG = "<RCSPreProvisiniongConfig>\n"
+            + "\t<VERS>\n"
+            + "\t\t<version>1</version>\n"
+            + "\t\t<validity>1728000</validity>\n"
+            + "\t</VERS>\n"
+            + "\t<TOKEN>\n"
+            + "\t\t<token>X</token>\n"
+            + "\t</TOKEN>\n"
+            + "\t<EXT>\n"
+            + "\t\t<url>https://rcs.mnc123.mcc456.pub.3gppnetwork.org</url>\n"
+            + "\t</EXT>\n"
+            + "</RCSPreProvisiniongConfig>";
     private static final int RCS_CONFIG_CB_UNKNOWN = Integer.MAX_VALUE;
     private static final int RCS_CONFIG_CB_CHANGED = 0;
     private static final int RCS_CONFIG_CB_ERROR   = 1;
     private static final int RCS_CONFIG_CB_RESET   = 2;
     private static final int RCS_CONFIG_CB_DELETE  = 3;
+    private static final int RCS_CONFIG_CB_PREPROV = 4;
+
+    private static final String CHAT_FEATURE_TAG =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"";
+    public static final String FILE_TRANSFER_FEATURE_TAG =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"";
+    private static final String CHAT_SERVICE_ID =
+            "org.openmobilealliance:ChatSession";
+    private static final String FILE_TRANSFER_SERVICE_ID =
+            "org.openmobilealliance:File-Transfer-HTTP";
 
     private static CarrierConfigReceiver sReceiver;
     private static SingleRegistrationCapabilityReceiver sSrcReceiver;
@@ -857,28 +911,20 @@
         assertEquals(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, deregResult.getCode());
 
         // Start registration
-        ImsRegistrationAttributes lteTagsAttr = new ImsRegistrationAttributes.Builder(
-                ImsRegistrationImplBase.REGISTRATION_TECH_LTE)
-                .setFeatureTags(featureTags)
-                .build();
-        sServiceConnector.getCarrierService().getImsRegistration().onRegistering(lteTagsAttr);
-        ImsRegistrationAttributes attrResult = waitForResult(mRegQueue);
-        assertNotNull(attrResult);
-        assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
-                attrResult.getRegistrationTechnology());
-        assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, attrResult.getTransportType());
-        assertEquals(0, attrResult.getAttributeFlags());
-        assertEquals(featureTags, attrResult.getFeatureTags());
+        verifyRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, featureTags, mRegQueue,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 0 /*expected flags*/);
+
+        // move to NR
+        verifyRegistering(ImsRegistrationImplBase.REGISTRATION_TECH_NR, featureTags, mRegQueue,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 0 /*expected flags*/);
 
         // Complete registration
-        sServiceConnector.getCarrierService().getImsRegistration().onRegistered(lteTagsAttr);
-        attrResult = waitForResult(mRegQueue);
-        assertNotNull(attrResult);
-        assertEquals(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
-                attrResult.getRegistrationTechnology());
-        assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, attrResult.getTransportType());
-        assertEquals(0, attrResult.getAttributeFlags());
-        assertEquals(featureTags, attrResult.getFeatureTags());
+        verifyRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, featureTags, mRegQueue,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 0 /*expected flags*/);
+
+        // move to NR
+        verifyRegistered(ImsRegistrationImplBase.REGISTRATION_TECH_NR, featureTags, mRegQueue,
+                AccessNetworkConstants.TRANSPORT_TYPE_WWAN, 0 /*expected flags*/);
 
         try {
             automan.adoptShellPermissionIdentity();
@@ -1086,8 +1132,8 @@
         capExchangeImpl.setPublishOperator((listener, pidfXml, cb) -> {
             int networkResp = 200;
             String reason = "";
-            listener.onPublish();
             cb.onNetworkResponse(networkResp, reason);
+            listener.onPublish();
         });
 
         // Unregister the publish state callback
@@ -1181,6 +1227,137 @@
     }
 
     @Test
+    public void testPublishImsReg() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        // Trigger carrier config changed
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+        bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, true);
+        overrideCarrierConfig(bundle);
+
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        if (imsManager == null) {
+            fail("Cannot find IMS service");
+        }
+
+        ImsRcsManager imsRcsManager = imsManager.getImsRcsManager(sTestSub);
+        RcsUceAdapter uceAdapter = imsRcsManager.getUceAdapter();
+
+        // Connect to device ImsService with MmTel feature and RCS feature
+        triggerFrameworkConnectToImsServiceBindMmTelAndRcsFeature();
+
+        TestRcsCapabilityExchangeImpl capExchangeImpl = sServiceConnector.getCarrierService()
+                .getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        // Register the callback to listen to the publish state changed
+        LinkedBlockingQueue<Integer> publishStateQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.OnPublishStateChangedListener publishStateCallback =
+                publishStateQueue::offer;
+
+        final UiAutomation automan = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            automan.adoptShellPermissionIdentity();
+            uceAdapter.addOnPublishStateChangedListener(getContext().getMainExecutor(),
+                    publishStateCallback);
+        } finally {
+            automan.dropShellPermissionIdentity();
+        }
+
+        // Verify receiving the publish state callback immediately after registering the callback.
+        assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED,
+                waitForIntResult(publishStateQueue));
+        publishStateQueue.clear();
+
+        LinkedBlockingQueue<String> pidfQueue = new LinkedBlockingQueue<>();
+        // Setup the operation of the publish request.
+        capExchangeImpl.setPublishOperator((listener, pidfXml, cb) -> {
+            pidfQueue.offer(pidfXml);
+            int networkResp = 200;
+            String reason = "";
+            cb.onNetworkResponse(networkResp, reason);
+            listener.onPublish();
+        });
+
+        LinkedBlockingQueue<ImsRegistrationAttributes> mQueue = new LinkedBlockingQueue<>();
+        RegistrationManager.RegistrationCallback callback =
+                new RegistrationManager.RegistrationCallback() {
+                    @Override
+                    public void onRegistered(ImsRegistrationAttributes attr) {
+                        mQueue.offer(attr);
+                    }
+
+                    @Override
+                    public void onRegistering(ImsRegistrationAttributes attr) {}
+
+                    @Override
+                    public void onUnregistered(ImsReasonInfo info) {}
+
+                    @Override
+                    public void onTechnologyChangeFailed(int type, ImsReasonInfo info) {}
+                };
+        ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(imsRcsManager,
+                (m) -> m.registerImsRegistrationCallback(getContext().getMainExecutor(), callback),
+                ImsException.class);
+
+        // IMS registers
+        ArraySet<String> featureTags = new ArraySet<>();
+        // Chat Session
+        featureTags.add(CHAT_FEATURE_TAG);
+        featureTags.add(FILE_TRANSFER_FEATURE_TAG);
+        ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE).setFeatureTags(featureTags).build();
+        sServiceConnector.getCarrierService().getImsRegistration().onRegistered(attr);
+        waitForParam(mQueue, attr);
+
+        // Notify framework that the RCS capability status is changed and PRESENCE UCE is enabled.
+        RcsImsCapabilities capabilities =
+                new RcsImsCapabilities(RcsUceAdapter.CAPABILITY_TYPE_PRESENCE_UCE);
+        sServiceConnector.getCarrierService().getRcsFeature()
+                .notifyCapabilitiesStatusChanged(capabilities);
+
+        CapabilityExchangeEventListener eventListener =
+                sServiceConnector.getCarrierService().getRcsFeature().getEventListener();
+
+        // ImsService triggers to notify framework publish device's capabilities.
+        eventListener.onRequestPublishCapabilities(
+                RcsUceAdapter.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN);
+
+        // Verify that the publish is triggered and receive the publish state changed callback.
+        assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_UCE_REQUEST_PUBLISH));
+        assertEquals(RcsUceAdapter.PUBLISH_STATE_OK, waitForIntResult(publishStateQueue));
+        publishStateQueue.clear();
+
+        // Can not verify the pidf fully, but we can ensure that the service id for the feature is
+        // contained in the XML. Multible PUBLISH requests may occur based on the state of the stack
+        // at the time of this call, retry to get correct PIDF up to 5 times.
+        boolean containsChatServiceId = false;
+        boolean containsFileTransferServiceId = false;
+        for (int retry = 0; retry < 5; retry++) {
+            String pidf = waitForResult(pidfQueue);
+            if (pidf == null) break;
+            containsChatServiceId = pidf.contains(CHAT_SERVICE_ID);
+            containsFileTransferServiceId  = pidf.contains(FILE_TRANSFER_SERVICE_ID);
+            if (containsChatServiceId && containsFileTransferServiceId) break;
+        }
+        assertTrue("PIDF XML doesn't contain chat service-id", containsChatServiceId);
+        assertTrue("PIDF XML doesn't contain FT service-id",
+                containsFileTransferServiceId);
+
+        // Trigger RcsFeature is unavailable
+        sServiceConnector.getCarrierService().getRcsFeature()
+                .setFeatureState(ImsFeature.STATE_UNAVAILABLE);
+
+        // Verify the RcsCapabilityExchangeImplBase will be removed.
+        assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_UCE_LISTENER_SET));
+
+        overrideCarrierConfig(null);
+    }
+
+    @Test
     public void testRcsCapabilitiesPublishNetworkResponseWithReasonHeader() throws Exception {
         if (!ImsUtils.shouldTestImsService()) {
             return;
@@ -1230,8 +1407,8 @@
         capExchangeImpl.setPublishOperator((listener, pidfXml, cb) -> {
             int networkResp = 200;
             String reason = "OK";
-            listener.onPublish();
             cb.onNetworkResponse(networkResp, reason);
+            listener.onPublish();
         });
 
         // IMS registers
@@ -1270,8 +1447,8 @@
             String reason = "";
             int reasonHeaderCause = 400;
             String reasonHeaderText = "Bad Request";
-            listener.onPublish();
             cb.onNetworkResponse(networkResp, reason, reasonHeaderCause, reasonHeaderText);
+            listener.onPublish();
         });
 
         // ImsService triggers to notify framework publish device's capabilities.
@@ -1297,6 +1474,129 @@
     }
 
     @Test
+    public void testRcsPublishThrottle() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+
+        // Trigger carrier config change
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
+        bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, true);
+        overrideCarrierConfig(bundle);
+
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        if (imsManager == null) {
+            fail("Cannot get the ImsManager");
+        }
+        ImsRcsManager imsRcsManager = imsManager.getImsRcsManager(sTestSub);
+        RcsUceAdapter uceAdapter = imsRcsManager.getUceAdapter();
+
+        // Connect to the ImsService
+        triggerFrameworkConnectToImsServiceBindMmTelAndRcsFeature();
+
+        TestRcsCapabilityExchangeImpl capExchangeImpl = sServiceConnector.getCarrierService()
+                .getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        // Setup the response of the publish request.
+        capExchangeImpl.setPublishOperator((listener, pidfXml, cb) -> {
+            int networkResp = 200;
+            String reason = "OK";
+            cb.onNetworkResponse(networkResp, reason);
+            listener.onPublish();
+        });
+
+        // Register the callback to listen to the publish state changed
+        LinkedBlockingQueue<Integer> publishStateQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.OnPublishStateChangedListener publishStateCallback =
+                new RcsUceAdapter.OnPublishStateChangedListener() {
+                    public void onPublishStateChange(int state) {
+                        publishStateQueue.offer(state);
+                    }
+                };
+
+        final UiAutomation automation = InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation();
+        try {
+            automation.adoptShellPermissionIdentity();
+            uceAdapter.addOnPublishStateChangedListener(getContext().getMainExecutor(),
+                    publishStateCallback);
+        } finally {
+            automation.dropShellPermissionIdentity();
+        }
+
+        // Verify receiving the publish state callback immediately after registering the callback.
+        assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED,
+                waitForIntResult(publishStateQueue));
+        publishStateQueue.clear();
+
+        // IMS registers
+        sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
+
+        // Verify the PUBLISH request should not be triggered and the publish state is still
+        // NOT_PUBLISHED even the IMS is registered.
+        if (publishStateQueue.poll() != null) {
+            fail("The PUBLISH request should not be triggered.");
+        }
+        try {
+            automation.adoptShellPermissionIdentity();
+            int publishState = uceAdapter.getUcePublishState();
+            assertEquals(RcsUceAdapter.PUBLISH_STATE_NOT_PUBLISHED, publishState);
+        } finally {
+            automation.dropShellPermissionIdentity();
+        }
+
+        // Notify framework that the RCS capability status is changed and PRESENCE UCE is enabled.
+        RcsImsCapabilities capabilities =
+                new RcsImsCapabilities(RcsUceAdapter.CAPABILITY_TYPE_PRESENCE_UCE);
+        sServiceConnector.getCarrierService().getRcsFeature()
+                .notifyCapabilitiesStatusChanged(capabilities);
+
+        CapabilityExchangeEventListener eventListener =
+                sServiceConnector.getCarrierService().getRcsFeature().getEventListener();
+
+        // Notify framework to send the PUBLISH request to the ImsService.
+        eventListener.onRequestPublishCapabilities(
+                RcsUceAdapter.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN);
+
+        // Verify that ImsService received the first PUBLISH
+        assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_UCE_REQUEST_PUBLISH));
+        assertEquals(RcsUceAdapter.PUBLISH_STATE_OK, waitForIntResult(publishStateQueue));
+        publishStateQueue.clear();
+        try {
+            automation.adoptShellPermissionIdentity();
+            int publishState = uceAdapter.getUcePublishState();
+            assertEquals(RcsUceAdapter.PUBLISH_STATE_OK, publishState);
+        } finally {
+            automation.dropShellPermissionIdentity();
+        }
+
+        // Now enable voice availability
+        sServiceConnector.getCarrierService().getMmTelFeature()
+                .notifyCapabilitiesStatusChanged(new MmTelFeature.MmTelCapabilities(
+                        MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE));
+
+        // The published just succeeded. The next publish should not be triggered immediately even
+        // the device capabilities has changed. Wait 3 seconds to verify the ImsService does not
+        // receive the publish request from the framework.
+        assertFalse(sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_UCE_REQUEST_PUBLISH, 3000 /* 3 seconds */));
+
+        // However, if the request is triggered from the service, a new publish request should be
+        // sent immediately.
+        eventListener.onRequestPublishCapabilities(
+                RcsUceAdapter.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN);
+
+        // Verify the ImsService receive the publish request
+        assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+                TestImsService.LATCH_UCE_REQUEST_PUBLISH, 3000 /* Wait up to 3 seconds */));
+
+        overrideCarrierConfig(null);
+    }
+
+    @Test
     public void testRcsManagerRegistrationCallback() throws Exception {
         if (!ImsUtils.shouldTestImsService()) {
             return;
@@ -1579,6 +1879,20 @@
         verifyRegistrationState(imsRcsManager, RegistrationManager.REGISTRATION_STATE_REGISTERED);
         verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
 
+        // Start registration over NR
+        sServiceConnector.getCarrierService().getImsRegistration().onRegistering(
+                ImsRegistrationImplBase.REGISTRATION_TECH_NR);
+        assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
+        verifyRegistrationState(imsRcsManager, RegistrationManager.REGISTRATION_STATE_REGISTERING);
+        verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
+        // Complete registration over NR
+        sServiceConnector.getCarrierService().getImsRegistration().onRegistered(
+                ImsRegistrationImplBase.REGISTRATION_TECH_NR);
+        assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, waitForIntResult(mQueue));
+        verifyRegistrationState(imsRcsManager, RegistrationManager.REGISTRATION_STATE_REGISTERED);
+        verifyRegistrationTransportType(imsRcsManager, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+
         // Fail handover to IWLAN
         sServiceConnector.getCarrierService().getImsRegistration().onTechnologyChangeFailed(
                 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
@@ -2007,6 +2321,21 @@
             automan.dropShellPermissionIdentity();
         }
 
+        // Remove availability changed listener
+        try {
+            automan.adoptShellPermissionIdentity();
+            imsRcsManager.removeOnAvailabilityChangedListener(callback);
+        } finally {
+            automan.dropShellPermissionIdentity();
+        }
+
+        // Notify capabilities status changes again.
+        sServiceConnector.getCarrierService().getRcsFeature()
+                .notifyCapabilitiesStatusChanged(optionsCap);
+
+        // The callback should not be called because the listener is removed.
+        assertTrue(availabilityChanged.isEmpty());
+
         overrideCarrierConfig(null);
     }
 
@@ -2360,7 +2689,7 @@
         try {
             automan.adoptShellPermissionIdentity();
             provisioningManager.notifyRcsAutoConfigurationReceived(
-                    TEST_RCS_CONFIG.getBytes(), false);
+                    TEST_RCS_CONFIG_DEFAULT.getBytes(), false);
         } finally {
             automan.dropShellPermissionIdentity();
         }
@@ -2381,7 +2710,7 @@
         assertEquals(res, RCS_CONFIG_CB_CHANGED);
         RcsProvisioningCallbackParams params = waitForResult(paramsQueue);
         assertNotNull(params);
-        assertTrue(Arrays.equals(params.mConfig, TEST_RCS_CONFIG.getBytes()));
+        assertTrue(Arrays.equals(params.mConfig, TEST_RCS_CONFIG_DEFAULT.getBytes()));
 
         //verify callback when rcs configuration removed
         config.getIImsConfig().notifyRcsAutoConfigurationRemoved();
@@ -2390,13 +2719,13 @@
 
         //verify callback when rcs configuration received, compressed
         config.getIImsConfig().notifyRcsAutoConfigurationReceived(
-                ImsUtils.compressGzip(TEST_RCS_CONFIG.getBytes()), true);
+                ImsUtils.compressGzip(TEST_RCS_CONFIG_DEFAULT.getBytes()), true);
 
         res = waitForIntResult(actionQueue);
         assertEquals(res, RCS_CONFIG_CB_CHANGED);
         params = waitForResult(paramsQueue);
         assertNotNull(params);
-        assertTrue(Arrays.equals(params.mConfig, TEST_RCS_CONFIG.getBytes()));
+        assertTrue(Arrays.equals(params.mConfig, TEST_RCS_CONFIG_DEFAULT.getBytes()));
 
         //verify callback when auto config error received
         config.notifyAutoConfigurationErrorReceived(errorCode, errorString);
@@ -2412,6 +2741,15 @@
         res = waitForIntResult(actionQueue);
         assertEquals(res, RCS_CONFIG_CB_RESET);
 
+        //verify callback when rcs pre-provisioning configuration received
+        TestAcsClient.getInstance().notifyPreProvisioning(TEST_RCS_PRE_CONFIG.getBytes());
+
+        res = waitForIntResult(actionQueue);
+        assertEquals(res, RCS_CONFIG_CB_PREPROV);
+        params = waitForResult(paramsQueue);
+        assertNotNull(params);
+        assertTrue(Arrays.equals(params.mConfig, TEST_RCS_PRE_CONFIG.getBytes()));
+
         //unregister callback and verify not to receive callback any more
         try {
             automan.adoptShellPermissionIdentity();
@@ -2443,7 +2781,7 @@
                 buildRcsProvisioningCallback(clientQueue, paramsQueue);
         ProvisioningManager provisioningManager =
                 ProvisioningManager.createForSubscriptionId(sTestSub);
-        String configStr = "<test01/>\n" + TEST_RCS_CONFIG;
+        String configStr = TEST_RCS_CONFIG_DEFAULT;
 
         //notify rcs configuration received, wait rcs gets ready and receives notification
         try {
@@ -2473,7 +2811,7 @@
         assertTrue(Arrays.equals(
                 configStr.getBytes(), TestAcsClient.getInstance().getConfig()));
 
-        configStr = "<test02/>\n" + TEST_RCS_CONFIG;
+        configStr = TEST_RCS_CONFIG_SINGLE_REGISTRATION_DISABLED;
         try {
             automan.adoptShellPermissionIdentity();
             provisioningManager.notifyRcsAutoConfigurationReceived(
@@ -2514,7 +2852,7 @@
         try {
             automan.adoptShellPermissionIdentity();
             provisioningManager.notifyRcsAutoConfigurationReceived(
-                    TEST_RCS_CONFIG.getBytes(), false);
+                    TEST_RCS_CONFIG_DEFAULT.getBytes(), false);
         } finally {
             automan.dropShellPermissionIdentity();
         }
@@ -2569,7 +2907,7 @@
         try {
             automan.adoptShellPermissionIdentity();
             provisioningManager.notifyRcsAutoConfigurationReceived(
-                    TEST_RCS_CONFIG.getBytes(), false);
+                    TEST_RCS_CONFIG_DEFAULT.getBytes(), false);
         } finally {
             automan.dropShellPermissionIdentity();
         }
@@ -2623,6 +2961,11 @@
         assertEquals(false, (ProvisioningManager.STATUS_CARRIER_NOT_CAPABLE & capability) == 0);
         try {
             automan.adoptShellPermissionIdentity();
+            //set the rcs config with single registration enabled
+            provisioningManager.notifyRcsAutoConfigurationReceived(
+                    TEST_RCS_CONFIG_DEFAULT.getBytes(), false);
+            int res = waitForIntResult(TestAcsClient.getInstance().getActionQueue());
+            assertEquals(res, TestAcsClient.ACTION_CONFIG_CHANGED);
             assertEquals(provisioningManager.isRcsVolteSingleRegistrationCapable(),
                     (ProvisioningManager.STATUS_CARRIER_NOT_CAPABLE & capability) == 0);
         } finally {
@@ -2660,6 +3003,31 @@
             automan.dropShellPermissionIdentity();
         }
 
+        sSrcReceiver.clearQueue();
+        sServiceConnector.setDeviceSingleRegistrationEnabled(true);
+        sSrcReceiver.waitForChanged();
+        capability = sSrcReceiver.getCapability();
+
+        assertEquals(true, (ProvisioningManager.STATUS_DEVICE_NOT_CAPABLE & capability) == 0);
+        try {
+            automan.adoptShellPermissionIdentity();
+            assertEquals(provisioningManager.isRcsVolteSingleRegistrationCapable(), true);
+        } finally {
+            automan.dropShellPermissionIdentity();
+        }
+
+        try {
+            automan.adoptShellPermissionIdentity();
+            //set the rcs config with single registration disabled
+            provisioningManager.notifyRcsAutoConfigurationReceived(
+                    TEST_RCS_CONFIG_SINGLE_REGISTRATION_DISABLED.getBytes(), false);
+            int res = waitForIntResult(TestAcsClient.getInstance().getActionQueue());
+            assertEquals(res, TestAcsClient.ACTION_CONFIG_CHANGED);
+            assertEquals(provisioningManager.isRcsVolteSingleRegistrationCapable(), false);
+        } finally {
+            automan.dropShellPermissionIdentity();
+        }
+
         sServiceConnector.setDeviceSingleRegistrationEnabled(null);
         overrideCarrierConfig(null);
     }
@@ -2794,6 +3162,7 @@
         return new ProvisioningManager.RcsProvisioningCallback() {
             @Override
             public void onConfigurationChanged(byte[] configXml) {
+                super.onConfigurationChanged(configXml);
                 actionQueue.offer(RCS_CONFIG_CB_CHANGED);
                 if (paramQueue != null) {
                     RcsProvisioningCallbackParams params = new RcsProvisioningCallbackParams();
@@ -2804,6 +3173,7 @@
 
             @Override
             public void onAutoConfigurationErrorReceived(int code, String str) {
+                super.onAutoConfigurationErrorReceived(code, str);
                 actionQueue.offer(RCS_CONFIG_CB_ERROR);
                 if (paramQueue != null) {
                     RcsProvisioningCallbackParams params = new RcsProvisioningCallbackParams();
@@ -2815,13 +3185,26 @@
 
             @Override
             public void onConfigurationReset() {
+                super.onConfigurationReset();
                 actionQueue.offer(RCS_CONFIG_CB_RESET);
             }
 
             @Override
             public void onRemoved() {
+                super.onRemoved();
                 actionQueue.offer(RCS_CONFIG_CB_DELETE);
             }
+
+            @Override
+            public void onPreProvisioningReceived(byte[] configXml) {
+                super.onPreProvisioningReceived(configXml);
+                actionQueue.offer(RCS_CONFIG_CB_PREPROV);
+                if (paramQueue != null) {
+                    RcsProvisioningCallbackParams params = new RcsProvisioningCallbackParams();
+                    params.mConfig = configXml;
+                    paramQueue.offer(params);
+                }
+            }
         };
     }
     // Waiting for ImsRcsManager to become public before implementing RegistrationManager,
@@ -2868,6 +3251,34 @@
         assertEquals(expectedTransportType, waitForIntResult(mQueue));
     }
 
+    private void verifyRegistering(int tech, ArraySet<String> featureTags,
+            LinkedBlockingQueue<ImsRegistrationAttributes> attrQueue, int expectedTransport,
+            int expectedAttrFlags) throws Exception {
+        ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(tech)
+                .setFeatureTags(featureTags).build();
+        sServiceConnector.getCarrierService().getImsRegistration().onRegistering(attr);
+        ImsRegistrationAttributes attrResult = waitForResult(attrQueue);
+        assertNotNull(attrResult);
+        assertEquals(tech, attrResult.getRegistrationTechnology());
+        assertEquals(expectedTransport, attrResult.getTransportType());
+        assertEquals(expectedAttrFlags, attrResult.getAttributeFlags());
+        assertEquals(featureTags, attrResult.getFeatureTags());
+    }
+
+    private void verifyRegistered(int tech, ArraySet<String> featureTags,
+            LinkedBlockingQueue<ImsRegistrationAttributes> attrQueue, int expectedTransport,
+            int expectedAttrFlags) throws Exception {
+        ImsRegistrationAttributes attr = new ImsRegistrationAttributes.Builder(tech)
+                .setFeatureTags(featureTags).build();
+        sServiceConnector.getCarrierService().getImsRegistration().onRegistered(attr);
+        ImsRegistrationAttributes attrResult = waitForResult(attrQueue);
+        assertNotNull(attrResult);
+        assertEquals(tech, attrResult.getRegistrationTechnology());
+        assertEquals(expectedTransport, attrResult.getTransportType());
+        assertEquals(expectedAttrFlags, attrResult.getAttributeFlags());
+        assertEquals(featureTags, attrResult.getFeatureTags());
+    }
+
     private <T> boolean waitForParam(LinkedBlockingQueue<T> queue, T waitParam) throws Exception {
         T result;
         while ((result = waitForResult(queue)) != null) {
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java
index 180f375..42fb83a 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsUtils.java
@@ -24,7 +24,6 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.ims.SipMessage;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -39,7 +38,7 @@
 import java.util.zip.GZIPOutputStream;
 
 public class ImsUtils {
-    public static final boolean VDBG = false;
+    public static final boolean VDBG = true;
 
     // ImsService rebind has an exponential backoff capping at 64 seconds. Wait for 70 seconds to
     // allow for the new poll to happen in the framework.
@@ -49,10 +48,6 @@
     public static final int ITEM_NON_COMPRESSED = 2000;
     // Id for compressed auto configuration xml.
     public static final int ITEM_COMPRESSED = 2001;
-    // TODO Replace with a real sip message once that logic is in.
-    public static final String TEST_TRANSACTION_ID = "z9hG4bK.TeSt";
-    public static final String TEST_CALL_ID = "testcall";
-    public static final SipMessage TEST_SIP_MESSAGE = new SipMessage("A", "B", new byte[0]);
 
     public static boolean shouldTestTelephony() {
         final PackageManager pm = InstrumentationRegistry.getInstrumentation().getContext()
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsClientConfigurationTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsClientConfigurationTest.java
new file mode 100644
index 0000000..f1a09de
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsClientConfigurationTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.telephony.ims.RcsClientConfiguration;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public final class RcsClientConfigurationTest {
+
+    private static final String RCS_VERSION = "9.0";
+    private static final String RCS_PROFILE = RcsClientConfiguration.RCS_PROFILE_2_3;
+    private static final String CLIENT_VENDOR = "Android";
+    private static final String CLIENT_VERSION = "RCSAndrd-1.0";
+
+    @Test
+    public void testRcsClientConfigurationApi() {
+        RcsClientConfiguration rcc = new RcsClientConfiguration(
+                RCS_VERSION, RCS_PROFILE, CLIENT_VENDOR, CLIENT_VERSION);
+
+        assertEquals(RCS_VERSION, rcc.getRcsVersion());
+        assertEquals(RCS_PROFILE, rcc.getRcsProfile());
+        assertEquals(CLIENT_VENDOR, rcc.getClientVendor());
+        assertEquals(CLIENT_VERSION, rcc.getClientVersion());
+    }
+
+    @Test
+    public void testRcsClientConfigurationParcel() {
+        RcsClientConfiguration rcc = new RcsClientConfiguration(
+                RCS_VERSION, RCS_PROFILE, CLIENT_VENDOR, CLIENT_VERSION);
+        Parcel rccParcel = Parcel.obtain();
+        rcc.writeToParcel(rccParcel, 0);
+        rccParcel.setDataPosition(0);
+        RcsClientConfiguration checkRcc =
+                RcsClientConfiguration.CREATOR.createFromParcel(rccParcel);
+
+        assertEquals(0, rcc.describeContents());
+        assertEquals(rcc, checkRcc);
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsContactUceCapabilityTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsContactUceCapabilityTest.java
index 4cf7a62..4fcc85b 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsContactUceCapabilityTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsContactUceCapabilityTest.java
@@ -17,13 +17,16 @@
 package android.telephony.ims.cts;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.net.Uri;
 import android.os.Parcel;
 import android.telephony.ims.RcsContactPresenceTuple;
 import android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities;
 import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.RcsContactUceCapability.OptionsBuilder;
 import android.telephony.ims.RcsContactUceCapability.PresenceBuilder;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -31,12 +34,29 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.time.Instant;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 @RunWith(AndroidJUnit4.class)
 public class RcsContactUceCapabilityTest {
 
-    private static final Uri TEST_CONTACT = Uri.fromParts("sip", "me.test", null);
+    private static final Uri TEST_CONTACT = Uri.fromParts("sip", "test1", null);
+
+    public static final String FEATURE_TAG_CHAT_IM =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.im\"";
+
+    public static final String FEATURE_TAG_CHAT_SESSION =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"";
+
+    public static final String FEATURE_TAG_FILE_TRANSFER =
+            "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"";
+
+    public static final String FEATURE_TAG_POST_CALL =
+            "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.gsma.callunanswered\"";
 
     @Test
     public void testParcelUnparcel() {
@@ -44,29 +64,17 @@
             return;
         }
 
-        final boolean isAudioCapable = true;
-        final boolean isVideoCapable = true;
-        final String serviceVersion = "1.0";
-        final String serviceDescription = "service description test";
-
-        // Create the test capability
-        ServiceCapabilities.Builder servCapsBuilder = new ServiceCapabilities.Builder(
-                isAudioCapable, isVideoCapable);
-        servCapsBuilder.addSupportedDuplexMode(ServiceCapabilities.DUPLEX_MODE_FULL);
-
-        RcsContactPresenceTuple.Builder tupleBuilder = new RcsContactPresenceTuple.Builder(
-                RcsContactPresenceTuple.TUPLE_BASIC_STATUS_OPEN,
-                RcsContactPresenceTuple.SERVICE_ID_MMTEL, serviceVersion);
-        tupleBuilder.setContactUri(TEST_CONTACT)
-                .setServiceDescription(serviceDescription)
-                .setServiceCapabilities(servCapsBuilder.build());
+        // Create two presence tuples for testing.
+        RcsContactPresenceTuple mmtelTuple = createPresenceMmtelTuple();
+        RcsContactPresenceTuple ftTuple = createPresenceFtTuple();
 
         PresenceBuilder presenceBuilder = new PresenceBuilder(TEST_CONTACT,
                 RcsContactUceCapability.SOURCE_TYPE_CACHED,
                 RcsContactUceCapability.REQUEST_RESULT_FOUND);
-        presenceBuilder.addCapabilityTuple(tupleBuilder.build());
+        presenceBuilder.addCapabilityTuple(mmtelTuple);
+        presenceBuilder.addCapabilityTuples(Collections.singletonList(ftTuple));
 
-        RcsContactUceCapability testCapability = presenceBuilder.build();
+        final RcsContactUceCapability testCapability = presenceBuilder.build();
 
         // parcel and unparcel
         Parcel infoParceled = Parcel.obtain();
@@ -76,35 +84,134 @@
                 RcsContactUceCapability.CREATOR.createFromParcel(infoParceled);
         infoParceled.recycle();
 
-        boolean unparceledVolteCapable = false;
-        boolean unparceledVtCapable = false;
-        String unparceledTupleStatus = RcsContactPresenceTuple.TUPLE_BASIC_STATUS_CLOSED;
-        String unparceledDuplexMode = ServiceCapabilities.DUPLEX_MODE_RECEIVE_ONLY;
+        assertEquals(unparceledCapability.getContactUri(), testCapability.getContactUri());
+        assertEquals(unparceledCapability.getSourceType(), testCapability.getSourceType());
+        assertEquals(unparceledCapability.getRequestResult(), testCapability.getRequestResult());
+        assertEquals(unparceledCapability.getCapabilityMechanism(),
+                testCapability.getCapabilityMechanism());
 
-        RcsContactPresenceTuple unparceledTuple =
+        // Verify mmtel tuple
+        RcsContactPresenceTuple unparceledMMtelTuple =
                 unparceledCapability.getCapabilityTuple(RcsContactPresenceTuple.SERVICE_ID_MMTEL);
+        verifyUnparceledTuple(unparceledMMtelTuple, mmtelTuple);
 
-        if (unparceledTuple != null) {
-            unparceledTupleStatus = unparceledTuple.getStatus();
+        // Verify File transfer tuple
+        RcsContactPresenceTuple unparceledFtTuple =
+                unparceledCapability.getCapabilityTuple(RcsContactPresenceTuple.SERVICE_ID_FT);
+        verifyUnparceledTuple(unparceledFtTuple, ftTuple);
 
-            ServiceCapabilities serviceCaps = unparceledTuple.getServiceCapabilities();
-            if (serviceCaps != null) {
-                unparceledVolteCapable = serviceCaps.isAudioCapable();
-                unparceledVtCapable = serviceCaps.isVideoCapable();
-                List<String> duplexModes = serviceCaps.getSupportedDuplexModes();
-                if (duplexModes != null && !duplexModes.isEmpty()) {
-                    unparceledDuplexMode = duplexModes.get(0);
-                }
+        // Verify all the tuples from the API getCapabilityTuples
+        List<RcsContactPresenceTuple> unparceledTuples = unparceledCapability.getCapabilityTuples();
+        assertNotNull(unparceledTuples);
+        assertEquals(2, unparceledTuples.size());
+        for (RcsContactPresenceTuple unparcelTuple : unparceledTuples) {
+            String serverId = unparcelTuple.getServiceId();
+            if (RcsContactPresenceTuple.SERVICE_ID_MMTEL.equals(serverId)) {
+                verifyUnparceledTuple(unparcelTuple, mmtelTuple);
+            } else if (RcsContactPresenceTuple.SERVICE_ID_FT.equals(serverId)) {
+                verifyUnparceledTuple(unparcelTuple, ftTuple);
+            } else {
+                fail("Invalid service ID: " + serverId);
             }
         }
+    }
 
-        assertEquals(TEST_CONTACT, unparceledCapability.getContactUri());
-        assertTrue(unparceledVolteCapable);
-        assertTrue(unparceledVtCapable);
-        assertEquals(RcsContactPresenceTuple.TUPLE_BASIC_STATUS_OPEN, unparceledTupleStatus);
-        assertEquals(ServiceCapabilities.DUPLEX_MODE_FULL, unparceledDuplexMode);
-        assertEquals(RcsContactPresenceTuple.SERVICE_ID_MMTEL, unparceledTuple.getServiceId());
-        assertEquals(serviceVersion, unparceledTuple.getServiceVersion());
-        assertEquals(serviceDescription, unparceledTuple.getServiceDescription());
+    private RcsContactPresenceTuple createPresenceMmtelTuple() {
+        ServiceCapabilities.Builder servCapsBuilder = new ServiceCapabilities.Builder(true, true);
+        servCapsBuilder.addSupportedDuplexMode(ServiceCapabilities.DUPLEX_MODE_FULL);
+
+        RcsContactPresenceTuple.Builder tupleBuilder = new RcsContactPresenceTuple.Builder(
+                RcsContactPresenceTuple.TUPLE_BASIC_STATUS_OPEN,
+                RcsContactPresenceTuple.SERVICE_ID_MMTEL, "1.0");
+        tupleBuilder.setContactUri(TEST_CONTACT)
+                .setTime(Instant.now())
+                .setServiceDescription("service description for contact 1")
+                .setServiceCapabilities(servCapsBuilder.build());
+        return tupleBuilder.build();
+    }
+
+    private RcsContactPresenceTuple createPresenceFtTuple() {
+        ServiceCapabilities.Builder servCapsBuilder = new ServiceCapabilities.Builder(true, true);
+        servCapsBuilder.addSupportedDuplexMode(ServiceCapabilities.DUPLEX_MODE_FULL);
+
+        RcsContactPresenceTuple.Builder tupleBuilder = new RcsContactPresenceTuple.Builder(
+                RcsContactPresenceTuple.TUPLE_BASIC_STATUS_OPEN,
+                RcsContactPresenceTuple.SERVICE_ID_FT, "1.0");
+        tupleBuilder.setContactUri(TEST_CONTACT)
+                .setServiceDescription("service description for contact2")
+                .setServiceCapabilities(servCapsBuilder.build());
+        return tupleBuilder.build();
+    }
+
+    private void verifyUnparceledTuple(RcsContactPresenceTuple unparceledTuple,
+            RcsContactPresenceTuple expectedTuple) {
+        assertNotNull(unparceledTuple);
+        assertEquals(unparceledTuple.getStatus(), expectedTuple.getStatus());
+        assertEquals(unparceledTuple.getServiceId(), expectedTuple.getServiceId());
+        assertEquals(unparceledTuple.getServiceDescription(),
+                expectedTuple.getServiceDescription());
+        assertEquals(unparceledTuple.getServiceVersion(), expectedTuple.getServiceVersion());
+        assertEquals(unparceledTuple.getContactUri(), expectedTuple.getContactUri());
+        assertEquals(unparceledTuple.getTime(), expectedTuple.getTime());
+
+        ServiceCapabilities unparceledServiceCaps = unparceledTuple.getServiceCapabilities();
+        ServiceCapabilities expectedServiceCaps = unparceledTuple.getServiceCapabilities();
+        assertNotNull(unparceledServiceCaps);
+        assertEquals(unparceledServiceCaps.isAudioCapable(), expectedServiceCaps.isAudioCapable());
+        assertEquals(unparceledServiceCaps.isVideoCapable(), expectedServiceCaps.isVideoCapable());
+
+        List<String> unparceledDuplexModes = unparceledServiceCaps.getSupportedDuplexModes();
+        List<String> expectedDuplexModes = expectedServiceCaps.getSupportedDuplexModes();
+        assertEquals(unparceledDuplexModes.size(), expectedDuplexModes.size());
+        assertTrue(unparceledDuplexModes.containsAll(expectedDuplexModes));
+
+        List<String> unparceledUnsupportedModes = unparceledServiceCaps.getUnsupportedDuplexModes();
+        List<String> expectedUnsupportedModes = expectedServiceCaps.getUnsupportedDuplexModes();
+        assertEquals(unparceledUnsupportedModes.size(), expectedUnsupportedModes.size());
+        assertTrue(unparceledUnsupportedModes.containsAll(expectedUnsupportedModes));
+    }
+
+    @Test
+    public void testParcelUnparcelForOptions() {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+
+        final Set<String> featureTags = new HashSet<>();
+        featureTags.add(FEATURE_TAG_CHAT_IM);
+        featureTags.add(FEATURE_TAG_CHAT_SESSION);
+        featureTags.add(FEATURE_TAG_FILE_TRANSFER);
+
+        OptionsBuilder optionsBuilder = new OptionsBuilder(TEST_CONTACT,
+                RcsContactUceCapability.SOURCE_TYPE_CACHED);
+        optionsBuilder.addFeatureTags(featureTags);
+        optionsBuilder.addFeatureTag(FEATURE_TAG_POST_CALL);
+        optionsBuilder.setRequestResult(RcsContactUceCapability.REQUEST_RESULT_FOUND);
+
+        RcsContactUceCapability testCapability = optionsBuilder.build();
+
+        // parcel and unparcel
+        Parcel infoParceled = Parcel.obtain();
+        testCapability.writeToParcel(infoParceled, 0);
+        infoParceled.setDataPosition(0);
+        RcsContactUceCapability unparceledCapability =
+                RcsContactUceCapability.CREATOR.createFromParcel(infoParceled);
+        infoParceled.recycle();
+
+        assertEquals(unparceledCapability.getContactUri(), testCapability.getContactUri());
+        assertEquals(unparceledCapability.getSourceType(), testCapability.getSourceType());
+        assertEquals(unparceledCapability.getRequestResult(), testCapability.getRequestResult());
+        assertEquals(unparceledCapability.getCapabilityMechanism(),
+                testCapability.getCapabilityMechanism());
+
+        Set<String> expectedFeatureTags = new HashSet<>(featureTags);
+        expectedFeatureTags.add(FEATURE_TAG_POST_CALL);
+
+        Set<String> unparceledFeatureTags = unparceledCapability.getFeatureTags();
+        assertEquals(unparceledFeatureTags.size(), expectedFeatureTags.size());
+        Iterator<String> expectedFeatureTag = expectedFeatureTags.iterator();
+        while (expectedFeatureTag.hasNext()) {
+            assertTrue(unparceledFeatureTags.contains(expectedFeatureTag.next()));
+        }
     }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
index 6307b2f..372301a 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
@@ -18,6 +18,8 @@
 
 import static android.telephony.ims.RcsContactUceCapability.REQUEST_RESULT_FOUND;
 import static android.telephony.ims.RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND;
+import static android.telephony.ims.RcsContactUceCapability.SOURCE_TYPE_CACHED;
+import static android.telephony.ims.RcsContactUceCapability.SOURCE_TYPE_NETWORK;
 import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_FETCH_ERROR;
 import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_GENERIC_FAILURE;
 import static android.telephony.ims.stub.RcsCapabilityExchangeImplBase.COMMAND_CODE_INSUFFICIENT_MEMORY;
@@ -65,6 +67,7 @@
 import android.telephony.ims.stub.CapabilityExchangeEventListener;
 import android.telephony.ims.stub.CapabilityExchangeEventListener.OptionsRequestCallback;
 import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 
@@ -73,6 +76,7 @@
 
 import com.android.compatibility.common.util.BlockedNumberUtil;
 import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.internal.os.SomeArgs;
 
 import org.junit.After;
 import org.junit.AfterClass;
@@ -82,16 +86,19 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Random;
+import java.util.Set;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
 @RunWith(AndroidJUnit4.class)
@@ -232,6 +239,8 @@
         overrideCarrierConfig(null);
         // Remove all the test contacts from EAB database
         removeTestContactFromEab();
+
+        removeUceRequestDisallowedStatus();
     }
 
     @Test
@@ -314,7 +323,7 @@
         ImsManager imsManager = getContext().getSystemService(ImsManager.class);
         RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
         assertNotNull("UCE adapter should not be null!", uceAdapter);
-        ArrayList<Uri> numbers = new ArrayList<>(1);
+        Collection<Uri> numbers = new ArrayList<>(1);
         numbers.add(sTestNumberUri);
 
         // isUceSettingEnabled - read
@@ -368,14 +377,6 @@
             }
         }
 
-        // Trigger carrier config changed
-        PersistableBundle bundle = new PersistableBundle();
-        bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, true);
-        overrideCarrierConfig(bundle);
-
-        // Connect to the TestImsService
-        connectTestImsService();
-
         // getUcePublishState without permission
         try {
             uceAdapter.getUcePublishState();
@@ -387,15 +388,12 @@
         // getUcePublishState with permission
         try {
             ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(uceAdapter,
-                    (m) -> m.getUcePublishState(), ImsException.class,
+                    RcsUceAdapter::getUcePublishState, ImsException.class,
                     "android.permission.READ_PRIVILEGED_PHONE_STATE");
         } catch (SecurityException e) {
             fail("getUcePublishState should succeed with READ_PRIVILEGED_PHONE_STATE.");
         } catch (ImsException e) {
-            // unsupported is a valid fail cause.
-            if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
-                fail("getUcePublishState failed with code " + e.getCode());
-            }
+            // ImsExceptions are still valid because it means the permission check passed.
         }
 
         final RcsUceAdapter.OnPublishStateChangedListener publishStateListener = (state) -> { };
@@ -419,10 +417,7 @@
             fail("addOnPublishStateChangedListener should succeed with "
                     + "READ_PRIVILEGED_PHONE_STATE.");
         } catch (ImsException e) {
-            // unsupported is a valid fail cause.
-            if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
-                fail("addOnPublishStateChangedListener failed with code " + e.getCode());
-            }
+            // ImsExceptions are still valid because it means the permission check passed.
         }
 
         // removeOnPublishStateChangedListener without permission
@@ -463,9 +458,6 @@
             //expected
         }
 
-        // Lunch an activity to stay in the foreground.
-        lunchUceActivity();
-
         // requestCapabilities in the foreground
         try {
             ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(uceAdapter,
@@ -475,10 +467,7 @@
         } catch (SecurityException e) {
             fail("requestCapabilities should succeed with ACCESS_RCS_USER_CAPABILITY_EXCHANGE.");
         } catch (ImsException e) {
-            // unsupported is a valid fail cause.
-            if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
-                fail("requestCapabilities failed with code " + e.getCode());
-            }
+            // ImsExceptions are still valid because it means the permission check passed.
         }
 
         // requestAvailability in the foreground
@@ -490,14 +479,9 @@
         } catch (SecurityException e) {
             fail("requestAvailability should succeed with ACCESS_RCS_USER_CAPABILITY_EXCHANGE.");
         } catch (ImsException e) {
-            // unsupported is a valid fail cause.
-            if (e.getCode() != ImsException.CODE_ERROR_UNSUPPORTED_OPERATION) {
-                fail("requestAvailability failed with code " + e.getCode());
-            }
+            // ImsExceptions are still valid because it means the permission check passed.
         }
 
-        // Finish the activity
-        finishUceActivity();
         overrideCarrierConfig(null);
     }
 
@@ -506,12 +490,18 @@
         if (!ImsUtils.shouldTestImsService()) {
             return;
         }
+        // Start cap exchange disabled and enable later.
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL,
+                false);
+        overrideCarrierConfig(bundle);
+
         ImsManager imsManager = getContext().getSystemService(ImsManager.class);
         RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
         assertNotNull("UCE adapter should not be null!", uceAdapter);
 
         // Prepare the test contact and the callback
-        ArrayList<Uri> numbers = new ArrayList<>(1);
+        Collection<Uri> numbers = new ArrayList<>(1);
         numbers.add(sTestNumberUri);
 
         ArrayList<String> pidfXmlList = new ArrayList<>(1);
@@ -552,16 +542,12 @@
         }
 
         // Trigger carrier config changed
-        PersistableBundle bundle = new PersistableBundle();
         bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL, true);
         overrideCarrierConfig(bundle);
 
         // Connect to the TestImsService
         connectTestImsService();
 
-        // Stay in the foreground.
-        lunchUceActivity();
-
         TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
                 .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
 
@@ -598,7 +584,7 @@
                 true);
         overrideCarrierConfig(bundle);
 
-        // Preapre the network response is 200 OK and the capabilities update
+        // Prepare the network response is 200 OK and the capabilities update
         int networkRespCode = 200;
         String networkRespReason = "OK";
         capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
@@ -611,7 +597,9 @@
 
         // Verify that the contact capability is received and the onCompleted is called.
         RcsContactUceCapability capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, sTestNumberUri, REQUEST_RESULT_FOUND, true, true);
+        assertNotNull("Capabilities were not received for contact: " + sTestNumberUri, capability);
+        verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, true, true);
         waitForResult(completeQueue);
 
         errorQueue.clear();
@@ -623,10 +611,10 @@
 
         // Verify that the contact capability is received and the onCompleted is called.
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, sTestNumberUri, REQUEST_RESULT_FOUND, true, true);
+        verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, true, true);
         waitForResult(completeQueue);
 
-        finishUceActivity();
         overrideCarrierConfig(null);
     }
 
@@ -642,10 +630,7 @@
         // Connect to the TestImsService
         setupTestImsService(uceAdapter, true, true, false);
 
-        // Stay in the foreground
-        lunchUceActivity();
-
-        List<Uri> contacts = Collections.singletonList(sTestNumberUri);
+        Collection<Uri> contacts = Collections.singletonList(sTestNumberUri);
 
         TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
                 .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
@@ -708,14 +693,13 @@
                 assertEquals(expectedCallbackResult.intValue(), waitForIntResult(errorQueue));
                 assertEquals(0L, waitForLongResult(retryAfterQueue));
             } catch (Exception e) {
-                fail("requestCapabilities with command error failed: " + e);
+                fail("requestAvailability with command error failed: " + e);
             } finally {
                 errorQueue.clear();
                 retryAfterQueue.clear();
             }
         });
 
-        finishUceActivity();
         overrideCarrierConfig(null);
     }
 
@@ -731,7 +715,7 @@
         // Connect to the ImsService
         setupTestImsService(uceAdapter, true, true /* presence cap */, false /* options */);
 
-        ArrayList<Uri> numbers = new ArrayList<>(1);
+        Collection<Uri> numbers = new ArrayList<>(1);
         numbers.add(sTestNumberUri);
 
         BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
@@ -815,9 +799,6 @@
             }
         }, RcsUceAdapter.ERROR_SERVER_UNAVAILABLE);
 
-        // Stay in the foreground.
-        lunchUceActivity();
-
         TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
                 .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
 
@@ -923,12 +904,11 @@
             retryAfterQueue.clear();
         }
 
-        finishUceActivity();
         overrideCarrierConfig(null);
     }
 
     @Test
-    public void testRequestCapabilities() throws Exception {
+    public void testRequestCapabilitiesWithPresenceMechanism() throws Exception {
         if (!ImsUtils.shouldTestImsService()) {
             return;
         }
@@ -945,10 +925,8 @@
         TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
                 .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
 
-        ArrayList<Uri> numbers = new ArrayList<>(1);
-        numbers.add(sTestNumberUri);
-
-        BlockingQueue<Long> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
         BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
         BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
         RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
@@ -962,8 +940,8 @@
             }
             @Override
             public void onError(int errorCode, long retryAfterMilliseconds) {
-                errorQueue.offer(new Long(errorCode));
-                errorQueue.offer(retryAfterMilliseconds);
+                errorQueue.offer(errorCode);
+                errorRetryQueue.offer(retryAfterMilliseconds);
             }
         };
 
@@ -972,7 +950,7 @@
         final Uri contact2 = sTestContact2Uri;
         final Uri contact3 = sTestContact3Uri;
 
-        ArrayList<Uri> contacts = new ArrayList<>(3);
+        Collection<Uri> contacts = new ArrayList<>(3);
         contacts.add(contact1);
         contacts.add(contact2);
         contacts.add(contact3);
@@ -991,39 +969,73 @@
             cb.onTerminated("", 0L);
         });
 
-        // Stay in the foreground.
-        lunchUceActivity();
-
         requestCapabilities(uceAdapter, contacts, callback);
 
         // Verify that all the three contact's capabilities are received
         RcsContactUceCapability capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact1, REQUEST_RESULT_FOUND, true, true);
+        assertNotNull("Capabilities were not received for contact: " + contact1, capability);
+        verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+                true, true);
 
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact2, REQUEST_RESULT_FOUND, true, false);
+        assertNotNull("Capabilities were not received for contact: " + contact2, capability);
+        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+                true, false);
 
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact3, REQUEST_RESULT_FOUND, false, false);
+        assertNotNull("Capabilities were not received for contact: " + contact3, capability);
+        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+                false, false);
 
         // Verify the onCompleted is called
         waitForResult(completeQueue);
 
         errorQueue.clear();
+        errorRetryQueue.clear();
         completeQueue.clear();
         capabilityQueue.clear();
         removeTestContactFromEab();
 
         // Setup the callback that some of the contacts are terminated.
         capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            cb.onNetworkResponse(404, "NOT FOUND");
+        });
+
+        requestCapabilities(uceAdapter, contacts, callback);
+
+        // Verify the contacts are not found.
+        capability = waitForResult(capabilityQueue);
+        verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+                false, false);
+
+        capability = waitForResult(capabilityQueue);
+        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+                false, false);
+
+        capability = waitForResult(capabilityQueue);
+        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+                false, false);
+
+        int errorCode = waitForResult(errorQueue);
+        assertEquals(RcsUceAdapter.ERROR_NOT_FOUND, errorCode);
+
+        errorQueue.clear();
+        errorRetryQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+        removeTestContactFromEab();
+
+        // Setup the callback that some of the contacts are terminated.
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            List<Uri> uriList = new ArrayList(uris);
             cb.onNetworkResponse(networkRespCode, networkRespReason);
             // Notify capabilities updated for the first contact
             String pidfXml = pidfXmlList.get(0);
             cb.onNotifyCapabilitiesUpdate(Collections.singletonList(pidfXml));
 
             List<Pair<Uri, String>> terminatedResources = new ArrayList<>();
-            for (int i = 1; i < uris.size(); i++) {
-                Pair<Uri, String> pair = Pair.create(uris.get(i), "noresource");
+            for (int i = 1; i < uriList.size(); i++) {
+                Pair<Uri, String> pair = Pair.create(uriList.get(i), "noresource");
                 terminatedResources.add(pair);
             }
             cb.onResourceTerminated(terminatedResources);
@@ -1034,19 +1046,301 @@
 
         // Verify the first contact is found.
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact1, REQUEST_RESULT_FOUND, true, true);
+        verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+                true, true);
 
         // Verify the reset contacts are not found.
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact2, REQUEST_RESULT_NOT_FOUND, true, false);
+        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+                true, false);
 
         capability = waitForResult(capabilityQueue);
-        verifyCapabilityResult(capability, contact3, REQUEST_RESULT_NOT_FOUND, false, false);
+        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+                false, false);
 
         // Verify the onCompleted is called
         waitForResult(completeQueue);
 
-        finishUceActivity();
+        overrideCarrierConfig(null);
+    }
+
+    @Test
+    public void testRequestCapabilitiesFromCacheWithPresenceMechanism() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.offer(c));
+            }
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                errorQueue.offer(errorCode);
+                errorRetryQueue.offer(retryAfterMilliseconds);
+            }
+        };
+
+        // Prepare three contacts
+        final Uri contact1 = sTestNumberUri;
+        final Uri contact2 = sTestContact2Uri;
+        final Uri contact3 = sTestContact3Uri;
+
+        Collection<Uri> contacts = new ArrayList<>(3);
+        contacts.add(contact1);
+        contacts.add(contact2);
+        contacts.add(contact3);
+
+        ArrayList<String> pidfXmlList = new ArrayList<>(3);
+        pidfXmlList.add(getPidfXmlData(contact1, true, true));
+        pidfXmlList.add(getPidfXmlData(contact2, true, false));
+        pidfXmlList.add(getPidfXmlData(contact3, false, false));
+
+        // Setup the network response is 200 OK and notify capabilities update
+        int networkRespCode = 200;
+        String networkRespReason = "OK";
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            cb.onNetworkResponse(networkRespCode, networkRespReason);
+            cb.onNotifyCapabilitiesUpdate(pidfXmlList);
+            cb.onTerminated("", 0L);
+        });
+
+        requestCapabilities(uceAdapter, contacts, callback);
+
+        // Verify that all the three contact's capabilities are received
+        RcsContactUceCapability capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received for contact: " + contact1, capability);
+        verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+                true, true);
+
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received for contact: " + contact2, capability);
+        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+                true, false);
+
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received for contact: " + contact3, capability);
+        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+                false, false);
+
+        // Verify the onCompleted is called
+        waitForResult(completeQueue);
+
+        errorQueue.clear();
+        errorRetryQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+
+        // The request should not be called because the capabilities should be retrieved from cache.
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            fail("The request should not be called.");
+        });
+
+        requestCapabilities(uceAdapter, contacts, callback);
+
+        // Verify that all the three contact's capabilities are received
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received for contact: " + contact1, capability);
+        verifyCapabilityResult(capability, contact1, SOURCE_TYPE_CACHED, REQUEST_RESULT_FOUND,
+                true, true);
+
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received for contact: " + contact2, capability);
+        verifyCapabilityResult(capability, contact2, SOURCE_TYPE_CACHED, REQUEST_RESULT_FOUND,
+                true, false);
+
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received for contact: " + contact3, capability);
+        verifyCapabilityResult(capability, contact3, SOURCE_TYPE_CACHED, REQUEST_RESULT_FOUND,
+                false, false);
+
+        // Verify the onCompleted is called
+        waitForResult(completeQueue);
+
+        errorQueue.clear();
+        errorRetryQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+        removeTestContactFromEab();
+
+        overrideCarrierConfig(null);
+    }
+
+    @Test
+    public void testIndividualRequestCapabilities() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+
+        // Override the carrier config to not support group subscribe.
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, false);
+        overrideCarrierConfig(bundle);
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        BlockingQueue<Long> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        List<RcsContactUceCapability> capabilityQueue = new ArrayList<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.add(c));
+            }
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                errorQueue.offer(new Long(errorCode));
+                errorQueue.offer(retryAfterMilliseconds);
+            }
+        };
+
+        // Prepare three contacts
+        final Uri contact1 = sTestNumberUri;
+        final Uri contact2 = sTestContact2Uri;
+        final Uri contact3 = sTestContact3Uri;
+
+        Collection<Uri> contacts = new ArrayList<>(3);
+        contacts.add(contact1);
+        contacts.add(contact2);
+        contacts.add(contact3);
+
+        List<String> pidfXml1 = Collections.singletonList(getPidfXmlData(contact1, true, true));
+        List<String> pidfXml2 = Collections.singletonList(getPidfXmlData(contact2, true, false));
+        List<String> pidfXml3 = Collections.singletonList(getPidfXmlData(contact3, false, false));
+
+        // Setup the network response is 200 OK and notify capabilities update
+        int networkRespCode = 200;
+        String networkRespReason = "OK";
+        AtomicInteger receiveRequestCount = new AtomicInteger(0);
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            receiveRequestCount.incrementAndGet();
+            cb.onNetworkResponse(networkRespCode, networkRespReason);
+            assertEquals(1, uris.size());
+            String uriPart = uris.iterator().next().getSchemeSpecificPart();
+            if (contact1.getSchemeSpecificPart().equalsIgnoreCase(uriPart)) {
+                cb.onNotifyCapabilitiesUpdate(pidfXml1);
+            } else if (contact2.getSchemeSpecificPart().equalsIgnoreCase(uriPart)) {
+                cb.onNotifyCapabilitiesUpdate(pidfXml2);
+            } else if (contact3.getSchemeSpecificPart().equalsIgnoreCase(uriPart)) {
+                cb.onNotifyCapabilitiesUpdate(pidfXml3);
+            }
+            cb.onTerminated("", 0L);
+        });
+
+        requestCapabilities(uceAdapter, contacts, callback);
+
+        // Verify the onCompleted is called
+        waitForResult(completeQueue);
+
+        // Verify the capability request has been split to individual requests.
+        assertEquals(contacts.size(), receiveRequestCount.get());
+
+        // verify the capabilities result
+        assertEquals(contacts.size(), capabilityQueue.size());
+        for (RcsContactUceCapability capability : capabilityQueue) {
+            Uri contact = capability.getContactUri();
+            if (contact1.equals(contact)) {
+                verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK,
+                        REQUEST_RESULT_FOUND, true, true);
+            } else if (contact2.equals(contact)) {
+                verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK,
+                        REQUEST_RESULT_FOUND, true, false);
+            } else if (contact3.equals(contact)) {
+                verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK,
+                        REQUEST_RESULT_FOUND, false, false);
+            } else {
+                fail("The contact of the capabilities result is invalid.");
+            }
+        }
+
+        errorQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+        removeTestContactFromEab();
+        receiveRequestCount.set(0);
+
+        // Setup the callback that some of the contacts are terminated.
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            receiveRequestCount.incrementAndGet();
+            cb.onNetworkResponse(networkRespCode, networkRespReason);
+            assertEquals(1, uris.size());
+            String uriPart = uris.iterator().next().getSchemeSpecificPart();
+            if (contact1.getSchemeSpecificPart().equalsIgnoreCase(uriPart)) {
+                cb.onNotifyCapabilitiesUpdate(pidfXml1);
+            } else {
+                // Notify resources terminated for the reset contacts
+                List<Uri> uriList = new ArrayList(uris);
+                List<Pair<Uri, String>> terminatedResources = new ArrayList<>();
+                for (int i = 0; i < uriList.size(); i++) {
+                    Pair<Uri, String> pair = Pair.create(uriList.get(i), "noresource");
+                    terminatedResources.add(pair);
+                }
+                cb.onResourceTerminated(terminatedResources);
+            }
+            cb.onTerminated("", 0L);
+        });
+
+        requestCapabilities(uceAdapter, contacts, callback);
+
+        // Verify the onCompleted is called
+        waitForResult(completeQueue);
+
+        // Verify the capability request has been split to individual requests.
+        assertEquals(contacts.size(), receiveRequestCount.get());
+
+        // verify the capabilities result
+        assertEquals(contacts.size(), capabilityQueue.size());
+        for (RcsContactUceCapability capability : capabilityQueue) {
+            Uri contact = capability.getContactUri();
+            if (contact1.equals(contact)) {
+                verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK,
+                        REQUEST_RESULT_FOUND, true, true);
+            } else if (contact2.equals(contact)) {
+                verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK,
+                        REQUEST_RESULT_NOT_FOUND, true, false);
+            } else if (contact3.equals(contact)) {
+                verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK,
+                        REQUEST_RESULT_NOT_FOUND, false,
+                        false);
+            } else {
+                fail("The contact of the capabilities result is invalid.");
+            }
+        }
         overrideCarrierConfig(null);
     }
 
@@ -1069,7 +1363,7 @@
                 .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
 
         // The test contact
-        ArrayList<Uri> contacts = new ArrayList<>(3);
+        Collection<Uri> contacts = new ArrayList<>(3);
         contacts.add(sTestNumberUri);
 
         // The result callback
@@ -1105,29 +1399,19 @@
         });
 
         // Request capabilities by calling the API requestCapabilities.
-        try {
-            ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
-                    uceAdapter,
-                    adapter -> adapter.requestCapabilities(contacts, Runnable::run, callback),
-                    ImsException.class,
-                    "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE");
-        } catch (SecurityException e) {
-            fail("requestCapabilities should succeed with ACCESS_RCS_USER_CAPABILITY_EXCHANGE.");
-        } catch (ImsException e) {
-            fail("requestCapabilities failed " + e);
-        }
+        requestCapabilities(uceAdapter, contacts, callback);
 
         // Verify the callback "onCapabilitiesReceived" is called.
         RcsContactUceCapability capability = waitForResult(capabilityQueue);
-        // Verify the callback "onComplete" is called.
-        waitForResult(completeQueue);
         assertNotNull("RcsContactUceCapability should not be null", capability);
+        // Verify the callback "onComplete" is called.
+        assertNotNull(waitForResult(completeQueue));
         assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capability.getSourceType());
         assertEquals(sTestNumberUri, capability.getContactUri());
         assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
         assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
                 capability.getCapabilityMechanism());
-        List<String> resultFeatureTags = capability.getOptionsFeatureTags();
+        Set<String> resultFeatureTags = capability.getFeatureTags();
         assertEquals(featureTags.size(), resultFeatureTags.size());
         for (String featureTag : featureTags) {
             if (!resultFeatureTags.contains(featureTag)) {
@@ -1141,18 +1425,7 @@
         removeTestContactFromEab();
 
         // Request capabilities by calling the API requestAvailability.
-        try {
-            ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
-                    uceAdapter,
-                    adapter -> adapter.requestAvailability(sTestContact2Uri,
-                            Runnable::run, callback),
-                    ImsException.class,
-                    "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE");
-        } catch (SecurityException e) {
-            fail("requestCapabilities should succeed with ACCESS_RCS_USER_CAPABILITY_EXCHANGE.");
-        } catch (ImsException e) {
-            fail("requestCapabilities failed " + e);
-        }
+        requestAvailability(uceAdapter, sTestContact2Uri, callback);
 
         // Verify the callback "onCapabilitiesReceived" is called.
         capability = waitForResult(capabilityQueue);
@@ -1164,7 +1437,7 @@
         assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
         assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
                 capability.getCapabilityMechanism());
-        resultFeatureTags = capability.getOptionsFeatureTags();
+        resultFeatureTags = capability.getFeatureTags();
         assertEquals(featureTags.size(), resultFeatureTags.size());
         for (String featureTag : featureTags) {
             if (!resultFeatureTags.contains(featureTag)) {
@@ -1185,25 +1458,261 @@
         });
 
         // Request capabilities by calling the API requestCapabilities.
-        try {
-            ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
-                    uceAdapter,
-                    adapter -> adapter.requestCapabilities(contacts, Runnable::run, callback),
-                    ImsException.class,
-                    "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE");
-        } catch (SecurityException e) {
-            fail("requestCapabilities should succeed with ACCESS_RCS_USER_CAPABILITY_EXCHANGE.");
-        } catch (ImsException e) {
-            fail("requestCapabilities failed " + e);
-        }
+        requestCapabilities(uceAdapter, contacts, callback);
 
         // Verify the callback "onError" is called.
         assertEquals(RcsUceAdapter.ERROR_GENERIC_FAILURE, waitForLongResult(errorQueue));
 
-        // The callback "onCapabilitiesReceived" should not be called.
-        if (capabilityQueue.poll() != null) {
-            fail("onCapabilitiesReceived should not be called.");
+        // The callback "onCapabilitiesReceived" should be called with NOT FOUND
+        capability = waitForResult(capabilityQueue);
+        assertEquals(RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND,
+                capability.getRequestResult());
+
+        overrideCarrierConfig(null);
+    }
+
+    @Test
+    public void testRequestCapabilitiesFromCacheWithOptionsMechanism() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
         }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, false, true /* OPTIONS enabled */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        // The result callback
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> retryAfterQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.offer(c));
+            }
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                errorQueue.offer(errorCode);
+                retryAfterQueue.offer(retryAfterMilliseconds);
+            }
+        };
+
+        // Set the result of the network response is 200 OK.
+        final List<String> featureTags = new ArrayList<>();
+        featureTags.add(FEATURE_TAG_CHAT);
+        featureTags.add(FEATURE_TAG_FILE_TRANSFER);
+        featureTags.add(FEATURE_TAG_MMTEL_AUDIO_CALL);
+        featureTags.add(FEATURE_TAG_MMTEL_VIDEO_CALL);
+        capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
+            int sipCode = 200;
+            String reason = "OK";
+            optionsCallback.onNetworkResponse(sipCode, reason, featureTags);
+        });
+
+        // Request capabilities with the for the first time.
+        requestCapabilities(uceAdapter, Collections.singletonList(sTestNumberUri), callback);
+
+        // Verify the callback "onCapabilitiesReceived" is called.
+        RcsContactUceCapability capability = waitForResult(capabilityQueue);
+        assertNotNull("RcsContactUceCapability should not be null", capability);
+        // Verify the callback "onComplete" is called.
+        assertNotNull(waitForResult(completeQueue));
+        assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capability.getSourceType());
+        assertEquals(sTestNumberUri, capability.getContactUri());
+        assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
+        assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
+                capability.getCapabilityMechanism());
+        Set<String> resultFeatureTags = capability.getFeatureTags();
+        assertEquals(featureTags.size(), resultFeatureTags.size());
+        for (String featureTag : featureTags) {
+            if (!resultFeatureTags.contains(featureTag)) {
+                fail("Cannot find feature tag in the result");
+            }
+        }
+        errorQueue.clear();
+        retryAfterQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+
+        // Request capabilities for the second time.
+        requestAvailability(uceAdapter, sTestContact2Uri, callback);
+
+        // Verify the callback "onCapabilitiesReceived" is called.
+        capability = waitForResult(capabilityQueue);
+        // Verify the callback "onComplete" is called.
+        waitForResult(completeQueue);
+        assertNotNull("RcsContactUceCapability should not be null", capability);
+        assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capability.getSourceType());
+        assertEquals(sTestContact2Uri, capability.getContactUri());
+        assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
+        assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
+                capability.getCapabilityMechanism());
+        resultFeatureTags = capability.getFeatureTags();
+        assertEquals(featureTags.size(), resultFeatureTags.size());
+        for (String featureTag : featureTags) {
+            if (!resultFeatureTags.contains(featureTag)) {
+                fail("Cannot find feature tag in the result");
+            }
+        }
+        errorQueue.clear();
+        retryAfterQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+
+        // Set the OPTIONS result is failed because the capabilities should be retrieved from cache.
+        capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
+            fail("The Options request should not be called.");
+        });
+
+        // The contact to requeste the capabilities is the same as the first time.
+        requestCapabilities(uceAdapter, Collections.singletonList(sTestNumberUri), callback);
+
+        // Verify the callback "onCapabilitiesReceived" is called.
+        capability = waitForResult(capabilityQueue);
+        assertNotNull("RcsContactUceCapability should not be null", capability);
+        // Verify the callback "onComplete" is called.
+        assertNotNull(waitForResult(completeQueue));
+        // Verify the capabilities are retrieved from the cache.
+        assertEquals(RcsContactUceCapability.SOURCE_TYPE_CACHED, capability.getSourceType());
+        assertEquals(sTestNumberUri, capability.getContactUri());
+        assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND, capability.getRequestResult());
+        assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
+                capability.getCapabilityMechanism());
+        resultFeatureTags = capability.getFeatureTags();
+        assertEquals(featureTags.size(), resultFeatureTags.size());
+        for (String featureTag : featureTags) {
+            if (!resultFeatureTags.contains(featureTag)) {
+                fail("Cannot find feature tag in the result");
+            }
+        }
+        errorQueue.clear();
+        retryAfterQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+        removeTestContactFromEab();
+
+        overrideCarrierConfig(null);
+    }
+
+    @Test
+    public void testIndividualRequestCapabilitiesWithOptionsMechanism() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, false, true /* OPTIONS enabled */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        // Prepare three test contacts
+        Collection<Uri> contacts = new ArrayList<>(3);
+        contacts.add(sTestNumberUri);
+        contacts.add(sTestContact2Uri);
+        contacts.add(sTestContact3Uri);
+
+        // The result callback
+        BlockingQueue<Long> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        List<RcsContactUceCapability> capabilityQueue = new ArrayList<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.add(c));
+            }
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                errorQueue.offer(new Long(errorCode));
+                errorQueue.offer(retryAfterMilliseconds);
+            }
+        };
+
+        // Set the result of the network response is 200 OK.
+        final List<String> featureTags = new ArrayList<>();
+        featureTags.add(FEATURE_TAG_CHAT);
+        featureTags.add(FEATURE_TAG_FILE_TRANSFER);
+        featureTags.add(FEATURE_TAG_MMTEL_AUDIO_CALL);
+        featureTags.add(FEATURE_TAG_MMTEL_VIDEO_CALL);
+
+        AtomicInteger receiveRequestCount = new AtomicInteger(0);
+
+        capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
+            receiveRequestCount.incrementAndGet();
+            int sipCode = 200;
+            String reason = "OK";
+            optionsCallback.onNetworkResponse(sipCode, reason, featureTags);
+        });
+
+        // Request capabilities by calling the API requestCapabilities.
+        requestCapabilities(uceAdapter, contacts, callback);
+
+        // Verify the callback "onComplete" is called.
+        assertNotNull(waitForResult(completeQueue));
+
+        // Verify the capability request has been split to individual requests.
+        assertEquals(contacts.size(), receiveRequestCount.get());
+
+        // Verify the result
+        verifyOptionsCapabilityResult(capabilityQueue, contacts,
+                RcsContactUceCapability.SOURCE_TYPE_NETWORK,
+                RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
+                RcsContactUceCapability.REQUEST_RESULT_FOUND, featureTags);
+
+        errorQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+        receiveRequestCount.set(0);
+        removeTestContactFromEab();
+
+        // Set the OPTIONS result is failed.
+        capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
+            receiveRequestCount.incrementAndGet();
+            int sipCode = 400;
+            String reason = "Bad Request";
+            optionsCallback.onNetworkResponse(sipCode, reason, Collections.EMPTY_LIST);
+        });
+
+        // Request capabilities by calling the API requestCapabilities.
+        requestCapabilities(uceAdapter, contacts, callback);
+
+        // Verify the callback "onError" is called.
+        assertEquals(RcsUceAdapter.ERROR_GENERIC_FAILURE, waitForLongResult(errorQueue));
+
+        // Verify the result
+        verifyOptionsCapabilityResult(capabilityQueue, contacts,
+                RcsContactUceCapability.SOURCE_TYPE_NETWORK,
+                RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
+                RcsContactUceCapability.REQUEST_RESULT_NOT_FOUND, Collections.EMPTY_LIST);
+
+        errorQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+        receiveRequestCount.set(0);
+        removeTestContactFromEab();
 
         overrideCarrierConfig(null);
     }
@@ -1227,7 +1736,7 @@
                 sServiceConnector.getCarrierService().getRcsFeature().getEventListener();
 
         final Uri contact = sTestContact2Uri;
-        List<String> remoteCapabilities = new ArrayList<>();
+        Set<String> remoteCapabilities = new ArraySet<>();
         remoteCapabilities.add(FEATURE_TAG_CHAT);
         remoteCapabilities.add(FEATURE_TAG_FILE_TRANSFER);
         remoteCapabilities.add(FEATURE_TAG_MMTEL_AUDIO_CALL);
@@ -1251,7 +1760,7 @@
         // Verify receive the result
         Pair<RcsContactUceCapability, Boolean> capability = waitForResult(respToCapRequestQueue);
         assertNotNull("RcsContactUceCapability should not be null", capability);
-        assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK, capability.first.getSourceType());
+        assertEquals(RcsContactUceCapability.SOURCE_TYPE_CACHED, capability.first.getSourceType());
         assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND,
                 capability.first.getRequestResult());
         assertEquals(RcsContactUceCapability.CAPABILITY_MECHANISM_OPTIONS,
@@ -1282,7 +1791,7 @@
                 sServiceConnector.getCarrierService().getRcsFeature().getEventListener();
 
         final Uri contact = sTestNumberUri;
-        List<String> remoteCapabilities = new ArrayList<>();
+        Set<String> remoteCapabilities = new ArraySet<>();
         remoteCapabilities.add(FEATURE_TAG_CHAT);
         remoteCapabilities.add(FEATURE_TAG_FILE_TRANSFER);
         remoteCapabilities.add(FEATURE_TAG_MMTEL_AUDIO_CALL);
@@ -1313,7 +1822,7 @@
             Pair<RcsContactUceCapability, Boolean> capability =
                     waitForResult(respToCapRequestQueue);
             assertNotNull("RcsContactUceCapability should not be null", capability);
-            assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK,
+            assertEquals(RcsContactUceCapability.SOURCE_TYPE_CACHED,
                     capability.first.getSourceType());
             assertEquals(RcsContactUceCapability.REQUEST_RESULT_FOUND,
                     capability.first.getRequestResult());
@@ -1330,6 +1839,501 @@
         overrideCarrierConfig(null);
     }
 
+    @Test
+    public void testForbidCapabilitiesRequest() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+
+        // Override the carrier config of SIP 489 request forbidden.
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.Ims.KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL, true);
+        overrideCarrierConfig(bundle);
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> retryAfterQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        List<RcsContactUceCapability> capabilityQueue = new ArrayList<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.add(c));
+            }
+
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+
+            @Override
+            public void onError(int errorCode, long retryAfterMillis) {
+                errorQueue.offer(errorCode);
+                retryAfterQueue.offer(retryAfterMillis);
+            }
+        };
+
+        // Prepare two contacts
+        final Uri contact1 = sTestNumberUri;
+        final Uri contact2 = sTestContact2Uri;
+        Collection<Uri> contacts = new ArrayList<>(2);
+        contacts.add(contact1);
+        contacts.add(contact2);
+
+        // Prepare the network response.
+        final int sipCodeBadEvent = 489;
+        final int sipCodeForbidden = 403;
+        AtomicInteger subscribeRequestCount = new AtomicInteger(0);
+
+        // Prepare a map to define the sip code and its associated result.
+        Map<Integer, Integer> networkSipCodeMap = new HashMap<>();
+        networkSipCodeMap.put(sipCodeBadEvent, RcsUceAdapter.ERROR_FORBIDDEN);
+        networkSipCodeMap.put(sipCodeForbidden, RcsUceAdapter.ERROR_FORBIDDEN);
+
+        // Verify each command error code and the expected callback result
+        networkSipCodeMap.forEach((sipCode, expectedCallbackResult) -> {
+            // Setup the capabilities request response with the given sip code.
+            capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+                subscribeRequestCount.incrementAndGet();
+                cb.onNetworkResponse(sipCode, "");
+            });
+
+            try {
+                // Request contact uce capabilities
+                requestCapabilities(uceAdapter, contacts, callback);
+
+                // Verify that the callback "onError" is called with the error code FORBIDDEN
+                assertEquals(RcsUceAdapter.ERROR_FORBIDDEN, waitForIntResult(errorQueue));
+                // Verify the retryAfter value
+                long retryAfterMillis = waitForLongResult(retryAfterQueue);
+                if (sipCode == sipCodeForbidden) {
+                    assertEquals(0L, retryAfterMillis);
+                } else if (sipCode == sipCodeBadEvent) {
+                    assertTrue(retryAfterMillis > 0L);
+                }
+
+                // Verify the ImsService received the capabilities request.
+                assertEquals(1, subscribeRequestCount.get());
+            } catch (Exception e) {
+                fail("testForbiddenResponseToCapabilitiesRequest with command error failed: " + e);
+            } finally {
+                errorQueue.clear();
+                retryAfterQueue.clear();
+                subscribeRequestCount.set(0);
+            }
+
+            // Prepare the network response with sip code 200 OK
+            capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+                subscribeRequestCount.incrementAndGet();
+                cb.onNetworkResponse(200, "OK");
+            });
+
+            try {
+                // Request contact uce capabilities again.
+                requestCapabilities(uceAdapter, contacts, callback);
+
+                // Verify that the callback "onError" is called with the error code FORBIDDEN
+                assertEquals(RcsUceAdapter.ERROR_FORBIDDEN, waitForIntResult(errorQueue));
+                // Verify the retryAfter value
+                long retryAfterMillis = waitForLongResult(retryAfterQueue);
+                if (sipCode == sipCodeForbidden) {
+                    assertEquals(0L, retryAfterMillis);
+                } else if (sipCode == sipCodeBadEvent) {
+                    assertTrue(retryAfterMillis > 0L);
+                }
+
+                // Verify that the capabilities won't be send to the ImsService because the
+                // uce request is forbidden.
+                assertEquals(0, subscribeRequestCount.get());
+            } catch (Exception e) {
+                fail("testForbiddenResponseToCapabilitiesRequest with command error failed: " + e);
+            } finally {
+                errorQueue.clear();
+                retryAfterQueue.clear();
+                subscribeRequestCount.set(0);
+            }
+
+            // Reset the device status
+            removeUceRequestDisallowedStatus();
+        });
+
+        overrideCarrierConfig(null);
+    }
+
+    @Test
+    public void testTerminatedCallbackWithCapabilitiesRequest() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
+
+        ArrayList<String> pidfXmlList = new ArrayList<>(1);
+        pidfXmlList.add(getPidfXmlData(sTestNumberUri, true, true));
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.offer(c));
+            }
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMillis) {
+                errorQueue.offer(errorCode);
+                errorRetryQueue.offer(retryAfterMillis);
+            }
+        };
+
+        // Prepare the test contact and the callback
+        Collection<Uri> numbers = new ArrayList<>(1);
+        numbers.add(sTestNumberUri);
+
+        // Prepare the network response is 200 OK and the capabilities update
+        int networkRespCode = 200;
+        String networkRespReason = "OK";
+
+        Map<SomeArgs, SomeArgs> terminatedMap = new HashMap<>();
+        SomeArgs deactivatedArgs = SomeArgs.obtain();
+        deactivatedArgs.arg1 = "deactivated";
+        deactivatedArgs.arg2 = Long.valueOf(3000L);
+        SomeArgs deactivatedExpectedArgs = SomeArgs.obtain();
+        deactivatedExpectedArgs.argi1 = RcsUceAdapter.ERROR_GENERIC_FAILURE;
+        deactivatedExpectedArgs.arg1 = Long.valueOf(3000L);
+        terminatedMap.put(deactivatedArgs, deactivatedExpectedArgs);
+
+        SomeArgs probationArgs = SomeArgs.obtain();
+        probationArgs.arg1 = "probation";
+        probationArgs.arg2 = Long.valueOf(4000L);
+        SomeArgs probationExpectedArgs = SomeArgs.obtain();
+        probationExpectedArgs.argi1 = RcsUceAdapter.ERROR_GENERIC_FAILURE;
+        probationExpectedArgs.arg1 = Long.valueOf(4000L);
+        terminatedMap.put(probationArgs, probationExpectedArgs);
+
+        SomeArgs rejectedArgs = SomeArgs.obtain();
+        rejectedArgs.arg1 = "rejected";
+        rejectedArgs.arg2 = Long.valueOf(5000L);
+        SomeArgs rejectedExpectedArgs = SomeArgs.obtain();
+        rejectedExpectedArgs.argi1 = RcsUceAdapter.ERROR_NOT_AUTHORIZED;
+        rejectedExpectedArgs.arg1 = Long.valueOf(0L);
+        terminatedMap.put(rejectedArgs, rejectedExpectedArgs);
+
+        SomeArgs timeoutArgs = SomeArgs.obtain();
+        timeoutArgs.arg1 = "timeout";
+        timeoutArgs.arg2 = Long.valueOf(6000L);
+        SomeArgs timeoutExpectedArgs = SomeArgs.obtain();
+        timeoutExpectedArgs.argi1 = RcsUceAdapter.ERROR_REQUEST_TIMEOUT;
+        timeoutExpectedArgs.arg1 = Long.valueOf(6000L);
+        terminatedMap.put(timeoutArgs, timeoutExpectedArgs);
+
+        SomeArgs giveupArgs = SomeArgs.obtain();
+        giveupArgs.arg1 = "giveup";
+        giveupArgs.arg2 = Long.valueOf(7000L);
+        SomeArgs giveupExpectedArgs = SomeArgs.obtain();
+        giveupExpectedArgs.argi1 = RcsUceAdapter.ERROR_NOT_AUTHORIZED;
+        giveupExpectedArgs.arg1 = Long.valueOf(7000L);
+        terminatedMap.put(giveupArgs, giveupExpectedArgs);
+
+        SomeArgs noresourceArgs = SomeArgs.obtain();
+        noresourceArgs.arg1 = "noresource";
+        noresourceArgs.arg2 = Long.valueOf(8000L);
+        SomeArgs noresourceExpectedArgs = SomeArgs.obtain();
+        noresourceExpectedArgs.argi1 = RcsUceAdapter.ERROR_NOT_FOUND;
+        noresourceExpectedArgs.arg1 = Long.valueOf(0L);
+        terminatedMap.put(giveupArgs, giveupExpectedArgs);
+
+        SomeArgs emptyReasonArgs = SomeArgs.obtain();
+        emptyReasonArgs.arg1 = "";
+        emptyReasonArgs.arg2 = Long.valueOf(9000L);
+        SomeArgs emptyReasonExpectedArgs = SomeArgs.obtain();
+        emptyReasonExpectedArgs.argi1 = RcsUceAdapter.ERROR_GENERIC_FAILURE;
+        emptyReasonExpectedArgs.arg1 = Long.valueOf(9000L);
+        terminatedMap.put(emptyReasonArgs, emptyReasonExpectedArgs);
+
+        // Verify each subscription terminated and the expected result
+        terminatedMap.forEach((reason, expectedResult) -> {
+            String terminatedReason = (String) reason.arg1;
+            Long terminatedRetryAfterMillis = (Long) reason.arg2;
+            capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+                cb.onNetworkResponse(networkRespCode, networkRespReason);
+                cb.onNotifyCapabilitiesUpdate(pidfXmlList);
+                cb.onTerminated(terminatedReason, terminatedRetryAfterMillis);
+            });
+
+            requestCapabilities(uceAdapter, numbers, callback);
+
+            try {
+                // Verify that the contact capability is received and the onCompleted is called.
+                RcsContactUceCapability capability = waitForResult(capabilityQueue);
+                assertNotNull("Capabilities were not received for contact: " + sTestNumberUri,
+                        capability);
+                verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+                        REQUEST_RESULT_FOUND, true, true);
+
+                int expectedErrorCode = expectedResult.argi1;
+                Long expectedRetryAfter = (Long) expectedResult.arg1;
+                assertEquals(expectedErrorCode, waitForIntResult(errorQueue));
+                assertEquals(expectedRetryAfter.longValue(), (waitForLongResult(errorRetryQueue)));
+            } catch (Exception e) {
+                fail("Unexpected exception " + e);
+            }
+
+            reason.recycle();
+            expectedResult.recycle();
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            completeQueue.clear();
+            capabilityQueue.clear();
+            removeTestContactFromEab();
+        });
+
+        /*
+         * Verify the subscribe request is successful when: A) The terminated is timeout and
+         * B) The retryAfter is 0L and C) All the capabilities have been received.
+         */
+        String terminatedReason = "timeout";
+        long terminatedRetryAfterMillis = 0L;
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            cb.onNetworkResponse(networkRespCode, networkRespReason);
+            cb.onNotifyCapabilitiesUpdate(pidfXmlList);
+            cb.onTerminated(terminatedReason, terminatedRetryAfterMillis);
+        });
+
+        requestCapabilities(uceAdapter, numbers, callback);
+
+        // Verify that the contact capability is received and the onCompleted is called.
+        RcsContactUceCapability capability = waitForResult(capabilityQueue);
+        assertNotNull("Capabilities were not received for contact: " + sTestNumberUri, capability);
+        verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+                REQUEST_RESULT_FOUND, true, true);
+        assertTrue(waitForResult(completeQueue));
+
+        errorQueue.clear();
+        errorRetryQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+        removeTestContactFromEab();
+
+        /*
+         * Set the subscribe request is failed because NOT all of the capabilities have been
+         * received.
+         */
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            cb.onNetworkResponse(networkRespCode, networkRespReason);
+            cb.onTerminated(terminatedReason, terminatedRetryAfterMillis);
+        });
+
+        requestCapabilities(uceAdapter, numbers, callback);
+
+        /*
+         * Verify the request is failed because NOT all of the capabilities have been received.
+         */
+        assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
+        assertEquals(0L, (waitForLongResult(errorRetryQueue)));
+
+        errorQueue.clear();
+        errorRetryQueue.clear();
+        completeQueue.clear();
+        capabilityQueue.clear();
+        removeTestContactFromEab();
+        overrideCarrierConfig(null);
+    }
+
+    @Test
+    public void testTimeoutToRequestCapabilitiesWithPresenceMechanism() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.offer(c));
+            }
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                errorQueue.offer(errorCode);
+                errorRetryQueue.offer(retryAfterMilliseconds);
+            }
+        };
+
+        // Prepare three contacts
+        final Uri contact1 = sTestNumberUri;
+        final Uri contact2 = sTestContact2Uri;
+        final Uri contact3 = sTestContact3Uri;
+
+        Collection<Uri> contacts = new ArrayList<>(3);
+        contacts.add(contact1);
+        contacts.add(contact2);
+        contacts.add(contact3);
+
+        // Setup the ImsService doesn't trigger any callbacks.
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            // It won't trigger any callbacks.
+        });
+
+        try {
+            setCapabilitiesRequestTimeout(3000L);
+
+            requestCapabilities(uceAdapter, contacts, callback);
+
+            // Verify that the clients receive the TIMEOUT error code.
+            assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
+            assertEquals(0L, (waitForLongResult(errorRetryQueue)));
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            completeQueue.clear();
+            capabilityQueue.clear();
+            removeTestContactFromEab();
+            setCapabilitiesRequestTimeout(-1L);
+        }
+
+        // Setup the ImsService only trigger the network response callback. However it doesn't
+        // trigger the onTerminated callback
+        int networkRespCode = 200;
+        String networkRespReason = "OK";
+        capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+            cb.onNetworkResponse(networkRespCode, networkRespReason);
+        });
+
+        try {
+            setCapabilitiesRequestTimeout(3000L);
+
+            requestCapabilities(uceAdapter, contacts, callback);
+
+            // Verify that the clients receive the TIMEOUT error code.
+            assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
+            assertEquals(0L, (waitForLongResult(errorRetryQueue)));
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            completeQueue.clear();
+            capabilityQueue.clear();
+            removeTestContactFromEab();
+            setCapabilitiesRequestTimeout(-1L);
+        }
+
+        overrideCarrierConfig(null);
+    }
+
+
+    @Test
+    public void testTimeoutToRequestCapabilitiesWithOptionsMechanism() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+        RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+        assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+        // Remove the test contact capabilities
+        removeTestContactFromEab();
+
+        // Connect to the ImsService
+        setupTestImsService(uceAdapter, true, false, true /* OPTIONS enabled */);
+
+        TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+                .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+        // The test contact
+        Collection<Uri> contacts = new ArrayList<>(3);
+        contacts.add(sTestNumberUri);
+
+        // The result callback
+        BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+        BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
+        RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+            @Override
+            public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+                capabilities.forEach(c -> capabilityQueue.offer(c));
+            }
+            @Override
+            public void onComplete() {
+                completeQueue.offer(true);
+            }
+            @Override
+            public void onError(int errorCode, long retryAfterMilliseconds) {
+                errorQueue.offer(errorCode);
+                errorRetryQueue.offer(retryAfterMilliseconds);
+            }
+        };
+
+        // Setup the ImsService doesn't trigger any callbacks.
+        capabilityExchangeImpl.setOptionsOperation((contact, myCapabilities, optionsCallback) -> {
+            // It won't trigger any callbacks.
+        });
+
+        try {
+            setCapabilitiesRequestTimeout(3000L);
+
+            requestCapabilities(uceAdapter, contacts, callback);
+
+            // Verify that the clients receive the TIMEOUT error code.
+            assertEquals(RcsUceAdapter.ERROR_REQUEST_TIMEOUT, waitForIntResult(errorQueue));
+            assertEquals(0L, (waitForLongResult(errorRetryQueue)));
+        } finally {
+            errorQueue.clear();
+            errorRetryQueue.clear();
+            completeQueue.clear();
+            capabilityQueue.clear();
+            removeTestContactFromEab();
+            setCapabilitiesRequestTimeout(-1L);
+        }
+
+        overrideCarrierConfig(null);
+    }
+
     private void setupTestImsService(RcsUceAdapter uceAdapter, boolean presencePublishEnabled,
             boolean presenceCapExchangeEnabled, boolean sipOptionsEnabled) throws Exception {
         // Trigger carrier config changed
@@ -1341,17 +2345,6 @@
         bundle.putBoolean(CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL, sipOptionsEnabled);
         overrideCarrierConfig(bundle);
 
-        // Enable the UCE setting.
-        try {
-            ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
-                    uceAdapter, adapter -> adapter.setUceSettingEnabled(true), ImsException.class,
-                    "android.permission.MODIFY_PHONE_STATE");
-        } catch (SecurityException e) {
-            fail("setUceSettingEnabled should succeed with MODIFY_PHONE_STATE.");
-        } catch (ImsException e) {
-            fail("setUceSettingEnabled failed with code " + e);
-        }
-
         // Connect to the TestImsService
         connectTestImsService();
     }
@@ -1379,17 +2372,18 @@
     }
 
     private void verifyCapabilityResult(RcsContactUceCapability resultCapability, Uri expectedUri,
-            int expectedResult, boolean expectedAudioSupported, boolean expectedVideoSupported) {
+            int expectedSourceType, int expectedResult, boolean expectedAudioSupported,
+            boolean expectedVideoSupported) {
         // Verify the contact URI
         assertEquals(expectedUri, resultCapability.getContactUri());
 
         // Verify the source type is the network type.
-        assertEquals(RcsContactUceCapability.SOURCE_TYPE_NETWORK,
+        assertEquals(expectedSourceType,
                 resultCapability.getSourceType());
 
         // Verify the request result is expected.
         final int requestResult = resultCapability.getRequestResult();
-        assertEquals(requestResult, expectedResult);
+        assertEquals(expectedResult, requestResult);
 
         // Return directly if the result is not found.
         if (requestResult == REQUEST_RESULT_NOT_FOUND) {
@@ -1414,6 +2408,30 @@
         assertEquals(expectedVideoSupported, capabilities.isVideoCapable());
     }
 
+    private void verifyOptionsCapabilityResult(List<RcsContactUceCapability> resultCapList,
+            Collection<Uri> expectedUriList, int expectedSourceType, int expectedMechanism,
+            int expectedResult, List<String> expectedFeatureTags) {
+        assertEquals(resultCapList.size(), expectedUriList.size());
+
+        assertTrue(resultCapList.stream().map(capability -> capability.getContactUri())
+                .anyMatch(expectedUriList::contains));
+
+        resultCapList.stream().map(capability -> capability.getSourceType())
+                .forEach(sourceType -> assertEquals((int) sourceType, (int) expectedSourceType));
+
+        resultCapList.stream().map(capability -> capability.getCapabilityMechanism())
+                .forEach(mechanism -> assertEquals((int) mechanism, (int) expectedMechanism));
+
+        resultCapList.stream().map(capability -> capability.getRequestResult())
+                .forEach(result -> assertEquals((int) result, (int) expectedResult));
+
+        resultCapList.stream().map(capability -> capability.getFeatureTags())
+                .forEach(featureTags -> {
+                    assertEquals((int) featureTags.size(), (int) expectedFeatureTags.size());
+                    assertTrue(featureTags.containsAll(expectedFeatureTags));
+                });
+    }
+
     private void registerUceObserver(Consumer<Uri> resultConsumer) {
         mUceObserver = new ContentObserver(new Handler(sHandlerThread.getLooper())) {
             @Override
@@ -1525,7 +2543,23 @@
         }
     }
 
-    private void requestCapabilities(RcsUceAdapter uceAdapter, List<Uri> numbers,
+    private static void removeUceRequestDisallowedStatus() {
+        try {
+            sServiceConnector.removeUceRequestDisallowedStatus(sTestSlot);
+        } catch (Exception e) {
+            Log.w("RcsUceAdapterTest", "Cannot remove request disallowed status: " + e);
+        }
+    }
+
+    private static void setCapabilitiesRequestTimeout(long timeoutAfterMillis) {
+        try {
+            sServiceConnector.setCapabilitiesRequestTimeout(sTestSlot, timeoutAfterMillis);
+        } catch (Exception e) {
+            Log.w("RcsUceAdapterTest", "Cannot set capabilities request timeout: " + e);
+        }
+    }
+
+    private void requestCapabilities(RcsUceAdapter uceAdapter, Collection<Uri> numbers,
             RcsUceAdapter.CapabilitiesCallback callback) {
         try {
             ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
@@ -1556,24 +2590,4 @@
             fail("requestAvailability failed " + e);
         }
     }
-
-    private void lunchUceActivity() throws Exception {
-        final CountDownLatch countdownLatch = new CountDownLatch(1);
-        final Intent activityIntent = new Intent(getContext(), UceActivity.class);
-        activityIntent.setAction(Intent.ACTION_MAIN);
-        activityIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        UceActivity.setCountDownLatch(countdownLatch);
-        getContext().startActivity(activityIntent);
-        countdownLatch.await(5000, TimeUnit.MILLISECONDS);
-    }
-
-    private void finishUceActivity() {
-        final Intent finishIntent = new Intent(getContext(), UceActivity.class);
-        finishIntent.setAction(UceActivity.ACTION_FINISH);
-        finishIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-        finishIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        finishIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        getContext().startActivity(finishIntent);
-    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDelegateManagerTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDelegateManagerTest.java
index 95935ac..dea6cf3 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDelegateManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDelegateManagerTest.java
@@ -30,6 +30,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.InetAddresses;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
@@ -41,6 +43,7 @@
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsManager;
 import android.telephony.ims.ImsService;
+import android.telephony.ims.SipDelegateConfiguration;
 import android.telephony.ims.SipDelegateImsConfiguration;
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.SipMessage;
@@ -61,6 +64,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.net.InetSocketAddress;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Set;
@@ -83,6 +87,9 @@
     private static final String FILE_TRANSFER_HTTP_TAG =
             "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gppapplication.ims.iari.rcs.fthttp\"";
 
+    private static final String[] DEFAULT_FEATURE_TAGS = {
+            ONE_TO_ONE_CHAT_TAG, GROUP_CHAT_TAG, FILE_TRANSFER_HTTP_TAG};
+
     private static class CarrierConfigReceiver extends BroadcastReceiver {
         private CountDownLatch mLatch = new CountDownLatch(1);
         private final int mSubId;
@@ -110,6 +117,65 @@
         }
     }
 
+    /**
+     * Encapsulates the interfaces created during SipDelegateManager testing.
+     */
+    public class TransportInterfaces {
+        public final DelegateRequest request;
+        public final Set<FeatureTagState> deniedTags;
+        public final SipDelegateManager manager;
+        public TestSipTransport transport;
+        public TestImsRegistration reg;
+        public TestSipDelegate delegate;
+        public TestSipDelegateConnection delegateConn;
+        private final int mDelegateIndex;
+
+        public TransportInterfaces(DelegateRequest request, Set<FeatureTagState>  deniedTags,
+                int delegateIndex) {
+            this.request = request;
+            this.deniedTags = deniedTags;
+            manager = getSipDelegateManager();
+            mDelegateIndex = delegateIndex;
+        }
+
+        public void connect() throws Exception {
+            assertTrue(sServiceConnector.setDefaultSmsApp());
+            connectTestImsServiceWithSipTransportAndConfig();
+
+            transport = sServiceConnector.getCarrierService().getSipTransport();
+            reg = sServiceConnector.getCarrierService().getImsRegistration();
+            delegateConn = new TestSipDelegateConnection(request);
+
+            delegate = createSipDelegateConnectionAndVerify(manager, delegateConn,
+                    transport, deniedTags, mDelegateIndex);
+            assertNotNull(delegate);
+            // ensure we got a callback for initial reg state.
+            verifyUpdateRegistrationCalled(reg);
+
+            InetSocketAddress localAddr = new InetSocketAddress(
+                    InetAddresses.parseNumericAddress("1.1.1.1"), 80);
+            InetSocketAddress serverAddr = new InetSocketAddress(
+                    InetAddresses.parseNumericAddress("2.2.2.2"), 81);
+            SipDelegateConfiguration c = new SipDelegateConfiguration.Builder(1,
+                    SipDelegateConfiguration.SIP_TRANSPORT_TCP, localAddr, serverAddr).build();
+            // send first SIP config and verify
+            verifyRegisteredAndSendSipConfig(delegateConn, delegate, request.getFeatureTags(),
+                    deniedTags, c);
+        }
+
+        /**
+         * Create a connection between fake app interface and fake ImsService impl and set up the
+         * framework to accept incoming/outgoing messages. Once done, verify the transport is open.
+         */
+        public void connectAndVerify() throws Exception {
+            connect();
+
+            // Verify message transport is open.
+            verifyOutgoingTransport(delegateConn, delegate);
+            verifyIncomingTransport(delegateConn, delegate);
+        }
+    }
+
     private static int sTestSlot = 0;
     private static int sTestSub = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private static ImsServiceConnector sServiceConnector;
@@ -149,6 +215,8 @@
             // APIs.
             sServiceConnector.setDeviceSingleRegistrationEnabled(true);
         }
+
+        setFeatureTagsCarrierAllowed(DEFAULT_FEATURE_TAGS);
     }
 
     @AfterClass
@@ -487,7 +555,7 @@
         createSipDelegateConnectionNoDelegateExpected(manager, delegateConn, transportImpl);
 
         // TODO deal with this case better when we can filter messages.
-        delegateConn.sendMessageAndVerifyFailure(ImsUtils.TEST_SIP_MESSAGE,
+        delegateConn.sendMessageAndVerifyFailure(SipMessageUtils.TEST_SIP_MESSAGE,
                 SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_DEAD);
 
         delegateConn.triggerFullNetworkRegistration(manager, 403, "FORBIDDEN");
@@ -504,6 +572,24 @@
         if (!ImsUtils.shouldTestImsService()) {
             return;
         }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connectAndVerify();
+
+        // Ensure requests to perform a full network re-registration work properly.
+        verifyFullRegistrationTriggered(ifaces);
+
+        destroySipDelegateAndVerify(ifaces);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testImsServiceDisconnected() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
         assertTrue(sServiceConnector.setDefaultSmsApp());
         connectTestImsServiceWithSipTransportAndConfig();
 
@@ -518,20 +604,71 @@
         assertNotNull(delegate);
         verifyUpdateRegistrationCalled(regImpl);
 
-        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
-                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
-                .build();
+        InetSocketAddress localAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("1.1.1.1"), 80);
+        InetSocketAddress serverAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("2.2.2.2"), 81);
+        SipDelegateConfiguration c = new SipDelegateConfiguration.Builder(1,
+                SipDelegateConfiguration.SIP_TRANSPORT_TCP, localAddr, serverAddr).build();
         verifyRegisteredAndSendSipConfig(delegateConn, delegate, request.getFeatureTags(),
                 Collections.emptySet(), c);
 
-        sendMessageAndVerifyAck(delegateConn, delegate);
-        receiveMessageAndVerifyAck(delegateConn, delegate);
+        verifyOutgoingTransport(delegateConn, delegate);
+        verifyIncomingTransport(delegateConn, delegate);
 
-        // Ensure requests to perform a full network re-registration work properly.
-        verifyFullRegistrationTriggered(manager, regImpl, delegateConn);
+        sServiceConnector.disconnectCarrierImsService();
+        // unbind ImsService suddenly and wait for on destroyed
+        delegateConn.setOperationCountDownLatch(1);
+        transportImpl.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+    }
 
-        destroySipDelegateAndVerify(manager, transportImpl, delegateConn, delegate,
-                request.getFeatureTags());
+    @Test
+    public void testCreateDelegateTestInvalidSipMessages() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connectAndVerify();
+
+        // Verify restricted SIP request methods are not sent to the delegate.
+        sendRestrictedRequestsAndVerifyFailed(ifaces.delegateConn);
+        // Verify malformed messages are not sent to the delegate.
+        sendInvalidRequestsAndVerifyFailed(ifaces.delegateConn);
+
+        destroySipDelegateAndVerify(ifaces);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testDeprecatedConfig() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        assertTrue(sServiceConnector.setDefaultSmsApp());
+        connectTestImsServiceWithSipTransportAndConfig();
+
+        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
+        TestImsRegistration regImpl = sServiceConnector.getCarrierService().getImsRegistration();
+        SipDelegateManager manager = getSipDelegateManager();
+        DelegateRequest request = getDefaultRequest();
+        TestSipDelegateConnection delegateConn = new TestSipDelegateConnection(request);
+
+        TestSipDelegate delegate = createSipDelegateConnectionAndVerify(manager, delegateConn,
+                transportImpl, Collections.emptySet(), 0);
+        assertNotNull(delegate);
+        verifyUpdateRegistrationCalled(regImpl);
+        verifyRegisteredAndSendOldSipConfig(delegateConn, delegate, request.getFeatureTags(),
+                Collections.emptySet());
+
+        destroySipDelegateAndVerifyConnDestroyed(manager, transportImpl, delegateConn, delegate);
         assertEquals("There should be no more delegates", 0,
                 transportImpl.getDelegates().size());
         verifyUpdateRegistrationCalled(regImpl);
@@ -542,49 +679,28 @@
         if (!ImsUtils.shouldTestImsService()) {
             return;
         }
-        assertTrue(sServiceConnector.setDefaultSmsApp());
-        connectTestImsServiceWithSipTransportAndConfig();
-
-        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
-        SipDelegateManager manager = getSipDelegateManager();
-        DelegateRequest request = getDefaultRequest();
-        TestSipDelegateConnection delegateConn = new TestSipDelegateConnection(request);
-
-        // Construct registered tags and denied tags, vendor denied FT tag.
-        Set<String> registeredTags = new ArraySet<>(request.getFeatureTags());
-        registeredTags.remove(FILE_TRANSFER_HTTP_TAG);
-        Set<FeatureTagState> deniedTags = new ArraySet<>(1);
-        deniedTags.add(new FeatureTagState(FILE_TRANSFER_HTTP_TAG,
-                SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
-        TestSipDelegate delegate = createSipDelegateConnectionAndVerify(manager, delegateConn,
-                transportImpl, deniedTags, 0);
-        assertNotNull(delegate);
-
-        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
-                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
-                .build();
-        verifyRegisteredAndSendSipConfig(delegateConn, delegate, registeredTags, deniedTags, c);
-
-        // TODO verify messages can be sent on registered tags, but generate error for denied tags.
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connectAndVerify();
+        Set<String> registeredTags = new ArraySet<>(ifaces.request.getFeatureTags());
 
         // move reg state to deregistering and then deregistered
-        delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.setOperationCountDownLatch(1);
         DelegateRegistrationState s = getDeregisteringState(registeredTags,
                 DelegateRegistrationState.DEREGISTERING_REASON_PDN_CHANGE);
-        delegate.notifyImsRegistrationUpdate(s);
-        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
-        delegateConn.verifyRegistrationStateEquals(s);
+        ifaces.delegate.notifyImsRegistrationUpdate(s);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyRegistrationStateEquals(s);
 
-        delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.setOperationCountDownLatch(1);
         s = getRegisteredRegistrationState(registeredTags);
-        delegate.notifyImsRegistrationUpdate(s);
-        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
-        delegateConn.verifyRegistrationStateEquals(s);
+        ifaces.delegate.notifyImsRegistrationUpdate(s);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyRegistrationStateEquals(s);
 
-        destroySipDelegateAndVerify(manager, transportImpl, delegateConn, delegate,
-                registeredTags);
+        destroySipDelegateAndVerify(ifaces);
         assertEquals("There should be no more delegates", 0,
-                transportImpl.getDelegates().size());
+                ifaces.transport.getDelegates().size());
     }
 
     @Test
@@ -605,9 +721,12 @@
                 transportImpl, Collections.emptySet(), 0);
         assertNotNull(delegate1);
 
-        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
-                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
-                .build();
+        InetSocketAddress localAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("1.1.1.1"), 80);
+        InetSocketAddress serverAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("2.2.2.2"), 81);
+        SipDelegateConfiguration c = new SipDelegateConfiguration.Builder(1,
+                SipDelegateConfiguration.SIP_TRANSPORT_TCP, localAddr, serverAddr).build();
         verifyRegisteredAndSendSipConfig(delegateConn1, delegate1, registeredTags1,
                 Collections.emptySet(), c);
 
@@ -627,21 +746,16 @@
                 deniedSet, c);
 
         // Destroying delegate 1 will transfer all feature tags over to delegate 2
-        delegateConn2.setOperationCountDownLatch(1);
-        destroySipDelegateAndVerify(manager, transportImpl, delegateConn1, delegate1,
-                registeredTags1);
-        delegateConn2.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        destroySipDelegate(manager, transportImpl, delegateConn1, delegate1);
         // This internally triggers the destruction of the internal delegate2 and then recreation
         // of another delegate with the new feature set that it supports.
-        verifySipDelegateDestroyed(transportImpl, delegateConn2, delegate2, registeredTags2,
-                DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING);
+        verifySipDelegateDestroyed(transportImpl, delegate2);
         delegate2 = getSipDelegate(transportImpl, Collections.emptySet(), 0);
         verifyUpdateRegistrationCalled(regImpl);
         verifyRegisteredAndSendSipConfig(delegateConn2, delegate2, request2.getFeatureTags(),
                 Collections.emptySet(), c);
 
-        destroySipDelegateAndVerify(manager, transportImpl, delegateConn2, delegate2,
-                request2.getFeatureTags());
+        destroySipDelegateAndVerifyConnDestroyed(manager, transportImpl, delegateConn2, delegate2);
         assertEquals("There should be no more delegates", 0,
                 transportImpl.getDelegates().size());
     }
@@ -670,13 +784,15 @@
                 ImsUtils.TEST_TIMEOUT_MS));
         TestSipDelegate delegate = getSipDelegate(transportImpl, Collections.emptySet(), 0);
         verifyUpdateRegistrationCalled(regImpl);
-        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
-                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
-                .build();
+        InetSocketAddress localAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("1.1.1.1"), 80);
+        InetSocketAddress serverAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("2.2.2.2"), 81);
+        SipDelegateConfiguration c = new SipDelegateConfiguration.Builder(1,
+                SipDelegateConfiguration.SIP_TRANSPORT_TCP, localAddr, serverAddr).build();
         verifyRegisteredAndSendSipConfig(delegateConn, delegate, request.getFeatureTags(),
                 Collections.emptySet(), c);
-        destroySipDelegateAndVerify(manager, transportImpl, delegateConn, delegate,
-                request.getFeatureTags());
+        destroySipDelegateAndVerifyConnDestroyed(manager, transportImpl, delegateConn, delegate);
         assertEquals("There should be no more delegates", 0,
                 transportImpl.getDelegates().size());
     }
@@ -687,49 +803,30 @@
             return;
         }
         // Make this app the DMA
-        assertTrue(sServiceConnector.setDefaultSmsApp());
-        connectTestImsServiceWithSipTransportAndConfig();
-        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
-        TestImsRegistration regImpl = sServiceConnector.getCarrierService().getImsRegistration();
-        SipDelegateManager manager = getSipDelegateManager();
-
-        DelegateRequest request = getDefaultRequest();
-        TestSipDelegateConnection delegateConn = new TestSipDelegateConnection(request);
-        TestSipDelegate delegate = createSipDelegateConnectionAndVerify(manager, delegateConn,
-                transportImpl, Collections.emptySet(), 0);
-        assertNotNull(delegate);
-        verifyUpdateRegistrationCalled(regImpl);
-
-        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1)
-                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
-                .build();
-        verifyRegisteredAndSendSipConfig(delegateConn, delegate, request.getFeatureTags(),
-                Collections.emptySet(), c);
-
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connectAndVerify();
 
         // Move DMA to another app, we should receive a registration update.
-        delegateConn.setOperationCountDownLatch(1);
-        regImpl.resetLatch(TestImsRegistration.LATCH_TRIGGER_DEREGISTRATION, 1);
+        ifaces.reg.resetLatch(TestImsRegistration.LATCH_TRIGGER_DEREGISTRATION, 1);
         sServiceConnector.restoreDefaultSmsApp();
-        assertTrue(regImpl.waitForLatchCountDown(TestImsRegistration.LATCH_TRIGGER_DEREGISTRATION,
-                ImsUtils.TEST_TIMEOUT_MS));
-        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.reg.waitForLatchCountDown(
+                TestImsRegistration.LATCH_TRIGGER_DEREGISTRATION, ImsUtils.TEST_TIMEOUT_MS));
         // we should get another reg update with all tags denied.
-        delegateConn.setOperationCountDownLatch(1);
-        verifySipDelegateDestroyed(transportImpl, delegateConn, delegate, request.getFeatureTags(),
-                DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING);
-        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
-        delegateConn.verifyRegistrationStateEmpty();
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        verifySipDelegateDestroyed(ifaces.transport, ifaces.delegate);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyRegistrationStateEmpty();
         // All requested features should have been denied due to the app not being the default sms
         // app.
-        delegateConn.verifyAllDenied(SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
+        ifaces.delegateConn.verifyAllDenied(SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
         // There should not be any delegates left, as the only delegate should have been cleaned up.
         assertEquals("SipDelegate should not have any delegates", 0,
-                transportImpl.getDelegates().size());
-        verifyUpdateRegistrationCalled(regImpl);
-
-        destroySipDelegateConnectionNoDelegate(manager, delegateConn);
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+        destroySipDelegateConnectionNoDelegate(ifaces.manager, ifaces.delegateConn);
     }
+
     @Test
     public void testParcelUnparcelDelegateRequest() {
         ArraySet<String> testTags = new ArraySet<>();
@@ -782,29 +879,86 @@
     }
 
     @Test
-    public void testParcelUnparcelImsConfiguration() {
-        SipDelegateImsConfiguration c = new SipDelegateImsConfiguration.Builder(1 /*version*/)
-                .addBoolean(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, true)
-                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, 1)
-                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "123")
-                .build();
+    public void testParcelUnparcelConfiguration() {
+        // Set everything to a non-default value
+        SipDelegateConfiguration c = generateNewTestConfig();
+        assertEquals(1, c.getVersion());
+        assertEquals(SipDelegateConfiguration.SIP_TRANSPORT_TCP, c.getTransportType());
+        assertEquals("1.1.1.1", c.getLocalAddress().getAddress().getHostAddress());
+        assertEquals(80, c.getLocalAddress().getPort());
+        assertEquals("2.2.2.2", c.getSipServerAddress().getAddress().getHostAddress());
+        assertEquals(81, c.getSipServerAddress().getPort());
+        assertTrue(c.isSipCompactFormEnabled());
+        assertTrue(c.isSipKeepaliveEnabled());
+        assertEquals(508, c.getMaxUdpPayloadSizeBytes());
+        assertEquals("test1", c.getPublicUserIdentifier());
+        assertEquals("test2", c.getPrivateUserIdentifier());
+        assertEquals("test.domain", c.getHomeDomain());
+        assertEquals("testImei", c.getImei());
+        assertEquals("sipauth", c.getSipAuthenticationHeader());
+        assertEquals("sipnonce", c.getSipAuthenticationNonce());
+        assertEquals("srvroute", c.getSipServiceRouteHeader());
+        assertEquals("path", c.getSipPathHeader());
+        assertEquals("ua", c.getSipUserAgentHeader());
+        assertEquals("user", c.getSipContactUserParameter());
+        assertEquals("pani", c.getSipPaniHeader());
+        assertEquals("plani", c.getSipPlaniHeader());
+        assertEquals("cni", c.getSipCniHeader());
+        assertEquals("uri", c.getSipAssociatedUriHeader());
+        Uri gruuUri = Uri.parse("sip:blah@gruu.net");
+        assertEquals(gruuUri, c.getPublicGruuUri());
+
         Parcel p = Parcel.obtain();
         c.writeToParcel(p, 0);
         p.setDataPosition(0);
-        SipDelegateImsConfiguration unparcel =
-                SipDelegateImsConfiguration.CREATOR.createFromParcel(p);
-        assertEquals(c.getVersion(), unparcel.getVersion());
-        assertEquals(c.getBoolean(
-                        SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false),
-                unparcel.getBoolean(
-                        SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false));
-        assertEquals(c.getInt(
-                SipDelegateImsConfiguration.KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT, -1),
-                unparcel.getInt(
-                        SipDelegateImsConfiguration.KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT,
-                        -1));
-        assertEquals(c.getString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING),
-                unparcel.getString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING));
+        SipDelegateConfiguration unparcelConfig =
+                SipDelegateConfiguration.CREATOR.createFromParcel(p);
+        assertEquals(c, unparcelConfig);
+    }
+
+    @Test
+    public void testCopyConfiguration() {
+        // Set everything to a non-default value
+        SipDelegateConfiguration c = generateNewTestConfig();
+        // The config should be exactly the same, but with an incremented version.
+        SipDelegateConfiguration configInc = new SipDelegateConfiguration.Builder(c).build();
+        assertEquals(2, configInc.getVersion());
+        assertEquals(SipDelegateConfiguration.SIP_TRANSPORT_TCP, configInc.getTransportType());
+        assertEquals("1.1.1.1", configInc.getLocalAddress().getAddress().getHostAddress());
+        assertEquals(80, configInc.getLocalAddress().getPort());
+        assertEquals("2.2.2.2",
+                configInc.getSipServerAddress().getAddress().getHostAddress());
+        assertEquals(81, configInc.getSipServerAddress().getPort());
+        assertTrue(configInc.isSipCompactFormEnabled());
+        assertTrue(configInc.isSipKeepaliveEnabled());
+        assertEquals(508, configInc.getMaxUdpPayloadSizeBytes());
+        assertEquals("test1", configInc.getPublicUserIdentifier());
+        assertEquals("test2", configInc.getPrivateUserIdentifier());
+        assertEquals("test.domain", configInc.getHomeDomain());
+        assertEquals("testImei", configInc.getImei());
+        assertEquals("sipauth", configInc.getSipAuthenticationHeader());
+        assertEquals("sipnonce", configInc.getSipAuthenticationNonce());
+        assertEquals("srvroute", configInc.getSipServiceRouteHeader());
+        assertEquals("path", configInc.getSipPathHeader());
+        assertEquals("ua", configInc.getSipUserAgentHeader());
+        assertEquals("user", configInc.getSipContactUserParameter());
+        assertEquals("pani", configInc.getSipPaniHeader());
+        assertEquals("plani", configInc.getSipPlaniHeader());
+        assertEquals("cni", configInc.getSipCniHeader());
+        assertEquals("uri", configInc.getSipAssociatedUriHeader());
+        Uri gruuUri = Uri.parse("sip:blah@gruu.net");
+        assertEquals(gruuUri, configInc.getPublicGruuUri());
+        SipDelegateConfiguration.IpSecConfiguration ipSecConfig = configInc.getIpSecConfiguration();
+        assertEquals(123, ipSecConfig.getLocalTxPort());
+        assertEquals(124, ipSecConfig.getLocalRxPort());
+        assertEquals(125, ipSecConfig.getLastLocalTxPort());
+        assertEquals(126, ipSecConfig.getRemoteTxPort());
+        assertEquals(127, ipSecConfig.getRemoteRxPort());
+        assertEquals(128, ipSecConfig.getLastRemoteTxPort());
+        assertEquals("secverify", ipSecConfig.getSipSecurityVerifyHeader());
+        InetSocketAddress natAddr = configInc.getNatSocketAddress();
+        assertEquals("3.3.3.3", natAddr.getAddress().getHostAddress());
+        assertEquals(129, natAddr.getPort());
     }
 
     @Test
@@ -854,7 +1008,7 @@
         byte[] content2 = new byte[0];
 
         SipMessage m = new SipMessage(startLine, header, content1);
-        byte[] encodedMsg = m.getEncodedMessage();
+        byte[] encodedMsg = m.toEncodedMessage();
         String decodedStr = new String(encodedMsg, UTF_8);
         SipMessage decodedMsg = generateSipMessage(decodedStr);
         assertEquals(decodedMsg.getStartLine(), m.getStartLine());
@@ -863,7 +1017,7 @@
 
         // Test empty content
         m = new SipMessage(startLine, header, content2);
-        encodedMsg = m.getEncodedMessage();
+        encodedMsg = m.toEncodedMessage();
         decodedStr = new String(encodedMsg, UTF_8);
         decodedMsg = generateSipMessage(decodedStr);
         assertEquals(decodedMsg.getStartLine(), m.getStartLine());
@@ -871,6 +1025,638 @@
         assertTrue(Arrays.equals(decodedMsg.getContent(), m.getContent()));
     }
 
+    @Test
+    public void testFeatureTagDeniedByCarrierConfig() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+
+        setFeatureTagsCarrierAllowed(new String[]{FILE_TRANSFER_HTTP_TAG});
+        assertTrue(sServiceConnector.setDefaultSmsApp());
+        connectTestImsServiceWithSipTransportAndConfig();
+        TestSipTransport transportImpl = sServiceConnector.getCarrierService().getSipTransport();
+        SipDelegateManager manager = getSipDelegateManager();
+        DelegateRequest request = getDefaultRequest();
+        TestSipDelegateConnection delegateConn = new TestSipDelegateConnection(request);
+        Set<String> deniedTags = new ArraySet<>(request.getFeatureTags());
+        deniedTags.remove(FILE_TRANSFER_HTTP_TAG);
+
+        TestSipDelegate delegate = createSipDelegateConnectionAndVerify(manager, delegateConn,
+                transportImpl, getDeniedTagsForReason(deniedTags,
+                        SipDelegateManager.DENIED_REASON_NOT_ALLOWED), 0);
+        assertNotNull(delegate);
+
+        Set<String> registeredTags = new ArraySet<>(
+                Arrays.asList(new String[]{FILE_TRANSFER_HTTP_TAG}));
+        delegateConn.setOperationCountDownLatch(1);
+        DelegateRegistrationState s = getRegisteredRegistrationState(registeredTags);
+        delegate.notifyImsRegistrationUpdate(s);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyRegistrationStateEquals(s);
+        destroySipDelegateAndVerifyConnDestroyed(manager, transportImpl, delegateConn, delegate);
+        assertEquals("There should be no more delegates", 0,
+                transportImpl.getDelegates().size());
+        setFeatureTagsCarrierAllowed(getDefaultRequest().getFeatureTags().toArray(new String[0]));
+    }
+
+    @Test
+    public void testCloseActiveDialog() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // send close from app
+        ifaces.delegateConn.disconnect(ifaces.manager,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        // Registration state will change to deregistering during this time.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.delegateConn.verifyDeregisteringStateContains(ONE_TO_ONE_CHAT_TAG,
+                DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING));
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Send ACK
+        sendAck(attr, ifaces);
+        // Send BYE
+        sendByeRequest(attr, ifaces);
+        // destroy should not be called until cleanupSession is sent.
+        assertFalse(ifaces.transport.isLatchCountDownFinished(
+                TestSipTransport.LATCH_DESTROY_DELEGATE));
+
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        // Send the cleanup, which will trigger destroy to complete.
+        ifaces.delegateConn.sendCleanupSession(attr.callId);
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+        ifaces.transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        ifaces.delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testReceivedActiveDialogClose() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // receive invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        receiveChatInvite(attr, ifaces);
+        // send close from app
+        ifaces.delegateConn.disconnect(ifaces.manager,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        // Registration state will change to deregistering during this time.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.delegateConn.verifyDeregisteringStateContains(ONE_TO_ONE_CHAT_TAG,
+                DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING));
+        // Send 200 OK
+        send200OkResponse(attr, ifaces);
+        // receive ACK
+        receiveAck(attr, ifaces);
+        // Send BYE
+        receiveByeRequest(attr, ifaces);
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        // Send the cleanup, which will trigger destroy to complete.
+        ifaces.delegateConn.sendCleanupSession(attr.callId);
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+        ifaces.transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        ifaces.delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testActiveDialogPendingNewInvite() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // send close from app
+        ifaces.delegateConn.disconnect(ifaces.manager,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        // Registration state will change to deregistering during this time.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.delegateConn.verifyDeregisteringStateContains(ONE_TO_ONE_CHAT_TAG,
+                DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING));
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Send ACK
+        sendAck(attr, ifaces);
+        // Send invite
+        SipDialogAttributes attr2 = new SipDialogAttributes();
+        attr2.addAcceptContactTag(ONE_TO_ONE_CHAT_TAG);
+        // Should be denied because the transport is now restricted
+        sendDeniedChatInvite(attr2, ifaces,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_DELEGATE_CLOSED);
+        // Send BYE on original invite
+        sendByeRequest(attr, ifaces);
+        // destroy should not be called until cleanupSession is sent.
+        assertFalse(ifaces.transport.isLatchCountDownFinished(
+                TestSipTransport.LATCH_DESTROY_DELEGATE));
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        // Send the cleanup, which will trigger destroy to complete.
+        ifaces.delegateConn.sendCleanupSession(attr.callId);
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+        ifaces.transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        ifaces.delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testCloseSessionByePendingCleanup() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // send close from app
+        ifaces.delegateConn.disconnect(ifaces.manager,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Send ACK
+        sendAck(attr, ifaces);
+        // Send BYE
+        sendByeRequest(attr, ifaces);
+        assertFalse(ifaces.transport.isLatchCountDownFinished(
+                TestSipTransport.LATCH_DESTROY_DELEGATE));
+        // Registration state will change to deregistering during this time.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.delegateConn.verifyDeregisteringStateContains(ONE_TO_ONE_CHAT_TAG,
+                DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING));
+        // waiting for delegate connection onDestroy to be called.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        // no cleanupSession called, so cleanup session should be called from internal and then
+        // the delegate should be closed.
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+        ifaces.transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        ifaces.delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testCloseSessionPendingBye() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // send close from app
+        ifaces.delegateConn.disconnect(ifaces.manager,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Send ACK
+        sendAck(attr, ifaces);
+        // Don't send BYE or cleanupSession
+        assertFalse(ifaces.transport.isLatchCountDownFinished(
+                TestSipTransport.LATCH_DESTROY_DELEGATE));
+        // Registration state will change to deregistering during this time.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.delegateConn.verifyDeregisteringStateContains(ONE_TO_ONE_CHAT_TAG,
+                DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING));
+        // waiting for delegate connection onDestroy to be called.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        // no cleanupSession called, so cleanup session should be called from internal and then
+        // the delegate should be closed.
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+        ifaces.transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        ifaces.delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testCloseMultipleSessionsPendingBye() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite 1
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // Send invite 2
+        SipDialogAttributes attr2 = new SipDialogAttributes();
+        sendChatInvite(attr2, ifaces);
+        // send close from app
+        ifaces.delegateConn.disconnect(ifaces.manager,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        receive200OkResponse(attr2, ifaces);
+        // Send ACK
+        sendAck(attr, ifaces);
+        sendAck(attr2, ifaces);
+        // Don't send BYE or cleanupSession
+        assertFalse(ifaces.transport.isLatchCountDownFinished(
+                TestSipTransport.LATCH_DESTROY_DELEGATE));
+        // Registration state will change to deregistering during this time.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.delegateConn.verifyDeregisteringStateContains(ONE_TO_ONE_CHAT_TAG,
+                DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING));
+        // waiting for delegate connection onDestroy to be called.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        // no cleanupSession called, so cleanup session should be called from internal and then
+        // the delegate should be closed.
+        ifaces.delegate.verifyCleanupSession(attr.callId, attr2.callId);
+        ifaces.transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        ifaces.delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testCloseSessionBye() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Send ACK
+        sendAck(attr, ifaces);
+        // send close from app
+        ifaces.delegateConn.disconnect(ifaces.manager,
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        // Send BYE
+        sendByeRequest(attr, ifaces);
+        assertFalse(ifaces.transport.isLatchCountDownFinished(
+                TestSipTransport.LATCH_DESTROY_DELEGATE));
+        // Registration state will change to deregistering during this time.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.delegateConn.verifyDeregisteringStateContains(ONE_TO_ONE_CHAT_TAG,
+                DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING));
+        // waiting for delegate connection onDestroy to be called.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        // Send the cleanup, which will trigger destroy to complete.
+        ifaces.delegateConn.sendCleanupSession(attr.callId);
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+        ifaces.transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        ifaces.delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testSwitchAppPendingBye() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Restore the default SMS app.
+        sServiceConnector.restoreDefaultSmsApp();
+        // Registration state will change to deregistering during this time.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.delegateConn.verifyDeregisteringStateContains(ONE_TO_ONE_CHAT_TAG,
+                DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING));
+        // Don't send BYE or cleanup, session should still be cleaned up.
+        assertFalse(ifaces.transport.isLatchCountDownFinished(
+                TestSipTransport.LATCH_DESTROY_DELEGATE));
+        // wait for delegate connection feature tag state to be updated with denied features.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        // verify framework internally calls cleanup on the session before destroy.
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+        ifaces.transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        ifaces.delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyRegistrationStateEmpty();
+        ifaces.delegateConn.verifyAllDenied(SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
+    }
+
+    @Test
+    public void testSwitchAppActiveSession() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Restore the default SMS app.
+        sServiceConnector.restoreDefaultSmsApp();
+        // Registration state will change to deregistering during this time.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        assertTrue(ifaces.delegateConn.verifyDeregisteringStateContains(ONE_TO_ONE_CHAT_TAG,
+                DelegateRegistrationState.DEREGISTERING_REASON_FEATURE_TAGS_CHANGING));
+        // BYE should still be able to be sent
+        sendByeRequest(attr, ifaces);
+        assertFalse(ifaces.transport.isLatchCountDownFinished(
+                TestSipTransport.LATCH_DESTROY_DELEGATE));
+        // wait for delegate connection feature tag state to be updated with denied features.
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        // Send the cleanup, which will trigger delegate destroy to complete.
+        ifaces.delegateConn.sendCleanupSession(attr.callId);
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+        ifaces.transport.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
+        ifaces.delegate.notifyOnDestroyed(
+                SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyAllDenied(SipDelegateManager.DENIED_REASON_NOT_ALLOWED);
+    }
+
+    @Test
+    public void testActiveSessionDeregistering() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // move chat to deregistering
+        Set<String> regFeatures = new ArraySet<>(Arrays.asList(DEFAULT_FEATURE_TAGS));
+        regFeatures.remove(ONE_TO_ONE_CHAT_TAG);
+        DelegateRegistrationState state = getDeregisteringState(regFeatures,
+                Collections.singleton(ONE_TO_ONE_CHAT_TAG),
+                DelegateRegistrationState.DEREGISTERING_REASON_PROVISIONING_CHANGE);
+        verifyRegistrationState(ifaces, state);
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Send ACK
+        sendAck(attr, ifaces);
+        // Send BYE and clean up
+        sendByeRequest(attr, ifaces);
+        ifaces.delegateConn.sendCleanupSession(attr.callId);
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+
+        destroySipDelegateAndVerify(ifaces);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testActiveSessionDeregisteringNoResponse() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // move chat to deregistering
+        Set<String> regFeatures = new ArraySet<>(Arrays.asList(DEFAULT_FEATURE_TAGS));
+        regFeatures.remove(ONE_TO_ONE_CHAT_TAG);
+        DelegateRegistrationState state = getDeregisteringState(regFeatures,
+                Collections.singleton(ONE_TO_ONE_CHAT_TAG),
+                DelegateRegistrationState.DEREGISTERING_REASON_PROVISIONING_CHANGE);
+        verifyRegistrationState(ifaces, state);
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Send ACK
+        sendAck(attr, ifaces);
+        // Don't send BYE or cleanup and ensure that we still get call to clean up after timeout.
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+
+        destroySipDelegateAndVerify(ifaces);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testMultipleActiveSessionDeregisteringNoResponse() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite 1
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // Send invite 2
+        SipDialogAttributes attr2 = new SipDialogAttributes();
+        sendChatInvite(attr2, ifaces);
+        // move chat to deregistering
+        Set<String> regFeatures = new ArraySet<>(Arrays.asList(DEFAULT_FEATURE_TAGS));
+        regFeatures.remove(ONE_TO_ONE_CHAT_TAG);
+        DelegateRegistrationState state = getDeregisteringState(regFeatures,
+                Collections.singleton(ONE_TO_ONE_CHAT_TAG),
+                DelegateRegistrationState.DEREGISTERING_REASON_PROVISIONING_CHANGE);
+        verifyRegistrationState(ifaces, state);
+        // receive 200 OK for invite 1
+        receive200OkResponse(attr, ifaces);
+        // Don't send BYE or cleanup and ensure that we still get call to clean up after timeout.
+        ifaces.delegate.verifyCleanupSession(attr.callId, attr2.callId);
+        destroySipDelegateAndVerify(ifaces);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testActiveSessionDeregisteringNewInviteDenied() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // Send invite
+        SipDialogAttributes attr = new SipDialogAttributes();
+        sendChatInvite(attr, ifaces);
+        // move chat to deregistering
+        Set<String> regFeatures = new ArraySet<>(Arrays.asList(DEFAULT_FEATURE_TAGS));
+        regFeatures.remove(ONE_TO_ONE_CHAT_TAG);
+        DelegateRegistrationState state = getDeregisteringState(regFeatures,
+                Collections.singleton(ONE_TO_ONE_CHAT_TAG),
+                DelegateRegistrationState.DEREGISTERING_REASON_PROVISIONING_CHANGE);
+        verifyRegistrationState(ifaces, state);
+        // receive 200 OK
+        receive200OkResponse(attr, ifaces);
+        // Send ACK
+        sendAck(attr, ifaces);
+        // send a new invite over the same feature tag, which should be denied because the tag
+        // is deregistering
+        SipDialogAttributes attr2 = new SipDialogAttributes();
+        attr2.addAcceptContactTag(ONE_TO_ONE_CHAT_TAG);
+        sendDeniedChatInvite(attr2, ifaces,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG);
+        // Send BYE and clean up
+        sendByeRequest(attr, ifaces);
+        ifaces.delegateConn.sendCleanupSession(attr.callId);
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+
+        destroySipDelegateAndVerify(ifaces);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testInviteDeniedTag() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        // Deny ONE_TO_ONE_CHAT access to this delegate
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.singleton(new FeatureTagState(ONE_TO_ONE_CHAT_TAG,
+                        SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE)), 0);
+        ifaces.connect();
+        // send a new invite over the chat feature tag, which should be denied because the tag was
+        // denied
+        SipDialogAttributes attr = new SipDialogAttributes();
+        attr.addAcceptContactTag(ONE_TO_ONE_CHAT_TAG);
+        sendDeniedChatInvite(attr, ifaces,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG);
+
+        destroySipDelegateAndVerify(ifaces);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testInviteAcceptContactNotAssociated() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // send a new invite over the MMTEL feature tag, which is not in the set of feature tags
+        // associated with this delegate.
+        SipDialogAttributes attr = new SipDialogAttributes();
+        attr.addAcceptContactTag(MMTEL_TAG);
+        sendDeniedChatInvite(attr, ifaces,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG);
+
+        destroySipDelegateAndVerify(ifaces);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
+    @Test
+    public void testIncomingInviteDeregistering() throws Exception {
+        if (!ImsUtils.shouldTestImsService()) {
+            return;
+        }
+        TransportInterfaces ifaces = new TransportInterfaces(getDefaultRequest(),
+                Collections.emptySet(), 0);
+        ifaces.connect();
+        // move chat to deregistering
+        Set<String> regFeatures = new ArraySet<>(Arrays.asList(DEFAULT_FEATURE_TAGS));
+        regFeatures.remove(ONE_TO_ONE_CHAT_TAG);
+        DelegateRegistrationState state = getDeregisteringState(regFeatures,
+                Collections.singleton(ONE_TO_ONE_CHAT_TAG),
+                DelegateRegistrationState.DEREGISTERING_REASON_PROVISIONING_CHANGE);
+        verifyRegistrationState(ifaces, state);
+        // receive invite, which can not be blocked
+        SipDialogAttributes attr = new SipDialogAttributes();
+        receiveChatInvite(attr, ifaces);
+        // ensure delegate connection can still respond to the request, even if in restricted state.
+        send200OkResponse(attr, ifaces);
+        receiveAck(attr, ifaces);
+        // receive BYE and clean up
+        receiveByeRequest(attr, ifaces);
+        ifaces.delegateConn.sendCleanupSession(attr.callId);
+        ifaces.delegate.verifyCleanupSession(attr.callId);
+
+        destroySipDelegateAndVerify(ifaces);
+        assertEquals("There should be no more delegates", 0,
+                ifaces.transport.getDelegates().size());
+        verifyUpdateRegistrationCalled(ifaces.reg);
+    }
+
     private SipMessage generateSipMessage(String str) {
         String crlf = "\r\n";
         String[] components = str.split(crlf);
@@ -908,6 +1694,82 @@
         return index;
     }
 
+    private void sendChatInvite(SipDialogAttributes attr,
+            TransportInterfaces ifaces) throws Exception {
+        SipDialogAttributes invAttr = attr.fromExisting().copyWithNewBranch();
+        invAttr.addAcceptContactTag(ONE_TO_ONE_CHAT_TAG);
+        SipMessage invite = SipMessageUtils.generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD,
+                invAttr);
+        sendMessageAndVerifyAck(invite, ifaces);
+    }
+
+    private void sendDeniedChatInvite(SipDialogAttributes attr,
+            TransportInterfaces ifaces, int denyReason) throws Exception {
+        SipDialogAttributes invAttr = attr.fromExisting().copyWithNewBranch();
+        SipMessage invite = SipMessageUtils.generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD,
+                invAttr);
+        ifaces.delegateConn.sendMessageAndVerifyFailure(invite, denyReason);
+    }
+
+    private void receiveChatInvite(SipDialogAttributes attr,
+            TransportInterfaces ifaces) throws Exception {
+        SipDialogAttributes invAttr = attr.fromExisting().copyWithNewBranch();
+        invAttr.addAcceptContactTag(ONE_TO_ONE_CHAT_TAG);
+        SipMessage invite = SipMessageUtils.generateSipRequest(SipMessageUtils.INVITE_SIP_METHOD,
+                invAttr);
+        receiveMessageAndVerifyAck(invite, ifaces);
+    }
+
+    private void send200OkResponse(SipDialogAttributes attr,
+            TransportInterfaces ifaces) throws Exception {
+        attr.setToTag();
+        // do not update branch here, as it is a response to a request.
+        SipMessage resp = SipMessageUtils.generateSipResponse("200", "OK",
+                attr);
+        sendMessageAndVerifyAck(resp, ifaces);
+    }
+
+    private void receive200OkResponse(SipDialogAttributes attr,
+            TransportInterfaces ifaces) throws Exception {
+        attr.setToTag();
+        // do not update branch here, as it is a response to a request.
+        SipMessage resp = SipMessageUtils.generateSipResponse("200", "OK",
+                attr);
+        receiveMessageAndVerifyAck(resp, ifaces);
+    }
+
+    private void sendAck(SipDialogAttributes attr,
+            TransportInterfaces ifaces) throws Exception {
+        attr = attr.copyWithNewBranch();
+        SipMessage invite = SipMessageUtils.generateSipRequest(SipMessageUtils.ACK_SIP_METHOD,
+                attr);
+        sendMessageAndVerifyAck(invite, ifaces);
+    }
+
+    private void receiveAck(SipDialogAttributes attr,
+            TransportInterfaces ifaces) throws Exception {
+        attr = attr.copyWithNewBranch();
+        SipMessage invite = SipMessageUtils.generateSipRequest(SipMessageUtils.ACK_SIP_METHOD,
+                attr);
+        receiveMessageAndVerifyAck(invite, ifaces);
+    }
+
+    private void sendByeRequest(SipDialogAttributes attr,
+            TransportInterfaces ifaces) throws Exception {
+        attr = attr.copyWithNewBranch();
+        SipMessage invite = SipMessageUtils.generateSipRequest(SipMessageUtils.BYE_SIP_METHOD,
+                attr);
+        sendMessageAndVerifyAck(invite, ifaces);
+    }
+
+    private void receiveByeRequest(SipDialogAttributes attr,
+            TransportInterfaces ifaces) throws Exception {
+        attr = attr.copyWithNewBranch();
+        SipMessage invite = SipMessageUtils.generateSipRequest(SipMessageUtils.BYE_SIP_METHOD,
+                attr);
+        receiveMessageAndVerifyAck(invite, ifaces);
+    }
+
     private void createSipDelegateConnectionNoDelegateExpected(SipDelegateManager manager,
             TestSipDelegateConnection conn, TestSipTransport transport) throws Exception {
         // wait for onCreated and reg state changed
@@ -935,34 +1797,35 @@
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
     }
 
-    private void destroySipDelegateAndVerify(SipDelegateManager manager,
+    private void destroySipDelegate(SipDelegateManager manager,
             TestSipTransport transportImpl, TestSipDelegateConnection delegateConn,
-            TestSipDelegate delegate, Set<String> registeredTags) throws Exception {
-        // wait for registration change upon disconnecting state change
-        delegateConn.setOperationCountDownLatch(1);
+            TestSipDelegate delegate) throws Exception {
         delegateConn.disconnect(manager,
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
-        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
-        // verify we move to deregistering for registered tags.
-        DelegateRegistrationState s = getDeregisteringState(registeredTags,
-                DelegateRegistrationState.DEREGISTERING_REASON_DESTROY_PENDING);
-        delegateConn.verifyRegistrationStateEquals(s);
-        // wait for on destroyed
-        delegateConn.setOperationCountDownLatch(1);
         transportImpl.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
         delegate.notifyOnDestroyed(
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+
+    }
+
+    private void destroySipDelegateAndVerifyConnDestroyed(SipDelegateManager manager,
+            TestSipTransport transportImpl, TestSipDelegateConnection delegateConn,
+            TestSipDelegate delegate) throws Exception {
+        delegateConn.setOperationCountDownLatch(1);
+        destroySipDelegate(manager, transportImpl, delegateConn, delegate);
         delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
         delegateConn.verifyDestroyed(
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
     }
 
+    private void destroySipDelegateAndVerify(TransportInterfaces ifaces) throws Exception {
+        // wait for on destroyed
+        destroySipDelegateAndVerifyConnDestroyed(ifaces.manager, ifaces.transport,
+                ifaces.delegateConn, ifaces.delegate);
+    }
+
     private void verifySipDelegateDestroyed(TestSipTransport transportImpl,
-            TestSipDelegateConnection delegateConn, TestSipDelegate delegate,
-            Set<String> registeredTags, int deregReason) {
-        // verify we move to deregistering for registered tags.
-        DelegateRegistrationState s = getDeregisteringState(registeredTags, deregReason);
-        delegateConn.verifyRegistrationStateEquals(s);
+            TestSipDelegate delegate) {
         transportImpl.waitForLatchCountdownAndReset(TestSipTransport.LATCH_DESTROY_DELEGATE);
         delegate.notifyOnDestroyed(
                 SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
@@ -992,7 +1855,7 @@
 
     private void verifyRegisteredAndSendSipConfig(TestSipDelegateConnection delegateConn,
             TestSipDelegate delegate, Set<String> registeredTags,
-            Set<FeatureTagState> deniedTags, SipDelegateImsConfiguration sipConfig) {
+            Set<FeatureTagState> deniedTags, SipDelegateConfiguration sipConfig) {
         // wait for reg change to be called
         delegateConn.setOperationCountDownLatch(1);
         DelegateRegistrationState s = getRegisteredRegistrationState(registeredTags);
@@ -1005,6 +1868,25 @@
         sendConfigChange(sipConfig, delegateConn, delegate);
     }
 
+    private void verifyRegisteredAndSendOldSipConfig(TestSipDelegateConnection delegateConn,
+            TestSipDelegate delegate, Set<String> registeredTags,
+            Set<FeatureTagState> deniedTags) {
+        // wait for reg change to be called
+        delegateConn.setOperationCountDownLatch(1);
+        DelegateRegistrationState s = getRegisteredRegistrationState(registeredTags);
+        delegate.notifyImsRegistrationUpdate(s);
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyRegistrationStateRegistered(registeredTags);
+        delegateConn.verifyDenied(deniedTags);
+
+        delegateConn.setOperationCountDownLatch(1);
+        // Use the old config here and ensure a equivalent version of the new config is received
+        // by app.
+        delegate.notifyImsConfigurationUpdate(generateOldConfig());
+        delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        delegateConn.verifyConfigEquals(generateNewTestConfig());
+    }
+
     private Set<FeatureTagState> generateDeniedSetFromRequest(Set<String> grantedTags,
             Set<String> newTags, int reason) {
         // Deny features from newTags that are already granted in grantedTags.
@@ -1021,53 +1903,223 @@
                 ImsUtils.TEST_TIMEOUT_MS));
     }
 
-    private void verifyFullRegistrationTriggered(SipDelegateManager manager,
-            TestImsRegistration regImpl, TestSipDelegateConnection delegateConn) throws Exception {
-        delegateConn.verifyDelegateCreated();
-        delegateConn.triggerFullNetworkRegistration(manager, 403, "FORBIDDEN");
+    private void sendRestrictedRequestsAndVerifyFailed(
+            TestSipDelegateConnection delegateConn) throws Exception {
+        delegateConn.sendMessageAndVerifyFailure(SipMessageUtils.TEST_INVALID_SIP_REGISTER,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE);
+        delegateConn.sendMessageAndVerifyFailure(SipMessageUtils.TEST_INVALID_SIP_PUBLISH,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE);
+        delegateConn.sendMessageAndVerifyFailure(SipMessageUtils.TEST_INVALID_SIP_OPTIONS,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE);
+        delegateConn.sendMessageAndVerifyFailure(
+                SipMessageUtils.TEST_INVALID_SIP_SUBSCRIBE_PRESENCE,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS);
+    }
+
+    private void verifyFullRegistrationTriggered(TransportInterfaces ifaces) throws Exception {
+        ifaces.delegateConn.verifyDelegateCreated();
+        ifaces.delegateConn.triggerFullNetworkRegistration(ifaces.manager, 403, "FORBIDDEN");
         TestImsRegistration.NetworkRegistrationInfo info =
-                regImpl.getNextFullNetworkRegRequest(ImsUtils.TEST_TIMEOUT_MS);
+                ifaces.reg.getNextFullNetworkRegRequest(ImsUtils.TEST_TIMEOUT_MS);
         assertNotNull("full registration requested, but ImsRegistrationImplBase "
                 + "implementation did not receive a request.", info);
         assertEquals(403, info.sipCode);
         assertEquals("FORBIDDEN", info.sipReason);
     }
 
-    private void sendMessageAndVerifyAck(TestSipDelegateConnection delegateConn,
+    private void sendInvalidRequestsAndVerifyFailed(
+            TestSipDelegateConnection delegateConn) throws Exception {
+        delegateConn.sendMessageAndVerifyFailure(SipMessageUtils.TEST_SIP_MESSAGE_INVALID_REQUEST,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE);
+        delegateConn.sendMessageAndVerifyFailure(SipMessageUtils.TEST_SIP_MESSAGE_INVALID_RESPONSE,
+                SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_START_LINE);
+    }
+
+    private void verifyOutgoingTransport(TestSipDelegateConnection delegateConn,
             TestSipDelegate delegate) throws Exception {
         // Send a message and ensure it gets received on the other end as well as acked
-        delegateConn.sendMessageAndVerifyCompletedSuccessfully(ImsUtils.TEST_SIP_MESSAGE);
-        delegate.verifyMessageSend(ImsUtils.TEST_SIP_MESSAGE);
-        delegateConn.sendCloseDialog(ImsUtils.TEST_CALL_ID);
-        delegate.verifyCloseDialog(ImsUtils.TEST_CALL_ID);
+        delegateConn.sendMessageAndVerifyCompletedSuccessfully(SipMessageUtils.TEST_SIP_MESSAGE);
+        delegate.verifyMessageSend(SipMessageUtils.TEST_SIP_MESSAGE);
+        delegateConn.sendCleanupSession(SipMessageUtils.TEST_SIP_MESSAGE.getCallIdParameter());
+        delegate.verifyCleanupSession(SipMessageUtils.TEST_SIP_MESSAGE.getCallIdParameter());
         // send a message and notify connection that it failed
         delegate.setSendMessageDenyReason(
                 SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
-        delegateConn.sendMessageAndVerifyFailure(ImsUtils.TEST_SIP_MESSAGE,
+        delegateConn.sendMessageAndVerifyFailure(SipMessageUtils.TEST_SIP_MESSAGE,
                 SipDelegateManager.MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE);
-        delegate.verifyMessageSend(ImsUtils.TEST_SIP_MESSAGE);
+        delegate.verifyMessageSend(SipMessageUtils.TEST_SIP_MESSAGE);
     }
 
-    private void receiveMessageAndVerifyAck(TestSipDelegateConnection delegateConn,
+    private void sendMessageAndVerifyAck(SipMessage message,
+            TransportInterfaces ifaces) throws Exception {
+        // Send a message and ensure it gets received on the other end as well as acked
+        ifaces.delegateConn.sendMessageAndVerifyCompletedSuccessfully(message);
+    }
+
+    private void verifyIncomingTransport(TestSipDelegateConnection delegateConn,
             TestSipDelegate delegate) throws Exception {
         // Receive a message and ensure it gets received on the other end as well as acked
-        delegate.receiveMessageAndVerifyReceivedCalled(ImsUtils.TEST_SIP_MESSAGE);
-        delegateConn.verifyMessageReceived(ImsUtils.TEST_SIP_MESSAGE);
+        delegate.receiveMessageAndVerifyReceivedCalled(SipMessageUtils.TEST_SIP_MESSAGE);
+        delegateConn.verifyMessageReceived(SipMessageUtils.TEST_SIP_MESSAGE);
         // Receive a message and have connection notify that it didn't complete
         delegateConn.setReceivedMessageErrorResponseReason(
                 SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT);
-        delegate.receiveMessageAndVerifyReceiveErrorCalled(ImsUtils.TEST_SIP_MESSAGE,
+        delegate.receiveMessageAndVerifyReceiveErrorCalled(SipMessageUtils.TEST_SIP_MESSAGE,
                 SipDelegateManager.MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT);
     }
 
-    private void sendConfigChange(SipDelegateImsConfiguration c,
+    private void receiveMessageAndVerifyAck(SipMessage message,
+            TransportInterfaces ifaces) throws Exception {
+        // Receive a message and ensure it gets received on the other end as well as acked
+        ifaces.delegate.receiveMessageAndVerifyReceivedCalled(message);
+        ifaces.delegateConn.verifyMessageReceived(message);
+    }
+
+    private void verifyRegistrationState(TransportInterfaces ifaces,
+            DelegateRegistrationState state) {
+        ifaces.delegateConn.setOperationCountDownLatch(1);
+        ifaces.delegate.notifyImsRegistrationUpdate(state);
+        ifaces.delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
+        ifaces.delegateConn.verifyRegistrationStateEquals(state);
+    }
+
+    private DelegateRegistrationState getDeregisteringState(Set<String> registered,
+            Set<String> deregistering, int deregisteringReason) {
+        DelegateRegistrationState.Builder b = new DelegateRegistrationState.Builder();
+        b.addRegisteredFeatureTags(registered);
+        for (String dereg : deregistering) {
+            b.addDeregisteringFeatureTag(dereg, deregisteringReason);
+        }
+        return b.build();
+    }
+
+    private void sendConfigChange(SipDelegateConfiguration c,
             TestSipDelegateConnection delegateConn, TestSipDelegate delegate) {
         delegateConn.setOperationCountDownLatch(1);
-        delegate.notifyImsConfigurationUpdate(c);
+        delegate.notifyConfigurationUpdate(c);
         delegateConn.waitForCountDown(ImsUtils.TEST_TIMEOUT_MS);
         delegateConn.verifyConfigEquals(c);
     }
 
+    /**
+     * @return A new test SipDelegateConfiguration that has all fields populated.1
+     */
+    private SipDelegateConfiguration generateNewTestConfig() {
+        InetSocketAddress localAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("1.1.1.1"), 80);
+        InetSocketAddress serverAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("2.2.2.2"), 81);
+        SipDelegateConfiguration.Builder b = new SipDelegateConfiguration.Builder(1,
+                SipDelegateConfiguration.SIP_TRANSPORT_TCP, localAddr, serverAddr);
+        b.setSipCompactFormEnabled(true);
+        b.setSipKeepaliveEnabled(true);
+        b.setMaxUdpPayloadSizeBytes(508);
+        b.setPublicUserIdentifier("test1");
+        b.setPrivateUserIdentifier("test2");
+        b.setHomeDomain("test.domain");
+        b.setImei("testImei");
+        b.setSipAuthenticationHeader("sipauth");
+        b.setSipAuthenticationNonce("sipnonce");
+        b.setSipServiceRouteHeader("srvroute");
+        b.setSipPathHeader("path");
+        b.setSipUserAgentHeader("ua");
+        b.setSipContactUserParameter("user");
+        b.setSipPaniHeader("pani");
+        b.setSipPlaniHeader("plani");
+        b.setSipCniHeader("cni");
+        b.setSipAssociatedUriHeader("uri");
+        Uri gruuUri = Uri.parse("sip:blah@gruu.net");
+        b.setPublicGruuUri(gruuUri);
+        SipDelegateConfiguration.IpSecConfiguration ipSecConfig =
+                new SipDelegateConfiguration.IpSecConfiguration(123, 124,
+                        125, 126, 127, 128, "secverify");
+        assertEquals(123, ipSecConfig.getLocalTxPort());
+        assertEquals(124, ipSecConfig.getLocalRxPort());
+        assertEquals(125, ipSecConfig.getLastLocalTxPort());
+        assertEquals(126, ipSecConfig.getRemoteTxPort());
+        assertEquals(127, ipSecConfig.getRemoteRxPort());
+        assertEquals(128, ipSecConfig.getLastRemoteTxPort());
+        assertEquals("secverify", ipSecConfig.getSipSecurityVerifyHeader());
+        b.setIpSecConfiguration(ipSecConfig);
+        InetSocketAddress natAddr = new InetSocketAddress(
+                InetAddresses.parseNumericAddress("3.3.3.3"), 129);
+        b.setNatSocketAddress(natAddr);
+        assertEquals("3.3.3.3", natAddr.getAddress().getHostAddress());
+        assertEquals(129, natAddr.getPort());
+        return b.build();
+    }
+
+
+    /**
+     * @return A instance of the deprecated SipDelegateImConfiguration, which has the same
+     * equivalent configuration as {@link #generateNewTestConfig()}.
+     */
+    private SipDelegateImsConfiguration generateOldConfig() {
+        return new SipDelegateImsConfiguration.Builder(1)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING,
+                        SipDelegateImsConfiguration.SIP_TRANSPORT_TCP)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING,
+                        "1.1.1.1")
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT, 80)
+                .addString(SipDelegateImsConfiguration
+                                .KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING,
+                        "2.2.2.2")
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT, 81)
+                .addBoolean(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL,
+                        true)
+                .addBoolean(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL,
+                        true)
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT,
+                        508)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING,
+                        "test1")
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING,
+                        "test2")
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_HOME_DOMAIN_STRING,
+                        "test.domain")
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IMEI_STRING, "testImei")
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING,
+                        "sipauth")
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING,
+                        "sipnonce")
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING,
+                        "srvroute")
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_PATH_HEADER_STRING, "path")
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING,
+                        "ua")
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_URI_USER_PART_STRING, "user")
+                .addString(SipDelegateImsConfiguration
+                        .KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING, "pani")
+                .addString(SipDelegateImsConfiguration
+                        .KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING, "plani")
+                .addString(SipDelegateImsConfiguration
+                        .KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING, "cni")
+                .addString(
+                        SipDelegateImsConfiguration.KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING,
+                        "uri")
+                .addBoolean(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, true)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING,
+                        "sip:blah@gruu.net")
+                .addBoolean(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL, true)
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT, 123)
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT, 124)
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT,
+                        125)
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT,
+                        126)
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT,
+                        127)
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT,
+                        128)
+                .addString(SipDelegateImsConfiguration.KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING,
+                        "secverify")
+                .addBoolean(SipDelegateImsConfiguration.KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL, true)
+                .addString(SipDelegateImsConfiguration
+                        .KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING, "3.3.3.3")
+                .addInt(SipDelegateImsConfiguration.KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT, 129)
+                .build();
+    }
+
     private DelegateRegistrationState getRegisteredRegistrationState(Set<String> registered) {
         return new DelegateRegistrationState.Builder().addRegisteredFeatureTags(registered).build();
     }
@@ -1151,17 +2203,14 @@
     }
 
     private DelegateRequest getDefaultRequest() {
-        ArraySet<String> features = new ArraySet<>(3);
-        features.add(TestSipTransport.ONE_TO_ONE_CHAT_TAG);
-        features.add(TestSipTransport.GROUP_CHAT_TAG);
-        features.add(TestSipTransport.FILE_TRANSFER_HTTP_TAG);
+        ArraySet<String> features = new ArraySet<>(Arrays.asList(DEFAULT_FEATURE_TAGS));
         return new DelegateRequest(features);
     }
 
     private DelegateRequest getChatOnlyRequest() {
         ArraySet<String> features = new ArraySet<>(3);
-        features.add(TestSipTransport.ONE_TO_ONE_CHAT_TAG);
-        features.add(TestSipTransport.GROUP_CHAT_TAG);
+        features.add(ONE_TO_ONE_CHAT_TAG);
+        features.add(GROUP_CHAT_TAG);
         return new DelegateRequest(features);
     }
 
@@ -1172,12 +2221,18 @@
                 .addFeature(sTestSlot, ImsFeature.FEATURE_RCS)
                 .build();
     }
+
     private ImsFeatureConfiguration getConfigForRcs() {
         return new ImsFeatureConfiguration.Builder()
                 .addFeature(sTestSlot, ImsFeature.FEATURE_RCS)
                 .build();
     }
 
+    private Set<FeatureTagState> getDeniedTagsForReason(Set<String> deniedTags, int reason) {
+        return deniedTags.stream().map(t -> new FeatureTagState(t, reason))
+                .collect(Collectors.toSet());
+    }
+
     private static void overrideCarrierConfig(PersistableBundle bundle) throws Exception {
         CarrierConfigManager carrierConfigManager = InstrumentationRegistry.getInstrumentation()
                 .getContext().getSystemService(CarrierConfigManager.class);
@@ -1187,6 +2242,13 @@
         sReceiver.waitForCarrierConfigChanged();
     }
 
+    private static void setFeatureTagsCarrierAllowed(String[] tags) throws Exception {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putStringArray(CarrierConfigManager.Ims.KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY,
+                tags);
+        overrideCarrierConfig(bundle);
+    }
+
     private SipDelegateManager getSipDelegateManager() {
         ImsManager imsManager = getContext().getSystemService(ImsManager.class);
         assertNotNull(imsManager);
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDialogAttributes.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDialogAttributes.java
new file mode 100644
index 0000000..dc8e9b2
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipDialogAttributes.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.cts;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import android.net.Uri;
+import android.util.ArraySet;
+import android.util.Base64;
+
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.Set;
+
+public class SipDialogAttributes {
+
+    // Generates sequential string values starting from a random number.
+    private static volatile int sNextStringCounter;
+    static {
+        sNextStringCounter = new Random().nextInt();
+    }
+
+    public final String branchId;
+    public final String callId;
+    public final String fromHeader;
+    public final String fromTag;
+    public final String toUri;
+    public final String toHeader;
+    private final String mFromUri;
+    private final Set<String> mAcceptContactTags;
+    private String mToTag;
+
+    public SipDialogAttributes() {
+        branchId = getNextString();
+        callId = getNextString();
+        mFromUri = getNextSipUri();
+        fromHeader = generateContactUri(mFromUri);
+        fromTag = getNextString();
+        toUri = getNextSipUri();
+        toHeader = generateContactUri(toUri);
+        mAcceptContactTags = new ArraySet<>();
+    }
+
+    private SipDialogAttributes(String branchId, String callId, String fromUri,
+            String fromTag, String toUri, String toTag, Set<String> acceptContactTags) {
+        this.branchId = branchId;
+        this.callId = callId;
+        this.mFromUri = fromUri;
+        this.fromHeader = generateContactUri(fromUri);
+        this.fromTag = fromTag;
+        this.toUri = toUri;
+        this.toHeader = generateContactUri(toUri);
+        mToTag = toTag;
+        mAcceptContactTags = acceptContactTags;
+    }
+
+    public void setToTag() {
+        if (mToTag == null) {
+            mToTag = getNextString();
+        }
+    }
+
+    public String getToTag() {
+        return mToTag;
+    }
+
+    /**
+     * Add a tag to the Accept-Contact header feature tag list.
+     */
+    public void addAcceptContactTag(String featureTag) {
+        mAcceptContactTags.add(featureTag);
+    }
+
+    /**
+     * @return The feature tags in the Accept-Contact feature tag list.
+     */
+    public Set<String> getAcceptContactTags() {
+        return mAcceptContactTags;
+    }
+
+    /**
+     * @return The same attributes with a refreshed via branch param.
+     */
+    public SipDialogAttributes copyWithNewBranch() {
+        return new SipDialogAttributes(branchId, callId, mFromUri, fromTag, toUri,
+                mToTag, mAcceptContactTags);
+    }
+
+    public SipDialogAttributes fromExisting() {
+        return new SipDialogAttributes(branchId, callId, mFromUri, fromTag, toUri,
+                null, mAcceptContactTags);
+    }
+
+    public SipDialogAttributes invertFromTo() {
+        return new SipDialogAttributes(branchId, callId, toUri, fromTag, mFromUri, mToTag,
+                mAcceptContactTags);
+    }
+
+    private String getNextSipUri() {
+        return "sip:" + getNextString() + "@" + SipMessageUtils.BASE_ADDRESS;
+    }
+
+    private String getNextString() {
+        // Get a string representation of the entry counter
+        byte[] idByteArray = ByteBuffer.allocate(4).putInt(sNextStringCounter++).array();
+        return Base64.encodeToString(idByteArray,
+                Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE);
+    }
+
+    private String generateContactUri(String sipUri) {
+        Uri uri = Uri.parse(sipUri);
+        assertNotNull(uri);
+        String[] user = uri.getSchemeSpecificPart().split("@", 2);
+        assertNotNull(user);
+        assertEquals(2, user.length);
+        return user[0] + " <" + sipUri + ">";
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/SipMessageParsingTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipMessageParsingTest.java
index a260caf..002833d 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/SipMessageParsingTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipMessageParsingTest.java
@@ -16,14 +16,14 @@
 
 package android.telephony.ims.cts;
 
+import static junit.framework.Assert.fail;
+
 import static org.junit.Assert.assertEquals;
 
 import android.telephony.ims.SipMessage;
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.telephony.SipMessageParsingUtils;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -189,41 +189,43 @@
 
     @Test
     public void testGetViaBranch() {
-        assertEquals(SIP_MESSAGE_1_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
-                SIP_MESSAGE_1.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_2_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
-                SIP_MESSAGE_2.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_3_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
-                SIP_MESSAGE_3.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_4_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
-                SIP_MESSAGE_4.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_5_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
-                SIP_MESSAGE_5.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_6_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
-                SIP_MESSAGE_6.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_7_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
-                SIP_MESSAGE_7.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_8_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
-                SIP_MESSAGE_8.getHeaderSection()));
+        assertEquals(SIP_MESSAGE_1_TRANSACTION_ID, SIP_MESSAGE_1.getViaBranchParameter());
+        assertEquals(SIP_MESSAGE_2_TRANSACTION_ID, SIP_MESSAGE_2.getViaBranchParameter());
+        assertEquals(SIP_MESSAGE_3_TRANSACTION_ID, SIP_MESSAGE_3.getViaBranchParameter());
+        assertEquals(SIP_MESSAGE_4_TRANSACTION_ID, SIP_MESSAGE_4.getViaBranchParameter());
+        assertEquals(SIP_MESSAGE_5_TRANSACTION_ID, SIP_MESSAGE_5.getViaBranchParameter());
+        assertEquals(SIP_MESSAGE_6_TRANSACTION_ID, SIP_MESSAGE_6.getViaBranchParameter());
+        assertEquals(SIP_MESSAGE_7_TRANSACTION_ID, SIP_MESSAGE_7.getViaBranchParameter());
+        assertEquals(SIP_MESSAGE_8_TRANSACTION_ID, SIP_MESSAGE_8.getViaBranchParameter());
+
+        try {
+            new SipMessage(
+                    "INVITE sip:bob@biloxi.com SIP/2.0",
+                    "Max-Forwards: 70\n"
+                            + "To: Bob <sip:bob@biloxi.com>\n"
+                            + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                            + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+                            + "CSeq: 314159 INVITE\n"
+                            + "Contact: <sip:alice@pc33.atlanta.com>\n"
+                            + "Content-Type: application/sdp\n"
+                            + "Content-Length: 142\n",
+                    new byte[0]);
+            fail("A SipMessage must throw an IllegalArgumentException if no Via branch parameter "
+                    + "is present in the headers");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
     }
 
     @Test
     public void testGetCallId() {
-        assertEquals(SIP_MESSAGE_1_CALL_ID, SipMessageParsingUtils.getCallId(
-                SIP_MESSAGE_1.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_2_CALL_ID, SipMessageParsingUtils.getCallId(
-                SIP_MESSAGE_2.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_3_CALL_ID, SipMessageParsingUtils.getCallId(
-                SIP_MESSAGE_3.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_4_CALL_ID, SipMessageParsingUtils.getCallId(
-                SIP_MESSAGE_4.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_5_CALL_ID, SipMessageParsingUtils.getCallId(
-                SIP_MESSAGE_5.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_6_CALL_ID, SipMessageParsingUtils.getCallId(
-                SIP_MESSAGE_6.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_7_CALL_ID, SipMessageParsingUtils.getCallId(
-                SIP_MESSAGE_7.getHeaderSection()));
-        assertEquals(SIP_MESSAGE_8_CALL_ID, SipMessageParsingUtils.getCallId(
-                SIP_MESSAGE_8.getHeaderSection()));
+        assertEquals(SIP_MESSAGE_1_CALL_ID, SIP_MESSAGE_1.getCallIdParameter());
+        assertEquals(SIP_MESSAGE_2_CALL_ID, SIP_MESSAGE_2.getCallIdParameter());
+        assertEquals(SIP_MESSAGE_3_CALL_ID, SIP_MESSAGE_3.getCallIdParameter());
+        assertEquals(SIP_MESSAGE_4_CALL_ID, SIP_MESSAGE_4.getCallIdParameter());
+        assertEquals(SIP_MESSAGE_5_CALL_ID, SIP_MESSAGE_5.getCallIdParameter());
+        assertEquals(SIP_MESSAGE_6_CALL_ID, SIP_MESSAGE_6.getCallIdParameter());
+        assertEquals(SIP_MESSAGE_7_CALL_ID, SIP_MESSAGE_7.getCallIdParameter());
+        assertEquals(SIP_MESSAGE_8_CALL_ID, SIP_MESSAGE_8.getCallIdParameter());
     }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/SipMessageUtils.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipMessageUtils.java
new file mode 100644
index 0000000..4ba11603
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/SipMessageUtils.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.cts;
+
+import android.telephony.ims.SipMessage;
+
+import java.util.Set;
+
+
+public class SipMessageUtils {
+
+    public static final String INVITE_SIP_METHOD = "INVITE";
+    public static final String CANCEL_SIP_METHOD = "CANCEL";
+    public static final String BYE_SIP_METHOD = "BYE";
+    public static final String ACK_SIP_METHOD = "ACK";
+    public static final String MESSAGE_SIP_METHOD = "MESSAGE";
+    public static final String BASE_ADDRESS = "client.example.com";
+
+    private static final String PARAM_SEPARATOR = ";";
+    private static final String PARAM_KEY_VALUE_SEPARATOR = "=";
+    private static final String BASE_VIA_HEADER_VALUE = "SIP/2.0/TCP " + BASE_ADDRESS + ":5060";
+
+    public static final SipMessage TEST_SIP_MESSAGE = generateSipRequest(MESSAGE_SIP_METHOD,
+            new SipDialogAttributes());
+
+    // Example from RFC 3665
+    public static final SipMessage TEST_INVALID_SIP_REGISTER = new SipMessage(
+            "REGISTER sips:ss2.biloxi.example.com SIP/2.0",
+            "Via: SIP/2.0/TLS client.biloxi.example.com:5061;branch=z9hG4bKnashds7\n"
+                    + "Max-Forwards: 70\n"
+                    + "From: Bob <sips:bob@biloxi.example.com>;tag=a73kszlfl\n"
+                    + "To: Bob <sips:bob@biloxi.example.com>\n"
+                    + "Call-ID: 1j9FpLxk3uxtm8tn@biloxi.example.com\n"
+                    + "CSeq: 1 REGISTER\n"
+                    + "Contact: <sips:bob@client.biloxi.example.com>\n"
+                    + "Content-Length: 0",
+            new byte[0]);
+
+    //Example from RFC3903, but does not include PIDF document. PUBLISH is not allowed.
+    public static final SipMessage TEST_INVALID_SIP_PUBLISH = new SipMessage(
+            "PUBLISH sip:presentity@example.com SIP/2.0",
+            "Via: SIP/2.0/UDP pua.example.com;branch=z9hG4bKcdad2\n"
+                    + "To: <sip:presentity@example.com>\n"
+                    + "From: <sip:presentity@example.com>;tag=54321mm\n"
+                    + "Call-ID: 5566778@pua.example.com\n"
+                    + "CSeq: 1 PUBLISH\n"
+                    + "Max-Forwards: 70\n"
+                    + "Expires: 3600\n"
+                    + "Event: presence\n"
+                    + "Content-Type: application/pidf+xml",
+            new byte[0]);
+
+    //Example from RFC3856 - SUBSCRIBE with event presence is not allowed
+    public static final SipMessage TEST_INVALID_SIP_SUBSCRIBE_PRESENCE = new SipMessage(
+            "SUBSCRIBE sip:resource@example.com SIP/2.0",
+            "Via: SIP/2.0/TCP watcherhost.example.com;branch=z9hG4bKnashds7\n"
+                    + "To: <sip:resource@example.com>\n"
+                    + "From: <sip:user@example.com>;tag=xfg9\n"
+                    + "Call-ID: 2010@watcherhost.example.com\n"
+                    + "CSeq: 17766 SUBSCRIBE\n"
+                    + "Max-Forwards: 70\n"
+                    + "Event: presence\n"
+                    + "Accept: application/pidf+xml\n"
+                    + "Contact: <sip:user@watcherhost.example.com>\n"
+                    + "Expires: 600\n"
+                    + "Content-Length: 0",
+            new byte[0]);
+
+    //Example from RFC3261, OPTIONS not allowed from app
+    public static final SipMessage TEST_INVALID_SIP_OPTIONS = new SipMessage(
+            "OPTIONS sip:carol@chicago.com SIP/2.0",
+            "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKhjhs8ass877\n"
+                    + "Max-Forwards: 70\n"
+                    + "To: <sip:carol@chicago.com>\n"
+                    + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                    + "Call-ID: a84b4c76e66710\n"
+                    + "CSeq: 63104 OPTIONS\n"
+                    + "Contact: <sip:alice@pc33.atlanta.com>\n"
+                    + "Accept: application/sdp\n"
+                    + "Content-Length: 0",
+            new byte[0]);
+
+    // Sample message from RFC 3261, malformed request start line
+    public static final SipMessage TEST_SIP_MESSAGE_INVALID_REQUEST = new SipMessage(
+            "INVITE sip:bob@biloxi.comSIP/2.0",
+            // Typical Via
+            "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK.TeSt\n"
+                    + "Max-Forwards: 70\n"
+                    + "To: Bob <sip:bob@biloxi.com>\n"
+                    + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+                    + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+                    + "CSeq: 314159 INVITE\n"
+                    + "Contact: <sip:alice@pc33.atlanta.com>\n"
+                    + "Content-Type: application/sdp\n"
+                    + "Content-Length: 142",
+            new byte[0]);
+
+    // Sample message from RFC 3261, malformed response start line
+    public static final SipMessage TEST_SIP_MESSAGE_INVALID_RESPONSE = new SipMessage(
+            "SIP/2.0 200OK",
+            "Via: SIP/2.0/TCP terminal.vancouver.example.com;"
+                    + "branch=z9hG4bKwYb6QREiCL\n"
+                    + "To: <sip:adam-buddies@pres.vancouver.example.com>;tag=zpNctbZq\n"
+                    + "From: <sip:adam@vancouver.example.com>;tag=ie4hbb8t\n"
+                    + "Call-ID: cdB34qLToC@terminal.vancouver.example.com\n"
+                    + "CSeq: 322723822 SUBSCRIBE\n"
+                    + "Contact: <sip:pres.vancouver.example.com>\n"
+                    + "Expires: 7200\n"
+                    + "Require: eventlist\n"
+                    + "Content-Length: 0",
+            new byte[0]);
+
+    /**
+     * @return A new SIP request from the given parameters.
+     */
+    public static SipMessage generateSipRequest(String requestMethod, String fromContact,
+            String toContact, String toUri, String branchId, String callId, String fromTag,
+            String toTag, Set<String> acceptContactTags) {
+        String header = "Via: " + addParamToHeader(BASE_VIA_HEADER_VALUE, "branch", branchId);
+        header += "\n";
+        header += "From: " + addParamToHeader(fromContact, "tag", fromTag);
+        header += "\n";
+        // To tag is optional
+        header += "To: " + ((toTag != null)
+                ? addParamToHeader(toContact, "tag", toTag) : toContact);
+        header += "\n";
+        header += "Call-ID: " + callId;
+        if (!acceptContactTags.isEmpty()) {
+            header += "\n";
+            header += "Accept-Contact: *";
+            for (String tag : acceptContactTags) {
+                header += ";" + tag;
+            }
+        }
+        return new SipMessage(requestMethod + " " + toUri + " SIP/2.0", header, new byte[0]);
+    }
+
+    /**
+     * @return Generates a SIP response.
+     */
+    public static SipMessage generateSipResponse(String statusCode, String statusString,
+            String fromContact, String toContact, String branchId, String callId, String fromTag,
+            String toTag) {
+        String header = "Via: " + addParamToHeader(BASE_VIA_HEADER_VALUE, "branch", branchId);
+        header += "\n";
+        header += "From: " + addParamToHeader(fromContact, "tag", fromTag);
+        header += "\n";
+        // To tag is optional
+        header += "To: " + ((toTag != null)
+                ? addParamToHeader(toContact, "tag", toTag) : toContact);
+        header += "\n";
+        header += "Call-ID: " + callId;
+        return new SipMessage("SIP/2.0 " + statusCode + " " + statusString, header,
+                new byte[0]);
+    }
+
+    public static SipMessage generateSipRequest(String requestMethod, SipDialogAttributes attr) {
+        return generateSipRequest(requestMethod, attr.fromHeader, attr.toHeader,
+                attr.toUri, attr.branchId, attr.callId, attr.fromTag, attr.getToTag(),
+                attr.getAcceptContactTags());
+    }
+
+    public static SipMessage generateSipResponse(String statusCode, String statusString,
+            SipDialogAttributes attr) {
+        return generateSipResponse(statusCode, statusString, attr.fromHeader,
+                attr.toHeader, attr.branchId, attr.callId, attr.fromTag, attr.getToTag());
+    }
+
+    private static String addParamToHeader(String headerValue, String paramKey, String paramValue) {
+        headerValue += PARAM_SEPARATOR + paramKey.trim() + PARAM_KEY_VALUE_SEPARATOR
+                + paramValue.trim();
+        return headerValue;
+    }
+}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestAcsClient.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestAcsClient.java
index 1cdf186..513ce83 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestAcsClient.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestAcsClient.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.cts;
 
 import android.telephony.ims.RcsClientConfiguration;
+import android.telephony.ims.stub.ImsConfigImplBase;
 
 import java.util.concurrent.LinkedBlockingQueue;
 
@@ -29,6 +30,7 @@
     private LinkedBlockingQueue<Integer> mActionQueue = new LinkedBlockingQueue<>();
     private RcsClientConfiguration mRcc;
     private byte[] mConfig;
+    private ImsConfigImplBase mImsConfigImpl;
 
     private static TestAcsClient sInstance;
 
@@ -77,4 +79,12 @@
         mRcc = null;
         mConfig = null;
     }
+
+    public void setImsConfigImpl(ImsConfigImplBase impl) {
+        mImsConfigImpl = impl;
+    }
+
+    public void notifyPreProvisioning(byte[] conf) {
+        mImsConfigImpl.notifyPreProvisioningReceived(conf);
+    }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsConfig.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsConfig.java
index 22af8e4..10a39d3 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsConfig.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsConfig.java
@@ -26,6 +26,10 @@
     private HashMap<Integer, Integer> mIntHashMap = new HashMap<>();
     private HashMap<Integer, String> mStringHashMap = new HashMap<>();
 
+    TestImsConfig() {
+        TestAcsClient.getInstance().setImsConfigImpl(this);
+    }
+
     @Override
     public int setConfig(int item, int value) {
         mIntHashMap.put(item, value);
@@ -56,16 +60,19 @@
 
     @Override
     public void notifyRcsAutoConfigurationRemoved() {
+        super.notifyRcsAutoConfigurationRemoved();
         TestAcsClient.getInstance().onConfigRemoved();
     }
 
     @Override
     public void setRcsClientConfiguration(RcsClientConfiguration rcc) {
+        super.setRcsClientConfiguration(rcc);
         TestAcsClient.getInstance().onSetRcsClientConfiguration(rcc);
     }
 
     @Override
     public void triggerAutoConfiguration() {
+        super.triggerAutoConfiguration();
         TestAcsClient.getInstance().onTriggerAutoConfiguration();
     }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
index 912d95c..a012513 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestImsService.java
@@ -88,7 +88,7 @@
     interface CapabilitiesSetListener {
         void onSet();
     }
-    interface RcsCapabilitySetListener {
+    interface RcsCapabilityExchangeEventListener {
         void onSet();
     }
     interface DeviceCapPublishListener {
@@ -148,7 +148,7 @@
         public RcsFeature createRcsFeature(int slotId) {
             synchronized (mLock) {
                 countDownLatch(LATCH_CREATE_RCS);
-                mTestRcsFeature = new TestRcsFeature(
+                mTestRcsFeature = new TestRcsFeature(getBaseContext(),
                         //onReady
                         () -> {
                             synchronized (mLock) {
@@ -345,6 +345,23 @@
         return complete;
     }
 
+    public boolean waitForLatchCountdown(int latchIndex, long waitMs) {
+        boolean complete = false;
+        try {
+            CountDownLatch latch;
+            synchronized (mLock) {
+                latch = sLatches[latchIndex];
+            }
+            complete = latch.await(waitMs, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            // complete == false
+        }
+        synchronized (mLock) {
+            sLatches[latchIndex] = new CountDownLatch(1);
+        }
+        return complete;
+    }
+
     public void countDownLatch(int latchIndex) {
         synchronized (mLock) {
             sLatches[latchIndex].countDown();
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsCapabilityExchangeImpl.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsCapabilityExchangeImpl.java
index 99639a3..c41e63e 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsCapabilityExchangeImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsCapabilityExchangeImpl.java
@@ -22,8 +22,8 @@
 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
 import android.util.Log;
 
-import java.util.List;
-import java.util.concurrent.Executor;
+import java.util.Collection;
+import java.util.Set;
 
 /**
  * A implementation class of RcsCapabilityExchangeImplBase for the TestRcsFeature.
@@ -40,12 +40,12 @@
 
     @FunctionalInterface
     public interface SubscribeOperation {
-        void execute(List<Uri> uris, SubscribeResponseCallback cb) throws ImsException;
+        void execute(Collection<Uri> uris, SubscribeResponseCallback cb) throws ImsException;
     }
 
     @FunctionalInterface
     public interface OptionsOperation {
-        void execute(Uri contactUri, List<String> myCapabilities, OptionsResponseCallback callback)
+        void execute(Uri contactUri, Set<String> myCapabilities, OptionsResponseCallback callback)
                 throws ImsException;
     }
 
@@ -62,10 +62,8 @@
 
     /**
      * Create a new RcsCapabilityExchangeImplBase instance.
-     * @param executor The executor that remote calls from the framework will be called on.
      */
-    public TestRcsCapabilityExchangeImpl(Executor executor, DeviceCapPublishListener listener) {
-        super(executor);
+    public TestRcsCapabilityExchangeImpl(DeviceCapPublishListener listener) {
         mPublishListener = listener;
     }
 
@@ -91,7 +89,7 @@
     }
 
     @Override
-    public void subscribeForCapabilities(List<Uri> uris, SubscribeResponseCallback cb) {
+    public void subscribeForCapabilities(Collection<Uri> uris, SubscribeResponseCallback cb) {
         try {
             mSubscribeOperation.execute(uris, cb);
         } catch (ImsException e) {
@@ -100,7 +98,7 @@
     }
 
     @Override
-    public void sendOptionsCapabilityRequest(Uri contactUri, List<String> myCapabilities,
+    public void sendOptionsCapabilityRequest(Uri contactUri, Set<String> myCapabilities,
             OptionsResponseCallback callback) {
         try {
             mOptionsOperation.execute(contactUri, myCapabilities, callback);
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java
index 593e61e..08eaa85 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestRcsFeature.java
@@ -16,16 +16,15 @@
 
 package android.telephony.ims.cts;
 
+import android.content.Context;
 import android.telephony.ims.feature.CapabilityChangeRequest;
-import android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair;
-import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.feature.RcsFeature;
 import android.telephony.ims.stub.CapabilityExchangeEventListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.RcsCapabilityExchangeImplBase;
 import android.util.Log;
 
 import java.util.List;
-import java.util.Optional;
 import java.util.concurrent.Executor;
 
 public class TestRcsFeature extends RcsFeature {
@@ -34,24 +33,30 @@
 
     private final TestImsService.ReadyListener mReadyListener;
     private final TestImsService.RemovedListener mRemovedListener;
-    private final TestImsService.CapabilitiesSetListener mCapSetListener;
-    private final TestImsService.RcsCapabilitySetListener mRcsCapabilitySetListener;
+    private final TestImsService.RcsCapabilityExchangeEventListener mCapExchangeEventListener;
+
+    private final RcsImsCapabilities mRcsCapabilitiesLte;
+    private final RcsImsCapabilities mRcsCapabilitiesIWan;
+    private final TestImsService.CapabilitiesSetListener mRcsCapabilityChangedListener;
 
     private TestRcsCapabilityExchangeImpl mCapExchangeImpl;
     private CapabilityExchangeEventListener mCapEventListener;
     private TestImsService.DeviceCapPublishListener mDeviceCapPublishListener;
 
-    private CapabilityChangeRequest mCapabilityChangeRequest;
-    private int mCapabilitiesChangedResult = ImsFeature.CAPABILITY_SUCCESS;
-
-    TestRcsFeature(TestImsService.ReadyListener readyListener,
-            TestImsService.RemovedListener listener,
+    TestRcsFeature(Context context,
+            TestImsService.ReadyListener readyListener,
+            TestImsService.RemovedListener removedListener,
             TestImsService.CapabilitiesSetListener setListener,
-            TestImsService.RcsCapabilitySetListener uceCallbackListener) {
+            TestImsService.RcsCapabilityExchangeEventListener capExchangeEventListener) {
+        super(context.getMainExecutor());
+
         mReadyListener = readyListener;
-        mRemovedListener = listener;
-        mCapSetListener = setListener;
-        mRcsCapabilitySetListener = uceCallbackListener;
+        mRemovedListener = removedListener;
+        mCapExchangeEventListener = capExchangeEventListener;
+
+        mRcsCapabilityChangedListener = setListener;
+        mRcsCapabilitiesLte = new RcsImsCapabilities(RcsImsCapabilities.CAPABILITY_TYPE_NONE);
+        mRcsCapabilitiesIWan = new RcsImsCapabilities(RcsImsCapabilities.CAPABILITY_TYPE_NONE);
 
         setFeatureState(STATE_READY);
     }
@@ -60,10 +65,6 @@
         mDeviceCapPublishListener = listener;
     }
 
-    public void overrideCapabilitiesEnabledResult(int result) {
-        mCapabilitiesChangedResult = result;
-    }
-
     @Override
     public void onFeatureReady() {
         if (ImsUtils.VDBG) {
@@ -82,20 +83,28 @@
 
     public RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(Executor executor,
             CapabilityExchangeEventListener listener) {
+        return createCapabilityExchangeImpl(listener);
+    }
+
+    @Override
+    public RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(
+            CapabilityExchangeEventListener listener) {
         if (ImsUtils.VDBG) {
             Log.d(TAG, "TestRcsFeature.createCapabilityExchangeImpl called");
         }
         mCapEventListener = listener;
-        mCapExchangeImpl = new TestRcsCapabilityExchangeImpl(executor, mDeviceCapPublishListener);
-        mRcsCapabilitySetListener.onSet();
+        mCapExchangeImpl = new TestRcsCapabilityExchangeImpl(mDeviceCapPublishListener);
+        mCapExchangeEventListener.onSet();
         return mCapExchangeImpl;
     }
 
-    public void removeCapabilityExchangeImpl(RcsCapabilityExchangeImplBase capExchangeImpl) {
+    @Override
+    public void destroyCapabilityExchangeImpl(RcsCapabilityExchangeImplBase capExchangeImpl) {
         if (ImsUtils.VDBG) {
-            Log.d(TAG, "TestRcsFeature.removeCapabilityExchangeImpl called");
+            Log.d(TAG, "TestRcsFeature.destroyCapabilityExchangeImpl called");
         }
-        mRcsCapabilitySetListener.onSet();
+        mCapEventListener = null;
+        mCapExchangeEventListener.onSet();
     }
 
     public CapabilityExchangeEventListener getEventListener() {
@@ -109,26 +118,39 @@
     @Override
     public void changeEnabledCapabilities(CapabilityChangeRequest request,
             CapabilityCallbackProxy c) {
-        // Trigger the error callback if the result is failed
-        if (mCapabilitiesChangedResult != ImsFeature.CAPABILITY_SUCCESS) {
-            CapabilityChangeRequest.CapabilityPair capPair = request.getCapabilitiesToEnable()
-                    .get(0);
-            c.onChangeCapabilityConfigurationError(capPair.getCapability(), capPair.getRadioTech(),
-                    ImsFeature.CAPABILITY_ERROR_GENERIC);
-            return;
+
+        // Enabled RCS capabilities
+        List<CapabilityChangeRequest.CapabilityPair> pairs = request.getCapabilitiesToEnable();
+        for (CapabilityChangeRequest.CapabilityPair pair : pairs) {
+            if (pair.getRadioTech() == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+                mRcsCapabilitiesLte.addCapabilities(pair.getCapability());
+            } else if (pair.getRadioTech() == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+                mRcsCapabilitiesIWan.addCapabilities(pair.getCapability());
+            }
         }
-        mCapabilityChangeRequest = request;
-        // Notify that the capabilities is changed.
-        mCapSetListener.onSet();
+
+        // Disabled RCS capabilities
+        pairs = request.getCapabilitiesToDisable();
+        for (CapabilityChangeRequest.CapabilityPair pair : pairs) {
+            if (pair.getRadioTech() == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+                mRcsCapabilitiesLte.removeCapabilities(pair.getCapability());
+            } else if (pair.getRadioTech() == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+                mRcsCapabilitiesIWan.removeCapabilities(pair.getCapability());
+            }
+        }
+        mRcsCapabilityChangedListener.onSet();
     }
 
     @Override
     public boolean queryCapabilityConfiguration(int capability, int radioTech) {
-        List<CapabilityPair> pairList =  mCapabilityChangeRequest.getCapabilitiesToEnable();
-        if (pairList == null) return false;
-        Optional<CapabilityPair> queryResult = pairList.stream().filter(pair -> {
-            return (pair.getCapability() == capability) && (pair.getRadioTech() == radioTech);
-        }).findAny();
-        return queryResult.isPresent();
+        if (ImsUtils.VDBG) {
+            Log.d(TAG, "TestRcsFeature.queryCapabilityConfiguration capability: " + capability);
+        }
+        if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_LTE) {
+            return mRcsCapabilitiesLte.isCapable(capability);
+        } else if (radioTech == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+            return mRcsCapabilitiesIWan.isCapable(capability);
+        }
+        return false;
     }
 }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegate.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegate.java
index 729efb9..5040dcf 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegate.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegate.java
@@ -16,6 +16,8 @@
 
 package android.telephony.ims.cts;
 
+import static junit.framework.Assert.assertTrue;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
@@ -24,9 +26,11 @@
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.DelegateStateCallback;
 import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateConfiguration;
 import android.telephony.ims.SipDelegateImsConfiguration;
 import android.telephony.ims.SipMessage;
 import android.telephony.ims.stub.SipDelegate;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 
@@ -48,7 +52,8 @@
     // Pair is <transactionId, error reason>
     private final LinkedBlockingQueue<Pair<String, Integer>> mReceivedMessageAcks =
             new LinkedBlockingQueue<>();
-    private final LinkedBlockingQueue<String> mCloseDialogRequests = new LinkedBlockingQueue<>();
+    private final LinkedBlockingQueue<String> mCleanupSipSessionRequests =
+            new LinkedBlockingQueue<>();
     private int mSendMessageDenyReason = -1;
 
     public TestSipDelegate(int sub, DelegateRequest request, DelegateStateCallback cb,
@@ -64,17 +69,17 @@
         if (ImsUtils.VDBG) Log.d(LOG_TAG, "sendMessage");
         mIncomingMessages.offer(message);
         if (mSendMessageDenyReason > -1) {
-            mMessageCallback.onMessageSendFailure(ImsUtils.TEST_TRANSACTION_ID,
+            mMessageCallback.onMessageSendFailure(message.getViaBranchParameter(),
                     mSendMessageDenyReason);
         } else {
-            mMessageCallback.onMessageSent(ImsUtils.TEST_TRANSACTION_ID);
+            mMessageCallback.onMessageSent(message.getViaBranchParameter());
         }
     }
 
     @Override
-    public void closeDialog(@NonNull String callId) {
-        if (ImsUtils.VDBG) Log.d(LOG_TAG, "closeDialog");
-        mCloseDialogRequests.offer(callId);
+    public void cleanupSession(@NonNull String callId) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "CleanSession");
+        mCleanupSipSessionRequests.offer(callId);
     }
 
     @Override
@@ -94,10 +99,15 @@
         assertEquals(messageToVerify, m);
     }
 
-    public void verifyCloseDialog(String callIdToVerify) throws Exception {
-        String requestedCallId = mCloseDialogRequests.poll(ImsUtils.TEST_TIMEOUT_MS,
-                TimeUnit.MILLISECONDS);
-        assertEquals(callIdToVerify, requestedCallId);
+    public void verifyCleanupSession(String... callIdsToVerify) throws Exception {
+        Set<String> ids = new ArraySet<>(callIdsToVerify);
+        for (int i = 0; i < callIdsToVerify.length; i++) {
+            String requestedCallId = mCleanupSipSessionRequests.poll(ImsUtils.TEST_TIMEOUT_MS,
+                    TimeUnit.MILLISECONDS);
+            assertTrue(ids.contains(requestedCallId));
+            ids.remove(requestedCallId);
+        }
+        assertTrue(ids.isEmpty());
     }
 
     public void setSendMessageDenyReason(int reason) {
@@ -108,7 +118,7 @@
         mMessageCallback.onMessageReceived(m);
         Pair<String, Integer> transactionAndIdPair = mReceivedMessageAcks.poll(
                 ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertEquals(ImsUtils.TEST_TRANSACTION_ID, transactionAndIdPair.first);
+        assertEquals(m.getViaBranchParameter(), transactionAndIdPair.first);
         assertNotNull(transactionAndIdPair.second);
         assertEquals(-1, transactionAndIdPair.second.intValue());
     }
@@ -118,7 +128,7 @@
         mMessageCallback.onMessageReceived(m);
         Pair<String, Integer> transactionAndIdPair = mReceivedMessageAcks.poll(
                 ImsUtils.TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        assertEquals(ImsUtils.TEST_TRANSACTION_ID, transactionAndIdPair.first);
+        assertEquals(m.getViaBranchParameter(), transactionAndIdPair.first);
         assertNotNull(transactionAndIdPair.second);
         assertEquals(reason, transactionAndIdPair.second.intValue());
     }
@@ -127,6 +137,10 @@
         mStateCallback.onFeatureTagRegistrationChanged(state);
     }
 
+    public void notifyConfigurationUpdate(SipDelegateConfiguration config) {
+        mStateCallback.onConfigurationChanged(config);
+    }
+
     public void notifyImsConfigurationUpdate(SipDelegateImsConfiguration config) {
         mStateCallback.onImsConfigurationChanged(config);
     }
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegateConnection.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegateConnection.java
index 1118009..dfa8066 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegateConnection.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipDelegateConnection.java
@@ -23,13 +23,12 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
-import android.os.PersistableBundle;
 import android.telephony.ims.DelegateRegistrationState;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.FeatureTagState;
 import android.telephony.ims.ImsException;
+import android.telephony.ims.SipDelegateConfiguration;
 import android.telephony.ims.SipDelegateConnection;
-import android.telephony.ims.SipDelegateImsConfiguration;
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.SipMessage;
 import android.telephony.ims.stub.DelegateConnectionMessageCallback;
@@ -61,7 +60,7 @@
     public SipDelegateConnection connection;
     public Set<FeatureTagState> deniedTags;
     public DelegateRegistrationState regState;
-    public SipDelegateImsConfiguration sipConfig;
+    public SipDelegateConfiguration sipConfig;
     public final DelegateRequest delegateRequest;
 
     private int mReceivedMessageErrorResponseReason = -1;
@@ -103,10 +102,10 @@
         if (ImsUtils.VDBG) Log.d(LOG_TAG, "onMessageReceived");
         mReceivedMessages.offer(message);
         if (mReceivedMessageErrorResponseReason > -1) {
-            connection.notifyMessageReceiveError(ImsUtils.TEST_TRANSACTION_ID,
+            connection.notifyMessageReceiveError(message.getViaBranchParameter(),
                     mReceivedMessageErrorResponseReason);
         } else {
-            connection.notifyMessageReceived(ImsUtils.TEST_TRANSACTION_ID);
+            connection.notifyMessageReceived(message.getViaBranchParameter());
         }
     }
 
@@ -139,9 +138,9 @@
     }
 
     @Override
-    public void onImsConfigurationChanged(
-            @NonNull SipDelegateImsConfiguration registeredSipConfig) {
-        if (ImsUtils.VDBG) Log.d(LOG_TAG, "onImsConfigurationChanged");
+    public void onConfigurationChanged(
+            @NonNull SipDelegateConfiguration registeredSipConfig) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "onConfigurationChanged");
         sipConfig = registeredSipConfig;
         mLatch.countDown();
     }
@@ -154,9 +153,9 @@
         mLatch.countDown();
     }
 
-    public void sendCloseDialog(String callId) {
-        assertNotNull("SipDelegate was null when closing dialog", connection);
-        connection.closeDialog(callId);
+    public void sendCleanupSession(String callId) {
+        assertNotNull("SipDelegate was null when cleaning up session", connection);
+        connection.cleanupSession(callId);
     }
 
     public void sendMessageAndVerifyCompletedSuccessfully(SipMessage messageToSend)
@@ -166,7 +165,7 @@
         Pair<String, Integer> ack = mSentMessageAcks.poll(ImsUtils.TEST_TIMEOUT_MS,
                 TimeUnit.MILLISECONDS);
         assertNotNull(ack);
-        assertEquals(ImsUtils.TEST_TRANSACTION_ID, ack.first);
+        assertEquals(messageToSend.getViaBranchParameter(), ack.first);
         assertNotNull(ack.second);
         assertEquals(-1, ack.second.intValue());
     }
@@ -201,20 +200,11 @@
         assertNotNull("SipDelegate is null when it should have been created", connection);
     }
 
-    public void verifyConfigEquals(SipDelegateImsConfiguration config) {
+    public void verifyConfigEquals(SipDelegateConfiguration config) {
         assertNotNull("SIP configuration should not be null", sipConfig);
         assertEquals("IMS config version is not correct", config.getVersion(),
                 sipConfig.getVersion());
-        PersistableBundle b = config.copyBundle();
-        for (String key : b.keySet()) {
-            assertTrue("tracked sip config does not contain the key [" + key + "}",
-                    sipConfig.containsKey(key));
-            // Not a true equality check, but close enough for the purposes of this test.
-            assertEquals(config.getString(key), sipConfig.getString(key));
-            assertEquals(config.getInt(key, -1), sipConfig.getInt(key, -1));
-            assertEquals(config.getBoolean(key, false),
-                    sipConfig.getBoolean(key, false));
-        }
+        assertEquals(config, sipConfig);
     }
 
     public void verifyRegistrationStateRegistered() {
@@ -249,6 +239,15 @@
                 regState.getDeregisteredFeatureTags());
     }
 
+    public boolean verifyDeregisteringStateContains(String featureTag, int state) {
+        for (FeatureTagState s : regState.getDeregisteringFeatureTags()) {
+            if (s.getFeatureTag().equals(featureTag) && s.getState() == state) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 
     public void verifyNoneDenied() {
         assertNotNull(deniedTags);
@@ -287,6 +286,7 @@
      * to wait for the operations to occur.
      */
     public void setOperationCountDownLatch(int operationCount) {
+        if (ImsUtils.VDBG) Log.d(LOG_TAG, "setOperationCountDownLatch: " + operationCount);
         mLatch = new CountDownLatch(operationCount);
     }
 
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipTransport.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipTransport.java
index ed5a07b..0ea195c 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipTransport.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestSipTransport.java
@@ -93,6 +93,14 @@
         }
     }
 
+    public boolean isLatchCountDownFinished(int latchIndex) {
+        CountDownLatch latch;
+        synchronized (mLock) {
+            latch = sLatches[latchIndex];
+        }
+        return latch.getCount() <= 0;
+    }
+
     public boolean waitForLatchCountdownAndReset(int latchIndex) {
         boolean complete = false;
         try {
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/UceActivity.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/UceActivity.java
deleted file mode 100644
index 5b631ec..0000000
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/UceActivity.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims.cts;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.SurfaceView;
-import android.view.ViewGroup;
-
-import java.util.concurrent.CountDownLatch;
-
-/**
- * The Activity to run the UCE APIs verification in the foreground.
- */
-public class UceActivity extends Activity {
-
-    public static final String ACTION_FINISH = "android.telephony.ims.cts.action_finish";
-
-    private static CountDownLatch sCountDownLatch;
-
-    public static void setCountDownLatch(CountDownLatch countDownLatch) {
-        sCountDownLatch = countDownLatch;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        SurfaceView mSurfaceView = new SurfaceView(this);
-        mSurfaceView.setWillNotDraw(false);
-        mSurfaceView.setZOrderOnTop(true);
-        setContentView(mSurfaceView,
-                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
-                        ViewGroup.LayoutParams.MATCH_PARENT));
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        if (sCountDownLatch != null) {
-            sCountDownLatch.countDown();
-        }
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        if (ACTION_FINISH.equals(intent.getAction())) {
-            finish();
-            sCountDownLatch = null;
-        }
-    }
-}
diff --git a/tests/tests/telephony/sdk28/src/android/telephony/sdk28/cts/CellInfoTest.java b/tests/tests/telephony/sdk28/src/android/telephony/sdk28/cts/CellInfoTest.java
index dc590b8..e9b7f16 100644
--- a/tests/tests/telephony/sdk28/src/android/telephony/sdk28/cts/CellInfoTest.java
+++ b/tests/tests/telephony/sdk28/src/android/telephony/sdk28/cts/CellInfoTest.java
@@ -30,6 +30,8 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
 import org.junit.Before;
 import org.junit.Test;
 
@@ -49,7 +51,9 @@
     private PackageManager mPm;
 
     private boolean isCamped() {
-        ServiceState ss = mTm.getServiceState();
+        ServiceState ss = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTm, TelephonyManager::getServiceState);
+
         if (ss == null) return false;
         return (ss.getState() == ServiceState.STATE_IN_SERVICE
                 || ss.getState() == ServiceState.STATE_EMERGENCY_ONLY);
@@ -76,7 +80,8 @@
 
         if (!isCamped()) fail("Device is not camped to a cell");
 
-        List<CellInfo> cellInfo = mTm.getAllCellInfo();
+        List<CellInfo> cellInfo = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                mTm, TelephonyManager::getAllCellInfo);
 
         // getAllCellInfo should never return null, and there should be at least one entry.
         assertNotNull("TelephonyManager.getAllCellInfo() returned NULL CellInfo", cellInfo);
@@ -90,7 +95,8 @@
             } catch (InterruptedException ie) {
                 fail("Thread was interrupted");
             }
-            List<CellInfo> newCellInfo = mTm.getAllCellInfo();
+            List<CellInfo> newCellInfo = ShellIdentityUtils.invokeMethodWithShellPermissions(
+                    mTm, TelephonyManager::getAllCellInfo);
             assertNotNull("TelephonyManager.getAllCellInfo() returned NULL CellInfo", newCellInfo);
             assertFalse("TelephonyManager.getAllCellInfo() returned an empty list",
                     newCellInfo.isEmpty());
diff --git a/tests/tests/telephonyprovider/AndroidManifest.xml b/tests/tests/telephonyprovider/AndroidManifest.xml
index 9a9e618..4adb19a 100755
--- a/tests/tests/telephonyprovider/AndroidManifest.xml
+++ b/tests/tests/telephonyprovider/AndroidManifest.xml
@@ -19,10 +19,11 @@
     package="android.telephonyprovider.cts"
     android:targetSandboxVersion="2">
 
-    <uses-permission android:name="android.permission.READ_SMS" />
-    <uses-permission android:name="android.permission.SEND_SMS" />
-    <uses-permission android:name="android.permission.RECEIVE_SMS" />
-    <uses-permission android:name="android.permission.RECEIVE_MMS" />
+    <uses-permission android:name="android.permission.READ_SMS"/>
+    <uses-permission android:name="android.permission.SEND_SMS"/>
+    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
+    <uses-permission android:name="android.permission.RECEIVE_MMS"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/ServiceStateTest.java b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/ServiceStateTest.java
new file mode 100644
index 0000000..09e4735
--- /dev/null
+++ b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/ServiceStateTest.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephonyprovider.cts;
+
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
+import static android.provider.Telephony.ServiceStateTable.DATA_NETWORK_TYPE;
+import static android.provider.Telephony.ServiceStateTable.DATA_REG_STATE;
+import static android.provider.Telephony.ServiceStateTable.DUPLEX_MODE;
+import static android.provider.Telephony.ServiceStateTable.VOICE_REG_STATE;
+import static android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_HOME;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.Manifest;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.provider.Telephony;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+@SmallTest
+public class ServiceStateTest {
+
+    private static final int DEFAULT_TIMEOUT = 1000;
+    // Keep the same as ServiceStateProvider#SERVICE_STATE which is NOT same as
+    // Telephony.ServiceStateTable.AUTHORITY;
+    private static final String SERVICE_STATE = "service_state";
+
+    private ContentResolver mContentResolver;
+    private TelephonyManager mTelephonyManager;
+    private int mSubId;
+    private @Nullable ServiceState mInitialServiceState;
+
+    @Before
+    public void setUp() {
+        assumeTrue(hasTelephonyFeature());
+
+        mContentResolver = getInstrumentation().getContext().getContentResolver();
+        mTelephonyManager =
+                getInstrumentation().getContext().getSystemService(TelephonyManager.class);
+        mSubId = SubscriptionManager.getDefaultSubscriptionId();
+        mInitialServiceState = mTelephonyManager.getServiceState();
+    }
+
+    @After
+    public void tearDown() {
+        if (!hasTelephonyFeature()) {
+            return;
+        }
+
+        // Recover the initial ServiceState to remove the impact of manual ServiceState insertion.
+        if (mInitialServiceState != null) {
+            insertServiceState(mInitialServiceState);
+        }
+    }
+
+    /**
+     * Verifies that the ServiceStateTable CONTENT_URI and AUTHORITY is valid.
+     */
+    @Test
+    public void testUriAndAuthority() {
+        Uri uri = Telephony.ServiceStateTable.CONTENT_URI;
+        assertThat(uri).isEqualTo(Uri.parse("content://service-state/"));
+
+        String authority = Telephony.ServiceStateTable.AUTHORITY;
+        assertThat(authority).isEqualTo("service-state");
+    }
+
+    /**
+     * Verifies that the voice reg state is valid and matches ServiceState#getState().
+     */
+    @Test
+    public void testGetVoiceRegState_query() {
+        try (Cursor cursor = mContentResolver.query(Telephony.ServiceStateTable.CONTENT_URI,
+                new String[]{VOICE_REG_STATE}, null, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+            cursor.moveToNext();
+
+            int voiceRegState = cursor.getInt(cursor.getColumnIndex(VOICE_REG_STATE));
+            assertThat(voiceRegState).isEqualTo(mTelephonyManager.getServiceState().getState());
+        }
+    }
+
+    /**
+     * Verifies that when voice reg state did not change, the observer should not receive the
+     * notification.
+     */
+    @Test
+    public void testGetVoiceRegState_noChangeObserved() throws Exception {
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+        oldSS.setState(ServiceState.STATE_OUT_OF_SERVICE);
+
+        ServiceState copyOfOldSS = new ServiceState();
+        copyOfOldSS.setStateOutOfService();
+        copyOfOldSS.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        // set additional fields which is not related to voice reg state
+        copyOfOldSS.setChannelNumber(65536);
+        copyOfOldSS.setIsManualSelection(true);
+
+        verifyNotificationObservedWhenFieldChanged(
+                VOICE_REG_STATE, oldSS, copyOfOldSS, false /*expectChange*/);
+    }
+
+    /**
+     * Verifies that when voice reg state changed, the observer should receive the notification.
+     */
+    @Test
+    public void testGetVoiceRegState_changeObserved() throws Exception {
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+        oldSS.setState(ServiceState.STATE_OUT_OF_SERVICE);
+
+        ServiceState newSS = new ServiceState();
+        newSS.setStateOutOfService();
+        newSS.setState(ServiceState.STATE_POWER_OFF);
+
+        verifyNotificationObservedWhenFieldChanged(
+                VOICE_REG_STATE, oldSS, newSS, true /*expectChange*/);
+    }
+
+    /**
+     * Verifies that the data network type is valid and matches ServiceState#getDataNetworkType()
+     */
+    @Test
+    public void testGetDataNetworkType_query() {
+        try (Cursor cursor = mContentResolver.query(Telephony.ServiceStateTable.CONTENT_URI,
+                new String[]{DATA_NETWORK_TYPE}, null, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+            cursor.moveToNext();
+
+            int dataNetworkType = cursor.getInt(cursor.getColumnIndex(DATA_NETWORK_TYPE));
+            assertThat(dataNetworkType).isEqualTo(
+                    mTelephonyManager.getServiceState().getDataNetworkType());
+        }
+    }
+
+    /**
+     * Verifies that when data network type did not change, the observer should not receive the
+     * notification.
+     */
+    @Test
+    public void testDataNetworkType_noChangeObserved() throws Exception {
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+
+        ServiceState copyOfOldSS = new ServiceState();
+        copyOfOldSS.setStateOutOfService();
+
+        // Add a DOMAIN_CS NRI which should not update DataNetworkType
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_GPRS)
+                .setRegistrationState(REGISTRATION_STATE_HOME)
+                .build();
+        copyOfOldSS.addNetworkRegistrationInfo(nri);
+
+        verifyNotificationObservedWhenFieldChanged(
+                DATA_NETWORK_TYPE, oldSS, copyOfOldSS, false /*expectChange*/);
+    }
+
+    /**
+     * Verifies that when data network type changed, the observer should receive the notification.
+     */
+    @Test
+    public void testDataNetworkType_changeObserved() throws Exception {
+        // While we don't have a method to directly set dataNetworkType, we emulate a ServiceState
+        // change that will trigger the change of dataNetworkType, according to the logic in
+        // ServiceState#getDataNetworkType()
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+
+        ServiceState newSS = new ServiceState();
+        newSS.setStateOutOfService();
+
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .setRegistrationState(REGISTRATION_STATE_HOME)
+                .build();
+        newSS.addNetworkRegistrationInfo(nri);
+
+        verifyNotificationObservedWhenFieldChanged(
+                DATA_NETWORK_TYPE, oldSS, newSS, true /*expectChange*/);
+    }
+
+    /**
+     * Verifies that the duplex mode is valid and matches ServiceState#getDuplexMode().
+     */
+    @Test
+    public void testGetDuplexMode_query() {
+        try (Cursor cursor = mContentResolver.query(Telephony.ServiceStateTable.CONTENT_URI,
+                new String[]{DUPLEX_MODE}, null, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+            cursor.moveToNext();
+
+            int duplexMode = cursor.getInt(cursor.getColumnIndex(DUPLEX_MODE));
+            assertThat(duplexMode).isEqualTo(mTelephonyManager.getServiceState().getDuplexMode());
+        }
+    }
+
+    /**
+     * Verifies that even we have duplex mode change, the observer should not receive the
+     * notification (duplex mode is a poll-only field).
+     */
+    @Test
+    public void testGetDuplexMode_noChangeObserved() throws Exception {
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+
+        ServiceState newSS = new ServiceState();
+        newSS.setStateOutOfService();
+
+        // Add NRI to trigger SS with duplex mode updated
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setDomain(NetworkRegistrationInfo.DOMAIN_PS)
+                .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+                .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+                .build();
+        newSS.addNetworkRegistrationInfo(nri);
+        newSS.setChannelNumber(65536); // EutranBand.BAND_65, DUPLEX_MODE_FDD
+
+        verifyNotificationObservedWhenFieldChanged(
+                DUPLEX_MODE, oldSS, newSS, false /*expectChange*/);
+    }
+
+    /**
+     * Verifies that the data reg state is valid and matches ServiceState#getDataRegState()
+     */
+    @Test
+    public void testGetDataRegState_query() {
+        try (Cursor cursor = mContentResolver.query(Telephony.ServiceStateTable.CONTENT_URI,
+                new String[]{DATA_REG_STATE}, null, null)) {
+            assertThat(cursor.getCount()).isEqualTo(1);
+            cursor.moveToNext();
+
+            int dataRegState = cursor.getInt(cursor.getColumnIndex(DATA_REG_STATE));
+            assertThat(dataRegState).isEqualTo(
+                    mTelephonyManager.getServiceState().getDataRegState());
+        }
+    }
+
+    /**
+     * Verifies that when data reg state did not change, the observer should not receive the
+     * notification.
+     */
+    @Test
+    public void testGetDataRegState_noChangeObserved() throws Exception {
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+        oldSS.setState(ServiceState.STATE_OUT_OF_SERVICE);
+
+        ServiceState copyOfOldSS = new ServiceState();
+        copyOfOldSS.setStateOutOfService();
+        copyOfOldSS.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        // set additional fields which is not related to data reg state
+        copyOfOldSS.setChannelNumber(65536);
+        copyOfOldSS.setIsManualSelection(true);
+
+        verifyNotificationObservedWhenFieldChanged(
+                DATA_REG_STATE, oldSS, copyOfOldSS, false /*expectChange*/);
+    }
+
+    /**
+     * Verifies that when data reg state changed, the observer should receive the notification.
+     */
+    @Test
+    public void testGetDataRegState_changeObserved() throws Exception {
+        ServiceState oldSS = new ServiceState();
+        oldSS.setStateOutOfService();
+
+        ServiceState newSS = new ServiceState();
+        newSS.setStateOutOfService();
+        newSS.setStateOff();
+
+        verifyNotificationObservedWhenFieldChanged(
+                DATA_REG_STATE, oldSS, newSS, true /*expectChange*/);
+    }
+
+    /**
+     * Insert new ServiceState over the old ServiceState and expect the observer receiving the
+     * notification over the observed field change.
+     */
+    private void verifyNotificationObservedWhenFieldChanged(String field, ServiceState oldSS,
+            ServiceState newSS, boolean expectChange) throws Exception {
+        final Uri uriForSubAndField =
+                Telephony.ServiceStateTable.getUriForSubscriptionIdAndField(mSubId, field);
+        insertServiceState(oldSS);
+
+        RecordingContentObserver observer = new RecordingContentObserver();
+        mContentResolver.registerContentObserver(uriForSubAndField, false, observer);
+        assertWithMessage("Observer is NOT empty in the beginning.").that(
+                observer.mObserved).isEmpty();
+
+        insertServiceState(newSS);
+
+        if (expectChange) {
+            // Only verify we did receive the notification for the expected field, instead of the
+            // number of notifications we received to remove flakiness for different cases.
+            PollingCheck.check(
+                    "Expect notification when " + field + " updated.",
+                    DEFAULT_TIMEOUT, () -> observer.mObserved.contains(uriForSubAndField));
+        } else {
+            // Let the bullets fly for a while before we check the target.
+            try {
+                Thread.sleep(DEFAULT_TIMEOUT);
+            } catch (InterruptedException ignored) {
+            }
+
+            // Fields in ServiceState are not orthogonal. In case we do receive notification(s),
+            // further check if it is for the expected field.
+            assertWithMessage("Unexpected notification for " + field).that(
+                    observer.mObserved).doesNotContain(uriForSubAndField);
+
+        }
+
+        mContentResolver.unregisterContentObserver(observer);
+    }
+
+    // Manually insert the ServiceState into table to test the notification.
+    private void insertServiceState(ServiceState state) {
+        ContentValues values = getContentValuesForServiceState(state);
+        SystemUtil.runWithShellPermissionIdentity(
+                () -> mContentResolver.insert(
+                        Telephony.ServiceStateTable.getUriForSubscriptionId(mSubId), values),
+                Manifest.permission.MODIFY_PHONE_STATE);
+    }
+
+    // Copied from ServiceStateProvider#getContentValuesForServiceState
+    private static ContentValues getContentValuesForServiceState(ServiceState state) {
+        ContentValues values = new ContentValues();
+        final Parcel p = Parcel.obtain();
+        state.writeToParcel(p, 0);
+        values.put(SERVICE_STATE, p.marshall());
+        return values;
+    }
+
+    private static class RecordingContentObserver extends ContentObserver {
+        List<Uri> mObserved = new CopyOnWriteArrayList<>();
+
+        RecordingContentObserver() {
+            super(new Handler(Looper.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange, @Nullable Uri uri) {
+            mObserved.add(uri);
+        }
+    }
+
+    private static boolean hasTelephonyFeature() {
+        return getInstrumentation().getContext().getPackageManager().hasSystemFeature(
+                FEATURE_TELEPHONY);
+    }
+}
diff --git a/tests/tests/text/res/values/style.xml b/tests/tests/text/res/values/style.xml
index 905a3b9..3bfaba9 100644
--- a/tests/tests/text/res/values/style.xml
+++ b/tests/tests/text/res/values/style.xml
@@ -82,7 +82,8 @@
     </style>
 
     <style name="TextAppearanceWithBoldAndWeight">
-      <item name="android:textStyle">bold</item>
+        <item name="android:typeface">sans</item>
+        <item name="android:textStyle">bold</item>
       <item name="android:textFontWeight">500</item>
     </style>
 
diff --git a/tests/tests/toast/OWNERS b/tests/tests/toast/OWNERS
index 9363c1a..47186b7 100644
--- a/tests/tests/toast/OWNERS
+++ b/tests/tests/toast/OWNERS
@@ -1,3 +1,4 @@
 # Bug component: 137825
 svetoslavganov@google.com
 toddke@google.com
+patb@google.com
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
index 9135e56..d784389 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -107,14 +107,14 @@
 
         // Access APIs guarded by a platform defined signature permissions
         try {
+            assertSame(packageManager.checkPermission(Manifest.permission.ANSWER_PHONE_CALLS,
+                   context.getPackageName()), PackageManager.PERMISSION_DENIED);
             getInstrumentation().getUiAutomation().adoptShellPermissionIdentity();
 
             // Access APIs guarded by a platform defined signature permission
             activityManager.getPackageImportance("foo.bar.baz");
 
             // Grant ourselves a runtime permission (was granted at install)
-            assertSame(packageManager.checkPermission(Manifest.permission.ANSWER_PHONE_CALLS,
-                    context.getPackageName()), PackageManager.PERMISSION_DENIED);
             packageManager.grantRuntimePermission(context.getPackageName(),
                     Manifest.permission.ANSWER_PHONE_CALLS, Process.myUserHandle());
         } catch (SecurityException e) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SurfaceViewTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SurfaceViewTests.java
index 5b8cb48..05fc15f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SurfaceViewTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SurfaceViewTests.java
@@ -47,7 +47,6 @@
 import java.util.concurrent.CountDownLatch;
 
 @LargeTest
-@SkipPresubmit // TODO: Figure out what's going on with this b/134716377
 @RunWith(AndroidJUnit4.class)
 public class SurfaceViewTests extends ActivityTestBase {
 
diff --git a/tests/tests/util/Android.bp b/tests/tests/util/Android.bp
index 0c5de3a..3998282 100644
--- a/tests/tests/util/Android.bp
+++ b/tests/tests/util/Android.bp
@@ -34,4 +34,5 @@
     ],
     srcs: ["src/**/*.java"],
     platform_apis: true,
+    min_sdk_version: "30",
 }
diff --git a/tests/tests/util/AndroidManifest.xml b/tests/tests/util/AndroidManifest.xml
index 77ab380..10de8ef 100644
--- a/tests/tests/util/AndroidManifest.xml
+++ b/tests/tests/util/AndroidManifest.xml
@@ -26,6 +26,8 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
+    <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.util.cts"
                      android:label="CTS tests of android.util">
diff --git a/tests/tests/util/src/android/util/cts/TimeUtilsTest.java b/tests/tests/util/src/android/util/cts/TimeUtilsTest.java
index d20ccdb..e08704d 100644
--- a/tests/tests/util/src/android/util/cts/TimeUtilsTest.java
+++ b/tests/tests/util/src/android/util/cts/TimeUtilsTest.java
@@ -16,6 +16,9 @@
 package android.util.cts;
 
 import static android.util.TimeUtils.isTimeBetween;
+
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.TestCase.assertFalse;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
@@ -27,6 +30,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.google.common.truth.Truth;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -172,6 +177,15 @@
     }
 
     @Test
+    public void getTimeZoneIdsForCountryCode_doesNotShowNoLongerActiveNames() {
+        List<String> usTimeZones = TimeUtils.getTimeZoneIdsForCountryCode("us");
+
+        assertThat(usTimeZones).contains("America/New_York");
+        // America/Detroit was available only till 27 April 1975
+        assertThat(usTimeZones).doesNotContain("America/Detroit");
+    }
+
+    @Test
     public void testGetTimeZoneIdsForCountryCode_unknownCountryCode() {
         String unknownCountryCode = "zx81";
         assertNull(TimeUtils.getTimeZoneIdsForCountryCode(unknownCountryCode));
diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java
new file mode 100644
index 0000000..20bdc0a
--- /dev/null
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.cts;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.vcn.VcnGatewayConnectionConfig;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class VcnGatewayConnectionConfigTest extends VcnTestBase {
+    private static final String VCN_GATEWAY_CONNECTION_NAME = "test-vcn-gateway-connection";
+    private static final long[] RETRY_INTERNAL_MILLIS =
+            new long[] {
+                TimeUnit.SECONDS.toMillis(1),
+                TimeUnit.MINUTES.toMillis(1),
+                TimeUnit.HOURS.toMillis(1)
+            };
+    private static final int MAX_MTU = 1360;
+
+    private static VcnGatewayConnectionConfig.Builder buildVcnGatewayConnectionConfigBase() {
+        return new VcnGatewayConnectionConfig.Builder(
+                        VCN_GATEWAY_CONNECTION_NAME, buildTunnelConnectionParams())
+                .addExposedCapability(NET_CAPABILITY_INTERNET)
+                .setRetryIntervalsMillis(RETRY_INTERNAL_MILLIS)
+                .setMaxMtu(MAX_MTU);
+    }
+
+    public static VcnGatewayConnectionConfig buildVcnGatewayConnectionConfig() {
+        return buildVcnGatewayConnectionConfigBase().build();
+    }
+
+    @Test
+    public void testBuildVcnGatewayConnectionConfig() throws Exception {
+        final VcnGatewayConnectionConfig gatewayConnConfig = buildVcnGatewayConnectionConfig();
+
+        assertEquals(VCN_GATEWAY_CONNECTION_NAME, gatewayConnConfig.getGatewayConnectionName());
+        assertEquals(buildTunnelConnectionParams(), gatewayConnConfig.getTunnelConnectionParams());
+        assertArrayEquals(
+                new int[] {NET_CAPABILITY_INTERNET}, gatewayConnConfig.getExposedCapabilities());
+        assertArrayEquals(RETRY_INTERNAL_MILLIS, gatewayConnConfig.getRetryIntervalsMillis());
+    }
+
+    @Test
+    public void testBuilderAddRemove() throws Exception {
+        final VcnGatewayConnectionConfig gatewayConnConfig =
+                buildVcnGatewayConnectionConfigBase()
+                        .addExposedCapability(NET_CAPABILITY_DUN)
+                        .removeExposedCapability(NET_CAPABILITY_DUN)
+                        .build();
+
+        assertArrayEquals(
+                new int[] {NET_CAPABILITY_INTERNET}, gatewayConnConfig.getExposedCapabilities());
+    }
+
+    @Test
+    public void testBuildWithoutMobikeEnabled() {
+        final IkeSessionParams ikeParams =
+                getIkeSessionParamsBase().removeIkeOption(IKE_OPTION_MOBIKE).build();
+        final IkeTunnelConnectionParams tunnelParams = buildTunnelConnectionParams(ikeParams);
+
+        try {
+            new VcnGatewayConnectionConfig.Builder(VCN_GATEWAY_CONNECTION_NAME, tunnelParams);
+            fail("Expected exception if MOBIKE not configured");
+        } catch (IllegalArgumentException e) {
+        }
+    }
+}
diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java
index a5646fa..e406815 100644
--- a/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnManagerTest.java
@@ -17,14 +17,11 @@
 package android.net.vcn.cts;
 
 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
-import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
-import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assume.assumeTrue;
 
 import android.annotation.NonNull;
@@ -33,15 +30,7 @@
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
-import android.net.ipsec.ike.ChildSaProposal;
-import android.net.ipsec.ike.IkeFqdnIdentification;
-import android.net.ipsec.ike.IkeSaProposal;
-import android.net.ipsec.ike.IkeSessionParams;
-import android.net.ipsec.ike.SaProposal;
-import android.net.ipsec.ike.TunnelModeChildSessionParams;
 import android.net.vcn.VcnConfig;
-import android.net.vcn.VcnControlPlaneIkeConfig;
-import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnManager;
 import android.os.ParcelUuid;
 import android.telephony.SubscriptionManager;
@@ -62,7 +51,7 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
-public class VcnManagerTest {
+public class VcnManagerTest extends VcnTestBase {
     private static final String TAG = VcnManagerTest.class.getSimpleName();
 
     private static final int TIMEOUT_MS = 500;
@@ -89,79 +78,29 @@
     }
 
     private VcnConfig buildVcnConfig() {
-        final IkeSaProposal ikeProposal =
-                new IkeSaProposal.Builder()
-                        .addEncryptionAlgorithm(
-                                ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
-                        .addDhGroup(DH_GROUP_2048_BIT_MODP)
-                        .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC)
-                        .build();
-
-        final String serverHostname = "2001:db8:1::100";
-        final String testLocalId = "test.client.com";
-        final String testRemoteId = "test.server.com";
-        final byte[] psk = "psk".getBytes();
-
-        // TODO: b/180521384: Build the IkeSessionParams without a Context when the no-arg
-        // IkeSessionParams.Builder constructor is exposed.
-        final IkeSessionParams ikeParams =
-                new IkeSessionParams.Builder(mContext)
-                        .setServerHostname(serverHostname)
-                        .addSaProposal(ikeProposal)
-                        .setLocalIdentification(new IkeFqdnIdentification(testLocalId))
-                        .setRemoteIdentification(new IkeFqdnIdentification(testRemoteId))
-                        .setAuthPsk(psk)
-                        .build();
-
-        final ChildSaProposal childProposal =
-                new ChildSaProposal.Builder()
-                        .addEncryptionAlgorithm(
-                                ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
-                        .build();
-        final TunnelModeChildSessionParams childParams =
-                new TunnelModeChildSessionParams.Builder().addSaProposal(childProposal).build();
-
-        final VcnControlPlaneIkeConfig controlConfig =
-                new VcnControlPlaneIkeConfig(ikeParams, childParams);
-
-        final VcnGatewayConnectionConfig gatewayConnConfig =
-                new VcnGatewayConnectionConfig.Builder(controlConfig)
-                        .addExposedCapability(NET_CAPABILITY_INTERNET)
-                        .addRequiredUnderlyingCapability(NET_CAPABILITY_INTERNET)
-                        .setRetryInterval(
-                                new long[] {
-                                    TimeUnit.SECONDS.toMillis(1),
-                                    TimeUnit.MINUTES.toMillis(1),
-                                    TimeUnit.HOURS.toMillis(1)
-                                })
-                        .setMaxMtu(1360)
-                        .build();
-
         return new VcnConfig.Builder(mContext)
-                .addGatewayConnectionConfig(gatewayConnConfig)
+                .addGatewayConnectionConfig(
+                        VcnGatewayConnectionConfigTest.buildVcnGatewayConnectionConfig())
                 .build();
     }
 
+    private int verifyAndGetValidDataSubId() {
+        final int dataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        assertNotEquals(
+                "There must be an active data subscription to complete CTS",
+                INVALID_SUBSCRIPTION_ID,
+                dataSubId);
+        return dataSubId;
+    }
+
     @Test(expected = SecurityException.class)
     public void testSetVcnConfig_noCarrierPrivileges() throws Exception {
-        // TODO: b/180521384: Remove the assertion when constructing IkeSessionParams does not
-        // require an active default network.
-        assertNotNull(
-                "You must have an active network connection to complete CTS",
-                mConnectivityManager.getActiveNetwork());
-
         mVcnManager.setVcnConfig(new ParcelUuid(UUID.randomUUID()), buildVcnConfig());
     }
 
     @Test
     public void testSetVcnConfig_withCarrierPrivileges() throws Exception {
-        // TODO: b/180521384: Remove the assertion when constructing IkeSessionParams does not
-        // require an active default network.
-        assertNotNull(
-                "You must have an active network connection to complete CTS",
-                mConnectivityManager.getActiveNetwork());
-
-        final int dataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        final int dataSubId = verifyAndGetValidDataSubId();
         CarrierPrivilegeUtils.withCarrierPrivileges(mContext, dataSubId, () -> {
             SubscriptionGroupUtils.withEphemeralSubscriptionGroup(mContext, dataSubId, (subGrp) -> {
                 mVcnManager.setVcnConfig(subGrp, buildVcnConfig());
@@ -178,7 +117,8 @@
 
     @Test
     public void testClearVcnConfig_withCarrierPrivileges() throws Exception {
-        final int dataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        final int dataSubId = verifyAndGetValidDataSubId();
+
         CarrierPrivilegeUtils.withCarrierPrivileges(mContext, dataSubId, () -> {
             SubscriptionGroupUtils.withEphemeralSubscriptionGroup(mContext, dataSubId, (subGrp) -> {
                 mVcnManager.clearVcnConfig(subGrp);
@@ -186,9 +126,9 @@
         });
     }
 
-    /** Test implementation of VcnNetworkPolicyListener for verification purposes. */
-    private static class TestVcnNetworkPolicyListener
-            implements VcnManager.VcnNetworkPolicyListener {
+    /** Test implementation of VcnNetworkPolicyChangeListener for verification purposes. */
+    private static class TestVcnNetworkPolicyChangeListener
+            implements VcnManager.VcnNetworkPolicyChangeListener {
         private final CompletableFuture<Void> mFutureOnPolicyChanged = new CompletableFuture<>();
 
         @Override
@@ -202,21 +142,24 @@
     }
 
     @Test(expected = SecurityException.class)
-    public void testAddVcnNetworkPolicyListener_noNetworkFactoryPermission() throws Exception {
-        final TestVcnNetworkPolicyListener listener = new TestVcnNetworkPolicyListener();
+    public void testAddVcnNetworkPolicyChangeListener_noNetworkFactoryPermission()
+            throws Exception {
+        final TestVcnNetworkPolicyChangeListener listener =
+                new TestVcnNetworkPolicyChangeListener();
 
         try {
-            mVcnManager.addVcnNetworkPolicyListener(INLINE_EXECUTOR, listener);
+            mVcnManager.addVcnNetworkPolicyChangeListener(INLINE_EXECUTOR, listener);
         } finally {
-            mVcnManager.removeVcnNetworkPolicyListener(listener);
+            mVcnManager.removeVcnNetworkPolicyChangeListener(listener);
         }
     }
 
     @Test
-    public void testRemoveVcnNetworkPolicyListener() {
-        final TestVcnNetworkPolicyListener listener = new TestVcnNetworkPolicyListener();
+    public void testRemoveVcnNetworkPolicyChangeListener_noNetworkFactoryPermission() {
+        final TestVcnNetworkPolicyChangeListener listener =
+                new TestVcnNetworkPolicyChangeListener();
 
-        mVcnManager.removeVcnNetworkPolicyListener(listener);
+        mVcnManager.removeVcnNetworkPolicyChangeListener(listener);
     }
 
     @Test(expected = SecurityException.class)
@@ -229,25 +172,25 @@
 
     /** Test implementation of VcnStatusCallback for verification purposes. */
     private static class TestVcnStatusCallback extends VcnManager.VcnStatusCallback {
-        private final CompletableFuture<Integer> mFutureOnVcnStatusChanged =
+        private final CompletableFuture<Integer> mFutureOnStatusChanged =
                 new CompletableFuture<>();
         private final CompletableFuture<GatewayConnectionError> mFutureOnGatewayConnectionError =
                 new CompletableFuture<>();
 
         @Override
-        public void onVcnStatusChanged(int statusCode) {
-            mFutureOnVcnStatusChanged.complete(statusCode);
+        public void onStatusChanged(int statusCode) {
+            mFutureOnStatusChanged.complete(statusCode);
         }
 
         @Override
         public void onGatewayConnectionError(
-                @NonNull int[] networkCapabilities, int errorCode, @Nullable Throwable detail) {
+                @NonNull String gatewayConnectionName, int errorCode, @Nullable Throwable detail) {
             mFutureOnGatewayConnectionError.complete(
-                    new GatewayConnectionError(networkCapabilities, errorCode, detail));
+                    new GatewayConnectionError(gatewayConnectionName, errorCode, detail));
         }
 
-        public int awaitOnVcnStatusChanged() throws Exception {
-            return mFutureOnVcnStatusChanged.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        public int awaitOnStatusChanged() throws Exception {
+            return mFutureOnStatusChanged.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         }
 
         public GatewayConnectionError awaitOnGatewayConnectionError() throws Exception {
@@ -257,13 +200,13 @@
 
     /** Info class for organizing VcnStatusCallback#onGatewayConnectionError response data. */
     private static class GatewayConnectionError {
-        @NonNull public final int[] networkCapabilities;
+        @NonNull public final String gatewayConnectionName;
         public final int errorCode;
         @Nullable public final Throwable detail;
 
         public GatewayConnectionError(
-                @NonNull int[] networkCapabilities, int errorCode, @Nullable Throwable detail) {
-            this.networkCapabilities = networkCapabilities.clone();
+                @NonNull String gatewayConnectionName, int errorCode, @Nullable Throwable detail) {
+            this.gatewayConnectionName = gatewayConnectionName;
             this.errorCode = errorCode;
             this.detail = detail;
         }
@@ -281,12 +224,12 @@
     @Test
     public void testRegisterVcnStatusCallback() throws Exception {
         final TestVcnStatusCallback callback = new TestVcnStatusCallback();
-        final int subId = SubscriptionManager.getDefaultSubscriptionId();
+        final int subId = verifyAndGetValidDataSubId();
 
         try {
             registerVcnStatusCallbackForSubId(callback, subId);
 
-            final int statusCode = callback.awaitOnVcnStatusChanged();
+            final int statusCode = callback.awaitOnStatusChanged();
             assertEquals(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED, statusCode);
         } finally {
             mVcnManager.unregisterVcnStatusCallback(callback);
@@ -296,7 +239,7 @@
     @Test
     public void testRegisterVcnStatusCallback_reuseUnregisteredCallback() throws Exception {
         final TestVcnStatusCallback callback = new TestVcnStatusCallback();
-        final int subId = SubscriptionManager.getDefaultSubscriptionId();
+        final int subId = verifyAndGetValidDataSubId();
 
         try {
             registerVcnStatusCallbackForSubId(callback, subId);
@@ -310,7 +253,7 @@
     @Test(expected = IllegalStateException.class)
     public void testRegisterVcnStatusCallback_duplicateRegister() throws Exception {
         final TestVcnStatusCallback callback = new TestVcnStatusCallback();
-        final int subId = SubscriptionManager.getDefaultSubscriptionId();
+        final int subId = verifyAndGetValidDataSubId();
 
         try {
             registerVcnStatusCallbackForSubId(callback, subId);
diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnTestBase.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnTestBase.java
new file mode 100644
index 0000000..7ed201e
--- /dev/null
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnTestBase.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn.cts;
+
+import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
+
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.ipsec.ike.SaProposal;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+
+public class VcnTestBase {
+    protected static IkeTunnelConnectionParams buildTunnelConnectionParams() {
+        final IkeSessionParams ikeParams = getIkeSessionParamsBase().build();
+        return buildTunnelConnectionParams(ikeParams);
+    }
+
+    protected static IkeTunnelConnectionParams buildTunnelConnectionParams(
+            IkeSessionParams ikeParams) {
+        final ChildSaProposal childProposal =
+                new ChildSaProposal.Builder()
+                        .addEncryptionAlgorithm(
+                                ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
+                        .build();
+        final TunnelModeChildSessionParams childParams =
+                new TunnelModeChildSessionParams.Builder().addSaProposal(childProposal).build();
+
+        return new IkeTunnelConnectionParams(ikeParams, childParams);
+    }
+
+    protected static IkeSessionParams.Builder getIkeSessionParamsBase() {
+        final IkeSaProposal ikeProposal =
+                new IkeSaProposal.Builder()
+                        .addEncryptionAlgorithm(
+                                ENCRYPTION_ALGORITHM_AES_GCM_12, SaProposal.KEY_LEN_AES_128)
+                        .addDhGroup(DH_GROUP_2048_BIT_MODP)
+                        .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC)
+                        .build();
+
+        final String serverHostname = "2001:db8:1::100";
+        final String testLocalId = "test.client.com";
+        final String testRemoteId = "test.server.com";
+        final byte[] psk = "psk".getBytes();
+
+        return new IkeSessionParams.Builder()
+                .setServerHostname(serverHostname)
+                .addSaProposal(ikeProposal)
+                .setLocalIdentification(new IkeFqdnIdentification(testLocalId))
+                .setRemoteIdentification(new IkeFqdnIdentification(testRemoteId))
+                .setAuthPsk(psk)
+                .addIkeOption(IKE_OPTION_MOBIKE);
+    }
+}
diff --git a/tests/tests/view/Android.bp b/tests/tests/view/Android.bp
new file mode 100644
index 0000000..22a9411
--- /dev/null
+++ b/tests/tests/view/Android.bp
@@ -0,0 +1,60 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "CtsViewTestCases",
+    defaults: ["cts_defaults"],
+
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+
+    compile_multilib: "both",
+
+    libs: [
+        "android.test.runner.stubs",
+        "android.test.base.stubs",
+    ],
+
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "compatibility-device-util-axt",
+        "ctsdeviceutillegacy-axt",
+        "ctstestrunner-axt",
+        "mockito-target-minus-junit4",
+        "platform-test-annotations",
+        "ub-uiautomator",
+        "truth-prebuilt",
+        "CtsSurfaceValidatorLib",
+        "cts-wm-util",
+    ],
+
+    jni_libs: [
+        "libctsview_jni",
+        "libnativehelper_compat_libc++",
+    ],
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    sdk_version: "test_current",
+}
diff --git a/tests/tests/view/Android.mk b/tests/tests/view/Android.mk
deleted file mode 100644
index 3c16650..0000000
--- a/tests/tests/view/Android.mk
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 2008 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# don't include this package in any target
-LOCAL_MODULE_TAGS := tests
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts general-tests
-
-LOCAL_MULTILIB := both
-
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.rules \
-    androidx.test.ext.junit \
-    compatibility-device-util-axt \
-    ctsdeviceutillegacy-axt \
-    ctstestrunner-axt \
-    mockito-target-minus-junit4 \
-    platform-test-annotations \
-    ub-uiautomator \
-    truth-prebuilt \
-    CtsSurfaceValidatorLib \
-    cts-wm-util
-
-
-LOCAL_JNI_SHARED_LIBRARIES := libctsview_jni libnativehelper_compat_libc++
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsViewTestCases
-LOCAL_SDK_VERSION := test_current
-
-include $(BUILD_CTS_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/view/jni/OWNERS b/tests/tests/view/jni/OWNERS
new file mode 100644
index 0000000..0fd2da2
--- /dev/null
+++ b/tests/tests/view/jni/OWNERS
@@ -0,0 +1 @@
+per-file android_view_cts_ASurfaceControlTest.cpp = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/tests/view/res/layout-round/gesture_exclusion_basic.xml b/tests/tests/view/res/layout-round/gesture_exclusion_basic.xml
new file mode 100644
index 0000000..71754b6
--- /dev/null
+++ b/tests/tests/view/res/layout-round/gesture_exclusion_basic.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/abslistview_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <View android:id="@+id/animating_view"
+          android:layout_width="5px"
+          android:layout_height="5px"
+          android:layout_gravity="center|left"
+          android:background="#ff00ff00" />
+</FrameLayout>
diff --git a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
index abae9e7..bf73550 100644
--- a/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
+++ b/tests/tests/view/src/android/view/cts/KeyEventInterceptTest.java
@@ -29,7 +29,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 
-import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.WindowUtil;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -67,7 +67,7 @@
     public void setup() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mActivity = mActivityRule.getActivity();
-        PollingCheck.waitFor(mActivity::hasWindowFocus);
+        WindowUtil.waitForFocus(mActivity);
     }
 
     @Test
diff --git a/tests/tests/view/src/android/view/cts/OWNERS b/tests/tests/view/src/android/view/cts/OWNERS
new file mode 100644
index 0000000..3da545b
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/OWNERS
@@ -0,0 +1,2 @@
+per-file ASurfaceControlTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
+per-file SurfaceViewSyncTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java b/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java
index 9262dfc..68d7353 100644
--- a/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java
+++ b/tests/tests/view/src/android/view/cts/VerifyInputEventTest.java
@@ -47,7 +47,7 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 
-import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.WindowUtil;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -80,7 +80,7 @@
         assertNotNull(mInputManager);
         mAutomation = instrumentation.getUiAutomation();
         mActivity = mActivityRule.getActivity();
-        PollingCheck.waitFor(mActivity::hasWindowFocus);
+        WindowUtil.waitForFocus(mActivity);
     }
 
     @Test
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 3f9f563..25a8a26 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -115,6 +115,7 @@
 import com.android.compatibility.common.util.CtsMouseUtil;
 import com.android.compatibility.common.util.CtsTouchUtils;
 import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.WindowUtil;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -164,7 +165,7 @@
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mContext = mInstrumentation.getTargetContext();
         mActivity = mActivityRule.getActivity();
-        PollingCheck.waitFor(mActivity::hasWindowFocus);
+        WindowUtil.waitForFocus(mActivity);
         mResources = mActivity.getResources();
         mMockParent = new MockViewParent(mActivity);
         PollingCheck.waitFor(5 * DateUtils.SECOND_IN_MILLIS, mActivity::hasWindowFocus);
diff --git a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
index aa752a5..edfb8c4 100644
--- a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
@@ -42,8 +42,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.CtsTouchUtils;
-import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.WidgetTestUtils;
+import com.android.compatibility.common.util.WindowUtil;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -70,7 +70,7 @@
     public void setup() throws Throwable {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mActivity = mActivityRule.getActivity();
-        PollingCheck.waitFor(mActivity::hasWindowFocus);
+        WindowUtil.waitForFocus(mActivity);
         layout(R.layout.viewtreeobserver_layout);
 
         mLinearLayout = (LinearLayout) mActivity.findViewById(R.id.linearlayout);
diff --git a/tests/tests/view/src/android/view/cts/ViewUnbufferedTest.java b/tests/tests/view/src/android/view/cts/ViewUnbufferedTest.java
index b0ec086..94acb96 100644
--- a/tests/tests/view/src/android/view/cts/ViewUnbufferedTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewUnbufferedTest.java
@@ -40,6 +40,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.WindowUtil;
 
 import org.junit.After;
 import org.junit.Before;
@@ -127,7 +128,7 @@
         mSentEvents.clear();
         mReceivedCountPerFrame.set(0);
         mMaxReceivedCountPerFrame = 0;
-        PollingCheck.waitFor(mActivity::hasWindowFocus);
+        WindowUtil.waitForFocus(mActivity);
         mView = mActivity.findViewById(R.id.test_view);
     }
 
diff --git a/tests/tests/view/surfacevalidator/Android.bp b/tests/tests/view/surfacevalidator/Android.bp
new file mode 100644
index 0000000..bad8631
--- /dev/null
+++ b/tests/tests/view/surfacevalidator/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_helper_library {
+
+    name: "CtsSurfaceValidatorLib",
+
+    sdk_version: "test_current",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "androidx.test.rules",
+        "cts-wm-util",
+        "ub-uiautomator",
+    ],
+
+}
diff --git a/tests/tests/view/surfacevalidator/Android.mk b/tests/tests/view/surfacevalidator/Android.mk
deleted file mode 100644
index 54129cd..0000000
--- a/tests/tests/view/surfacevalidator/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := CtsSurfaceValidatorLib
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SDK_VERSION := test_current
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.rules \
-    cts-wm-util \
-    ub-uiautomator
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/view/surfacevalidator/OWNERS b/tests/tests/view/surfacevalidator/OWNERS
new file mode 100644
index 0000000..361760d
--- /dev/null
+++ b/tests/tests/view/surfacevalidator/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
index 9401370..6e643f8 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java
@@ -151,7 +151,6 @@
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
             startActivityForResult(mProjectionManager.createScreenCaptureIntent(), PERMISSION_CODE);
-            dismissPermissionDialog();
             mProjectionServiceBound = true;
         }
 
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelColor.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelColor.java
index b50944b..bd37fa0 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelColor.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelColor.java
@@ -59,27 +59,10 @@
     }
 
     private int getMinValue(short color) {
-        if (color - 4 > 0) {
-            return color - 4;
-        }
-        return 0;
+        return Math.max(color - 4, 0);
     }
 
     private int getMaxValue(short color) {
-        if (color + 4 < 0xFF) {
-            return color + 4;
-        }
-        return 0xFF;
-    }
-
-    public void addToPixelCounter(ScriptC_PixelCounter script) {
-        script.set_MIN_ALPHA(mMinAlpha);
-        script.set_MAX_ALPHA(mMaxAlpha);
-        script.set_MIN_RED(mMinRed);
-        script.set_MAX_RED(mMaxRed);
-        script.set_MIN_BLUE(mMinBlue);
-        script.set_MAX_BLUE(mMaxBlue);
-        script.set_MIN_GREEN(mMinGreen);
-        script.set_MAX_GREEN(mMaxGreen);
+        return Math.min(color + 4, 0xFF);
     }
 }
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelCounter.rscript b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelCounter.rscript
deleted file mode 100644
index b4fe3be..0000000
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/PixelCounter.rscript
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#pragma version(1)
-#pragma rs java_package_name(android.view.cts.surfacevalidator)
-#pragma rs reduce(countBlackishPixels) accumulator(countBlackishPixelsAccum) combiner(countBlackishPixelsCombiner)
-
-uchar MIN_ALPHA;
-uchar MAX_ALPHA;
-uchar MIN_RED;
-uchar MAX_RED;
-uchar MIN_GREEN;
-uchar MAX_GREEN;
-uchar MIN_BLUE;
-uchar MAX_BLUE;
-int BOUNDS[4];
-
-static void countBlackishPixelsAccum(int *accum, uchar4 pixel, uint32_t x, uint32_t y) {
-
-    if (pixel.a <= MAX_ALPHA
-            && pixel.a >= MIN_ALPHA
-            && pixel.r <= MAX_RED
-            && pixel.r >= MIN_RED
-            && pixel.g <= MAX_GREEN
-            && pixel.g >= MIN_GREEN
-            && pixel.b <= MAX_BLUE
-            && pixel.b >= MIN_BLUE
-            && x >= BOUNDS[0]
-            && x < BOUNDS[2]
-            && y >= BOUNDS[1]
-            && y < BOUNDS[3]) {
-        *accum += 1;
-    }
-}
-
-static void countBlackishPixelsCombiner(int *accum, const int *other){
-    *accum += *other;
-}
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java
deleted file mode 100644
index f1a1660..0000000
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/SurfacePixelValidator.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view.cts.surfacevalidator;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Trace;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RenderScript;
-import android.renderscript.Type;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Surface;
-
-public class SurfacePixelValidator {
-    private static final String TAG = "SurfacePixelValidator";
-
-    /**
-     * Observed that first few frames have errors with SurfaceView placement, so we skip for now.
-     * b/29603849 tracking that issue.
-     */
-    private static final int NUM_FIRST_FRAMES_SKIPPED = 8;
-
-    private static final int MAX_CAPTURED_FAILURES = 5;
-
-    private final int mWidth;
-    private final int mHeight;
-
-    private final HandlerThread mWorkerThread;
-    private final Handler mWorkerHandler;
-
-    private final PixelChecker mPixelChecker;
-
-    private final RenderScript mRS;
-
-    private final Allocation mInPixelsAllocation;
-    private final ScriptC_PixelCounter mScript;
-
-
-    private final Object mResultLock = new Object();
-    private int mResultSuccessFrames;
-    private int mResultFailureFrames;
-    private SparseArray<Bitmap> mFirstFailures = new SparseArray<>(MAX_CAPTURED_FAILURES);
-
-    private Runnable mConsumeRunnable = new Runnable() {
-        int mNumSkipped = 0;
-        @Override
-        public void run() {
-            Trace.beginSection("consume buffer");
-            mInPixelsAllocation.ioReceive();
-            Trace.endSection();
-
-            Trace.beginSection("compare and sum");
-            int blackishPixelCount = mScript.reduce_countBlackishPixels(mInPixelsAllocation).get();
-            Trace.endSection();
-
-            boolean success = mPixelChecker.checkPixels(blackishPixelCount, mWidth, mHeight);
-            synchronized (mResultLock) {
-                if (mNumSkipped < NUM_FIRST_FRAMES_SKIPPED) {
-                    mNumSkipped++;
-                    Log.d(TAG, "skipped frame nr " + mNumSkipped + ", success = " + success);
-                } else {
-                    if (success) {
-                        mResultSuccessFrames++;
-                    } else {
-                        mResultFailureFrames++;
-                        int totalFramesSeen = mResultSuccessFrames + mResultFailureFrames;
-                        Log.d(TAG, "Failure (pixel count = " + blackishPixelCount
-                                + ") occurred on frame " + totalFramesSeen);
-
-                        if (mFirstFailures.size() < MAX_CAPTURED_FAILURES) {
-                            Log.d(TAG, "Capturing bitmap #" + mFirstFailures.size());
-                            // error, worth looking at...
-                            Bitmap capture = Bitmap.createBitmap(mWidth, mHeight,
-                                    Bitmap.Config.ARGB_8888);
-                            mInPixelsAllocation.copyTo(capture);
-                            mFirstFailures.put(totalFramesSeen, capture);
-                        }
-                    }
-                }
-            }
-        }
-    };
-
-    public SurfacePixelValidator(Context context, Point size, Rect boundsToCheck,
-            PixelChecker pixelChecker) {
-        mWidth = size.x;
-        mHeight = size.y;
-
-        mWorkerThread = new HandlerThread("SurfacePixelValidator");
-        mWorkerThread.start();
-        mWorkerHandler = new Handler(mWorkerThread.getLooper());
-
-        mPixelChecker = pixelChecker;
-
-        mRS = RenderScript.create(context);
-        mScript = new ScriptC_PixelCounter(mRS);
-
-        mInPixelsAllocation = createBufferQueueAllocation();
-        mScript.set_BOUNDS(new int[] {boundsToCheck.left, boundsToCheck.top,
-                boundsToCheck.right, boundsToCheck.bottom});
-        pixelChecker.getColor().addToPixelCounter(mScript);
-
-        mInPixelsAllocation.setOnBufferAvailableListener(
-                allocation -> mWorkerHandler.post(mConsumeRunnable));
-    }
-
-    public Surface getSurface() {
-        return mInPixelsAllocation.getSurface();
-    }
-
-    private Allocation createBufferQueueAllocation() {
-        return Allocation.createAllocations(mRS, Type.createXY(mRS,
-                Element.RGBA_8888(mRS)
-                /*Element.U32(mRS)*/, mWidth, mHeight),
-                Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT,
-                1)[0];
-    }
-
-    /**
-     * Shuts down processing pipeline, and returns current pass/fail counts.
-     *
-     * Wait for pipeline to flush before calling this method. If not, frames that are still in
-     * flight may be lost.
-     */
-    public void finish(CapturedActivity.TestResult testResult) {
-        synchronized (mResultLock) {
-            // could in theory miss results still processing, but only if latency is extremely high.
-            // Caller should only call this
-            testResult.failFrames = mResultFailureFrames;
-            testResult.passFrames = mResultSuccessFrames;
-
-            for (int i = 0; i < mFirstFailures.size(); i++) {
-                testResult.failures.put(mFirstFailures.keyAt(i), mFirstFailures.valueAt(i));
-            }
-        }
-        mWorkerThread.quitSafely();
-    }
-}
diff --git a/tests/tests/webkit/assets/webkit/page_with_link.html b/tests/tests/webkit/assets/webkit/page_with_link.html
index 50fb78a..4fbe869 100644
--- a/tests/tests/webkit/assets/webkit/page_with_link.html
+++ b/tests/tests/webkit/assets/webkit/page_with_link.html
@@ -15,6 +15,8 @@
 
 <html>
   <body>
-    <a href="http://foo.com" id="link">a link</a>
+    <!-- This is a relative link to an arbitrary page also hosted on this
+        server. -->
+    <a href="/assets/webkit/test_blankPage.html" id="link">a link</a>
   </body>
 </html>
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
index ac0283c..360895a 100644
--- a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
+++ b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
@@ -57,7 +57,11 @@
 
     public static final String LOGIN_FORM_URL = "webkit/test_loginForm.html";
 
-    public static final String EXT_WEB_URL1 = "http://www.example.com/";
+    // Note: tests should avoid loading external URLs if at all possible, since any changes to that
+    // public site (even if it doesn't currently exist) can affect test behavior. The ".test" TLD is
+    // OK because (1) it's reserved for testing by RFC2606 and (2) the test never navigates to this
+    // page.
+    public static final String EXT_WEB_URL1 = "http://www.example.test/";
 
     public static final String PARAM_ASSET_URL = "webkit/test_queryparam.html";
     public static final String ANCHOR_ASSET_URL = "webkit/test_anchor.html";
@@ -66,8 +70,9 @@
     public static final String DATABASE_ACCESS_URL = "webkit/test_databaseaccess.html";
     public static final String STOP_LOADING_URL = "webkit/test_stop_loading.html";
     public static final String BLANK_TAG_URL = "webkit/blank_tag.html";
+    // A page with a link to an arbitrary page controlled by the test server (in this case,
+    // BLANK_PAGE_URL).
     public static final String PAGE_WITH_LINK_URL = "webkit/page_with_link.html";
-    public static final String URL_IN_PAGE_WITH_LINK = "http://foo.com/";
     // Not a real page, just triggers a 404 response.
     public static final String NON_EXISTENT_PAGE_URL = "webkit/generate_404.html";
     public static final String BAD_IMAGE_PAGE_URL = "webkit/test_bad_image_url.html";
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index 81ea695..a99e9af 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -204,8 +204,10 @@
             }
         }.run();
         assertEquals(mainCallCount, mainWebViewClient.getShouldOverrideUrlLoadingCallCount());
-        assertEquals(
-            TestHtmlConstants.URL_IN_PAGE_WITH_LINK, childWebViewClient.getLastShouldOverrideUrl());
+        // PAGE_WITH_LINK_URL has a link to BLANK_PAGE_URL (an arbitrary page also controlled by the
+        // test server)
+        assertEquals(mWebServer.getAssetUrl(TestHtmlConstants.BLANK_PAGE_URL),
+                childWebViewClient.getLastShouldOverrideUrl());
     }
 
     private void clickOnLinkUsingJs(final String linkId, WebViewOnUiThread webViewOnUiThread) {
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessClientTest.java
index 2d6a196..39e3eb9 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessClientTest.java
@@ -23,7 +23,7 @@
 import android.webkit.WebView;
 import android.webkit.WebViewRenderProcess;
 import android.webkit.WebViewRenderProcessClient;
-
+import com.android.compatibility.common.util.NullWebViewUtils;
 import com.google.common.util.concurrent.SettableFuture;
 
 import java.util.concurrent.CountDownLatch;
@@ -44,7 +44,9 @@
         super.setUp();
         final WebViewCtsActivity activity = getActivity();
         WebView webView = activity.getWebView();
-        mOnUiThread = new WebViewOnUiThread(webView);
+        if (webView != null) {
+            mOnUiThread = new WebViewOnUiThread(webView);
+        }
     }
 
     @Override
@@ -145,10 +147,17 @@
     }
 
     public void testWebViewRenderProcessClientWithoutExecutor() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
         testWebViewRenderProcessClientOnExecutor(null);
     }
 
     public void testWebViewRenderProcessClientWithExecutor() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
         final AtomicInteger executorCount = new AtomicInteger();
         testWebViewRenderProcessClientOnExecutor(new Executor() {
             @Override
@@ -161,6 +170,10 @@
     }
 
     public void testSetWebViewRenderProcessClient() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
         assertNull("Initially the renderer client should be null",
                 mOnUiThread.getWebViewRenderProcessClient());
 
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessTest.java
index 6e25b5f..941d970 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewRenderProcessTest.java
@@ -25,6 +25,7 @@
 import android.webkit.WebViewClient;
 import android.webkit.WebViewRenderProcess;
 
+import com.android.compatibility.common.util.NullWebViewUtils;
 import com.google.common.util.concurrent.SettableFuture;
 
 import java.util.concurrent.Future;
@@ -42,7 +43,9 @@
         super.setUp();
         final WebViewCtsActivity activity = getActivity();
         WebView webView = activity.getWebView();
-        mOnUiThread = new WebViewOnUiThread(webView);
+        if (webView != null) {
+            mOnUiThread = new WebViewOnUiThread(webView);
+        }
     }
 
     @Override
@@ -104,6 +107,10 @@
     }
 
     public void testGetWebViewRenderProcess() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
         final WebView webView = mOnUiThread.getWebView();
         final WebViewRenderProcess preStartRenderProcess = getRenderProcessOnUiThread(webView);
 
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
index d59d395..bb7994e 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
@@ -822,6 +822,26 @@
         assertEquals(callCount + 1, webViewClient.getClientCertRequestCount());
     }
 
+    /**
+     * Loads a url until a specific error code. This is meant to be used when two different errors
+     * can race. Specifically, this is meant to be used to workaround the TLS 1.3 (Android Q and
+     * above) race condition where a server <b>may</b> close the connection at the same time the
+     * client sends the HTTP request, emitting {@code ERROR_CONNECT} instead of {@code
+     * ERROR_FAILED_SSL_HANDSHAKE}.
+     */
+    private void loadUrlUntilError(SslErrorWebViewClient client, String url,
+            int expectedErrorCode) {
+        int maxTries = 40;
+        for (int i = 0; i < maxTries; i++) {
+            mOnUiThread.loadUrlAndWaitForCompletion(url);
+            if (client.onReceivedErrorCode() == expectedErrorCode) {
+                return;
+            }
+        }
+        throw new RuntimeException(
+                "Reached max number of tries and never saw error " + expectedErrorCode);
+    }
+
     public void testIgnoreClientCertRequest() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -833,18 +853,23 @@
         clearClientCertPreferences();
         // Ignore the request. Load should fail.
         webViewClient.setAction(ClientCertWebViewClient.IGNORE);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        loadUrlUntilError(webViewClient, url, WebViewClient.ERROR_FAILED_SSL_HANDSHAKE);
         assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
-        assertFailedHandshakeOrConnectionError(webViewClient.onReceivedErrorCode());
+        // At least one of the loads done by loadUrlUntilError() should produce
+        // onReceivedClientCertRequest.
+        assertTrue("onReceivedClientCertRequest should be called at least once",
+                webViewClient.getClientCertRequestCount() >= 1);
 
         // Load a different page from the same domain, ignoring the request. We should get a callback,
         // and load should fail.
         int callCount = webViewClient.getClientCertRequestCount();
         url = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        loadUrlUntilError(webViewClient, url, WebViewClient.ERROR_FAILED_SSL_HANDSHAKE);
         assertFalse(TestHtmlConstants.HTML_URL1_TITLE.equals(mOnUiThread.getTitle()));
-        assertFailedHandshakeOrConnectionError(webViewClient.onReceivedErrorCode());
-        assertEquals(callCount + 1, webViewClient.getClientCertRequestCount());
+        // At least one of the loads done by loadUrlUntilError() should produce
+        // onReceivedClientCertRequest.
+        assertTrue("onReceivedClientCertRequest should be called at least once for second URL",
+                webViewClient.getClientCertRequestCount() >= callCount + 1);
 
         // Reload, proceeding the request. Load should succeed.
         webViewClient.setAction(ClientCertWebViewClient.PROCEED);
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index b9311c6..ab96e6f 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -19,15 +19,35 @@
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="force-install-mode" value="FULL"/>
+        <option name="test-file-name" value="TestIme.apk" />
+        <option name="test-file-name" value="CtsWidgetApp.apk" />
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsWidgetTestCases.apk" />
     </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="force-install-mode" value="FULL"/>
-        <option name="test-file-name" value="CtsWidgetApp.apk" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <!--
+            Workaround for the possible downtime between [adb install <IME.apk>]
+            and [adb shell ime <IME ID>].  See b/188105339 for detais.
+        -->
+        <option name="run-command" value="am wait-for-broadcast-idle" />
+        <!--
+            One more workaround. Consider increasing this if the test is still unstable.
+            See b/188105339 for detais.
+        -->
+        <option name="run-command" value="sleep 1" />
+        <option name="run-command" value="ime enable com.android.cts.testime/.TestIme" />
+        <option name="run-command" value="ime set com.android.cts.testime/.TestIme" />
+        <option name="teardown-command" value="ime reset" />
     </target_preparer>
+
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.widget.cts" />
         <option name="runtime-hint" value="11m55s" />
diff --git a/tests/tests/widget/res/layout-round/edittext_layout.xml b/tests/tests/widget/res/layout-round/edittext_layout.xml
new file mode 100644
index 0000000..4f7f62b
--- /dev/null
+++ b/tests/tests/widget/res/layout-round/edittext_layout.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+
+    <LinearLayout
+        android:id="@+id/edit_text"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <EditText
+            android:id="@+id/edittext_simple1"
+            android:layout_width="200dp"
+            android:layout_marginLeft="50dip"
+            android:layout_height="wrap_content" />
+
+        <EditText
+            android:id="@+id/edittext_simple2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+        <EditText android:id="@+id/edittext1"
+            style="@android:style/Widget.EditText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dip"
+            android:scrollHorizontally="true"
+            android:capitalize="sentences"
+            android:autoText="false"
+            android:maxLines="3"
+            android:textColor="#FF0000"
+            android:text="@string/edit_text" />
+
+        <EditText
+            android:id="@+id/edittext_autosize"
+            android:layout_width="300dp"
+            android:layout_height="400dp"
+            android:text="@string/long_text"
+            android:autoSizeTextType="uniform"
+            android:textSize="50dp"
+            android:autoSizeStepGranularity="2dp" />
+    </LinearLayout>
+</ScrollView>
diff --git a/tests/tests/widget/res/layout-watch/magnifier_activity_centered_view_layout.xml b/tests/tests/widget/res/layout-watch/magnifier_activity_centered_view_layout.xml
new file mode 100644
index 0000000..4a3b211
--- /dev/null
+++ b/tests/tests/widget/res/layout-watch/magnifier_activity_centered_view_layout.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/magnifier_activity_centered_view_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center" >
+    <FrameLayout
+        android:id="@+id/magnifier_centered_view"
+        android:layout_width="40dp"
+        android:layout_height="16dp"
+        android:background="@android:color/holo_blue_bright" />
+</LinearLayout>
diff --git a/tests/tests/widget/res/layout-watch/magnifier_activity_scrollable_views_layout.xml b/tests/tests/widget/res/layout-watch/magnifier_activity_scrollable_views_layout.xml
new file mode 100644
index 0000000..450d3a0
--- /dev/null
+++ b/tests/tests/widget/res/layout-watch/magnifier_activity_scrollable_views_layout.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/magnifier_activity_scrollable_views_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:orientation="vertical" >
+
+    <HorizontalScrollView
+        android:id="@+id/horizontal_scroll_container"
+        android:layout_width="100dp"
+        android:layout_height="wrap_content">
+        <FrameLayout
+            android:id="@+id/magnifier_activity_horizontally_scrolled_view"
+            android:layout_width="wrap_content"
+            android:layout_height="40dp"
+            android:minWidth="500dp"
+            android:background="@android:color/holo_blue_bright" />
+    </HorizontalScrollView>
+
+    <ScrollView
+        android:id="@+id/vertical_scroll_container"
+        android:layout_width="wrap_content"
+        android:layout_height="40dp" >
+        <FrameLayout
+            android:id="@+id/magnifier_activity_vertically_scrolled_view"
+            android:layout_width="40dp"
+            android:layout_height="wrap_content"
+            android:minHeight="500dp"
+            android:background="@android:color/holo_red_light" />
+    </ScrollView>
+</LinearLayout>
diff --git a/tests/tests/widget/res/layout/magnifier_activity_centered_view_layout.xml b/tests/tests/widget/res/layout/magnifier_activity_centered_view_layout.xml
index 1a11740..1bad59c 100644
--- a/tests/tests/widget/res/layout/magnifier_activity_centered_view_layout.xml
+++ b/tests/tests/widget/res/layout/magnifier_activity_centered_view_layout.xml
@@ -23,7 +23,7 @@
     android:gravity="center" >
     <FrameLayout
         android:id="@+id/magnifier_centered_view"
-        android:layout_width="100dp"
+        android:layout_width="80dp"
         android:layout_height="56dp"
         android:background="@android:color/holo_blue_bright" />
 </LinearLayout>
diff --git a/tests/tests/widget/res/layout/magnifier_activity_scrollable_views_layout.xml b/tests/tests/widget/res/layout/magnifier_activity_scrollable_views_layout.xml
index 4b2bc6f..f1ccef1 100644
--- a/tests/tests/widget/res/layout/magnifier_activity_scrollable_views_layout.xml
+++ b/tests/tests/widget/res/layout/magnifier_activity_scrollable_views_layout.xml
@@ -38,8 +38,7 @@
     <ScrollView
         android:id="@+id/vertical_scroll_container"
         android:layout_width="wrap_content"
-        android:layout_height="100dp"
-        android:layout_marginTop="100dp" >
+        android:layout_height="100dp" >
         <FrameLayout
             android:id="@+id/magnifier_activity_vertically_scrolled_view"
             android:layout_width="100dp"
diff --git a/tests/tests/widget/res/layout/timepicker.xml b/tests/tests/widget/res/layout/timepicker.xml
index 8581008..d23366a 100644
--- a/tests/tests/widget/res/layout/timepicker.xml
+++ b/tests/tests/widget/res/layout/timepicker.xml
@@ -20,6 +20,7 @@
         android:orientation="vertical">
     <TimePicker
             android:id="@+id/timepicker_clock"
+            android:timePickerMode="clock"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"/>
     <TimePicker
diff --git a/tests/tests/widget/src/android/widget/cts/EditTextTest.java b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
index 957edf0..c5e40ca 100755
--- a/tests/tests/widget/src/android/widget/cts/EditTextTest.java
+++ b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
@@ -467,7 +467,7 @@
     @Test
     public void testCursorDrag() throws Exception {
         AtomicReference<SparseArray<Point>> dragStartEnd = new AtomicReference<>();
-        String text = "Hello world, how are you doing today?";
+        String text = "Hello, how are you today?";
         mInstrumentation.runOnMainSync(() -> {
             mEditText1.setText(text);
             mEditText1.requestFocus();
@@ -497,8 +497,10 @@
     }
 
     private static Point getScreenCoords(TextView textView, int offset) {
-        // Get the x,y coordinates for the given offset in the text. These are relative to the view.
-        int x = (int) textView.getLayout().getPrimaryHorizontal(offset);
+        // Get the x,y coordinates for the given offset in the text.
+	// These are relative to the view.
+	// Please note that we compensate for rounding error here by adding 1.
+        int x = (int) textView.getLayout().getPrimaryHorizontal(offset) + 1;
         int line = textView.getLayout().getLineForOffset(offset);
         int yTop = textView.getLayout().getLineTop(line);
         int yBottom = textView.getLayout().getLineBottom(line);
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
index 148e6a8..8d9e2a2 100644
--- a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -498,7 +498,7 @@
         final Rect rect = new Rect();
         mPopupWindow.getBackground().getPadding(rect);
         CtsTouchUtils.emulateTapOnView(instrumentation, mActivityRule, popupListView,
-                -rect.left - 20, popupListView.getHeight() + rect.top + rect.bottom + 20);
+                -rect.left - 20, popupListView.getHeight() / 2);
 
         // At this point our popup should not be showing and should have notified its
         // dismiss listener
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index e0fb5e8..adf7e67 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -4201,6 +4201,9 @@
 
     @Test
     public void testHandleDrawable_canBeSet_whenInsertionHandleIsShown() throws Throwable {
+        if (isWatch()) {
+            return; // watch does not support overlay keyboard.
+        }
         initTextViewForTypingOnUiThread();
         mActivityRule.runOnUiThread(() -> {
             mTextView.setTextIsSelectable(true);
@@ -6572,18 +6575,7 @@
     public void testSetGetBreakStrategy() {
         TextView tv = new TextView(mActivity);
 
-        final PackageManager pm = mInstrumentation.getTargetContext().getPackageManager();
-
-        // The default value is from the theme, here the default is BREAK_STRATEGY_HIGH_QUALITY for
-        // TextView except for Android Wear. The default value for Android Wear is
-        // BREAK_STRATEGY_BALANCED.
-        if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
-            // Android Wear
-            assertEquals(Layout.BREAK_STRATEGY_BALANCED, tv.getBreakStrategy());
-        } else {
-            // All other form factor.
-            assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, tv.getBreakStrategy());
-        }
+        assertEquals(Layout.BREAK_STRATEGY_HIGH_QUALITY, tv.getBreakStrategy());
 
         tv.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE);
         assertEquals(Layout.BREAK_STRATEGY_SIMPLE, tv.getBreakStrategy());
diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java
index fb1c7ba..d3718a0 100644
--- a/tests/tests/widget/src/android/widget/cts/ToastTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java
@@ -16,6 +16,7 @@
 
 package android.widget.cts;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -28,6 +29,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 
+import android.app.ActivityOptions;
 import android.app.UiAutomation;
 import android.app.UiAutomation.AccessibilityEventFilter;
 import android.content.BroadcastReceiver;
@@ -449,7 +451,7 @@
          * watch. Unlike the phone, which displays toast centered horizontally at the bottom of the
          * screen, the watch now displays toast in the center of the screen.
          */
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+        if (Gravity.CENTER == mToast.getGravity()) {
             assertTrue(xy1[0] > xy2[0]);
             assertTrue(xy1[1] > xy2[1]);
         } else {
@@ -522,6 +524,7 @@
     @UiThreadTest
     @Test
     public void testMakeTextFromString() {
+        assumeFalse("Skipping test: Watch does not support new Toast behavior yet", isWatch());
         Toast toast = Toast.makeText(mContext, "android", Toast.LENGTH_SHORT);
         assertNotNull(toast);
         assertEquals(Toast.LENGTH_SHORT, toast.getDuration());
@@ -550,6 +553,7 @@
     @UiThreadTest
     @Test
     public void testMakeTextFromResource() {
+        assumeFalse("Skipping test: Watch does not support new Toast behavior yet", isWatch());
         Toast toast = Toast.makeText(mContext, R.string.hello_world, Toast.LENGTH_LONG);
 
         assertNotNull(toast);
@@ -604,6 +608,7 @@
     @UiThreadTest
     @Test(expected = IllegalStateException.class)
     public void testSetTextFromStringNonNullView() {
+        assumeFalse("Skipping test: Watch does not support new Toast behavior yet", isWatch());
         Toast toast = Toast.makeText(mContext, R.string.text, Toast.LENGTH_LONG);
         toast.setView(new TextView(mContext));
         toast.setText(null);
@@ -699,6 +704,7 @@
 
     @Test
     public void testTextToastAllowed_whenInTheBackground() throws Throwable {
+        assumeFalse("Skipping test: Watch does not support new Toast behavior yet", isWatch());
         // Make it background
         mActivityRule.finishActivity();
         makeToast();
@@ -729,25 +735,25 @@
         Intent intent = new Intent();
         intent.setComponent(COMPONENT_TRANSLUCENT_ACTIVITY);
         intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(intent);
+        // Launch the translucent activity in fullscreen to ensure the test activity won't resume
+        // even on the freeform-first multi-window device.
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        mContext.startActivity(intent, options.toBundle());
         activityStarted.block();
         makeCustomToast();
         View view = mToast.getView();
 
         mActivityRule.runOnUiThread(mToast::show);
 
-        // The custom toast should not be blocked in multi-window mode. Otherwise, it should be.
-        if (mActivityRule.getActivity().isInMultiWindowMode()) {
-            assertShowCustomToast(view);
-        } else {
-            assertNotShowCustomToast(view);
-        }
+        assertNotShowCustomToast(view);
         mContext.sendBroadcast(new Intent(ACTION_TRANSLUCENT_ACTIVITY_FINISH));
     }
 
     @UiThreadTest
     @Test
     public void testGetWindowParams_whenTextToast_returnsNull() {
+          assumeFalse("Skipping test: Watch does not support new Toast behavior yet", isWatch());
         Toast toast = Toast.makeText(mContext, "Text", Toast.LENGTH_LONG);
         assertNull(toast.getWindowParams());
     }
@@ -843,6 +849,11 @@
         return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
     }
 
+    private boolean isWatch() {
+        PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+    }
+
     private static void uncheck(ThrowingRunnable runnable) {
         try {
             runnable.run();
diff --git a/tests/tests/widget29/src/android/widget/cts29/ToastTest.java b/tests/tests/widget29/src/android/widget/cts29/ToastTest.java
index 49cd0de..cb917b7 100644
--- a/tests/tests/widget29/src/android/widget/cts29/ToastTest.java
+++ b/tests/tests/widget29/src/android/widget/cts29/ToastTest.java
@@ -341,7 +341,7 @@
          * watch. Unlike the phone, which displays toast centered horizontally at the bottom of the
          * screen, the watch now displays toast in the center of the screen.
          */
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+        if (Gravity.CENTER == mToast.getGravity()) {
             assertTrue(xy1[0] > xy2[0]);
             assertTrue(xy1[1] > xy2[1]);
         } else {
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java b/tests/tests/wifi/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java
index 07256a3..e0768d9 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java
@@ -41,7 +41,7 @@
 public class EasyConnectStatusCallbackTest extends WifiJUnit3TestBase {
     private static final String TEST_SSID = "\"testSsid\"";
     private static final String TEST_PASSPHRASE = "\"testPassword\"";
-    private static final int TEST_WAIT_DURATION_MS = 12_000; // Long delay is necessary, see below
+    private static final int TEST_WAIT_DURATION_MS = 22_000; // Long delay is necessary, see below
     private WifiManager mWifiManager;
     private static final String TEST_DPP_URI =
             "DPP:C:81/1,117/40;I:Easy_Connect_Demo;M:000102030405;"
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
index bdbc699a..2fb377d 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
@@ -16,7 +16,7 @@
 
 package android.net.wifi.cts;
 
-import static android.net.NetworkCapabilitiesProto.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.os.Process.myUid;
 
 import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/tests/wrap/nowrap/Android.mk b/tests/tests/wrap/nowrap/Android.mk
index 8f50ddc..269f4ad 100644
--- a/tests/tests/wrap/nowrap/Android.mk
+++ b/tests/tests/wrap/nowrap/Android.mk
@@ -30,6 +30,8 @@
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := CtsWrapNoWrapTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest.xml
 
 # Jarjar to make WrapTest unique.
diff --git a/tests/tests/wrap/wrap_debug/Android.mk b/tests/tests/wrap/wrap_debug/Android.mk
index e8d8f1f..8743cc4 100644
--- a/tests/tests/wrap/wrap_debug/Android.mk
+++ b/tests/tests/wrap/wrap_debug/Android.mk
@@ -37,6 +37,8 @@
 LOCAL_PREBUILT_JNI_LIBS_x86_64 := ../wrap.sh
 
 LOCAL_PACKAGE_NAME := CtsWrapWrapDebugTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest.xml
 
 # Jarjar to make WrapTest unique.
diff --git a/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk b/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
index 06a2850..06d1d5c 100644
--- a/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
+++ b/tests/tests/wrap/wrap_debug_malloc_debug/Android.mk
@@ -37,6 +37,8 @@
 LOCAL_PREBUILT_JNI_LIBS_x86_64 := wrap.sh
 
 LOCAL_PACKAGE_NAME := CtsWrapWrapDebugMallocDebugTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest.xml
 
 # Jarjar to make WrapTest unique.
diff --git a/tests/tests/wrap/wrap_nodebug/Android.mk b/tests/tests/wrap/wrap_nodebug/Android.mk
index aaebda2..f7823c5 100644
--- a/tests/tests/wrap/wrap_nodebug/Android.mk
+++ b/tests/tests/wrap/wrap_nodebug/Android.mk
@@ -37,6 +37,8 @@
 LOCAL_PREBUILT_JNI_LIBS_x86_64 := ../wrap.sh
 
 LOCAL_PACKAGE_NAME := CtsWrapWrapNoDebugTestCases
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MANIFEST_FILE := AndroidManifest.xml
 
 # Jarjar to make WrapTest unique.
diff --git a/tests/uwb/src/android/uwb/cts/AngleMeasurementTest.java b/tests/uwb/src/android/uwb/cts/AngleMeasurementTest.java
index c6ddead..f96b798 100644
--- a/tests/uwb/src/android/uwb/cts/AngleMeasurementTest.java
+++ b/tests/uwb/src/android/uwb/cts/AngleMeasurementTest.java
@@ -35,41 +35,52 @@
 @RunWith(AndroidJUnit4.class)
 public class AngleMeasurementTest {
     @Test
-    public void testBuilder() {
+    public void testConstructs() {
         double radians = 0.1234;
         double errorRadians = 0.5678;
         double confidence = 0.5;
 
-        AngleMeasurement.Builder builder = new AngleMeasurement.Builder();
-        tryBuild(builder, false);
-
-        builder.setRadians(radians);
-        tryBuild(builder, false);
-
-        builder.setErrorRadians(errorRadians);
-        tryBuild(builder, false);
-
-        builder.setConfidenceLevel(confidence);
-        AngleMeasurement measurement = tryBuild(builder, true);
-
+        AngleMeasurement measurement = new AngleMeasurement(radians, errorRadians, confidence);
         assertEquals(measurement.getRadians(), radians, 0);
         assertEquals(measurement.getErrorRadians(), errorRadians, 0);
         assertEquals(measurement.getConfidenceLevel(), confidence, 0);
     }
 
-    private AngleMeasurement tryBuild(AngleMeasurement.Builder builder, boolean expectSuccess) {
-        AngleMeasurement measurement = null;
+    @Test
+    public void testInvalidRadians() {
+        double radians = Math.PI + 0.01;
+        double errorRadians = 0.5678;
+        double confidence = 0.5;
+
+        constructExpectFailure(radians, errorRadians, confidence);
+        constructExpectFailure(-radians, errorRadians, confidence);
+    }
+
+    @Test
+    public void testInvalidErrorRadians() {
+        double radians = 0.1234;
+        double confidence = 0.5;
+
+        constructExpectFailure(radians, -0.01, confidence);
+        constructExpectFailure(-radians, Math.PI + 0.01, confidence);
+    }
+
+    @Test
+    public void testInvalidConfidence() {
+        double radians = 0.1234;
+        double errorRadians = 0.5678;
+
+        constructExpectFailure(radians, errorRadians, -0.01);
+        constructExpectFailure(radians, errorRadians, 1.01);
+    }
+
+    private void constructExpectFailure(double radians, double errorRadians, double confidence) {
         try {
-            measurement = builder.build();
-            if (!expectSuccess) {
-                fail("Expected AngleMeasurement.Builder.build() to fail, but it succeeded");
-            }
-        } catch (IllegalStateException e) {
-            if (expectSuccess) {
-                fail("Expected AngleMeasurement.Builder.build() to succeed, but it failed");
-            }
+            new AngleMeasurement(radians, errorRadians, confidence);
+            fail();
+        } catch (IllegalArgumentException e) {
+            // Expected
         }
-        return measurement;
     }
 
     @Test
diff --git a/tests/uwb/src/android/uwb/cts/AngleOfArrivalMeasurementTest.java b/tests/uwb/src/android/uwb/cts/AngleOfArrivalMeasurementTest.java
index 16dc4c2..085ce2e 100644
--- a/tests/uwb/src/android/uwb/cts/AngleOfArrivalMeasurementTest.java
+++ b/tests/uwb/src/android/uwb/cts/AngleOfArrivalMeasurementTest.java
@@ -41,27 +41,15 @@
         AngleMeasurement azimuth = UwbTestUtils.getAngleMeasurement();
         AngleMeasurement altitude = UwbTestUtils.getAngleMeasurement();
 
-        AngleOfArrivalMeasurement.Builder builder = new AngleOfArrivalMeasurement.Builder();
-        tryBuild(builder, false);
-
+        AngleOfArrivalMeasurement.Builder builder = new AngleOfArrivalMeasurement.Builder(azimuth);
         builder.setAltitude(altitude);
-        tryBuild(builder, false);
 
-        builder.setAzimuth(azimuth);
         AngleOfArrivalMeasurement measurement = tryBuild(builder, true);
 
         assertEquals(azimuth, measurement.getAzimuth());
         assertEquals(altitude, measurement.getAltitude());
     }
 
-    private AngleMeasurement getAngleMeasurement(double radian, double error, double confidence) {
-        return new AngleMeasurement.Builder()
-                .setRadians(radian)
-                .setErrorRadians(error)
-                .setConfidenceLevel(confidence)
-                .build();
-    }
-
     private AngleOfArrivalMeasurement tryBuild(AngleOfArrivalMeasurement.Builder builder,
             boolean expectSuccess) {
         AngleOfArrivalMeasurement measurement = null;
diff --git a/tests/uwb/src/android/uwb/cts/RangingSessionTest.java b/tests/uwb/src/android/uwb/cts/RangingSessionTest.java
index b936142..b9725f1 100644
--- a/tests/uwb/src/android/uwb/cts/RangingSessionTest.java
+++ b/tests/uwb/src/android/uwb/cts/RangingSessionTest.java
@@ -172,11 +172,11 @@
         session.start(PARAMS);
 
         verifyNoThrowIllegalState(session::stop);
-        verify(callback, times(1)).onStopped();
+        verify(callback, times(1)).onStopped(anyInt(), any());
 
         // Calling stop again should throw an illegal state
         verifyThrowIllegalState(session::stop);
-        verify(callback, times(1)).onStopped();
+        verify(callback, times(1)).onStopped(anyInt(), any());
     }
 
     @Test
@@ -202,7 +202,7 @@
         verify(callback, times(2)).onReconfigured(any());
         verifyOpenState(session, true);
 
-        session.onRangingStopped();
+        session.onRangingStopped(REASON, PARAMS);
         verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
         verify(callback, times(3)).onReconfigured(any());
         verifyOpenState(session, true);
@@ -363,7 +363,7 @@
 
         @Override
         public Object answer(InvocationOnMock invocation) {
-            mSession.onRangingStopped();
+            mSession.onRangingStopped(REASON, PARAMS);
             return null;
         }
     }
diff --git a/tests/uwb/src/android/uwb/cts/UwbTestUtils.java b/tests/uwb/src/android/uwb/cts/UwbTestUtils.java
index b5d8e1b..3790b52 100644
--- a/tests/uwb/src/android/uwb/cts/UwbTestUtils.java
+++ b/tests/uwb/src/android/uwb/cts/UwbTestUtils.java
@@ -39,17 +39,15 @@
     }
 
     public static AngleMeasurement getAngleMeasurement() {
-        return new AngleMeasurement.Builder()
-                .setRadians(getDoubleInRange(-Math.PI, Math.PI))
-                .setErrorRadians(getDoubleInRange(0, Math.PI))
-                .setConfidenceLevel(getDoubleInRange(0, 1))
-                .build();
+        return new AngleMeasurement(
+                getDoubleInRange(-Math.PI, Math.PI),
+                getDoubleInRange(0, Math.PI),
+                getDoubleInRange(0, 1));
     }
 
     public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
-        return new AngleOfArrivalMeasurement.Builder()
+        return new AngleOfArrivalMeasurement.Builder(getAngleMeasurement())
                 .setAltitude(getAngleMeasurement())
-                .setAzimuth(getAngleMeasurement())
                 .build();
     }
 
diff --git a/tests/video/Android.bp b/tests/video/Android.bp
index e3f5133b..55eaa74 100644
--- a/tests/video/Android.bp
+++ b/tests/video/Android.bp
@@ -29,6 +29,8 @@
         "android.test.runner",
         "android.test.base",
     ],
+    platform_apis: true,
+    jni_uses_sdk_apis: true,
     jni_libs: [
         "libctscodecutils_jni",
         "libnativehelper_compat_libc++",
@@ -39,5 +41,5 @@
         "cts",
         "general-tests",
     ],
-    sdk_version: "test_current",
+    min_sdk_version: "29",
 }
diff --git a/tests/video/AndroidManifest.xml b/tests/video/AndroidManifest.xml
index 534fc3d..3b25dd5 100644
--- a/tests/video/AndroidManifest.xml
+++ b/tests/video/AndroidManifest.xml
@@ -18,10 +18,14 @@
         package="android.video.cts">
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
     <application>
+        android:requestLegacyExternalStorage="true"
+        android:usesCleartextTraffic="true">
         <uses-library android:name="android.test.runner" />
+        <activity android:name="android.video.cts.CodecTestActivity" />
     </application>
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
             android:targetPackage="android.video.cts"
@@ -30,4 +34,5 @@
             android:name="listener"
             android:value="com.android.cts.runner.CtsTestRunListener" />
     </instrumentation>
+    <uses-sdk android:minSdkVersion="29"   android:targetSdkVersion="29" />
 </manifest>
diff --git a/tests/video/AndroidTest.xml b/tests/video/AndroidTest.xml
index ed55f13..d99ccc1 100644
--- a/tests/video/AndroidTest.xml
+++ b/tests/video/AndroidTest.xml
@@ -19,6 +19,16 @@
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="host" />
+        <option name="config-filename" value="CtsVideoTestCases" />
+        <option name="version" value="1.0"/>
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
+        <option name="push-all" value="true" />
+        <option name="media-folder-name" value="CtsVideoTestCases-1.1" />
+        <option name="dynamic-config-module" value="CtsVideoTestCases" />
+    </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsVideoTestCases.apk" />
diff --git a/tests/video/DynamicConfig.xml b/tests/video/DynamicConfig.xml
new file mode 100644
index 0000000..1bf19811
--- /dev/null
+++ b/tests/video/DynamicConfig.xml
@@ -0,0 +1,5 @@
+<dynamicConfig>
+    <entry key="media_files_url">
+      <value>https://storage.googleapis.com/android_media/cts/tests/video/CtsVideoTestCases-1.1.zip</value>
+    </entry>
+</dynamicConfig>
diff --git a/tests/video/README.md b/tests/video/README.md
new file mode 100644
index 0000000..f1c9dfe
--- /dev/null
+++ b/tests/video/README.md
@@ -0,0 +1,13 @@
+## Video CTS Tests
+Current folder comprises of files necessary for:
+1. Testing video encoder/decoder performance by running encoding/decoding without displaying the raw data.
+2. Testing key operating rate for Hardware video encoder/decoder.
+
+The test vectors used by the test suite is available at [link](https://storage.googleapis.com/android_media/cts/tests/video/CtsVideoTestCases-1.1.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory.
+
+### Commands
+```sh
+$ atest android.video.cts
+$ atest android.video.cts.CodecEncoderPerformanceTest
+$ atest android.video.cts.CodecDecoderPerformanceTest
+```
diff --git a/tests/video/copy_media.sh b/tests/video/copy_media.sh
new file mode 100644
index 0000000..86be1e7
--- /dev/null
+++ b/tests/video/copy_media.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+## script to install cts video test files manually
+
+adbOptions=" "
+resLabel=CtsVideoTestCases-1.1
+srcDir="/tmp/$resLabel"
+tgtDir="/sdcard/test"
+usage="Usage: $0 [-h] [-s serial]"
+
+if [ $# -gt 0 ]; then
+  if [ "$1" = "-h" ]; then
+    echo $usage
+    exit 1
+  elif [ "$1" = "-s" -a "$2" != "" ] ; then
+    adbOptions=""$1" "$2""
+  else
+    echo "bad options"
+    echo $usage
+    exit 1
+  fi
+fi
+
+## download resources if not already done
+if [ ! -f "/tmp/$resLabel.zip" ]; then
+  wget "https://storage.googleapis.com/android_media/cts/tests/video/$resLabel.zip" -O /tmp/$resLabel.zip
+fi
+unzip -qo "/tmp/$resLabel" -d $srcDir
+
+## install on target device
+echo "adb $adbOptions push $srcDir $tgtDir"
+adb $adbOptions shell mkdir -p $tgtDir
+adb $adbOptions push $srcDir/. $tgtDir
diff --git a/tests/video/res/layout/media_decoder_surface_layout.xml b/tests/video/res/layout/media_decoder_surface_layout.xml
new file mode 100644
index 0000000..8ac820a
--- /dev/null
+++ b/tests/video/res/layout/media_decoder_surface_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <SurfaceView android:id="@+id/surface"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+    </SurfaceView>
+</LinearLayout>
diff --git a/tests/video/src/android/video/cts/CodecDecoderPerformanceTest.java b/tests/video/src/android/video/cts/CodecDecoderPerformanceTest.java
new file mode 100644
index 0000000..6daebf4
--- /dev/null
+++ b/tests/video/src/android/video/cts/CodecDecoderPerformanceTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.video.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.util.Log;
+import android.view.Surface;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Parameterized.class)
+public class CodecDecoderPerformanceTest extends CodecPerformanceTestBase {
+    private static final String LOG_TAG = CodecDecoderPerformanceTest.class.getSimpleName();
+    private Surface mSurface;
+
+    @Rule
+    public ActivityTestRule<CodecTestActivity> mActivityRule =
+            new ActivityTestRule<>(CodecTestActivity.class);
+
+    public CodecDecoderPerformanceTest(String decoderName, String testFile, int keyPriority,
+            float scalingFactor) {
+        super(decoderName, testFile, keyPriority, scalingFactor);
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{2}_{3})")
+    public static Collection<Object[]> input() throws IOException {
+        final String[] fileList = new String[]{
+                // Video - Filename
+                // AVC
+                "crowd_run_720x480_30fps_avc.mp4",
+                "crowd_run_1280x720_30fps_avc.mp4",
+                "crowd_run_1920x1080_30fps_avc.mp4",
+                "crowd_run_3840x2160_30fps_avc.mp4",
+                // HEVC
+                "crowd_run_720x480_30fps_hevc.mp4",
+                "crowd_run_1280x720_30fps_hevc.mp4",
+                "crowd_run_1920x1080_30fps_hevc.mp4",
+                "crowd_run_3840x2160_30fps_hevc.mp4",
+                "crowd_run_7680x4320_30fps_hevc.mp4",
+                // VP8
+                "crowd_run_720x480_30fps_vp8.webm",
+                "crowd_run_1280x720_30fps_vp8.webm",
+                "crowd_run_1920x1080_30fps_vp8.webm",
+                "crowd_run_3840x2160_30fps_vp8.webm",
+                // VP9
+                "crowd_run_720x480_30fps_vp9.webm",
+                "crowd_run_1280x720_30fps_vp9.webm",
+                "crowd_run_1920x1080_30fps_vp9.webm",
+                "crowd_run_3840x2160_30fps_vp9.webm",
+                "crowd_run_7680x4320_30fps_vp9.webm",
+                // AV1
+                "crowd_run_720x480_30fps_av1.mp4",
+                "crowd_run_1280x720_30fps_av1.mp4",
+                "crowd_run_1920x1080_30fps_av1.mp4",
+                "crowd_run_3840x2160_30fps_av1.mp4",
+                "crowd_run_7680x4320_30fps_av1.mp4",
+                // MPEG-2
+                "crowd_run_720x480_30fps_mpeg2.mp4",
+                "crowd_run_1280x720_30fps_mpeg2.mp4",
+                "crowd_run_1920x1080_30fps_mpeg2.mp4",
+                "crowd_run_3840x2160_30fps_mpeg2.mp4",
+                "crowd_run_7680x4320_30fps_mpeg2.mp4",
+        };
+        // Prepares the params list combining with supported Hardware decoders, key priority
+        // and scaling factor.
+        final List<Object[]> argsList = new ArrayList<>();
+        for (String fileName : fileList) {
+            // Gets the format for the first video track found
+            MediaFormat format = getVideoFormat(fileName);
+            if (format == null) {
+                Log.e(LOG_TAG, "Test vector is ignored as it has no video tracks present " +
+                        "in the file: " + fileName);
+                continue;
+            }
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            ArrayList<MediaFormat> formatsList = new ArrayList<>();
+            formatsList.add(format);
+            ArrayList<String> listOfDecoders = selectHardwareCodecs(mime, formatsList, null,
+                    false);
+            for (String decoder : listOfDecoders) {
+                for (int keyPriority : KEY_PRIORITIES_LIST) {
+                    for (float scalingFactor : SCALING_FACTORS_LIST) {
+                        if (keyPriority == 1 || (scalingFactor > 0.0 && scalingFactor <= 1.0)) {
+                            argsList.add(new Object[]{decoder, fileName, keyPriority,
+                                    scalingFactor});
+                        }
+                    }
+                }
+            }
+        }
+        return argsList;
+    }
+
+    private void setUpFormat(MediaFormat format) throws Exception {
+        mDecoderFormat = new MediaFormat(format);
+        mDecoderFormat.setInteger(MediaFormat.KEY_PRIORITY, mKeyPriority);
+        mDecoderFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+        mOperatingRateExpected = getMaxOperatingRate(mDecoderName, mDecoderMime);
+        if (mMaxOpRateScalingFactor > 0.0f) {
+            int operatingRateToSet = (int) (mOperatingRateExpected * mMaxOpRateScalingFactor);
+            if (mMaxOpRateScalingFactor < 1.0f) {
+                mOperatingRateExpected = operatingRateToSet;
+            }
+            mDecoderFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, operatingRateToSet);
+        }
+    }
+
+    /**
+     * Validates performance of hardware accelerated video decoders
+     */
+    @LargeTest
+    @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testPerformanceOfHardwareVideoDecoders() throws Exception {
+        MediaFormat format = setUpDecoderInput();
+        assertNotNull("Video track not present in " + mTestFile, format);
+        mActivityRule.getActivity().waitTillSurfaceIsCreated();
+        mSurface = mActivityRule.getActivity().getSurface();
+        assertTrue("Surface created is null.", mSurface != null);
+        assertTrue("Surface created is invalid.", mSurface.isValid());
+        setUpFormat(format);
+        mDecoder = MediaCodec.createByCodecName(mDecoderName);
+        mDecoder.configure(mDecoderFormat, mSurface, null, 0);
+        mDecoder.start();
+        long start = System.currentTimeMillis();
+        doWork();
+        long finish = System.currentTimeMillis();
+        mDecoder.stop();
+        mDecoder.release();
+        assertTrue("Decoder output count is zero", mDecOutputNum > 0);
+        double achievedFps = mDecOutputNum / ((finish - start) / 1000.0);
+        String log = String.format("DecodeMime: %s, Decoder: %s, resolution: %dp, " +
+                "Key-priority: %d :: ", mDecoderMime, mDecoderName, mHeight, mKeyPriority);
+        Log.d(LOG_TAG, log + "act/exp fps: " + achievedFps + "/" + mOperatingRateExpected);
+        assertTrue("Unable to achieve the expected rate. " + log + "act/exp fps: " + achievedFps
+                + "/" + mOperatingRateExpected, achievedFps >= mOperatingRateExpected);
+    }
+
+    void doWork() {
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        while (!mSawDecOutputEOS) {
+            if (!mSawDecInputEOS) {
+                int inputBufIndex = mDecoder.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+                if (inputBufIndex >= 0) {
+                    enqueueDecoderInput(inputBufIndex);
+                }
+            }
+            int outputBufIndex = mDecoder.dequeueOutputBuffer(info, Q_DEQ_TIMEOUT_US);
+            if (outputBufIndex >= 0) {
+                dequeueDecoderOutput(outputBufIndex, info, false);
+            }
+        }
+    }
+}
diff --git a/tests/video/src/android/video/cts/CodecEncoderPerformanceTest.java b/tests/video/src/android/video/cts/CodecEncoderPerformanceTest.java
new file mode 100644
index 0000000..db95ea0
--- /dev/null
+++ b/tests/video/src/android/video/cts/CodecEncoderPerformanceTest.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.video.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.util.Log;
+import android.view.Surface;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Operating rate is expected to be met by encoder only in surface mode and not in byte buffer mode.
+ * As camera has limited frame rates and resolutions, it is not possible to test encoder
+ * operating rate alone. So we are going ahead with transcode tests as a way to verify
+ * encoder performances. This test calls decoder to decode to a surface that is coupled to encoder.
+ * This assumes encoder will not be faster than decode and expects half the operating rate
+ * to be met for encoders.
+ */
+@RunWith(Parameterized.class)
+public class CodecEncoderPerformanceTest extends CodecPerformanceTestBase {
+    private static final String LOG_TAG = CodecEncoderPerformanceTest.class.getSimpleName();
+    private static final Map<String, Float> transcodeAVCToTargetBitrateMap = new HashMap<>();
+
+    private final String mEncoderMime;
+    private final String mEncoderName;
+    private final int mBitrateAVC;
+
+    private boolean mSawEncInputEOS = false;
+    private boolean mSawEncOutputEOS = false;
+    private int mEncOutputNum = 0;
+    private MediaCodec mEncoder;
+    private MediaFormat mEncoderFormat;
+
+    // Suggested bitrate scaling factors for transcoding avc to target format.
+    static {
+        transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_VP8, 1.25f);
+        transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_AVC, 1.0f);
+        transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, 0.7f);
+        transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_HEVC, 0.6f);
+        transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_AV1, 0.4f);
+    }
+
+    public CodecEncoderPerformanceTest(String decoderName, String testFile, String encoderMime,
+            String encoderName, int bitrate, int keyPriority, float scalingFactor) {
+        super(decoderName, testFile, keyPriority, scalingFactor);
+        mEncoderMime = encoderMime;
+        mEncoderName = encoderName;
+        mBitrateAVC = bitrate;
+    }
+
+    static ArrayList<String> getMimesOfAvailableHardwareVideoEncoders() {
+        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+        ArrayList<String> listOfMimes = new ArrayList<>();
+        for (MediaCodecInfo codecInfo : codecInfos) {
+            if (!codecInfo.isEncoder() || !codecInfo.isHardwareAccelerated()) continue;
+            String[] types = codecInfo.getSupportedTypes();
+            for (String type : types) {
+                if (type.startsWith("video/") && !listOfMimes.contains(type)) {
+                    listOfMimes.add(type);
+                }
+            }
+        }
+        return listOfMimes;
+    }
+
+    private static MediaFormat setUpEncoderFormat(MediaFormat format, String mime, int bitrate) {
+        MediaFormat fmt = new MediaFormat();
+        fmt.setString(MediaFormat.KEY_MIME, mime);
+        fmt.setInteger(MediaFormat.KEY_WIDTH, format.getInteger(MediaFormat.KEY_WIDTH));
+        fmt.setInteger(MediaFormat.KEY_HEIGHT, format.getInteger(MediaFormat.KEY_HEIGHT));
+        fmt.setInteger(MediaFormat.KEY_BIT_RATE,
+                (int) (bitrate * transcodeAVCToTargetBitrateMap.getOrDefault(mime, 1.5f)));
+        fmt.setInteger(MediaFormat.KEY_FRAME_RATE,
+                format.getInteger(MediaFormat.KEY_FRAME_RATE,30));
+        fmt.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
+        fmt.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+        return fmt;
+    }
+
+    @Parameterized.Parameters(name = "{index}({0}_{2}_{3}_{5}_{6})")
+    public static Collection<Object[]> input() throws IOException {
+        final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
+                // Filename, Recommended AVC bitrate
+                {"crowd_run_720x480_30fps_avc.mp4", 200000},
+                {"crowd_run_1280x720_30fps_avc.mp4", 4000000},
+                {"crowd_run_1920x1080_30fps_avc.mp4", 8000000},
+                {"crowd_run_3840x2160_30fps_hevc.mp4", 20000000},
+                {"crowd_run_7680x4320_30fps_hevc.mp4", 40000000},
+        });
+        // Prepares the params list with the supported Hardware decoder, encoders in the device
+        // combined with the key priority and scaling factor
+        final List<Object[]> argsList = new ArrayList<>();
+        for (Object[] arg : exhaustiveArgsList) {
+            // Gets the format for the first video track found
+            MediaFormat format = getVideoFormat((String) arg[0]);
+            if (format == null) {
+                Log.e(LOG_TAG, "Test vector is ignored as it has no video tracks present " +
+                        "in the file: " + ((String) arg[0]));
+                continue;
+            }
+            String decoderMime = format.getString(MediaFormat.KEY_MIME);
+            ArrayList<MediaFormat> formatsList = new ArrayList<>();
+            formatsList.add(format);
+            ArrayList<String> listOfDecoders = selectHardwareCodecs(decoderMime, formatsList,
+                    null, false);
+            if (listOfDecoders.size() == 0) continue;
+            String decoder = listOfDecoders.get(0);
+            for (String encoderMime : getMimesOfAvailableHardwareVideoEncoders()) {
+                MediaFormat mockFmt = setUpEncoderFormat(format, encoderMime, (int) arg[1]);
+                ArrayList<MediaFormat> mockFmtList = new ArrayList<>();
+                mockFmtList.add(mockFmt);
+                ArrayList<String> listOfEncoders = selectHardwareCodecs(encoderMime, mockFmtList,
+                        null, true);
+                for (String encoder : listOfEncoders) {
+                    for (int keyPriority : KEY_PRIORITIES_LIST) {
+                        for (float scalingFactor : SCALING_FACTORS_LIST) {
+                            if (keyPriority == 1 || (scalingFactor > 0.0 && scalingFactor <= 1.0)) {
+                                argsList.add(new Object[]{decoder, arg[0], encoderMime, encoder,
+                                        arg[1], keyPriority, scalingFactor});
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return argsList;
+    }
+
+    private void dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            mEncOutputNum++;
+        }
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawEncOutputEOS = true;
+        }
+        mEncoder.releaseOutputBuffer(bufferIndex, false);
+    }
+
+    private void setUpFormats(MediaFormat format) throws Exception {
+        mDecoderFormat = new MediaFormat(format);
+        mDecoderFormat.setInteger(MediaFormat.KEY_PRIORITY, mKeyPriority);
+        mEncoderFormat = setUpEncoderFormat(mDecoderFormat, mEncoderMime, mBitrateAVC);
+        mEncoderFormat.setInteger(MediaFormat.KEY_PRIORITY, mKeyPriority);
+
+        double maxOperatingRateDecoder = getMaxOperatingRate(mDecoderName, mDecoderMime);
+        double maxOperatingRateEncoder = getMaxOperatingRate(mEncoderName, mEncoderMime);
+        mOperatingRateExpected = Math.min(maxOperatingRateDecoder, maxOperatingRateEncoder);
+        if (mMaxOpRateScalingFactor > 0.0f) {
+            int operatingRateToSet = (int) (mOperatingRateExpected * mMaxOpRateScalingFactor);
+            if (mMaxOpRateScalingFactor < 1.0f) {
+                mOperatingRateExpected = operatingRateToSet;
+            }
+            mDecoderFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, operatingRateToSet);
+            mEncoderFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, operatingRateToSet);
+        }
+        mOperatingRateExpected /= 2.0;
+    }
+
+    /**
+     * Validates performance of hardware accelerated video encoders
+     */
+    @LargeTest
+    @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+    public void testPerformanceOfHardwareVideoEncoders() throws Exception {
+        MediaFormat format = setUpDecoderInput();
+        assertNotNull("Video track not present in " + mTestFile, format);
+        setUpFormats(format);
+        mDecoder = MediaCodec.createByCodecName(mDecoderName);
+        mEncoder = MediaCodec.createByCodecName(mEncoderName);
+        mEncoder.configure(mEncoderFormat, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null);
+        Surface surface = mEncoder.createInputSurface();
+        assertTrue("Surface is not valid", surface.isValid());
+        mDecoder.configure(mDecoderFormat, surface, null, 0);
+        mDecoder.start();
+        mEncoder.start();
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        long start = System.currentTimeMillis();
+        while (!mSawEncOutputEOS) {
+            if (!mSawDecInputEOS) {
+                int inputBufIndex = mDecoder.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+                if (inputBufIndex >= 0) {
+                    enqueueDecoderInput(inputBufIndex);
+                }
+            }
+            if (!mSawDecOutputEOS) {
+                int outputBufIndex = mDecoder.dequeueOutputBuffer(info, Q_DEQ_TIMEOUT_US);
+                if (outputBufIndex >= 0) {
+                    dequeueDecoderOutput(outputBufIndex, info, true);
+                }
+            }
+            if (mSawDecOutputEOS && !mSawEncInputEOS) {
+                mEncoder.signalEndOfInputStream();
+                mSawEncInputEOS = true;
+            }
+            MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+            int outputBufferId = mEncoder.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+            if (outputBufferId >= 0) {
+                dequeueEncoderOutput(outputBufferId, outInfo);
+            }
+        }
+        long finish = System.currentTimeMillis();
+        mEncoder.stop();
+        surface.release();
+        mEncoder.release();
+        mDecoder.stop();
+        mDecoder.release();
+        assertTrue("Encoder output count is zero", mEncOutputNum > 0);
+        double achievedFps = mEncOutputNum / ((finish - start) / 1000.0);
+        String log = String.format("DecodeMime: %s, Decoder: %s, resolution: %dp, " +
+                "EncodeMime: %s, Encoder: %s, Key-priority: %d :: ", mDecoderMime, mDecoderName,
+                mHeight, mEncoderMime, mEncoderName, mKeyPriority);
+        Log.d(LOG_TAG, log + "act/exp fps: " + achievedFps + "/" + mOperatingRateExpected);
+        assertTrue("Unable to achieve the expected rate. " + log + "act/exp fps: " + achievedFps
+                + "/" + mOperatingRateExpected, achievedFps >= mOperatingRateExpected);
+    }
+}
diff --git a/tests/video/src/android/video/cts/CodecPerformanceTestBase.java b/tests/video/src/android/video/cts/CodecPerformanceTestBase.java
new file mode 100644
index 0000000..cf4caed
--- /dev/null
+++ b/tests/video/src/android/video/cts/CodecPerformanceTestBase.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.video.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.os.Build;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+class CodecPerformanceTestBase {
+    private static final String LOG_TAG = CodecPerformanceTestBase.class.getSimpleName();
+    static final long Q_DEQ_TIMEOUT_US = 5000; // block at most 5ms while looking for io buffers
+    static final int PER_TEST_TIMEOUT_LARGE_TEST_MS = 300000;
+    static final int MIN_FRAME_COUNT = 500;
+    static final int SELECT_ALL = 0; // Select all codecs
+    static final int SELECT_HARDWARE = 1; // Select Hardware codecs only
+    static final int SELECT_SOFTWARE = 2; // Select Software codecs only
+    static final String mInputPrefix = WorkDir.getMediaDirString();
+
+    ArrayList<MediaCodec.BufferInfo> mBufferInfos;
+    ByteBuffer mBuff;
+
+    final String mDecoderName;
+    final String mTestFile;
+    final int mKeyPriority;
+    final float mMaxOpRateScalingFactor;
+
+    String mDecoderMime;
+    int mWidth;
+    int mHeight;
+    int mFrameRate;
+
+    boolean mSawDecInputEOS = false;
+    boolean mSawDecOutputEOS = false;
+    int mDecInputNum = 0;
+    int mDecOutputNum = 0;
+    int mSampleIndex = 0;
+
+    MediaCodec mDecoder;
+    MediaFormat mDecoderFormat;
+    double mOperatingRateExpected;
+
+    static final float[] SCALING_FACTORS_LIST = new float[]{2.0f, 1.25f, 1.0f, 0.75f, 0.0f};
+    static final int[] KEY_PRIORITIES_LIST = new int[]{1, 0};
+
+    public CodecPerformanceTestBase(String decoderName, String testFile, int keyPriority,
+            float maxOpRateScalingFactor) {
+        mDecoderName = decoderName;
+        mTestFile = testFile;
+        mKeyPriority = keyPriority;
+        mMaxOpRateScalingFactor = maxOpRateScalingFactor;
+        mBufferInfos = new ArrayList<>();
+    }
+
+    static MediaFormat getVideoFormat(String filePath) throws IOException {
+        final String input = mInputPrefix + filePath;
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(input);
+        for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) {
+            MediaFormat format = extractor.getTrackFormat(trackID);
+            if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
+                extractor.release();
+                return format;
+            }
+        }
+        extractor.release();
+        return null;
+    }
+
+    static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
+            String[] features, boolean isEncoder) {
+        return selectCodecs(mime, formats, features, isEncoder, SELECT_ALL);
+    }
+
+    static ArrayList<String> selectHardwareCodecs(String mime, ArrayList<MediaFormat> formats,
+            String[] features, boolean isEncoder) {
+        return selectCodecs(mime, formats, features, isEncoder, SELECT_HARDWARE);
+    }
+
+    static ArrayList<String> selectCodecs(String mime, ArrayList<MediaFormat> formats,
+            String[] features, boolean isEncoder, int selectCodecOption) {
+        MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+        MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
+        ArrayList<String> listOfCodecs = new ArrayList<>();
+        for (MediaCodecInfo codecInfo : codecInfos) {
+            if (codecInfo.isEncoder() != isEncoder) continue;
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
+            if (selectCodecOption == SELECT_HARDWARE && !codecInfo.isHardwareAccelerated())
+                continue;
+            else if (selectCodecOption == SELECT_SOFTWARE && !codecInfo.isSoftwareOnly())
+                continue;
+            String[] types = codecInfo.getSupportedTypes();
+            for (String type : types) {
+                if (type.equalsIgnoreCase(mime)) {
+                    boolean isOk = true;
+                    MediaCodecInfo.CodecCapabilities codecCapabilities =
+                            codecInfo.getCapabilitiesForType(type);
+                    if (formats != null) {
+                        for (MediaFormat format : formats) {
+                            if (!codecCapabilities.isFormatSupported(format)) {
+                                isOk = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (features != null) {
+                        for (String feature : features) {
+                            if (!codecCapabilities.isFeatureSupported(feature)) {
+                                isOk = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (isOk) listOfCodecs.add(codecInfo.getName());
+                }
+            }
+        }
+        return listOfCodecs;
+    }
+
+    MediaFormat setUpDecoderInput() throws IOException {
+        final String input = mInputPrefix + mTestFile;
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(input);
+        for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) {
+            MediaFormat format = extractor.getTrackFormat(trackID);
+            if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
+                extractor.selectTrack(trackID);
+                File file = new File(input);
+                int bufferSize = ((int) file.length()) << 1;
+                mBuff = ByteBuffer.allocate(bufferSize);
+                int offset = 0;
+                long maxPTS = 0;
+                while (true) {
+                    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+                    bufferInfo.size = extractor.readSampleData(mBuff, offset);
+                    if (bufferInfo.size < 0) break;
+                    bufferInfo.offset = offset;
+                    bufferInfo.presentationTimeUs = extractor.getSampleTime();
+                    maxPTS = Math.max(maxPTS, bufferInfo.presentationTimeUs);
+                    int flags = extractor.getSampleFlags();
+                    bufferInfo.flags = 0;
+                    if ((flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+                        bufferInfo.flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+                    }
+                    mBufferInfos.add(bufferInfo);
+                    extractor.advance();
+                    offset += bufferInfo.size;
+                }
+
+                // If the clip doesn't have sufficient frames, loopback by copying bufferInfos
+                // from the start of the list and incrementing the timestamp.
+                int actualBufferInfosCount = mBufferInfos.size();
+                long ptsOffset;
+                while (mBufferInfos.size() < MIN_FRAME_COUNT) {
+                    ptsOffset = maxPTS + 1000000L;
+                    for (int i = 0; i < actualBufferInfosCount; i++) {
+                        MediaCodec.BufferInfo tmpBufferInfo = mBufferInfos.get(i);
+                        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+                        bufferInfo.set(tmpBufferInfo.offset, tmpBufferInfo.size,
+                                ptsOffset + tmpBufferInfo.presentationTimeUs,
+                                tmpBufferInfo.flags);
+                        maxPTS = Math.max(maxPTS, bufferInfo.presentationTimeUs);
+                        mBufferInfos.add(bufferInfo);
+                    }
+                }
+                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+                bufferInfo.set(0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+                mBufferInfos.add(bufferInfo);
+                mDecoderMime = format.getString(MediaFormat.KEY_MIME);
+                mWidth = format.getInteger(MediaFormat.KEY_WIDTH);
+                mHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
+                mFrameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE, 30);
+                extractor.release();
+                return format;
+            }
+        }
+        extractor.release();
+        fail("No video track found in file: " + mTestFile);
+        return null;
+    }
+
+    int getMaxOperatingRate(String codecName, String mime) throws Exception {
+        MediaCodec codec = MediaCodec.createByCodecName(codecName);
+        MediaCodecInfo mediaCodecInfo = codec.getCodecInfo();
+        List<MediaCodecInfo.VideoCapabilities.PerformancePoint> pps = mediaCodecInfo
+                .getCapabilitiesForType(mime).getVideoCapabilities()
+                .getSupportedPerformancePoints();
+        assertTrue(pps.size() > 0);
+        MediaCodecInfo.VideoCapabilities.PerformancePoint cpp =
+                new MediaCodecInfo.VideoCapabilities.PerformancePoint(mWidth, mHeight, mFrameRate);
+        int macroblocks = cpp.getMaxMacroBlocks();
+        int maxOperatingRate = -1;
+        for (MediaCodecInfo.VideoCapabilities.PerformancePoint pp : pps) {
+            if (pp.covers(cpp)) {
+                maxOperatingRate = Math.max(Math.min(pp.getMaxFrameRate(),
+                        (int) pp.getMaxMacroBlockRate() / macroblocks), maxOperatingRate);
+            }
+        }
+        codec.release();
+        assertTrue(maxOperatingRate != -1);
+        return maxOperatingRate;
+    }
+
+    void enqueueDecoderInput(int bufferIndex) {
+        MediaCodec.BufferInfo info = mBufferInfos.get(mSampleIndex++);
+        if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+            ByteBuffer dstBuf = mDecoder.getInputBuffer(bufferIndex);
+            dstBuf.put(mBuff.array(), info.offset, info.size);
+            mDecInputNum++;
+        }
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawDecInputEOS = true;
+        }
+        mDecoder.queueInputBuffer(bufferIndex, 0, info.size, info.presentationTimeUs, info.flags);
+    }
+
+    void dequeueDecoderOutput(int bufferIndex, MediaCodec.BufferInfo info, boolean render) {
+        if (info.size > 0) {
+            mDecOutputNum++;
+        }
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            mSawDecOutputEOS = true;
+        }
+        mDecoder.releaseOutputBuffer(bufferIndex, render);
+    }
+}
diff --git a/tests/video/src/android/video/cts/CodecTestActivity.java b/tests/video/src/android/video/cts/CodecTestActivity.java
new file mode 100644
index 0000000..89c669d
--- /dev/null
+++ b/tests/video/src/android/video/cts/CodecTestActivity.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.video.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class CodecTestActivity extends Activity implements SurfaceHolder.Callback {
+    private static final String LOG_TAG = CodecTestActivity.class.getSimpleName();
+    private SurfaceView mSurfaceView;
+    private SurfaceHolder mHolder;
+    private Surface mSurface;
+    private final Lock mLock = new ReentrantLock();
+    private final Condition mCondition = mLock.newCondition();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.media_decoder_surface_layout);
+        mSurfaceView = findViewById(R.id.surface);
+        mHolder = mSurfaceView.getHolder();
+        mHolder.addCallback(this);
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        Log.v(LOG_TAG, "surface created");
+        mLock.lock();
+        mSurface = mHolder.getSurface();
+        mLock.unlock();
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        Log.v(LOG_TAG, "surface changed " + format + " " + width + " " + height);
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        Log.v(LOG_TAG, "surface deleted");
+        mLock.lock();
+        mSurface = null;
+        mLock.unlock();
+    }
+
+    public void waitTillSurfaceIsCreated() throws InterruptedException {
+        final long mWaitTimeMs = 1000;
+        final int retries = 3;
+        mLock.lock();
+        final long start = SystemClock.elapsedRealtime();
+        while ((SystemClock.elapsedRealtime() - start) < (retries * mWaitTimeMs) &&
+                mSurface == null) {
+            mCondition.await(mWaitTimeMs, TimeUnit.MILLISECONDS);
+        }
+        mLock.unlock();
+        if (mSurface == null) {
+            throw new InterruptedException("Taking too long to attach a SurfaceView to a window.");
+        }
+    }
+
+    public Surface getSurface() { return mSurface; }
+}
diff --git a/tests/video/src/android/video/cts/WorkDir.java b/tests/video/src/android/video/cts/WorkDir.java
new file mode 100644
index 0000000..c69e0f0
--- /dev/null
+++ b/tests/video/src/android/video/cts/WorkDir.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.video.cts;
+
+import android.os.Environment;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Assert;
+
+import java.io.File;
+
+class WorkDir {
+    private static final String MEDIA_PATH_INSTR_ARG_KEY = "media-path";
+    static private final File getTopDir() {
+        Assert.assertEquals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED);
+        return Environment.getExternalStorageDirectory();
+    }
+    static private final String getTopDirString() {
+        return (getTopDir().getAbsolutePath() + File.separator);
+    }
+    static final String getMediaDirString() {
+        android.os.Bundle bundle = InstrumentationRegistry.getArguments();
+        String mediaDirString = bundle.getString(MEDIA_PATH_INSTR_ARG_KEY);
+        if (mediaDirString != null) {
+            // user has specified the mediaDirString via instrumentation-arg
+            return mediaDirString + ((mediaDirString.endsWith("/")) ? "" : "/");
+        } else {
+            return (getTopDirString() + "test/CtsVideoTestCases-1.1/");
+        }
+    }
+}
diff --git a/tools/cts-device-info/Android.mk b/tools/cts-device-info/Android.mk
index cc67eee..2fcbd6b 100644
--- a/tools/cts-device-info/Android.mk
+++ b/tools/cts-device-info/Android.mk
@@ -38,6 +38,8 @@
     com.android.cts.deviceinfo.VulkanDeviceInfo
 
 LOCAL_PACKAGE_NAME := CtsDeviceInfo
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts general-tests sts mts vts
diff --git a/tools/cts-tradefed/OWNERS b/tools/cts-tradefed/OWNERS
index 3842024..06936014 100644
--- a/tools/cts-tradefed/OWNERS
+++ b/tools/cts-tradefed/OWNERS
@@ -14,4 +14,5 @@
 per-file cts-meerkat.xml = alanstokes@google.com, brufino@google.com, lus@google.com, rickywai@google.com
 per-file cts-on-csi*.xml = ycchen@google.com, hsinyichen@google.com, tyanh@google.com
 per-file csi-*.xml = ycchen@google.com, hsinyichen@google.com, tyanh@google.com
+per-file cts-on-gsi*.xml = bettyzhou@google.com, ycchen@google.com, hsinyichen@google.com, tyanh@google.com
 
diff --git a/tools/cts-tradefed/etc/cts-tradefed b/tools/cts-tradefed/etc/cts-tradefed
index 117f4cd..b9d5547 100755
--- a/tools/cts-tradefed/etc/cts-tradefed
+++ b/tools/cts-tradefed/etc/cts-tradefed
@@ -55,12 +55,6 @@
   RDBG_FLAG=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=${TF_DEBUG_PORT}
 fi
 
-JAVA_BINARY=${CTS_ROOT}/android-cts/jdk/bin/java
-
-if [ ! -f "${JAVA_BINARY}" ]; then
-    JAVA_BINARY=java
-fi
-
 # get OS
 HOST=`uname`
 if [ "$HOST" == "Linux" ]; then
@@ -92,6 +86,14 @@
     CTS_ROOT="$(dirname $(realpath $0))/../.."
 fi;
 
+if [ -z ${JAVA_BINARY} ]; then
+    JAVA_BINARY=${CTS_ROOT}/android-cts/jdk/bin/java
+fi;
+
+if [ ! -f "${JAVA_BINARY}" ]; then
+    JAVA_BINARY=java
+fi
+
 JAR_DIR=${CTS_ROOT}/android-cts/tools
 
 for JAR in ${JAR_DIR}/*.jar; do
diff --git a/tools/cts-tradefed/res/config/cts-foldable.xml b/tools/cts-tradefed/res/config/cts-foldable.xml
index 1a3e256..1b6b9bb 100644
--- a/tools/cts-tradefed/res/config/cts-foldable.xml
+++ b/tools/cts-tradefed/res/config/cts-foldable.xml
@@ -23,4 +23,8 @@
     <!-- CTS tests to be excluded in this plan-->
     <option name="compatibility:exclude-filter" value="CtsDeqpTestCases" />
 
+    <!-- b/178344549: CtsCameraTestCases failures due to covered lenses in folded mode-->
+    <option name="compatibility:exclude-filter" value="CtsCameraTestCases android.hardware.camera2.cts.BurstCaptureTest#testJpegBurst" />
+    <option name="compatibility:exclude-filter" value="CtsCameraTestCases[instant] android.hardware.camera2.cts.BurstCaptureTest#testJpegBurst" />
+
 </configuration>
diff --git a/tools/cts-tradefed/res/config/cts-on-aosp-exclude.xml b/tools/cts-tradefed/res/config/cts-on-aosp-exclude.xml
index a9d2ac5..9b6250d 100644
--- a/tools/cts-tradefed/res/config/cts-on-aosp-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-aosp-exclude.xml
@@ -80,7 +80,6 @@
 
     <!-- b/73727333: CtsSystemUiTestCases failure flaky -->
     <option name="compatibility:exclude-filter" value="CtsSystemUiTestCases android.systemui.cts.LightBarTests#testLightNavigationBar" />
-    <option name="compatibility:exclude-filter" value="CtsSystemUiTestCases android.systemui.cts.LightBarThemeTest#testNavigationBarDivider" />
 
     <!-- b/80388296: CtsDevicePolicyManagerTestCases failure flaky -->
     <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testDisallowAutofill_allowed" />
@@ -91,21 +90,7 @@
     <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testPermissionPolicy" />
     <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testSuspendPackage" />
 
-    <!-- b/80407835: CtsServicesHostTestCases failure flaky -->
-    <option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.KeyguardTests#testDialogShowWhenLockedActivity" />
-    <option name="compatibility:exclude-filter" value="CtsServicesHostTestCases android.server.cts.KeyguardTests#testTranslucentShowWhenLockedActivity" />
-
-    <!-- b/80284482: Flaky tests -->
-    <option name="compatibility:exclude-filter" value="CtsAlarmManagerTestCases android.alarmmanager.cts.AppStandbyTests#testAllowWhileIdleAlarms" />
-    <option name="compatibility:exclude-filter" value="CtsAlarmManagerTestCases android.alarmmanager.cts.AppStandbyTests#testBucketUpgradeToNoDelay" />
-    <option name="compatibility:exclude-filter" value="CtsAlarmManagerTestCases android.alarmmanager.cts.AppStandbyTests#testBucketUpgradeToSmallerDelay" />
-    <option name="compatibility:exclude-filter" value="CtsAlarmManagerTestCases android.alarmmanager.cts.AppStandbyTests#testFrequentDelay" />
-    <option name="compatibility:exclude-filter" value="CtsAlarmManagerTestCases android.alarmmanager.cts.AppStandbyTests#testRareDelay" />
-    <option name="compatibility:exclude-filter" value="CtsAlarmManagerTestCases android.alarmmanager.cts.AppStandbyTests#testWorkingSetDelay" />
-
     <!-- b/110260628: A confirmed GSI incompatibility (waiver) -->
-    <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.DeviceOwnerTest#testCreateAndManageUser_DontSkipSetupWizard" />
-    <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.DeviceOwnerTest#testSecurityLoggingWithSingleUser" />
     <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedDeviceOwnerTest#testKeyManagement" />
     <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedProfileOwnerTest#testKeyManagement" />
     <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.MixedManagedProfileOwnerTest#testKeyManagement" />
@@ -128,15 +113,6 @@
     <option name="compatibility:exclude-filter" value="CtsOsTestCases android.os.cts.BuildTest#testIsSecureUserBuild" />
     <option name="compatibility:exclude-filter" value="CtsOsTestCases android.os.cts.BuildVersionTest#testBuildFingerprint" />
 
-    <!-- b/110405126: CtsPermissionTestCases flaky (due to SIM card setting) -->
-    <option name="compatibility:exclude-filter" value="CtsPermissionTestCases android.permission.cts.TelephonyManagerPermissionTest#testGetDeviceId" />
-    <option name="compatibility:exclude-filter" value="CtsPermissionTestCases android.permission.cts.TelephonyManagerPermissionTest#testGetImei" />
-    <option name="compatibility:exclude-filter" value="CtsPermissionTestCases android.permission.cts.TelephonyManagerPermissionTest#testGetLine1Number" />
-    <option name="compatibility:exclude-filter" value="CtsPermissionTestCases android.permission.cts.TelephonyManagerPermissionTest#testGetSimSerialNumber" />
-    <option name="compatibility:exclude-filter" value="CtsPermissionTestCases android.permission.cts.TelephonyManagerPermissionTest#testGetSubscriberId" />
-    <option name="compatibility:exclude-filter" value="CtsPermissionTestCases android.permission.cts.TelephonyManagerPermissionTest#testSetDataEnabled" />
-    <option name="compatibility:exclude-filter" value="CtsPermissionTestCases android.permission.cts.TelephonyManagerPermissionTest#testVoiceMailNumber" />
-
     <!-- b/111967702: CtsSecurityTestCases irrelevant test cases -->
     <option name="compatibility:exclude-filter" value="CtsSecurityTestCases android.security.cts.BannedFilesTest#testNoSu" />
     <option name="compatibility:exclude-filter" value="CtsSecurityTestCases android.security.cts.BannedFilesTest#testNoSuInPath" />
diff --git a/tools/cts-tradefed/res/config/cts-on-gsi-exclude-non-hal.xml b/tools/cts-tradefed/res/config/cts-on-gsi-exclude-non-hal.xml
new file mode 100644
index 0000000..03d6e41
--- /dev/null
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude-non-hal.xml
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Excluded non-HAL tests from cts-on-gsi">
+
+    <!-- b/182542290 Exclude non-HAL tests from cts-on-gsi -->
+    <option name="compatibility:exclude-filter" value="CtsAbiOverrideHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAccessibilityServiceSdk29TestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAccessibilityServiceTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAccessibilityTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAccountManagerTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAccountsHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsActivityManagerBackgroundActivityTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAdminPackageInstallerTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAdminTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAndroidAppTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAndroidTestBase28ApiSignatureTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAndroidTestBaseCurrentApiSignatureTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAndroidTestMockCurrentApiSignatureTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAndroidTestRunnerCurrentApiSignatureTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsApacheHttpLegacy27ApiSignatureTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsApacheHttpLegacyCurrentApiSignatureTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsApacheHttpLegacyUsesLibraryApiSignatureTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsApexTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAppBindingHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAppCompatHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAppComponentFactoryTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAppEnumerationTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAppExitTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAppIntegrityDeviceTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAppOpsTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAtraceHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsAttentionServiceDeviceTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsBionicAppTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsBionicTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsBootStatsTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsCalendarcommon2TestCases" />
+    <option name="compatibility:exclude-filter" value="CtsCalendarProviderTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsCodePathHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsCompilationTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsContactsProviderTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsContactsProviderWipe" />
+    <option name="compatibility:exclude-filter" value="CtsContentCaptureServiceTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsContentTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsControlsDeviceTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsCppToolsTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsCurrentApiSignatureTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsDatabaseTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsHiddenApiKillswitchDebugClassTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsHiddenApiKillswitchWildcardTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJdwpTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJdwpTunnelHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJniTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiAttachingHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiAttachingTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRedefineClassesHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1900HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1901HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1902HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1903HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1904HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1906HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1907HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1908HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1909HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1910HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1911HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1912HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1913HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1914HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1915HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1916HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1917HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1920HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1921HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1922HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1923HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1924HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1925HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1926HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1927HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1928HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1930HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1931HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1932HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1933HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1934HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1936HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1937HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1939HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1941HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1942HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1943HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1953HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1958HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1962HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1967HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1968HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1969HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1970HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1971HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1974HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1975HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1976HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1977HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1978HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1979HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1981HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1982HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1983HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1984HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1988HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1989HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1990HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1991HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1992HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1994HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1995HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1996HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1997HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1998HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest1999HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest2001HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest2002HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest2003HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest2004HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest2005HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest2006HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest2007HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest902HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest903HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest904HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest905HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest906HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest907HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest908HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest910HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest911HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest912HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest913HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest914HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest915HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest917HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest918HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest919HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest920HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest922HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest923HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest924HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest926HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest927HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest928HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest930HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest931HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest932HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest940HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest942HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest944HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest945HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest947HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest951HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest982HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest983HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest984HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest985HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest986HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest988HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest989HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest990HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest991HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest992HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest993HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest994HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest995HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest996HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiRunTest997HostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiTaggingHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsJvmtiTrackingHostTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsLegacyNotification20TestCases" />
+    <option name="compatibility:exclude-filter" value="CtsLegacyNotification27TestCases" />
+    <option name="compatibility:exclude-filter" value="CtsLegacyNotification28TestCases" />
+    <option name="compatibility:exclude-filter" value="CtsLegacyNotification29TestCases" />
+    <option name="compatibility:exclude-filter" value="CtsLocationNoneTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsNetTestCasesInternetPermission" />
+    <option name="compatibility:exclude-filter" value="CtsOsTestCases android.os.cts.StrictModeTest" />
+    <option name="compatibility:exclude-filter" value="CtsPackageInstallAppOpDefaultTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsPackageInstallAppOpDeniedTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsPackageInstallerTapjackingTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsPackageInstallTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsPackageUninstallTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsPackageWatchdogTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsSimpleCpuTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsSoundTriggerTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsSystemIntentTestCases" />
+    <option name="compatibility:exclude-filter" value="CtsTelephony3TestCases" />
+    <option name="compatibility:exclude-filter" value="CtsThemeDeviceTestCases" />
+
+</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
index 930bc4d..fa286bc 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -53,4 +53,16 @@
     <!-- No SettingsIntelligence -->
     <option name="compatibility:exclude-filter" value="CtsContentTestCases android.content.cts.AvailableIntentsTest#testSettingsSearchIntent" />
 
+    <!-- No AccessibilityService -->
+    <option name="compatibility:exclude-filter" value="CtsAccessibilityServiceTestCases android.accessibilityservice.cts" />
+
+    <!-- No Statsd -->
+    <option name="compatibility:exclude-filter" value="CtsStatsdHostTestCases" />
+
+    <!-- b/183654427 Remove CtsTelecomTestCases from cts-on-gsi -->
+    <option name="compatibility:exclude-filter" value="CtsTelecomTestCases" />
+
+    <!-- b/183659262 Remove CtsPreferenceTestCases from cts-on-gsi -->
+    <option name="compatibility:exclude-filter" value="CtsPreferenceTestCases" />
+
 </configuration>
diff --git a/tools/cts-tradefed/res/config/cts-on-gsi.xml b/tools/cts-tradefed/res/config/cts-on-gsi.xml
index be89e79..327ba547 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi.xml
@@ -17,6 +17,7 @@
 
     <include name="cts-on-aosp" />
     <include name="cts-on-gsi-exclude" />
+    <include name="cts-on-gsi-exclude-non-hal" />
 
     <option name="plan" value="cts-on-gsi" />
 
diff --git a/tools/cts-tradefed/res/config/cts-sim-include.xml b/tools/cts-tradefed/res/config/cts-sim-include.xml
index 8f093a7..e9a69ea 100644
--- a/tools/cts-tradefed/res/config/cts-sim-include.xml
+++ b/tools/cts-tradefed/res/config/cts-sim-include.xml
@@ -30,6 +30,7 @@
     <option name="compatibility:include-filter" value="CtsSecureElementAccessControlTestCases1" />
     <option name="compatibility:include-filter" value="CtsSecureElementAccessControlTestCases2" />
     <option name="compatibility:include-filter" value="CtsSecureElementAccessControlTestCases3" />
+    <option name="compatibility:include-filter" value="CtsSimPhonebookProviderTestCases" />
     <option name="compatibility:include-filter" value="CtsSimRestrictedApisTestCases" />
     <option name="compatibility:include-filter" value="CtsStatsdHostTestCases" />
     <option name="compatibility:include-filter" value="CtsTelecomTestCases" />
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/DupFileTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/DupFileTest.java
deleted file mode 100644
index c4055ef..0000000
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/DupFileTest.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.compatibility.common.tradefed.presubmit;
-
-import static org.junit.Assert.fail;
-
-import com.android.tradefed.config.ConfigurationException;
-
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-
-/**
- * Test to check for duplicate files in different jars and prevent the same dependencies of being
- * included several time (which might result in version conflicts).
- */
-@RunWith(JUnit4.class)
-public class DupFileTest {
-
-    // We ignore directories part of the common java and google packages.
-    private static final String[] IGNORE_DIRS =
-            new String[] {
-                "android/",
-                "javax/annotation/",
-                "com/google/protobuf/",
-                "kotlin/",
-                "perfetto/protos/"
-            };
-    // Temporarily exclude some Tradefed jar while we work on unbundling them.
-    private static final Set<String> IGNORE_JARS =
-            ImmutableSet.of("tradefed-no-fwk.jar", "tradefed-test-framework.jar",
-                    "compatibility-tradefed.jar");
-
-    /** test if there are duplicate files in different jars. */
-    @Test
-    public void testDupFilesExist() throws Exception {
-        // Get list of jars.
-        List<File> jars = getListOfBuiltJars();
-
-        // Create map of files to jars.
-        Map<String, List<String>> filesToJars = getMapOfFilesAndJars(jars);
-
-        // Check if there are any files with the same name in diff jars.
-        int dupedFiles = 0;
-        StringBuilder dupedFilesSummary = new StringBuilder();
-        for (Map.Entry<String, List<String>> entry : filesToJars.entrySet()) {
-            String file = entry.getKey();
-            List<String> jarFiles = entry.getValue();
-
-            if (jarFiles.size() != 1) {
-                dupedFiles++;
-                dupedFilesSummary.append(file + ": " + jarFiles.toString() + "\n");
-            }
-        }
-
-        if (dupedFiles != 0) {
-            fail(
-                    String.format(
-                            "%d files are duplicated in different jars:\n%s",
-                            dupedFiles, dupedFilesSummary.toString()));
-        }
-    }
-
-    /** Create map of file to jars */
-    private Map<String, List<String>> getMapOfFilesAndJars(List<File> jars) throws IOException {
-        Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();
-        JarFile jarFile;
-        List<String> jarFileList;
-        // Map all the files from all the jars.
-        for (File jar : jars) {
-            if (IGNORE_JARS.contains(jar.getName())) {
-                continue;
-            }
-            jarFile = new JarFile(jar);
-            jarFileList = getListOfFiles(jarFile);
-            jarFile.close();
-
-            // Add in the jar file to the map.
-            for (String file : jarFileList) {
-                if (!map.containsKey(file)) {
-                    map.put(file, new LinkedList<String>());
-                }
-
-                map.get(file).add(jar.getName());
-            }
-        }
-        return map;
-    }
-
-    /** Get the list of jars specified in the path. */
-    private List<File> getListOfBuiltJars() throws ConfigurationException {
-        String classpathStr = System.getProperty("java.class.path");
-        if (classpathStr == null) {
-            throw new ConfigurationException(
-                    "Could not find the classpath property: java.class.path");
-        }
-        List<File> listOfJars = new ArrayList<File>();
-        for (String jar : classpathStr.split(":")) {
-            File jarFile = new File(jar);
-            if (jarFile.exists()) {
-                listOfJars.add(jarFile);
-            }
-        }
-        return listOfJars;
-    }
-
-    /** Return the list of files in the jar. */
-    private List<String> getListOfFiles(JarFile jar) {
-        List<String> files = new ArrayList<String>();
-        Enumeration<JarEntry> e = jar.entries();
-        while (e.hasMoreElements()) {
-            JarEntry entry = e.nextElement();
-            String filename = entry.getName();
-            if (checkThisFile(filename)) {
-                files.add(filename);
-            }
-        }
-        return files;
-    }
-
-    /** Check if we should add this file to list of files. We only want to check for classes. */
-    private Boolean checkThisFile(String filename) {
-        if (!filename.endsWith(".class")) {
-            return false;
-        }
-
-        for (String skipDir : IGNORE_DIRS) {
-            if (filename.startsWith(skipDir)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-}
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsUnitTests.java b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsUnitTests.java
index d989014..821491d 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsUnitTests.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsUnitTests.java
@@ -17,7 +17,6 @@
 
 import com.android.compatibility.common.tradefed.presubmit.ApkPackageNameCheck;
 import com.android.compatibility.common.tradefed.presubmit.CtsConfigLoadingTest;
-import com.android.compatibility.common.tradefed.presubmit.DupFileTest;
 import com.android.compatibility.common.tradefed.presubmit.PresubmitSetupValidation;
 import com.android.compatibility.common.tradefed.presubmit.ValidateTestsAbi;
 
@@ -38,7 +37,6 @@
     // presubmit
     ApkPackageNameCheck.class,
     CtsConfigLoadingTest.class,
-    DupFileTest.class,
     PresubmitSetupValidation.class,
     ValidateTestsAbi.class,
 })
diff --git a/tools/incremental-cts/OWNERS b/tools/incremental-cts/OWNERS
new file mode 100644
index 0000000..3f054e0
--- /dev/null
+++ b/tools/incremental-cts/OWNERS
@@ -0,0 +1,3 @@
+yichunli@google.com
+fdeng@google.com
+normancheung@google.com
diff --git a/tools/incremental-cts/README.md b/tools/incremental-cts/README.md
new file mode 100644
index 0000000..c5cb03d
--- /dev/null
+++ b/tools/incremental-cts/README.md
@@ -0,0 +1,24 @@
+# Note
+incremental-cts is an EXPERIMENTAL project. It's for analysis purpose only and is NOT meant to be used for approval runs.
+
+# Examples
+
+## Generate dEQP dependency
+This command will generate a dEQP dependency file named `dEQP-dependency.txt`:
+```
+python3 incremental_deqp.py -s device_serial_number -t /path/to/test_resources
+--generate_deps_only
+```
+
+## Check if current build could skip dEQP
+
+Before running this command, please create `extra_deqp_dependency.txt` file and
+copy it to test resources directory. `extra_deqp_dependency.txt` includes the
+dEQP dependencies that can't be detected by this tool such as firmwares.
+```
+python3 incremental_deqp.py -s device_serial_number -t /path/to/test_resources
+-b /path/to/target_file
+```
+
+Target file is an build artifact produced by Android build system, e.g.
+https://ci.android.com/builds/submitted/7230538/aosp_arm64-userdebug/latest/aosp_arm64-target_files-7230538.zip
diff --git a/tools/incremental-cts/abstract_build_file_handler.py b/tools/incremental-cts/abstract_build_file_handler.py
new file mode 100644
index 0000000..321deda
--- /dev/null
+++ b/tools/incremental-cts/abstract_build_file_handler.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2021 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""Abstract class that reads build file."""
+
+class AbstractBuildFileHandler(object):
+  """Build file handler interface."""
+
+  def __init__(self, build_file):
+    self.build_file = build_file
+
+  def get_file_hash(self, file_names, hash_func=None):
+    """Get hash value of file's content.
+
+    Args:
+      file_names: list of file names inside build file, the format should be
+        consistent with the file in device, e.g. /system/build.prop.
+      hash_func: optional hash function.
+    Returns:
+      A dictionary where key is file name and value is hash value of its content.
+    """
+    raise NotImplementedError('You need to implement get_file_hash function.')
+
+  def get_system_fingerprint(self):
+    """Get build fingerprint in SYSTEM partition.
+
+    Fingerprint format: $(BRAND)/$(PRODUCT)/$(DEVICE)/$(BOARD):
+    $(VERSION.RELEASE)/$(ID)/$(VERSION.INCREMENTAL):$(TYPE)/$(TAGS)
+
+    Returns:
+      String of build fingerprint.
+    """
+    raise NotImplementedError('You need to implement get_system_fingerprint function.')
diff --git a/tools/incremental-cts/custom_build_file_handler.py b/tools/incremental-cts/custom_build_file_handler.py
new file mode 100644
index 0000000..7065ed7
--- /dev/null
+++ b/tools/incremental-cts/custom_build_file_handler.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2021 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+"""Implement this class to read your build file."""
+
+from abstract_build_file_handler import *
+
+class CustomBuildFileHandler(AbstractBuildFileHandler):
+  """Custom handler."""
+
+  def __init__(self, build_file):
+    super().__init__(build_file)
+
+  def get_file_hash(self, file_names, hash_func=None):
+    """See base class."""
+    raise NotImplementedError('You need to implement get_file_hash function.')
+
+  def get_system_fingerprint(self):
+    """See base class."""
+    raise NotImplementedError('You need to implement get_system_fingerprint function.')
+
diff --git a/tools/incremental-cts/incremental_deqp.py b/tools/incremental-cts/incremental_deqp.py
new file mode 100644
index 0000000..0ff7a27
--- /dev/null
+++ b/tools/incremental-cts/incremental_deqp.py
@@ -0,0 +1,712 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2021 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""Incremental dEQP
+
+This script will run a subset of dEQP test on device to get dEQP dependency.
+
+Usage 1: Compare with a base build to check if any dEQP dependency has
+changed. Output a decision if dEQP could be skipped, and a cts-tradefed
+command to be used based on the decision.
+
+python3 incremental_deqp.py -s [device serial] -t [test directory] -b
+[base build target file] -c [current build target file]
+
+Usage 2: Generate a file containing a list of dEQP dependencies for the
+build on device.
+
+python3 incremental_deqp.py -s [device serial] -t [test directory]
+--generate_deps_only
+
+"""
+import argparse
+import importlib
+import json
+import logging
+import os
+import pkgutil
+import re
+import subprocess
+import tempfile
+import time
+import uuid
+from target_file_handler import TargetFileHandler
+from custom_build_file_handler import CustomBuildFileHandler
+from zipfile import ZipFile
+
+
+DEFAULT_CTS_XML = ('<?xml version="1.0" encoding="utf-8"?>\n'
+                   '<configuration description="Runs CTS from a pre-existing CTS installation">\n'
+                   '   <include name="cts-common" />\n'
+                   '   <include name="cts-exclude" />\n'
+                   '   <include name="cts-exclude-instant" />\n'
+                   '   <option name="enable-token-sharding" '
+                   'value="true" />\n'
+                   '   <option name="plan" value="cts" />\n'
+                   '</configuration>\n')
+
+INCREMENTAL_DEQP_XML = ('<?xml version="1.0" encoding="utf-8"?>\n'
+                        '<configuration description="Runs CTS with incremental dEQP">\n'
+                        '   <include name="cts-common" />\n'
+                        '   <include name="cts-exclude" />\n'
+                        '   <include name="cts-exclude-instant" />\n'
+                        '   <option name="enable-token-sharding" '
+                        'value="true" />\n'
+                        '   <option name="compatibility:exclude-filter" '
+                        'value="CtsDeqpTestCases" />\n'
+                        '   <option name="plan" value="cts" />\n'
+                        '</configuration>\n')
+
+REPORT_FILENAME = 'incremental_dEQP_report.json'
+
+logger = logging.getLogger()
+
+
+class AtsError(Exception):
+  """Error when running incremental dEQP with Android Test Station"""
+  pass
+
+class AdbError(Exception):
+  """Error when running adb command."""
+  pass
+
+class TestError(Exception):
+  """Error when running dEQP test."""
+  pass
+
+class TestResourceError(Exception):
+  """Error with test resource. """
+  pass
+
+class BuildHelper(object):
+  """Helper class for analyzing build."""
+
+  def __init__(self, custom_handler=False):
+    """Init BuildHelper.
+
+    Args:
+      custom_handler: use custom build handler.
+    """
+    self._build_file_handler = TargetFileHandler
+    if custom_handler:
+      self._build_file_handler = CustomBuildFileHandler
+
+
+  def compare_base_build_with_current_build(self, deqp_deps, current_build_file,
+                                            base_build_file):
+    """Compare the difference of current build and base build with dEQP dependency.
+
+    If the difference doesn't involve dEQP dependency, current build could skip dEQP test if
+    base build has passed test.
+
+    Args:
+      deqp_deps: a set of dEQP dependency.
+      current_build_file: current build's file name.
+      base_build_file: base build's file name.
+    Returns:
+      True if current build could skip dEQP, otherwise False.
+      Dictionary of changed dependencies and their details.
+    """
+    print('Comparing base build and current build...')
+    current_build_handler = self._build_file_handler(current_build_file)
+    current_build_hash = current_build_handler.get_file_hash(deqp_deps)
+
+    base_build_handler = self._build_file_handler(base_build_file)
+    base_build_hash = base_build_handler.get_file_hash(deqp_deps)
+
+    return self._compare_build_hash(current_build_hash, base_build_hash)
+
+
+  def compare_base_build_with_device_files(self, deqp_deps, adb, base_build_file):
+    """Compare the difference of files on device and base build with dEQP dependency.
+
+    If the difference doesn't involve dEQP dependency, current build could skip dEQP test if
+    base build has passed test.
+
+    Args:
+      deqp_deps: a set of dEQP dependency.
+      adb: an instance of AdbHelper for current device under test.
+      base_build_file: base build file name.
+    Returns:
+      True if current build could skip dEQP, otherwise False.
+      Dictionary of changed dependencies and their details.
+    """
+    print('Comparing base build and current build on the device...')
+    # Get current build's hash.
+    current_build_hash = dict()
+    for dep in deqp_deps:
+      content = adb.run_shell_command('cat ' + dep)
+      current_build_hash[dep] = hash(content)
+
+    base_build_handler = self._build_file_handler(base_build_file)
+    base_build_hash = base_build_handler.get_file_hash(deqp_deps)
+
+    return self._compare_build_hash(current_build_hash, base_build_hash)
+
+  def get_system_fingerprint(self, build_file):
+    """Get build fingerprint in SYSTEM partition.
+
+    Returns:
+      String of build fingerprint.
+    """
+    return self._build_file_handler(build_file).get_system_fingerprint()
+
+
+  def _compare_build_hash(self, current_build_hash, base_build_hash):
+    """Compare the hash value of current build and base build.
+
+    Args:
+      current_build_hash: map of current build where key is file name, and value is content hash.
+      base_build_hash: map of base build where key is file name and value is content hash.
+    Returns:
+      Boolean about if two builds' hash is the same.
+      Dictionary of changed dependencies and their details.
+    """
+    changes = {}
+    if current_build_hash == base_build_hash:
+      print('Done!')
+      return True, changes
+
+    for key, val in current_build_hash.items():
+      if key not in base_build_hash:
+        detail = 'File:{build_file} was not found in base build'.format(build_file=key)
+        changes[key] = detail
+        logger.info(detail)
+      elif base_build_hash[key] != val:
+        detail = ('Detected dEQP dependency file difference:{deps}. Base build hash:{base}, '
+                  'current build hash:{current}'.format(deps=key, base=base_build_hash[key],
+                                                        current=val))
+        changes[key] = detail
+        logger.info(detail)
+
+    print('Done!')
+    return False, changes
+
+
+class AdbHelper(object):
+  """Helper class for running adb."""
+
+  def __init__(self, device_serial=None):
+    """Initialize AdbHelper.
+
+    Args:
+      device_serial: A string of device serial number, optional.
+    """
+    self._device_serial = device_serial
+
+  def _run_adb_command(self, *args):
+    """Run adb command."""
+    adb_cmd = ['adb']
+    if self._device_serial:
+      adb_cmd.extend(['-s', self._device_serial])
+    adb_cmd.extend(args)
+    adb_cmd = ' '.join(adb_cmd)
+    completed = subprocess.run(adb_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+    if completed.returncode != 0:
+      raise AdbError('adb command: {cmd} failed with error: {error}'
+                     .format(cmd=adb_cmd, error=completed.stderr))
+
+    return completed.stdout
+
+  def push_file(self, source_file, destination_file):
+    """Push a file from device to host.
+
+    Args:
+      source_file: A string representing file to push.
+      destination_file: A string representing target on device to push to.
+    """
+    return self._run_adb_command('push', source_file, destination_file)
+
+  def pull_file(self, source_file, destination_file):
+    """Pull a file to device.
+
+    Args:
+      source_file: A string representing file on device to pull.
+      destination_file: A string representing target on host to pull to.
+    """
+    return self._run_adb_command('pull', source_file, destination_file)
+
+  def get_fingerprint(self):
+    """Get fingerprint of the device."""
+    return self._run_adb_command('shell', 'getprop ro.build.fingerprint').decode("utf-8").strip()
+
+  def run_shell_command(self, command):
+    """Run a adb shell command.
+
+    Args:
+      command: A string of command to run, executed through 'adb shell'
+    """
+    return self._run_adb_command('shell', command)
+
+
+class DeqpDependencyCollector(object):
+  """Collect dEQP dependency from device under test."""
+
+  def __init__(self, work_dir, test_dir, adb):
+    """Init DeqpDependencyCollector.
+
+    Args:
+      work_dir: path of directory for saving script result and logs.
+      test_dir: path of directory for incremental dEQP test file.
+      adb: an instance of AdbHelper.
+    """
+    self._work_dir = work_dir
+    self._test_dir = test_dir
+    self._adb = adb
+    # dEQP dependency with pattern below are not an actual file:
+    # files has prefix /data/ are not system files, e.g. intermediate files.
+    # [vdso] is virtual dynamic shared object.
+    # /dmabuf is a temporary file.
+    self._exclude_deqp_pattern = re.compile('(^/data/|^\[vdso\]|^/dmabuf)')
+
+  def check_test_log(self, test_file, log_file):
+    """Check test's log to see if all tests are executed.
+
+    Args:
+      test_file: Name of test .txt file.
+      log_content: Name of log file.
+    Returns:
+      True if all tests are executed, otherwise False.
+    """
+    test_cnt = 0
+    with open(test_file, 'r') as f:
+      for _ in f:
+        test_cnt += 1
+
+    executed_test_cnt = 0
+
+    with open(log_file, 'r') as f:
+      for line in f:
+        # 'NotSupported' status means test is not supported in device.
+        # TODO(yichunli): Check with graphics team if failed test is allowed.
+        if ('StatusCode="Pass"' in line or 'StatusCode="NotSupported"' in line or
+            'StatusCode="Fail"' in line):
+          executed_test_cnt += 1
+    return executed_test_cnt == test_cnt
+
+  def update_dependency(self, deps, dump):
+    """Parse perf dump file and update dEQP dependency.
+
+     Below is an example of how dump file looks like:
+     630 record comm: type 3, misc 0, size 64
+     631   pid 23365, tid 23365, comm simpleperf
+     632   sample_id: pid 0, tid 0
+     633   sample_id: time 0
+     634   sample_id: id 23804
+     635   sample_id: cpu 0, res 0
+  .......
+     684 record comm: type 3, misc 8192, size 64
+     685   pid 23365, tid 23365, comm deqp-binary64
+     686   sample_id: pid 23365, tid 23365
+     687   sample_id: time 595063921159958
+     688   sample_id: id 23808
+     689   sample_id: cpu 4, res 0
+  .......
+     698 record mmap2: type 10, misc 8194, size 136
+     699   pid 23365, tid 23365, addr 0x58b817b000, len 0x3228000
+     700   pgoff 0x0, maj 253, min 9, ino 14709, ino_generation 2575019956
+     701   prot 1, flags 6146, filename /data/local/tmp/deqp-binary64
+     702   sample_id: pid 23365, tid 23365
+     703   sample_id: time 595063921188552
+     704   sample_id: id 23808
+     705   sample_id: cpu 4, res 0
+
+    Args:
+      deps: a set of string containing dEQP dependency.
+      dump: perf dump file's name.
+    """
+    binary_executed = False
+    correct_mmap = False
+    with open(dump, 'r') as f:
+      for line in f:
+        # It means dEQP binary starts to be executed.
+        if re.search(' comm .*deqp-binary', line):
+          binary_executed = True
+        if not binary_executed:
+          continue
+        # We get a new perf event
+        if not line.startswith(' '):
+          # mmap with misc 1 is not for deqp binary.
+          correct_mmap = line.startswith('record mmap') and 'misc 1,' not in line
+        # Get file name in memory map.
+        if 'filename' in line and correct_mmap:
+          deps_file = line[line.find('filename') + 9:].strip()
+          if not re.search(self._exclude_deqp_pattern, deps_file):
+            deps.add(deps_file)
+
+
+  def get_test_binary_name(self, test_name):
+    """Get dEQP binary's name based on test name.
+
+    Args:
+      test_name: name of test.
+    Returns:
+      dEQP binary's name.
+    """
+    if test_name.endswith('32'):
+      return 'deqp-binary'
+    elif test_name.endswith('64'):
+      return 'deqp-binary64'
+    else:
+      raise TestError('Fail to get dEQP binary due to unknonw test name: ' + test_name)
+
+  def get_test_log_name(self, test_name):
+    """Get test log's name based on test name.
+
+    Args:
+      test_name: name of test.
+    Returns:
+      test log's name when running dEQP test.
+    """
+    return test_name + '.qpa'
+
+  def get_test_perf_name(self, test_name):
+    """Get perf file's name based on test name.
+
+    Args:
+      test_name: name of test.
+    Returns:
+      perf file's name.
+    """
+    return test_name + '.data'
+
+  def get_perf_dump_name(self, test_name):
+    """Get perf dump file's name based on test name.
+
+    Args:
+      test_name: name of test.
+    Returns:
+      perf dump file's name.
+    """
+    return test_name + '-perf-dump.txt'
+
+  def get_test_list_name(self, test_name):
+    """Get test list file's name based on test name.
+
+    test list file is used to run dEQP test.
+
+    Args:
+      test_name: name of test.
+    Returns:
+      test list file's name.
+    """
+    if test_name.startswith('vk'):
+      return 'vk-master-subset.txt'
+    elif test_name.startswith('gles3'):
+      return 'gles3-master-subset.txt'
+    else:
+      raise TestError('Fail to get test list due to unknown test name: ' + test_name)
+
+  def get_deqp_dependency(self):
+    """Get dEQP dependency.
+
+    Returns:
+      A set of dEQP dependency.
+    """
+    device_deqp_dir = '/data/local/tmp'
+    device_deqp_out_dir = '/data/local/tmp/out'
+    test_list = ['vk-32', 'vk-64', 'gles3-32', 'gles3-64']
+
+    # Clean up the device.
+    self._adb.run_shell_command('mkdir -p ' + device_deqp_out_dir)
+
+    # Copy test resources to device.
+    logger.info(self._adb.push_file(self._test_dir + '/*', device_deqp_dir))
+
+    # Run the dEQP binary with simpleperf
+    print('Running a subset of dEQP tests as binary on the device...')
+    deqp_deps = set()
+    for test in test_list:
+      test_file = os.path.join(device_deqp_dir, self.get_test_list_name(test))
+      log_file = os.path.join(device_deqp_out_dir, self.get_test_log_name(test))
+      perf_file = os.path.join(device_deqp_out_dir, self.get_test_perf_name(test))
+      deqp_binary = os.path.join(device_deqp_dir, self.get_test_binary_name(test))
+      simpleperf_command = ('"cd {device_deqp_dir} && simpleperf record -o {perf_file} {binary} '
+                            '--deqp-caselist-file={test_list} --deqp-log-images=disable '
+                            '--deqp-log-shader-sources=disable --deqp-log-filename={log_file} '
+                            '--deqp-surface-type=fbo --deqp-surface-width=2048 '
+                            '--deqp-surface-height=2048"')
+      self._adb.run_shell_command(
+          simpleperf_command.format(device_deqp_dir=device_deqp_dir, binary=deqp_binary,
+                                    perf_file=perf_file, test_list=test_file, log_file=log_file))
+
+      # Check test log.
+      host_log_file = os.path.join(self._work_dir, self.get_test_log_name(test))
+      self._adb.pull_file(log_file, host_log_file )
+      if not self.check_test_log(os.path.join(self._test_dir, self.get_test_list_name(test)),
+                                 host_log_file):
+        error_msg = ('Fail to run incremental dEQP because of crashed test. Check test'
+                     'log {} for more detail.').format(host_log_file)
+        logger.error(error_msg)
+        raise TestError(error_msg)
+    print('Tests are all passed!')
+
+    # Parse perf dump result to get dependency.
+    print('Analyzing dEQP dependency...')
+    for test in test_list:
+      perf_file = os.path.join(device_deqp_out_dir, self.get_test_perf_name(test))
+      dump_file = os.path.join(self._work_dir, self.get_perf_dump_name(test))
+      self._adb.run_shell_command('simpleperf dump {perf_file} > {dump_file}'
+                                  .format(perf_file=perf_file, dump_file=dump_file))
+      self.update_dependency(deqp_deps, dump_file)
+    print('Done!')
+    return deqp_deps
+
+def _is_deqp_dependency(dependency_name):
+  """Check if dependency is related to dEQP."""
+  # dEQP dependency with pattern below will not be used to compare build:
+  # files has /apex/ prefix are not related to dEQP.
+  return not re.search(re.compile('^/apex/'), dependency_name)
+
+def _get_parser():
+  parser = argparse.ArgumentParser(description='Run incremental dEQP on devices.')
+  parser.add_argument('-s', '--serial', help='Optional. Use device with given serial.')
+  parser.add_argument('-t', '--test', help=('Optional. Directory of incremental deqp test file. '
+                                            'This directory should have test resources and dEQP '
+                                            'binaries.'))
+  parser.add_argument('-b', '--base_build', help=('Target file of base build that has passed dEQP '
+                                                  'test, e.g. flame-target_files-6935423.zip.'))
+  parser.add_argument('-c', '--current_build',
+                      help=('Optional. When empty, the script will read files in the build from '
+                            'the device via adb. When set, the script will read build files from '
+                            'the file provided by this argument. And this file should be the '
+                            'current build that is flashed to device, such as a target file '
+                            'like flame-target_files-6935424.zip. This argument can be used when '
+                            'some dependencies files are not accessible via adb.'))
+  parser.add_argument('--generate_deps_only', action='store_true',
+                      help=('Run test and generate dEQP dependency list only '
+                            'without comparing build.'))
+  parser.add_argument('--ats_mode', action='store_true',
+                      help=('Run incremental dEQP with Android Test Station.'))
+  parser.add_argument('--custom_handler', action='store_true',
+                      help='Use custome build file handler')
+  return parser
+
+def _create_logger(log_file_name):
+  """Create logger.
+
+  Args:
+    log_file_name: absolute path of the log file.
+  Returns:
+    a logging.Logger
+  """
+  logging.basicConfig(filename=log_file_name)
+  logger = logging.getLogger()
+  logger.setLevel(level=logging.NOTSET)
+  return logger
+
+def _save_deqp_deps(deqp_deps, file_name):
+  """Save dEQP dependency to file.
+
+  Args:
+    deqp_deps: a set of dEQP dependency.
+    file_name: name of the file to save dEQP dependency.
+  Returns:
+    name of the file that saves dEQP dependency.
+  """
+  with open(file_name, 'w') as f:
+    for dep in sorted(deqp_deps):
+      f.write(dep+'\n')
+  return file_name
+
+def _generate_report(
+    report_name,
+    base_build_fingerprint,
+    current_build_fingerprint,
+    deqp_deps,
+    extra_deqp_deps,
+    deqp_deps_changes):
+  """Generate a json report.
+
+  Args:
+    report_name: absolute file name of report.
+    base_build_fingerprint: fingerprint of the base build.
+    current_build_fingerprint: fingerprint of the current build.
+    deqp_deps: list of dEQP dependencies generated by the tool.
+    extra_deqp_deps: list of extra dEQP dependencies.
+    deqp_deps_changes: dictionary of dependency changes.
+  """
+  data = {}
+  data['base_build_fingerprint'] = base_build_fingerprint
+  data['current_build_fingerprint'] = current_build_fingerprint
+  data['deqp_deps'] = sorted(list(deqp_deps))
+  data['extra_deqp_deps'] = sorted(list(extra_deqp_deps))
+  data['deqp_deps_changes'] = deqp_deps_changes
+
+  with open(report_name, 'w') as f:
+    json.dump(data, f, indent=4)
+
+  print('Incremental dEQP report is generated at: ' + report_name)
+
+
+def _local_run(args, work_dir):
+  """Run incremental dEQP locally.
+
+  Args:
+    args: return of parser.parse_args().
+    work_dir: path of directory for saving script result and logs.
+  """
+  print('Logs and simpleperf results will be copied to: ' + work_dir)
+  if args.test:
+    test_dir = args.test
+  else:
+    test_dir = os.path.dirname(os.path.abspath(__file__))
+  # Extra dEQP dependencies are the files can't be loaded to memory such as firmware.
+  extra_deqp_deps = set()
+  extra_deqp_deps_file = os.path.join(test_dir, 'extra_deqp_dependency.txt')
+  if not os.path.exists(extra_deqp_deps_file):
+    if not args.generate_deps_only:
+      raise TestResourceError('{test_resource} doesn\'t exist'
+                             .format(test_resource=extra_deqp_deps_file))
+  else:
+    with open(extra_deqp_deps_file, 'r') as f:
+      for line in f:
+        extra_deqp_deps.add(line.strip())
+
+  if args.serial:
+    adb = AdbHelper(args.serial)
+  else:
+    adb = AdbHelper()
+
+  dependency_collector = DeqpDependencyCollector(work_dir, test_dir, adb)
+  deqp_deps = dependency_collector.get_deqp_dependency()
+  aggregated_deqp_deps = deqp_deps.union(extra_deqp_deps)
+
+  deqp_deps_file_name = _save_deqp_deps(aggregated_deqp_deps,
+                                        os.path.join(work_dir, 'dEQP-dependency.txt'))
+  print('dEQP dependency list has been generated in: ' + deqp_deps_file_name)
+
+  if args.generate_deps_only:
+    return
+
+  # Compare the build difference with dEQP dependency
+  valid_deqp_deps = [dep for dep in aggregated_deqp_deps if _is_deqp_dependency(dep)]
+  build_helper = BuildHelper(args.custom_handler)
+  if args.current_build:
+    skip_dEQP, changes = build_helper.compare_base_build_with_current_build(
+        valid_deqp_deps, args.current_build, args.base_build)
+  else:
+    skip_dEQP, changes = build_helper.compare_base_build_with_device_files(
+        valid_deqp_deps, adb, args.base_build)
+  if skip_dEQP:
+    print('Congratulations, current build could skip dEQP test.\n'
+          'If you run CTS through suite, you could pass filter like '
+          '\'--exclude-filter CtsDeqpTestCases\'.')
+  else:
+    print('Sorry, current build can\'t skip dEQP test because dEQP dependency has been '
+          'changed.\nPlease check logs for more details.')
+
+  _generate_report(os.path.join(work_dir, REPORT_FILENAME),
+                   build_helper.get_system_fingerprint(args.base_build),
+                   adb.get_fingerprint(),
+                   deqp_deps,
+                   extra_deqp_deps,
+                   changes)
+
+def _generate_cts_xml(out_dir, content):
+  """Generate cts configuration for Android Test Station.
+
+  Args:
+   out_dir: output directory for cts confiugration.
+   content: configuration content.
+  """
+  with open(os.path.join(out_dir, 'incremental_deqp.xml'), 'w') as f:
+    f.write(content)
+
+
+def _ats_run(args, work_dir):
+  """Run incremental dEQP with Android Test Station.
+
+  Args:
+    args: return of parser.parse_args().
+    work_dir: path of directory for saving script result and logs.
+  """
+  # Extra dEQP dependencies are the files can't be loaded to memory such as firmware.
+  extra_deqp_deps = set()
+  with open(os.path.join(work_dir, 'extra_deqp_dependency.txt'), 'r') as f:
+    for line in f:
+      if line.strip():
+        extra_deqp_deps.add(line.strip())
+
+  android_serials = os.getenv('ANDROID_SERIALS')
+  if not android_serials:
+    raise AtsError('Fail to read environment variable ANDROID_SERIALS.')
+  first_device_serial = android_serials.split(',')[0]
+  adb = AdbHelper(first_device_serial)
+
+  dependency_collector = DeqpDependencyCollector(work_dir,
+                                                 os.path.join(work_dir, 'test_resources'), adb)
+  deqp_deps = dependency_collector.get_deqp_dependency()
+  aggregated_deqp_deps = deqp_deps.union(extra_deqp_deps)
+
+  deqp_deps_file_name = _save_deqp_deps(aggregated_deqp_deps,
+                                        os.path.join(work_dir, 'dEQP-dependency.txt'))
+
+  if args.generate_deps_only:
+    _generate_cts_xml(work_dir, DEFAULT_CTS_XML)
+    return
+
+  # Compare the build difference with dEQP dependency
+  valid_deqp_deps = [dep for dep in aggregated_deqp_deps if _is_deqp_dependency(dep)]
+
+  # base build target file is from test resources.
+  base_build_target = os.path.join(work_dir, 'base_build_target_files')
+  build_helper = BuildHelper(args.custom_handler)
+  if args.current_build:
+    skip_dEQP, changes = build_helper.compare_base_build_with_current_build(
+        valid_deqp_deps, args.current_build, args.base_build)
+  else:
+    skip_dEQP, changes = build_helper.compare_base_build_with_device_files(
+        valid_deqp_deps, adb, args.base_build)
+  if skip_dEQP:
+    _generate_cts_xml(work_dir, INCREMENTAL_DEQP_XML)
+  else:
+    _generate_cts_xml(work_dir, DEFAULT_CTS_XML)
+
+  _generate_report(os.path.join(work_dir, REPORT_FILENAME),
+                   build_helper.get_system_fingerprint(args.base_build),
+                   adb.get_fingerprint(),
+                   deqp_deps,
+                   extra_deqp_deps,
+                   changes)
+
+def main():
+  parser = _get_parser()
+  args = parser.parse_args()
+  if not args.generate_deps_only and not args.base_build and not args.ats_mode:
+    parser.error('Base build argument: \'-b [file] or --base_build [file]\' '
+                 'is required to compare build.')
+
+  work_dir = ''
+  log_file_name = ''
+  if args.ats_mode:
+    work_dir = os.getenv('TF_WORK_DIR')
+    log_file_name = os.path.join('/data/tmp', 'incremental-deqp-log-'+str(uuid.uuid4()))
+  else:
+    work_dir = tempfile.mkdtemp(prefix='incremental-deqp-'
+                                + time.strftime("%Y%m%d-%H%M%S"))
+    log_file_name = os.path.join(work_dir, 'incremental-deqp-log')
+  global logger
+  logger = _create_logger(log_file_name)
+
+  if args.ats_mode:
+    _ats_run(args, work_dir)
+  else:
+    _local_run(args, work_dir)
+
+if __name__ == '__main__':
+  main()
+
diff --git a/tools/incremental-cts/incremental_deqp_test.py b/tools/incremental-cts/incremental_deqp_test.py
new file mode 100644
index 0000000..eedcc88
--- /dev/null
+++ b/tools/incremental-cts/incremental_deqp_test.py
@@ -0,0 +1,185 @@
+# Lint as: python3
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for incremental_deqp."""
+
+import incremental_deqp
+import os
+import unittest
+from unittest.mock import MagicMock
+from unittest.mock import patch
+
+@patch('incremental_deqp.AdbHelper', MagicMock())
+class IncrementalDeqpTest(unittest.TestCase):
+
+  def setUp(self):
+    testfile_dir = os.path.dirname(os.path.abspath(__file__))
+    self.testdata_dir = testfile_dir +'/testdata'
+    self.dependency_collector = incremental_deqp.DeqpDependencyCollector(None, None, None)
+    self.parser = incremental_deqp._get_parser()
+
+  def test_update_dependency(self):
+    """Test update_dependency gets correct dEQP dependency from dump file."""
+    dump_file = 'testdata/perf_dump.txt'
+    deps = set()
+    self.dependency_collector.update_dependency(deps, dump_file)
+    self.assertEqual(len(deps),2)
+    self.assertIn('file_2', deps)
+    self.assertIn('file_3', deps)
+
+  def test_check_test_log_all_test_executed(self):
+    """Test check_test_log returns true if all tests are executed."""
+    test_file = 'testdata/test_list.txt'
+    log_file = 'testdata/log_1.qpa'
+    self.assertTrue(self.dependency_collector.check_test_log(test_file, log_file))
+
+  def test_check_test_log_test_crashed(self):
+    """Test check_test_log returns false if tests are crashed."""
+    test_file = 'testdata/test_list.txt'
+    log_file = 'testdata/log_2.qpa'
+    self.assertFalse(self.dependency_collector.check_test_log(test_file, log_file))
+
+  def test_get_test_binary_name(self):
+    """Test get_test_binary_name gets dEQP binary name based on test name."""
+    self.assertEqual(self.dependency_collector.get_test_binary_name('vk-32'), 'deqp-binary')
+    self.assertEqual(self.dependency_collector.get_test_binary_name('vk-64'), 'deqp-binary64')
+    with self.assertRaises(incremental_deqp.TestError):
+      self.dependency_collector.get_test_binary_name('test')
+
+  def test_get_test_log_name(self):
+    """Test get_test_log_name gets correct test log name based on test name."""
+    self.assertEqual(self.dependency_collector.get_test_log_name('test'), 'test.qpa')
+
+  def test_get_perf_name(self):
+    """Test get_perf_name gets correct perf file name based on test name."""
+    self.assertEqual(self.dependency_collector.get_test_perf_name('test'), 'test.data')
+
+  def test_get_perf_dump_name(self):
+    """Test get_perf_dump_name gets correct perf dump file name based on test name."""
+    self.assertEqual(self.dependency_collector.get_perf_dump_name('test'), 'test-perf-dump.txt')
+
+  def test_get_test_list_name(self):
+    """Test get_test_list_name gets test list name based on test name."""
+    self.assertEqual(self.dependency_collector.get_test_list_name('vk-32'), 'vk-master-subset.txt')
+    self.assertEqual(self.dependency_collector.get_test_list_name('gles3-32'),
+                     'gles3-master-subset.txt')
+    with self.assertRaises(incremental_deqp.TestError):
+      self.dependency_collector.get_test_list_name('test')
+
+  def test_valid_dependency(self):
+    """Test if dependency is valid."""
+    self.assertTrue(incremental_deqp._is_deqp_dependency('/file/a.so'))
+    self.assertFalse(incremental_deqp._is_deqp_dependency('/apex/a.so'))
+
+  def test_build_helper_compare_build_with_device_files_true(self):
+    """Test BuildHelper.compare_base_build_with_device_files returns true."""
+    build_helper = incremental_deqp.BuildHelper()
+    deqp_deps = ['/system/deqp_dependency_file_a.so', '/vendor/deqp_dependency_file_b.so']
+    base_build_file = './testdata/base_build_target-files.zip'
+
+    def side_effect(command):
+      if 'file_a.so' in command:
+        return b'placeholder\nplaceholder\n'
+      if 'file_b.so' in command:
+        return b'placeholder\nplaceholder\nplaceholder\n\n'
+
+    adb = incremental_deqp.AdbHelper()
+    adb.run_shell_command = MagicMock(side_effect=side_effect)
+    self.assertTrue(build_helper.compare_base_build_with_device_files(
+        deqp_deps, adb, base_build_file)[0])
+
+  def test_compare_build_with_device_files_false(self):
+    """Test BuildHelper.compare_base_build_with_device_files returns false."""
+    deqp_deps = ['/system/deqp_dependency_file_a.so', '/vendor/deqp_dependency_file_b.so']
+    build_helper = incremental_deqp.BuildHelper()
+    base_build_file = './testdata/base_build_target-files.zip'
+    def side_effect(command):
+      if 'file_a.so' in command:
+        return b'different text'
+      if 'file_b.so' in command:
+        return b'placeholder\nplaceholder\nplaceholder\n\n'
+
+    adb = incremental_deqp.AdbHelper()
+    adb.run_shell_command = MagicMock(side_effect=side_effect)
+    self.assertFalse(build_helper.compare_base_build_with_device_files(
+        deqp_deps, adb, base_build_file)[0])
+
+  def test_build_helper_compare_build_with_current_build_true(self):
+    """Test BuildHelper.compare_base_build_with_current_build returns true."""
+    build_helper = incremental_deqp.BuildHelper()
+    deqp_deps = ['/system/deqp_dependency_file_a.so', '/vendor/deqp_dependency_file_b.so']
+    base_build_file = './testdata/base_build_target-files.zip'
+
+    self.assertTrue(build_helper.compare_base_build_with_current_build(
+        deqp_deps, base_build_file, base_build_file)[0])
+
+  def test_build_helper_compare_build_with_current_build_false(self):
+    """Test BuildHelper.compare_base_build_with_current_build returns false."""
+    build_helper = incremental_deqp.BuildHelper()
+    deqp_deps = ['/system/deqp_dependency_file_a.so', '/vendor/deqp_dependency_file_b.so']
+    base_build_file = './testdata/base_build_target-files.zip'
+    current_build_file = './testdata/current_build_target-files.zip'
+
+    self.assertFalse(build_helper.compare_base_build_with_current_build(
+        deqp_deps, current_build_file, base_build_file)[0])
+
+  def test_build_helper_get_system_fingerprint(self):
+    """Test BuildHelper gets system fingerprint."""
+    build_helper = incremental_deqp.BuildHelper()
+    build_file = './testdata/base_build_target-files.zip'
+
+    self.assertEqual(('generic/aosp_cf_x86_64_phone/vsoc_x86_64:S/AOSP.MASTER/7363308:'
+                      'userdebug/test-keys'), build_helper.get_system_fingerprint(build_file))
+
+
+  @patch('incremental_deqp.BuildHelper', autospec=True)
+  @patch('incremental_deqp._save_deqp_deps', autospec=True)
+  @patch('incremental_deqp.DeqpDependencyCollector', autospec=True)
+  @patch('incremental_deqp.AdbHelper', autospec=True)
+  def test_local_run_generate_deps_only(self, adb_helper_mock, dependency_collector_mock,
+                                        save_deps_mock, build_helper_mock):
+    """Test generate_deps_only option in local_run."""
+    dependency_collector_mock.return_value.get_deqp_dependency.return_value = {'a.so'}
+    args = self.parser.parse_args(['--generate_deps_only'])
+    incremental_deqp._local_run(args, '')
+    save_deps_mock.assert_called_once_with({'a.so'}, 'dEQP-dependency.txt')
+    build_helper_mock.assert_not_called()
+
+  def test_local_run_missing_extra_deps(self):
+    """Test local_run throws exception if extra_deqp_dependency.txt is missing."""
+    args = self.parser.parse_args(['-t ./testdata'])
+    with self.assertRaises(incremental_deqp.TestResourceError):
+      incremental_deqp._local_run(args, '')
+
+  @patch('incremental_deqp._generate_report', autospec=True)
+  @patch('incremental_deqp.BuildHelper', autospec=True)
+  @patch('incremental_deqp._save_deqp_deps', autospec=True)
+  @patch('incremental_deqp.DeqpDependencyCollector', autospec=True)
+  @patch('incremental_deqp.AdbHelper', autospec=True)
+  def test_local_run_compare_build(self, adb_helper_mock, dependency_collector_mock,
+                                   save_deps_mock, build_helper_mock, generate_report_mock):
+    """Test local_run could compare build based on dependency."""
+    dependency_collector_mock.return_value.get_deqp_dependency.return_value = {'a.so'}
+    build_helper_mock.return_value.compare_base_build_with_device_files.return_value = [False, {}]
+    args = self.parser.parse_args(['-b', 'base_build', '-t', self.testdata_dir])
+
+    incremental_deqp._local_run(args, '')
+
+    save_deps_mock.assert_called_once_with({'a.so', 'extra_a.so'}, 'dEQP-dependency.txt')
+    build_helper_mock.assert_called_once_with(False)
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/incremental-cts/target_file_handler.py b/tools/incremental-cts/target_file_handler.py
new file mode 100644
index 0000000..69189e6
--- /dev/null
+++ b/tools/incremental-cts/target_file_handler.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2021 - The Android Open Source Project
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+"""File handler that reads target files."""
+
+from abstract_build_file_handler import *
+from zipfile import ZipFile
+
+class TargetFileHandler(AbstractBuildFileHandler):
+  """Reads file's content from target files."""
+
+  def __init__(self, build_file):
+    super().__init__(build_file)
+
+  def get_file_hash(self, file_names, hash_func=None):
+    """See base class."""
+    hash_dict = dict()
+    with ZipFile(self.build_file) as zip_file:
+      for file_name in file_names:
+        # Convert top directory's name to upper case.
+        idx = file_name[1:].find('/')+1
+        real_file_name = file_name[1:idx].upper()+file_name[idx:]
+        if real_file_name not in zip_file.namelist():
+          continue
+        with zip_file.open(real_file_name) as f:
+          if hash_func:
+            hash_dict[file_name] = hash_func(f.read())
+          else:
+            hash_dict[file_name] = hash(f.read())
+    return hash_dict
+
+  def get_system_fingerprint(self):
+    """See base class."""
+    fingerprint = ''
+    with ZipFile(self.build_file) as zip_file:
+      with zip_file.open('SYSTEM/build.prop') as build_prop:
+        for line in build_prop:
+          line = line.decode('UTF-8')
+          if 'build.fingerprint' not in line:
+            continue
+          fingerprint = line.split('=')[1].strip()
+          break
+    return fingerprint
diff --git a/tools/incremental-cts/target_file_handler_test.py b/tools/incremental-cts/target_file_handler_test.py
new file mode 100644
index 0000000..e89a1ad
--- /dev/null
+++ b/tools/incremental-cts/target_file_handler_test.py
@@ -0,0 +1,54 @@
+# Lint as: python3
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for target_file_handler."""
+
+import unittest
+from target_file_handler import *
+
+class BuildFileHandlerTest(unittest.TestCase):
+
+  def test_get_hash(self):
+    """Test TargetFileHandler could get hash from target file."""
+    build_file = './testdata/base_build_target-files.zip'
+    handler = TargetFileHandler(build_file)
+    deqp_deps = ['/system/deqp_dependency_file_a.so', '/vendor/deqp_dependency_file_b.so',
+                 '/vendor/file_not_exists.so']
+    hash_dict = handler.get_file_hash(deqp_deps)
+
+    self.assertEqual(hash_dict['/system/deqp_dependency_file_a.so'],
+                     hash(b'placeholder\nplaceholder\n'))
+    self.assertEqual(hash_dict['/vendor/deqp_dependency_file_b.so'],
+                     hash(b'placeholder\nplaceholder\nplaceholder\n\n'))
+    self.assertEqual(len(hash_dict), 2)
+
+  def test_get_system_fingerprint(self):
+    """Test TargetFileHandler could get SYSTEM fingerprint from target file."""
+    build_file = './testdata/base_build_target-files.zip'
+    handler = TargetFileHandler(build_file)
+    self.assertEqual(('generic/aosp_cf_x86_64_phone/vsoc_x86_64:S/AOSP.MASTER/7363308:'
+                      'userdebug/test-keys'), handler.get_system_fingerprint())
+
+  def test_get_system_fingerprint_without_buildprop(self):
+    """Test TargetFileHandler get fingerprint raises exception if build.prop doesn't exist."""
+    build_file = './testdata/current_build_target-files.zip'
+    handler = TargetFileHandler(build_file)
+    with self.assertRaises(KeyError):
+      handler.get_system_fingerprint()
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/tools/incremental-cts/testdata/base_build_target-files.zip b/tools/incremental-cts/testdata/base_build_target-files.zip
new file mode 100644
index 0000000..077a3f1
--- /dev/null
+++ b/tools/incremental-cts/testdata/base_build_target-files.zip
Binary files differ
diff --git a/tools/incremental-cts/testdata/current_build_target-files.zip b/tools/incremental-cts/testdata/current_build_target-files.zip
new file mode 100644
index 0000000..1bea4d4
--- /dev/null
+++ b/tools/incremental-cts/testdata/current_build_target-files.zip
Binary files differ
diff --git a/tools/incremental-cts/testdata/extra_deqp_dependency.txt b/tools/incremental-cts/testdata/extra_deqp_dependency.txt
new file mode 100644
index 0000000..57ee1ea
--- /dev/null
+++ b/tools/incremental-cts/testdata/extra_deqp_dependency.txt
@@ -0,0 +1 @@
+extra_a.so
diff --git a/tools/incremental-cts/testdata/log_1.qpa b/tools/incremental-cts/testdata/log_1.qpa
new file mode 100644
index 0000000..c0767f2
--- /dev/null
+++ b/tools/incremental-cts/testdata/log_1.qpa
@@ -0,0 +1,43 @@
+#beginTestCaseResult dEQP-VK.info.build
+<?xml version="1.0" encoding="UTF-8"?>
+<TestCaseResult Version="0.3.4" CasePath="dEQP-VK.info.build" CaseType="SelfValidate">
+ <Text>DE_OS: DE_OS_ANDROID
+DE_CPU: DE_CPU_ARM
+DE_PTR_SIZE: 4
+DE_ENDIANNESS: DE_LITTLE_ENDIAN
+DE_COMPILER: DE_COMPILER_CLANG
+DE_DEBUG: false
+</Text>
+ <Number Name="TestDuration" Description="Test case duration in microseconds" Tag="Time" Unit="us">52</Number>
+ <Result StatusCode="Pass">Not validated</Result>
+</TestCaseResult>
+
+#endTestCaseResult
+
+#beginTestCaseResult dEQP-VK.info.device
+<?xml version="1.0" encoding="UTF-8"?>
+<TestCaseResult Version="0.3.4" CasePath="dEQP-VK.info.device" CaseType="SelfValidate">
+ <Text>Using --deqp-vk-device-id=1</Text>
+ <Text>apiVersion: 1.1.128
+driverVersion: 0x801ea000
+deviceName: Adreno (TM) 640
+vendorID: 0x00005143
+deviceID: 0x06040001
+</Text>
+ <Number Name="TestDuration" Description="Test case duration in microseconds" Tag="Time" Unit="us">24</Number>
+ <Result StatusCode="Fail">Not validated</Result>
+</TestCaseResult>
+
+#endTestCaseResult
+
+#beginTestCaseResult dEQP-VK.info.platform
+<?xml version="1.0" encoding="UTF-8"?>
+<TestCaseResult Version="0.3.4" CasePath="dEQP-VK.info.platform" CaseType="SelfValidate">
+ <Text>OS: Linux 4.14.180-g051355490483-ab6669324 #1 SMP PREEMPT Fri Jul 10 09:40:47 UTC 2020
+CPU: armv8l
+</Text>
+ <Number Name="TestDuration" Description="Test case duration in microseconds" Tag="Time" Unit="us">16</Number>
+ <Result StatusCode="NotSupported">Not validated</Result>
+</TestCaseResult>
+
+#endTestCaseResult
diff --git a/tools/incremental-cts/testdata/log_2.qpa b/tools/incremental-cts/testdata/log_2.qpa
new file mode 100644
index 0000000..98c9946
--- /dev/null
+++ b/tools/incremental-cts/testdata/log_2.qpa
@@ -0,0 +1,31 @@
+#beginTestCaseResult dEQP-VK.info.build
+<?xml version="1.0" encoding="UTF-8"?>
+<TestCaseResult Version="0.3.4" CasePath="dEQP-VK.info.build" CaseType="SelfValidate">
+ <Text>DE_OS: DE_OS_ANDROID
+DE_CPU: DE_CPU_ARM
+DE_PTR_SIZE: 4
+DE_ENDIANNESS: DE_LITTLE_ENDIAN
+DE_COMPILER: DE_COMPILER_CLANG
+DE_DEBUG: false
+</Text>
+ <Number Name="TestDuration" Description="Test case duration in microseconds" Tag="Time" Unit="us">52</Number>
+ <Result StatusCode="Pass">Not validated</Result>
+</TestCaseResult>
+
+#endTestCaseResult
+
+#beginTestCaseResult dEQP-VK.info.device
+<?xml version="1.0" encoding="UTF-8"?>
+<TestCaseResult Version="0.3.4" CasePath="dEQP-VK.info.device" CaseType="SelfValidate">
+ <Text>Using --deqp-vk-device-id=1</Text>
+ <Text>apiVersion: 1.1.128
+driverVersion: 0x801ea000
+deviceName: Adreno (TM) 640
+vendorID: 0x00005143
+deviceID: 0x06040001
+</Text>
+ <Number Name="TestDuration" Description="Test case duration in microseconds" Tag="Time" Unit="us">24</Number>
+ <Result StatusCode="Pass">Not validated</Result>
+</TestCaseResult>
+
+#endTestCaseResult
diff --git a/tools/incremental-cts/testdata/perf_dump.txt b/tools/incremental-cts/testdata/perf_dump.txt
new file mode 100644
index 0000000..1acd1b6
--- /dev/null
+++ b/tools/incremental-cts/testdata/perf_dump.txt
@@ -0,0 +1,68 @@
+record mmap2: type 10, misc 1, size 136
+  pid 23365, tid 23365, addr 0x58b817b000, len 0x3228000
+  pgoff 0x0, maj 253, min 9, ino 14709, ino_generation 2575019956
+  prot 1, flags 6146, filename file_0
+  sample_id: pid 23365, tid 23365
+  sample_id: time 595063921188552
+  sample_id: id 23808
+  sample_id: cpu 4, res 0
+record mmap2: type 10, misc 8194, size 136
+  pid 23365, tid 23365, addr 0x58b817b000, len 0x3228000
+  pgoff 0x0, maj 253, min 9, ino 14709, ino_generation 2575019956
+  prot 1, flags 6146, filename file_1
+  sample_id: pid 23365, tid 23365
+  sample_id: time 595063921188552
+  sample_id: id 23808
+  sample_id: cpu 4, res 0
+record comm: type 3, misc 0, size 64
+  pid 23365, tid 23365, comm simpleperf
+  sample_id: pid 0, tid 0
+  sample_id: time 0
+  sample_id: id 23804
+  sample_id: cpu 0, res 0
+record comm: type 3, misc 8192, size 64
+  pid 23365, tid 23365, comm deqp-binary64
+  sample_id: pid 23365, tid 23365
+  sample_id: time 595063921159958
+  sample_id: id 23808
+  sample_id: cpu 4, res 0
+record mmap2: type 10, misc 8194, size 136
+  pid 23365, tid 23365, addr 0x58b817b000, len 0x3228000
+  pgoff 0x0, maj 253, min 9, ino 14709, ino_generation 2575019956
+  prot 1, flags 6146, filename file_2
+  sample_id: pid 23365, tid 23365
+  sample_id: time 595063921188552
+  sample_id: id 23808
+  sample_id: cpu 4, res 0
+record mmap2: type 10, misc 8194, size 136
+  pid 23365, tid 23365, addr 0x58b817b000, len 0x3228000
+  pgoff 0x0, maj 253, min 9, ino 14709, ino_generation 2575019956
+  prot 1, flags 6146, filename /data/file
+  sample_id: pid 23365, tid 23365
+  sample_id: time 595063921188552
+  sample_id: id 23808
+  sample_id: cpu 4, res 0
+record mmap2: type 10, misc 8194, size 136
+  pid 23365, tid 23365, addr 0x58b817b000, len 0x3228000
+  pgoff 0x0, maj 253, min 9, ino 14709, ino_generation 2575019956
+  prot 1, flags 6146, filename /dmabuf:dmabuf1234
+  sample_id: pid 23365, tid 23365
+  sample_id: time 595063921188552
+  sample_id: id 23808
+  sample_id: cpu 4, res 0
+record mmap2: type 10, misc 8194, size 136
+  pid 23365, tid 23365, addr 0x58b817b000, len 0x3228000
+  pgoff 0x0, maj 253, min 9, ino 14709, ino_generation 2575019956
+  prot 1, flags 6146, filename [vdso]
+  sample_id: pid 23365, tid 23365
+  sample_id: time 595063921188552
+  sample_id: id 23808
+  sample_id: cpu 4, res 0
+record mmap2: type 10, misc 8194, size 136
+  pid 23365, tid 23365, addr 0x58b817b000, len 0x3228000
+  pgoff 0x0, maj 253, min 9, ino 14709, ino_generation 2575019956
+  prot 1, flags 6146, filename file_3
+  sample_id: pid 23365, tid 23365
+  sample_id: time 595063921188552
+  sample_id: id 23808
+  sample_id: cpu 4, res 0
diff --git a/tools/incremental-cts/testdata/test_list.txt b/tools/incremental-cts/testdata/test_list.txt
new file mode 100644
index 0000000..4811273
--- /dev/null
+++ b/tools/incremental-cts/testdata/test_list.txt
@@ -0,0 +1,3 @@
+dEQP-GLES3.info.vendor
+dEQP-GLES3.info.renderer
+dEQP-GLES3.info.version
diff --git a/tools/selinux/SELinuxNeverallowTestFrame.py b/tools/selinux/SELinuxNeverallowTestFrame.py
index 740e983..370b40b 100644
--- a/tools/selinux/SELinuxNeverallowTestFrame.py
+++ b/tools/selinux/SELinuxNeverallowTestFrame.py
@@ -36,13 +36,13 @@
  * Neverallow Rules SELinux tests.
  */
 public class SELinuxNeverallowRulesTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest {
-    private static final int Q_SEPOLICY_VERSION = 29;
     private File sepolicyAnalyze;
     private File devicePolicyFile;
     private File deviceSystemPolicyFile;
 
     private IBuildInfo mBuild;
     private int mVendorSepolicyVersion = -1;
+    private int mSystemSepolicyVersion = -1;
 
     /**
      * A reference to the device under test.
@@ -83,6 +83,10 @@
                 mVendorSepolicyVersion =
                         android.security.cts.SELinuxHostTest.getVendorSepolicyVersion(mBuild, mDevice);
             }
+            if (mSystemSepolicyVersion == -1) {
+                mSystemSepolicyVersion =
+                        android.security.cts.SELinuxHostTest.getSystemSepolicyVersion(mBuild);
+            }
         }
     }
 
@@ -140,7 +144,7 @@
         // If sepolicy is split and vendor sepolicy version is behind platform's,
         // only test against platform policy.
         File policyFile =
-                (isSepolicySplit() && mVendorSepolicyVersion < Q_SEPOLICY_VERSION) ?
+                (isSepolicySplit() && mVendorSepolicyVersion < mSystemSepolicyVersion) ?
                 deviceSystemPolicyFile :
                 devicePolicyFile;
 
diff --git a/tools/utils/java-cert-list-generator.sh b/tools/utils/java-cert-list-generator.sh
index 1f33c4a..b69bec6 100755
--- a/tools/utils/java-cert-list-generator.sh
+++ b/tools/utils/java-cert-list-generator.sh
@@ -40,14 +40,12 @@
  */
 
 package android.security.cts;
-import android.platform.test.annotations.SecurityTest;
 
 /**
  * Run "./cts/tools/utils/java-cert-list-generator.sh >
  * cts/tests/tests/security/src/android/security/cts/CertificateData.java"
  * to generate this file.
  */
-@SecurityTest
 class CertificateData {
   static final String[] CERTIFICATE_DATA = {
 STARTCLASS
diff --git a/tools/vm-tests-tf/targetprep/Android.bp b/tools/vm-tests-tf/targetprep/Android.bp
new file mode 100644
index 0000000..76cb248
--- /dev/null
+++ b/tools/vm-tests-tf/targetprep/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+    name: "compatibility-host-vm-targetprep",
+    srcs: ["src/**/*.java"],
+    libs: [
+        "compatibility-host-util",
+        "cts-tradefed",
+        "tradefed",
+    ],
+
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "general-tests",
+    ],
+}
diff --git a/tools/vm-tests-tf/targetprep/Android.mk b/tools/vm-tests-tf/targetprep/Android.mk
deleted file mode 100644
index 8de699b..0000000
--- a/tools/vm-tests-tf/targetprep/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := compatibility-host-util cts-tradefed tradefed
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := compatibility-host-vm-targetprep
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts general-tests
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))