Merge "STS test for Android Security CVE-2021-0398" into sc-dev
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 ae658b4..57bf63b 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPrivUpgradeWrongSHA.apk"
}
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 0ec2b3f..9e44013 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPrivUpgrade.apk"
}
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 141ae82..556f08d 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPrivUpgradeWrongSHA.apk"
}
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 0b2017c..5015040 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPrivUpgrade.apk"
}
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 779dddb..d3d9a95 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v1.apex"
}
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 65ba29a..e328379 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_additional_file.apex"
}
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 872b470..8483625 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_additional_folder.apex"
}
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 c372928..f478e11 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2.apex"
}
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 f4fd435..c92a9a9 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex"
}
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 06f779e..96db91b 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_different_certificate.apex"
}
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 88879d4..71f21c3 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_different_package_name.apex"
}
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 0009342..49618a0 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_no_hashtree.apex"
}
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 6151ae3..2502dc6 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_sdk_target_p.apex"
}
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..ab51f93
--- /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,12 @@
+drops {
+ android_build_drop {
+ build_id: "7351002"
+ 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"
+}
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 378cbdb..9aaa75f 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_signed_bob.apex"
}
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 48e5ed6..2f884a1 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_signed_bob_rot.apex"
}
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 52ebaf0..9c7f872 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex"
}
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 97f78b3..c8b6903 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_unsigned_payload.apex"
}
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 437ff2b..ce54bd4 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_with_post_install_hook.apex"
}
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 cc573a400..8bbe71d 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_with_pre_install_hook.apex"
}
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 ae0243f..6ae0fc0 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_without_apk_in_apex.apex"
}
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 d043084..a21a5ec 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v2_wrong_sha.apex"
}
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 3c67ecb..aead21f 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v3.apex"
}
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 3d78040..6eb4002 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v3_signed_bob.apex"
}
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 e3181b2..3c24063 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/com.android.apex.cts.shim.v3_signed_bob_rot.apex"
}
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 0d7a7ad..ce15d76 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v1.apex"
}
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 98537ce..be46c3d 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_additional_file.apex"
}
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 7a3bb1f..b529bf8 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_additional_folder.apex"
}
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 d2a0443..fd12f2a 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2.apex"
}
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 922786e..65deb79 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex"
}
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 bb924c9..6a70aa4 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_different_certificate.apex"
}
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 0d881f4..f31c25a 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_different_package_name.apex"
}
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 18032c2..7216913 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_no_hashtree.apex"
}
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 f9d23f7..bf59f43 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_sdk_target_p.apex"
}
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..10bc505
--- /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,12 @@
+drops {
+ android_build_drop {
+ build_id: "7351002"
+ 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"
+}
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 f3638c65..5b04c31 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_signed_bob.apex"
}
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 b9a1e22..e32d6c8 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_signed_bob_rot.apex"
}
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 9d69dff..9dde059 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex"
}
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 a056b2f..a393cdb 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_unsigned_payload.apex"
}
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 2d181f0..a7811ac 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_with_post_install_hook.apex"
}
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 b0bc1f9..71c1b15 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_with_pre_install_hook.apex"
}
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 48fac6c..aaad0cf 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_without_apk_in_apex.apex"
}
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 457f945..4408eee 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_wrong_sha.apex"
}
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 58c8864..a650f82 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v3.apex"
}
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 c73132c..fecd548 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v3_signed_bob.apex"
}
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 e295e80..d3a2473e 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/com.android.apex.cts.shim.v3_signed_bob_rot.apex"
}
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 f5f8566..d69ae9e 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimTargetPSdk.apk"
}
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 f22f680..ad924e3 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: "7197701"
+ build_id: "7351002"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimTargetPSdk.apk"
}
diff --git a/apps/CameraITS/tests/scene1_1/test_ae_af.py b/apps/CameraITS/tests/scene1_1/test_ae_af.py
index c383200..09a1470 100644
--- a/apps/CameraITS/tests/scene1_1/test_ae_af.py
+++ b/apps/CameraITS/tests/scene1_1/test_ae_af.py
@@ -68,13 +68,15 @@
mono_camera=mono_camera)
except error_util.CameraItsError:
- logging.error('%s did not converge.', k)
+ raise AssertionError(f'{k} did not converge.')
logging.debug('AWB gains: %s, xform: %s', str(awb_gains),
str(awb_xform))
if three_a_req[0]: # can report None for AF only
- assert e, 'No valid exposure time returned even though do_ae.'
- assert s, 'No valid sensitivity returned even though do_ae.'
+ if not e:
+ raise AssertionError('No valid exposure time returned for AE.')
+ if not s:
+ raise AssertionError('No valid sensitivity returned for AE.')
logging.debug('AE sensitivity: %d, exposure: %dns', s, e)
else:
logging.debug('AE sensitivity: None, exposure: None')
@@ -83,22 +85,32 @@
else:
logging.debug('AF fd: None')
# check AWB values
- assert len(awb_gains) == AWB_GAINS_LENGTH
+ if len(awb_gains) != AWB_GAINS_LENGTH:
+ raise AssertionError(f'Problem with AWB gains: {awb_gains}')
for g in awb_gains:
- assert not np.isnan(g)
- assert len(awb_xform) == AWB_XFORM_LENGTH
+ if np.isnan(g):
+ raise AssertionError('Gain in AWB is NaN.')
+ if len(awb_xform) != AWB_XFORM_LENGTH:
+ raise AssertionError(f'Problem with AWB transform: {awb_xform}')
for x in awb_xform:
- assert not np.isnan(x)
- assert np.isclose(awb_gains[G_CHANNEL], G_GAIN, G_GAIN_TOL)
+ if np.isnan(x):
+ raise AssertionError('Value in AWB transform is NaN.')
+ if not np.isclose(awb_gains[G_CHANNEL], G_GAIN, G_GAIN_TOL):
+ raise AssertionError(
+ f'AWB G channel mismatch. AWB(G): {awb_gains[G_CHANNEL]}, '
+ f'REF: {G_GAIN}, TOL: {G_GAIN_TOL}')
# check AE values
if k == 'full_3a' or k == 'ae':
- assert s > 0
- assert e > 0
+ if s < min(props['android.sensor.info.sensitivityRange']):
+ raise AssertionError(f'ISO is < minimum! ISO: {s}')
+ if e < min(props['android.sensor.info.exposureTimeRange']):
+ raise AssertionError(f'Exposure is < minimum! exp: {e}')
# check AF values
if k == 'full_3a' or k == 'af':
- assert fd >= 0
+ if fd < 0:
+ raise AssertionError(f'Focal distance is < 0! fd: {fd}')
if __name__ == '__main__':
test_runner.main()
diff --git a/apps/CameraITS/tests/scene1_1/test_exposure.py b/apps/CameraITS/tests/scene1_1/test_exposure.py
index 4fd34ca..17cb5d7 100644
--- a/apps/CameraITS/tests/scene1_1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1_1/test_exposure.py
@@ -90,7 +90,7 @@
pylab.figure(title)
pylab.semilogx(x, r, 'ro-', label='R')
pylab.semilogx(x, gr, 'go-', label='Gr')
- pylab.semilogx(x, gb, 'bo-', label='Gb')
+ pylab.semilogx(x, gb, 'ko-', label='Gb')
pylab.semilogx(x, b, 'bo-', label='B')
pylab.title(NAME + title)
pylab.xlabel('Gain Multiplier')
@@ -123,18 +123,20 @@
max_diff = max_val - min_val
logging.debug('Channel %d line fit (y = mx+b): m = %f, b = %f', chan, m, b)
logging.debug('Channel min %f max %f diff %f', min_val, max_val, max_diff)
- e_msg = 'max_diff: %.4f, THRESH: %.3f' % (max_diff, thresh_max_level_diff)
- assert max_diff < thresh_max_level_diff, e_msg
- e_msg = 'b: %.2f, THRESH_MIN: %.1f, THRESH_MAX: %.1f' % (
- b, THRESH_MIN_LEVEL, THRESH_MAX_LEVEL)
- assert THRESH_MAX_LEVEL > b > THRESH_MIN_LEVEL, e_msg
+ if max_diff >= thresh_max_level_diff:
+ raise AssertionError(f'max_diff: {max_diff:.4f}, '
+ f'THRESH: {thresh_max_level_diff:.3f}')
+ if not THRESH_MAX_LEVEL > b > THRESH_MIN_LEVEL:
+ raise AssertionError(f'b: {b:.2f}, THRESH_MIN: {THRESH_MIN_LEVEL}, '
+ f'THRESH_MAX: {THRESH_MAX_LEVEL}')
for v in values:
- e_msg = 'v: %.2f, THRESH_MIN: %.1f, THRESH_MAX: %.1f' % (
- v, THRESH_MIN_LEVEL, THRESH_MAX_LEVEL)
- assert THRESH_MAX_LEVEL > v > THRESH_MIN_LEVEL, e_msg
- e_msg = 'v: %.2f, b: %.2f, THRESH_MAX_OUTLIER_DIFF: %.1f' % (
- v, b, THRESH_MAX_OUTLIER_DIFF)
- assert abs(v - b) < THRESH_MAX_OUTLIER_DIFF, e_msg
+ if not THRESH_MAX_LEVEL > v > THRESH_MIN_LEVEL:
+ raise AssertionError(f'v: {v:.2f}, THRESH_MIN: {THRESH_MIN_LEVEL}, '
+ f'THRESH_MAX: {THRESH_MAX_LEVEL}')
+
+ if abs(v - b) >= THRESH_MAX_OUTLIER_DIFF:
+ raise AssertionError(f'v: {v:.2f}, b: {b:.2f}, '
+ f'THRESH_DIFF: {THRESH_MAX_OUTLIER_DIFF}')
def get_raw_active_array_size(props):
@@ -216,12 +218,13 @@
THRESH_ROUND_DOWN_EXP +
(THRESH_ROUND_DOWN_EXP0 - THRESH_ROUND_DOWN_EXP) *
(THRESH_EXP_KNEE - e_test) / THRESH_EXP_KNEE)
- s_msg = 's_write: %d, s_read: %d, TOL=%.f%%' % (
- s_test, s_res, THRESH_ROUND_DOWN_GAIN*100)
- assert 0 <= s_test - s_res < s_test * THRESH_ROUND_DOWN_GAIN, s_msg
- e_msg = 'e_write: %.3fms, e_read: %.3fms, TOL=%.f%%' % (
- e_test/1.0E6, e_res/1.0E6, thresh_round_down_exp*100)
- assert 0 <= e_test - e_res < e_test * thresh_round_down_exp, e_msg
+ if not 0 <= s_test - s_res < s_test * THRESH_ROUND_DOWN_GAIN:
+ raise AssertionError(f's_write: {s_test}, s_read: {s_res}, '
+ f'TOL={THRESH_ROUND_DOWN_GAIN*100:.f%%}')
+ if not 0 <= e_test - e_res < e_test * thresh_round_down_exp:
+ raise AssertionError(f'e_write: {e_test/1.0E6:.3f}ms, '
+ f'e_read: {e_res/1.0E6:.3f}ms, '
+ f'TOL={thresh_round_down_exp*100:.f%%}')
s_e_product_res = s_res * e_res
req_res_ratio = s_e_product / s_e_product_res
logging.debug('Capture result s: %d, e: %dns', s_res, e_res)
@@ -259,11 +262,6 @@
thresh_max_level_diff = THRESH_MAX_LEVEL_DIFF_WIDE_RANGE
# Draw plots and check data
- plot_rgb_means('RGB data', mults, r_means, g_means, b_means, self.log_path)
- for ch, _ in enumerate(['r', 'g', 'b']):
- values = [r_means, g_means, b_means][ch]
- check_line_fit(ch, mults, values, thresh_max_level_diff)
-
if raw_avlb and debug:
plot_raw_means('RAW data', mults, raw_r_means, raw_gr_means, raw_gb_means,
raw_b_means, self.log_path)
@@ -271,5 +269,10 @@
values = [raw_r_means, raw_gr_means, raw_gb_means, raw_b_means][ch]
check_line_fit(ch, mults, values, thresh_max_level_diff)
+ plot_rgb_means('RGB data', mults, r_means, g_means, b_means, self.log_path)
+ for ch, _ in enumerate(['r', 'g', 'b']):
+ values = [r_means, g_means, b_means][ch]
+ check_line_fit(ch, mults, values, thresh_max_level_diff)
+
if __name__ == '__main__':
test_runner.main()
diff --git a/apps/CameraITS/tests/scene1_2/test_param_sensitivity.py b/apps/CameraITS/tests/scene1_2/test_param_sensitivity.py
index 2c0446b..d27e527 100644
--- a/apps/CameraITS/tests/scene1_2/test_param_sensitivity.py
+++ b/apps/CameraITS/tests/scene1_2/test_param_sensitivity.py
@@ -53,6 +53,7 @@
props = cam.get_camera_properties()
props = cam.override_with_hidden_physical_camera_props(props)
log_path = self.log_path
+ test_name_with_path = os.path.join(log_path, NAME)
# check SKIP conditions
camera_properties_utils.skip_unless(
@@ -74,7 +75,7 @@
sens_range = props['android.sensor.info.sensitivityRange']
sens_step = (sens_range[1] - sens_range[0]) / float(NUM_STEPS-1)
sensitivities = [
- sens_range[0] + i * sens_step for i in range(NUM_STEPS)]
+ int(sens_range[0] + i * sens_step) for i in range(NUM_STEPS)]
for s in sensitivities:
logging.debug('Capturing with sensitivity: %d', s)
@@ -82,8 +83,8 @@
cap = its_session_utils.do_capture_with_latency(
cam, req, sync_latency, fmt)
img = image_processing_utils.convert_capture_to_rgb_image(cap)
- image_processing_utils.write_image(img, '%s_iso=%04d.jpg' % (
- os.path.join(log_path, NAME), s))
+ image_processing_utils.write_image(
+ img, f'{test_name_with_path}_iso={s}.jpg')
patch = image_processing_utils.get_image_patch(
img, PATCH_X, PATCH_Y, PATCH_W, PATCH_H)
rgb_means = image_processing_utils.compute_image_means(patch)
@@ -104,15 +105,14 @@
pylab.title(NAME)
pylab.xlabel('Gain (ISO)')
pylab.ylabel('RGB means')
- matplotlib.pyplot.savefig(
- '%s_plot_means.png' % os.path.join(log_path, NAME))
+ matplotlib.pyplot.savefig(f'{test_name_with_path}_plot_means.png')
# Test for pass/fail: check that each shot is brighter than previous
for i, means in enumerate([r_means, g_means, b_means]):
for j in range(len(means)-1):
- e_msg = '%s cap %d mean[j+1]: %.3f, means[j]: %3.f' % (
- COLORS[i], j, means[j+1], means[j])
- assert means[j+1] > means[j], e_msg
+ if means[j+1] <= means[j]:
+ raise AssertionError(f'{COLORS[i]} cap {j} means[j+1]: '
+ f'{means[j+1]:.3f}, means[j]: {means[j]:.3f}')
if __name__ == '__main__':
test_runner.main()
diff --git a/apps/CameraITS/tests/scene1_2/test_param_tonemap_mode.py b/apps/CameraITS/tests/scene1_2/test_param_tonemap_mode.py
index 93147f1..257ff3e 100644
--- a/apps/CameraITS/tests/scene1_2/test_param_tonemap_mode.py
+++ b/apps/CameraITS/tests/scene1_2/test_param_tonemap_mode.py
@@ -116,10 +116,12 @@
cap = cam.do_capture(req, fmt)
img_name = '%s_n=%d.jpg' % (os.path.join(log_path, _NAME), n)
means_1.append(compute_means_and_save(cap, img_name))
+ if 0.0 in means_1[1]:
+ raise AssertionError(f'0.0 value in test 1 means: {means_1[0]}')
rgb_ratios = [means_1[1][i]/means_1[0][i] for i in range(_NUM_COLORS)]
logging.debug('Test 1: RGB ratios: %s', str(rgb_ratios))
- # assert proper behavior
+ # Assert proper behavior
for i in range(_NUM_COLORS-1):
if rgb_ratios[i+1]-rgb_ratios[i] < _MIN_RGB_RATIO_DIFF:
raise AssertionError(
diff --git a/apps/CameraITS/tests/scene1_2/test_post_raw_sensitivity_boost.py b/apps/CameraITS/tests/scene1_2/test_post_raw_sensitivity_boost.py
index 40bc979..1847d31 100644
--- a/apps/CameraITS/tests/scene1_2/test_post_raw_sensitivity_boost.py
+++ b/apps/CameraITS/tests/scene1_2/test_post_raw_sensitivity_boost.py
@@ -192,7 +192,7 @@
if not np.isclose(ratio_per_step, expected_ratio, atol=_RATIO_TOL):
raise AssertionError(
f'step: {step}, ratio: {ratio_per_step}, expected ratio: '
- f'{expected_ratio}.3f, ATOL: {_RATIO_TOL}')
+ f'{expected_ratio:.3f}, ATOL: {_RATIO_TOL}')
# YUV asserts
for ch, _ in enumerate(_COLORS):
diff --git a/apps/CameraITS/tests/scene1_2/test_raw_exposure.py b/apps/CameraITS/tests/scene1_2/test_raw_exposure.py
index f00e661..9b4afe0 100644
--- a/apps/CameraITS/tests/scene1_2/test_raw_exposure.py
+++ b/apps/CameraITS/tests/scene1_2/test_raw_exposure.py
@@ -131,9 +131,10 @@
for ch, color in enumerate(COLORS):
e_msg = 'ISO=%d, %s, exp %3fms mean: %.2f, %s mean: %.2f, TOL=%.f%%' % (
sens, color, exps[i-1], mean[ch],
- 'black level' if i == 1 else 'exp_time %3fms'%exps[i-2],
+ 'black level' if i == 1 else 'exp_time %.3fms'%exps[i-2],
prev_mean[ch], IMG_DELTA_THRESH*100)
- assert mean[ch] > prev_mean[ch] * IMG_DELTA_THRESH, e_msg
+ if mean[ch] <= prev_mean[ch] * IMG_DELTA_THRESH:
+ raise AssertionError(e_msg)
class RawExposureTest(its_base_test.ItsBaseTest):
diff --git a/apps/CameraITS/tests/scene1_2/test_raw_sensitivity.py b/apps/CameraITS/tests/scene1_2/test_raw_sensitivity.py
index 0a57945..2a67ade 100644
--- a/apps/CameraITS/tests/scene1_2/test_raw_sensitivity.py
+++ b/apps/CameraITS/tests/scene1_2/test_raw_sensitivity.py
@@ -36,12 +36,11 @@
def define_raw_stats_fmt(props):
"""Define format with active array width and height."""
- aax = props['android.sensor.info.preCorrectionActiveArraySize']['left']
- aay = props['android.sensor.info.preCorrectionActiveArraySize']['top']
- aaw = props['android.sensor.info.preCorrectionActiveArraySize']['right'] - aax
- aah = props[
- 'android.sensor.info.preCorrectionActiveArraySize']['bottom'] - aay
-
+ aaw = (props['android.sensor.info.preCorrectionActiveArraySize']['right'] -
+ props['android.sensor.info.preCorrectionActiveArraySize']['left'])
+ aah = (props['android.sensor.info.preCorrectionActiveArraySize']['bottom'] -
+ props['android.sensor.info.preCorrectionActiveArraySize']['top'])
+ logging.debug('Active array W,H: %d,%d', aaw, aah)
return {'format': 'rawStats',
'gridWidth': aaw // IMG_STATS_GRID,
'gridHeight': aah // IMG_STATS_GRID}
@@ -64,7 +63,7 @@
camera_properties_utils.read_3a(props) and
camera_properties_utils.per_frame_control(props) and
not camera_properties_utils.mono_camera(props))
- log_path = self.log_path
+ name_with_log_path = os.path.join(self.log_path, NAME)
camera_fov = float(cam.calc_camera_fov(props))
# Load chart for scene
@@ -95,6 +94,12 @@
fmt = define_raw_stats_fmt(props)
cap = cam.do_capture(req, fmt)
+ if self.debug_mode:
+ img = image_processing_utils.convert_capture_to_rgb_image(
+ cap, props=props)
+ image_processing_utils.write_image(
+ img, f'{name_with_log_path}_{s}_{e}ns.jpg', True)
+
# Measure variance
_, var_image = image_processing_utils.unpack_rawstats_capture(cap)
cfa_idxs = image_processing_utils.get_canonical_cfa_order(props)
@@ -112,14 +117,13 @@
pylab.ylabel('Image Center Patch Variance')
pylab.ticklabel_format(axis='y', style='sci', scilimits=(-6, -6))
pylab.title(NAME)
- matplotlib.pyplot.savefig(
- '%s_variances.png' % os.path.join(log_path, NAME))
+ matplotlib.pyplot.savefig(f'{name_with_log_path}_variances.png')
# Test that each shot is noisier than previous
for i in range(len(variances) - 1):
- e_msg = 'variances [i]: %.5f, [i+1]: %.5f, THRESH: %.2f' % (
- variances[i], variances[i+1], VAR_THRESH)
- assert variances[i] < variances[i+1]/VAR_THRESH, e_msg
+ if variances[i] >= variances[i+1]/VAR_THRESH:
+ raise AssertionError(f'variances [i]: {variances[i]:5f}, [i+1]: '
+ f'{variances[i+1]:.5f}, THRESH: {VAR_THRESH}')
if __name__ == '__main__':
test_runner.main()
diff --git a/apps/CameraITS/tests/scene2_a/test_effects.py b/apps/CameraITS/tests/scene2_a/test_effects.py
index b437194..367abea 100644
--- a/apps/CameraITS/tests/scene2_a/test_effects.py
+++ b/apps/CameraITS/tests/scene2_a/test_effects.py
@@ -115,10 +115,10 @@
(v_max - v_min) > MONO_UV_SPREAD_MAX):
failed.append({'effect': EFFECTS[effect], 'error': e_msg})
if failed:
- logging.error('Failed effects:')
+ logging.debug('Failed effects:')
for fail in failed:
- logging.error(' %s: %s', fail['effect'], fail['error'])
- assert not failed
+ logging.debug(' %s: %s', fail['effect'], fail['error'])
+ raise AssertionError(f'{NAME} failed. See test_log.DEBUG for errors.')
if __name__ == '__main__':
test_runner.main()
diff --git a/apps/CameraITS/tests/scene2_a/test_jpeg_quality.py b/apps/CameraITS/tests/scene2_a/test_jpeg_quality.py
index c4d91b4..6073995 100644
--- a/apps/CameraITS/tests/scene2_a/test_jpeg_quality.py
+++ b/apps/CameraITS/tests/scene2_a/test_jpeg_quality.py
@@ -146,7 +146,8 @@
if dqt_size % 2 == 0: # even payload means luma & chroma
logging.debug(' both luma & chroma DQT matrices in marker')
dqt_size = (dqt_size - 2) // 2 # subtact off luma/chroma markers
- assert is_square(dqt_size), 'DQT size: %d' % dqt_size
+ if is_square(dqt_size):
+ raise AssertionError(f'DQT size: {dqt_size}')
luma_start = dqt + 5 # skip header, length, & matrix id
chroma_start = luma_start + dqt_size + 1 # skip lumen & matrix_id
luma = np.array(jpeg[luma_start: luma_start + dqt_size])
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..6e9ee5d
--- /dev/null
+++ b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
@@ -0,0 +1,65 @@
+# 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 logging
+
+from mobly import test_runner
+
+import camera_properties_utils
+import its_base_test
+import its_session_utils
+
+CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 500 # ms
+
+
+class CameraLaunchSPerfClassTest(its_base_test.ItsBaseTest):
+ """Test camera launch latency for S performance class as specified in CDD.
+
+ [7.5/H-1-7] MUST have camera2 startup latency (open camera to first preview
+ frame) < 500ms as measured by the CTS camera PerformanceTest under ITS
+ lighting conditions (3000K) for both primary cameras.
+ """
+
+ def test_camera_launch(self):
+ # Open camera with "with" semantics to check skip condition and load chart
+ #
+ with its_session_utils.ItsSession(
+ device_id=self.dut.serial,
+ camera_id=self.camera_id) as cam:
+
+ camera_properties_utils.skip_unless(
+ cam.is_s_performance_class_primary_camera())
+
+ # Load chart for scene.
+ props = cam.get_camera_properties()
+ its_session_utils.load_scene(
+ cam, props, self.scene, self.tablet, self.chart_distance)
+
+ # Create an its session without opening the camera to test camera launch
+ # latency
+ cam = its_session_utils.ItsSession(
+ device_id=self.dut.serial,
+ camera_id=self.camera_id)
+
+ launch_ms = cam.measure_camera_launch_ms()
+ if launch_ms >= CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD:
+ raise AssertionError(f'camera launch time: {launch_ms} ms, THRESH: '
+ f'{CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD} ms')
+ else:
+ logging.debug('camera launch time: %.1f ms', launch_ms)
+
+if __name__ == '__main__':
+ test_runner.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..2fe7175
--- /dev/null
+++ b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
@@ -0,0 +1,66 @@
+# 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 logging
+
+from mobly import test_runner
+
+import camera_properties_utils
+import its_base_test
+import its_session_utils
+
+JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD = 1000 # ms
+
+
+class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest):
+ """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.
+ """
+
+ def test_jpeg_capture(self):
+ # Open camera with "with" semantics to check skip condition and load chart
+ #
+ with its_session_utils.ItsSession(
+ device_id=self.dut.serial,
+ camera_id=self.camera_id) as cam:
+
+ camera_properties_utils.skip_unless(
+ cam.is_s_performance_class_primary_camera())
+
+ # Load chart for scene.
+ props = cam.get_camera_properties()
+ its_session_utils.load_scene(
+ cam, props, self.scene, self.tablet, self.chart_distance)
+
+ # Create an Its session without opening the camera to test camera jpeg
+ # capture latency because the test opens camera internally
+ cam = its_session_utils.ItsSession(
+ device_id=self.dut.serial,
+ camera_id=self.camera_id)
+
+ jpeg_capture_ms = cam.measure_camera_1080p_jpeg_capture_ms()
+ if jpeg_capture_ms >= JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD:
+ raise AssertionError(f'1080p jpeg capture time: {jpeg_capture_ms} ms, '
+ f'THRESH: '
+ f'{JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD} ms')
+ else:
+ logging.debug('1080p jpeg capture time: %.1f ms', jpeg_capture_ms)
+
+if __name__ == '__main__':
+ test_runner.main()
diff --git a/apps/CameraITS/tests/scene6/test_zoom.py b/apps/CameraITS/tests/scene6/test_zoom.py
index 54fcc4a..777bec6 100644
--- a/apps/CameraITS/tests/scene6/test_zoom.py
+++ b/apps/CameraITS/tests/scene6/test_zoom.py
@@ -111,6 +111,10 @@
if colour == color and (1 - CIRCLE_TOL <= circlish <= 1 + CIRCLE_TOL):
circles.append([shape['ctx'], shape['cty'], radius, circlish, area])
+ if not circles:
+ raise AssertionError('No circle was detected. Please take pictures '
+ 'according to instructions carefully!')
+
if debug:
logging.debug('circles [x, y, r, pi*r**2/area, area]: %s', str(circles))
@@ -131,10 +135,6 @@
cv2.circle(img, center_i, radius_i, LINE_COLOR, LINE_THICKNESS)
image_processing_utils.write_image(img / 255.0, img_name)
- if not circles:
- raise AssertionError('No circle was detected. Please take pictures '
- 'according to instructions carefully!')
-
return [circle[0], circle[1], circle[2]]
diff --git a/apps/CameraITS/tools/dng_noise_model.py b/apps/CameraITS/tools/dng_noise_model.py
index 056dac0..a70ef8f 100644
--- a/apps/CameraITS/tools/dng_noise_model.py
+++ b/apps/CameraITS/tools/dng_noise_model.py
@@ -37,9 +37,10 @@
_MAX_SIGNAL_VALUE = 0.25 # Maximum value to allow mean of the tiles to go.
_NAME = os.path.basename(__file__).split('.')[0]
_RTOL_EXP_GAIN = 0.97
-_STEPS_PER_STOP = 2 # How many sensitivities per stop to sample.
+_STEPS_PER_STOP = 3 # How many sensitivities per stop to sample.
_TILE_SIZE = 32 # Tile size to compute mean/variance. Large tiles may have
# their variance corrupted by low freq image changes.
+_TILE_CROP_N = 0 # Number of tiles to crop from edge of image. Usually 0.
def check_auto_exposure_targets(auto_e, sens_min, sens_max, props):
@@ -159,8 +160,10 @@
plots = []
measured_models = [[], [], [], []]
color_plane_plots = {}
+ isos = []
while int(round(iso)) <= sens_max_meas:
iso_int = int(round(iso))
+ isos.append(iso_int)
logging.info('ISO %d', iso_int)
fig, [[plt_r, plt_gr], [plt_gb, plt_b]] = plt.subplots(
2, 2, figsize=(11, 11))
@@ -192,8 +195,20 @@
mean_img, var_img = image_processing_utils.unpack_rawstats_capture(
cap)
idxs = image_processing_utils.get_canonical_cfa_order(props)
- means = [mean_img[:, :, i] for i in idxs]
- vars_ = [var_img[:, :, i] for i in idxs]
+ raw_stats_size = mean_img.shape
+ means = [mean_img[_TILE_CROP_N:raw_stats_size[0]-_TILE_CROP_N,
+ _TILE_CROP_N:raw_stats_size[1]-_TILE_CROP_N, i]
+ for i in idxs]
+ vars_ = [var_img[_TILE_CROP_N:raw_stats_size[0]-_TILE_CROP_N,
+ _TILE_CROP_N:raw_stats_size[1]-_TILE_CROP_N, i]
+ for i in idxs]
+ if self.debug_mode:
+ logging.info('rawStats image size: %s', str(raw_stats_size))
+ logging.info('R planes means image size: %s', str(means[0].shape))
+ logging.info('means min: %.3f, median: %.3f, max: %.3f',
+ np.min(means), np.median(means), np.max(means))
+ logging.info('vars_ min: %.4f, median: %.4f, max: %.4f',
+ np.min(vars_), np.median(vars_), np.max(vars_))
s_read = cap['metadata']['android.sensor.sensitivity']
if not 1.0 >= s_read/float(iso_int) >= _RTOL_EXP_GAIN:
@@ -310,13 +325,18 @@
plt_slope.loglog(sens, slp_measured, 'rgkb'[pidx]+'+', base=10,
label='Measured')
- plt_slope.loglog(sens, slp_model, 'rgkb'[pidx]+'x', base=10,
- label='Model')
+ plt_slope.loglog(sens, slp_model, 'rgkb'[pidx]+'o', base=10,
+ label='Model', alpha=0.3)
plt_intercept.loglog(sens, int_measured, 'rgkb'[pidx]+'+', base=10,
label='Measured')
- plt_intercept.loglog(sens, int_model, 'rgkb'[pidx]+'x', base=10,
- label='Model')
+ plt_intercept.loglog(sens, int_model, 'rgkb'[pidx]+'o', base=10,
+ label='Model', alpha=0.3)
plt_slope.legend()
+ plt_slope.set_xticks(isos)
+ plt_slope.set_xticklabels(isos)
+
+ plt_intercept.set_xticks(isos)
+ plt_intercept.set_xticklabels(isos)
plt_intercept.legend()
fig.savefig(f'{name_with_log_path}.png')
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index ff14f3f..2095f0f 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -52,6 +52,7 @@
TIME_KEY_START = 'start'
TIME_KEY_END = 'end'
VALID_CONTROLLERS = ('arduino', 'canakit')
+_INT_STR_DICT = {'11': '1_1', '12': '1_2'} # recover replaced '_' in scene def
# All possible scenes
# Notes on scene names:
@@ -127,6 +128,7 @@
SUB_CAMERA_TESTS = {
'scene0': [
'test_burst_capture',
+ 'test_jitter',
'test_metadata',
'test_read_write',
'test_sensor_events',
@@ -134,13 +136,15 @@
'test_unified_timestamps',
],
'scene1_1': [
- 'test_exposure',
+ 'test_burst_sameness_manual',
'test_dng_noise_model',
+ 'test_exposure',
'test_linearity',
],
'scene1_2': [
'test_raw_exposure',
'test_raw_sensitivity',
+ 'test_yuv_plus_raw',
],
'scene2_a': [
'test_faces',
@@ -392,6 +396,7 @@
camera_id_combos = str(test_params_content['camera']).split(',')
if not scenes:
scenes = str(test_params_content['scene']).split(',')
+ scenes = [_INT_STR_DICT.get(n, n) for n in scenes] # recover '1_1' & '1_2'
device_id = get_device_serial_number('dut', config_file_contents)
# Enable external storage on DUT to send summary report to CtsVerifier.apk
diff --git a/apps/CameraITS/utils/its_session_utils.py b/apps/CameraITS/utils/its_session_utils.py
index 92c29df..1155fc9 100644
--- a/apps/CameraITS/utils/its_session_utils.py
+++ b/apps/CameraITS/utils/its_session_utils.py
@@ -248,12 +248,12 @@
self._device_id = device_id
self._hidden_physical_id = hidden_physical_id
- def __enter__(self):
# Initialize device id and adb command.
self.adb = 'adb -s ' + self._device_id
self.__wait_for_service()
self.__init_socket_port()
+ def __enter__(self):
self.__close_camera()
self.__open_camera()
return self
@@ -1075,6 +1075,59 @@
' support')
return data['strValue'] == 'true'
+ def is_s_performance_class_primary_camera(self):
+ """Query whether the camera device is a 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).encode() + '\n'.encode())
+
+ 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).encode() + '\n'.encode())
+
+ 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).encode() + '\n'.encode())
+
+ 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 parse_camera_ids(ids):
"""Parse the string of camera IDs into array of CameraIdCombo tuples.
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 614241f..1d7e8d6 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -46,6 +46,7 @@
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.NFC_TRANSACTION_EVENT" />
<uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_PASSWORD_COMPLEXITY" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
@@ -183,6 +184,20 @@
android:value="multi_display_mode" />
</activity>
+ <activity
+ android:name=".battery.IgnoreBatteryOptimizationsTestActivity"
+ android:label="@string/ibo_test"
+ android:exported="true"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_other" />
+ <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive" />
+ <meta-data android:name="display_mode" android:value="multi_display_mode" />
+ </activity>
+
<activity android:name=".forcestop.RecentTaskRemovalTestActivity"
android:label="@string/remove_from_recents_test"
android:exported="true"
@@ -2999,7 +3014,7 @@
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
<meta-data android:name="android.service.notification.default_filter_types"
- android:value="alerting,silent" />
+ android:value="alerting|silent" />
<meta-data android:name="android.service.notification.disabled_filter_types"
android:value="ongoing" />
</service>
diff --git a/apps/CtsVerifier/res/layout-watch/display_cutout.xml b/apps/CtsVerifier/res/layout-watch/display_cutout.xml
index da8314c..b1cfa01 100644
--- a/apps/CtsVerifier/res/layout-watch/display_cutout.xml
+++ b/apps/CtsVerifier/res/layout-watch/display_cutout.xml
@@ -18,17 +18,18 @@
<RelativeLayout
android:layout_width="match_parent"
- android:layout_height="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_centerInParent="true"
+ android:layout_below="@id/top_buttons"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:text="@string/display_cutout_test_instruction"
- android:textSize="20dp" />
+ android:textSize="12dp" />
<include
android:id="@+id/pass_fail_buttons"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index acfce50..545df9d 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -872,6 +872,27 @@
<string name="hifi_ultrasound_speaker_test_reference_side">
Please report on the testing device.\n</string>
+ <!-- Strings for IgnoreBatteryOptimizationsTestActivity -->
+ <string name="ibo_test">Ignore Battery Optimizations Test</string>
+ <string name="ibo_test_info">
+ This test verifies that the device provides a user affordance to ask the user if the system
+ should disable battery optimizations for an app.
+ </string>
+ <string name="ibo_test_start_unexempt_app">
+ Remove the test app from the ignore battery optimizations list to begin the test. (Try going
+ to the App Info page and make sure the system is optimizing battery for the app.)
+ </string>
+ <string name="ibo_exempt_app">
+ Click Next to have the test request the exemption. Grant the exemption when prompted.
+ </string>
+ <string name="ibo_next_to_confirm">Press next to confirm.</string>
+ <string name="ibo_app_not_exempted">The app is not exempted from battery optimizations.</string>
+ <string name="ibo_unexempt_app">
+ Remove the test app from the ignore battery optimizations list. (Try going
+ to the App Info page and make sure the system is optimizing battery for the app.)
+ </string>
+ <string name="ibo_app_is_exempted">The app is exempted from battery optimizations.</string>
+
<!-- Strings for Location tests -->
<string name="location_gps_test">GPS Test</string>
<string name="location_gps_test_info">This test verifies basic GPS behavior
@@ -3496,8 +3517,9 @@
<string name="device_owner_wifi_lockdown_info">
Please enter the SSID and auth method of an available WiFi Access Point and press the button to create a
WiFi configuration. This configuration must NOT EXIST yet (you can use Settings > WiFi to verify - if it exists,
- then select the option to forget it). The test cases
- are going to use this config. Please go through test cases in order (from top to bottom).
+ then select the option to forget it). If the Wifi requires a password, wait for the \'Could not connect to the wifi network\'
+ notification, tap the notification, set the password and connect to the network.
+ The test cases are going to use this config. Please go through test cases in order (from top to bottom).
</string>
<string name="switch_wifi_lockdown_off_button">WiFi config lockdown off</string>
<string name="switch_wifi_lockdown_on_button">WiFi config lockdown on</string>
@@ -5804,11 +5826,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>
<!-- TTS Test Resources -->
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..3d2639b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
@@ -1101,7 +1101,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/battery/IgnoreBatteryOptimizationsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/battery/IgnoreBatteryOptimizationsTestActivity.java
new file mode 100644
index 0000000..5f41fa7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/battery/IgnoreBatteryOptimizationsTestActivity.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.battery;
+
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.view.View;
+
+import com.android.cts.verifier.OrderedTestActivity;
+import com.android.cts.verifier.R;
+
+/** Test activity to check fulfillment of the ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS intent. */
+public class IgnoreBatteryOptimizationsTestActivity extends OrderedTestActivity {
+ private PowerManager mPowerManager;
+ private UsageStatsManager mUsageStatsManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setInfoResources(R.string.ibo_test, R.string.ibo_test_info, -1);
+
+ mPowerManager = getSystemService(PowerManager.class);
+ mUsageStatsManager = getSystemService(UsageStatsManager.class);
+ }
+
+ @Override
+ protected Test[] getTests() {
+ return new Test[]{
+ mConfirmNotExemptedAtStart,
+ mRequestExemption,
+ mIntermediate,
+ mConfirmIsExempted,
+ mRemoveExemption,
+ mIntermediate,
+ mConfirmIsNotExempted
+ };
+ }
+
+ private boolean isExempted() {
+ return mUsageStatsManager.getAppStandbyBucket() == UsageStatsManager.STANDBY_BUCKET_EXEMPTED
+ && mPowerManager.isIgnoringBatteryOptimizations(getPackageName());
+ }
+
+ private final Test mConfirmNotExemptedAtStart = new Test(R.string.ibo_test_start_unexempt_app) {
+ @Override
+ protected void run() {
+ super.run();
+
+ if (!isExempted()) {
+ succeed();
+ }
+ }
+
+ @Override
+ protected void onNextClick() {
+ if (isExempted()) {
+ Intent appInfoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ appInfoIntent.setData(Uri.parse("package:" + getPackageName()));
+ startActivity(appInfoIntent);
+ } else {
+ succeed();
+ }
+ }
+ };
+
+ private final Test mRequestExemption = new Test(R.string.ibo_exempt_app) {
+ @Override
+ protected void onNextClick() {
+ Intent request = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ request.setData(Uri.parse("package:" + getPackageName()));
+ startActivity(request);
+ succeed();
+ }
+ };
+
+ private final Test mIntermediate = new Test(R.string.ibo_next_to_confirm) {
+ @Override
+ protected void onNextClick() {
+ succeed();
+ }
+ };
+
+ private final Test mConfirmIsExempted = new Test(R.string.ibo_app_not_exempted) {
+ @Override
+ protected void run() {
+ super.run();
+
+ if (isExempted()) {
+ succeed();
+ }
+ }
+ };
+
+ private final Test mRemoveExemption = new Test(R.string.ibo_unexempt_app) {
+ @Override
+ protected void run() {
+ super.run();
+
+ if (!isExempted()) {
+ succeed();
+ }
+ }
+
+ @Override
+ protected void onNextClick() {
+ if (isExempted()) {
+ Intent appInfoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ appInfoIntent.setData(Uri.parse("package:" + getPackageName()));
+ startActivity(appInfoIntent);
+ } else {
+ succeed();
+ }
+ }
+ };
+
+ private final Test mConfirmIsNotExempted = new Test(R.string.ibo_app_is_exempted) {
+ @Override
+ protected void run() {
+ super.run();
+
+ if (!isExempted()) {
+ succeed();
+ } else {
+ findViewById(R.id.btn_next).setVisibility(View.GONE);
+ }
+ }
+ };
+}
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 e7e9c3f..10775fe 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
@@ -36,6 +36,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;
@@ -50,6 +51,8 @@
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;
@@ -63,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;
@@ -137,6 +148,9 @@
// 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;
+
public static final int SERVERPORT = 6000;
public static final String REGION_KEY = "regions";
@@ -234,6 +248,11 @@
private SensorPrivacyManager mSensorPrivacyManager;
+ // 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;
@@ -617,7 +636,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);
@@ -716,6 +735,15 @@
doCheckStreamCombination(cmdObj);
} else if ("isCameraPrivacyModeSupported".equals(cmdObj.getString("cmdName"))) {
doCheckCameraPrivacyModeSupport();
+ } 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);
}
@@ -1052,6 +1080,93 @@
hasPrivacySupport ? "true" : "false");
}
+ private void doCheckSPerformanceClassPrimaryCamera(String cameraId) throws ItsException {
+ boolean isSPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == 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/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
index 23e1de5..08cd8b2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
@@ -100,7 +100,7 @@
add("scene_change");
add("sensor_fusion");
}};
- // This must match scenes of HIDDEN_PHYSICAL_CAMERA_TESTS in run_all_tests.py
+ // This must match scenes of SUB_CAMERA_TESTS in tools/run_all_tests.py
private static final ArrayList<String> mHiddenPhysicalCameraSceneIds =
new ArrayList<String> () {{
add("scene0");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/orientation/CameraOrientationActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/orientation/CameraOrientationActivity.java
index d753d6a..4a09df8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/orientation/CameraOrientationActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/orientation/CameraOrientationActivity.java
@@ -55,6 +55,8 @@
private static final int STATE_CAPTURE = 2;
private static final int NUM_ORIENTATIONS = 4;
private static final String STAGE_INDEX_EXTRA = "stageIndex";
+ private static final int VGA_WIDTH = 640;
+ private static final int VGA_HEIGHT = 480;
private ImageButton mPassButton;
private ImageButton mFailButton;
@@ -65,7 +67,9 @@
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
private List<Camera.Size> mPreviewSizes;
- private Camera.Size mOptimalSize;
+ private List<Camera.Size> mPictureSizes;
+ private Camera.Size mOptimalPreviewSize;
+ private Camera.Size mOptimalPictureSize;
private List<Integer> mPreviewOrientations;
private int mNextPreviewOrientation;
private int mNumCameras;
@@ -187,8 +191,6 @@
Camera.Parameters p = mCamera.getParameters();
- // Get preview resolutions
- List<Camera.Size> unsortedSizes = p.getSupportedPreviewSizes();
class SizeCompare implements Comparator<Camera.Size> {
@Override
public int compare(Camera.Size lhs, Camera.Size rhs) {
@@ -201,9 +203,18 @@
}
SizeCompare s = new SizeCompare();
TreeSet<Camera.Size> sortedResolutions = new TreeSet<Camera.Size>(s);
+
+ // Get preview resolutions
+ List<Camera.Size> unsortedSizes = p.getSupportedPreviewSizes();
sortedResolutions.addAll(unsortedSizes);
mPreviewSizes = new ArrayList<Camera.Size>(sortedResolutions);
+ // Get picture resolutions
+ unsortedSizes = p.getSupportedPictureSizes();
+ sortedResolutions.clear();
+ sortedResolutions.addAll(unsortedSizes);
+ mPictureSizes = new ArrayList<Camera.Size>(sortedResolutions);
+
startPreview();
}
@@ -245,13 +256,14 @@
Camera.Parameters p = mCamera.getParameters();
Log.v(TAG, "Initializing picture format");
p.setPictureFormat(ImageFormat.JPEG);
- mOptimalSize = getOptimalPreviewSize(mPreviewSizes, 640, 480);
+ mOptimalPictureSize = getOptimalSize(mPictureSizes, VGA_WIDTH, VGA_HEIGHT);
Log.v(TAG, "Initializing picture size to "
- + mOptimalSize.width + "x" + mOptimalSize.height);
- p.setPictureSize(mOptimalSize.width, mOptimalSize.height);
+ + mOptimalPictureSize.width + "x" + mOptimalPictureSize.height);
+ p.setPictureSize(mOptimalPictureSize.width, mOptimalPictureSize.height);
+ mOptimalPreviewSize = getOptimalSize(mPreviewSizes, VGA_WIDTH, VGA_HEIGHT);
Log.v(TAG, "Initializing preview size to "
- + mOptimalSize.width + "x" + mOptimalSize.height);
- p.setPreviewSize(mOptimalSize.width, mOptimalSize.height);
+ + mOptimalPreviewSize.width + "x" + mOptimalPreviewSize.height);
+ p.setPreviewSize(mOptimalPreviewSize.width, mOptimalPreviewSize.height);
Log.v(TAG, "Setting camera parameters");
mCamera.setParameters(p);
@@ -375,10 +387,10 @@
// find a supported size with ratio less than tolerance threshold, and
// which is closest to height and width of given dimensions without
// being larger than either of given dimensions
- private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w,
+ private Camera.Size getOptimalSize(List<Camera.Size> sizes, int w,
int h) {
final double ASPECT_TOLERANCE = 0.1;
- double targetRatio = (double) 640 / (double) 480;
+ double targetRatio = (double) w / (double) h;
if (sizes == null) return null;
Camera.Size optimalSize = null;
@@ -474,13 +486,13 @@
// make preview width same as output image width,
// then calculate height using output image's height/width ratio
newWidth = viewWidth;
- newHeight = (int) (viewWidth * ((double) mOptimalSize.height /
- (double) mOptimalSize.width));
+ newHeight = (int) (viewWidth * ((double) mOptimalPreviewSize.height /
+ (double) mOptimalPreviewSize.width));
}
else {
newHeight = viewHeight;
- newWidth = (int) (viewHeight * ((double) mOptimalSize.height /
- (double) mOptimalSize.width));
+ newWidth = (int) (viewHeight * ((double) mOptimalPreviewSize.height /
+ (double) mOptimalPreviewSize.width));
}
LayoutParams layoutParams = new LayoutParams(newWidth, newHeight);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index d201b58..c6359eb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -185,7 +185,7 @@
@Override
public void finish() {
String action = getIntent().getAction();
- switch(action) {
+ switch(action != null ? action : "") {
case ACTION_CHECK_DEVICE_OWNER:
case ACTION_CHECK_PROFILE_OWNER:
case ACTION_CHECK_CURRENT_USER_AFFILIATED:
@@ -379,20 +379,22 @@
}
// setKeyguardDisabled
- adapter.add(createInteractiveTestItem(this, DISABLE_KEYGUARD_TEST_ID,
- R.string.device_owner_disable_keyguard_test,
- R.string.device_owner_disable_keyguard_test_info,
- new ButtonInfo[] {
- new ButtonInfo(
- R.string.device_owner_disable_keyguard_button,
- createDeviceOwnerIntentWithBooleanParameter(
- CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
- true)),
- new ButtonInfo(
- R.string.device_owner_reenable_keyguard_button,
- createDeviceOwnerIntentWithBooleanParameter(
- CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
- false))}));
+ if (isKeyguardShownWhenUserDoesntHaveCredentials()) {
+ adapter.add(createInteractiveTestItem(this, DISABLE_KEYGUARD_TEST_ID,
+ R.string.device_owner_disable_keyguard_test,
+ R.string.device_owner_disable_keyguard_test_info,
+ new ButtonInfo[] {
+ new ButtonInfo(
+ R.string.device_owner_disable_keyguard_button,
+ createDeviceOwnerIntentWithBooleanParameter(
+ CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
+ true)),
+ new ButtonInfo(
+ R.string.device_owner_reenable_keyguard_button,
+ createDeviceOwnerIntentWithBooleanParameter(
+ CommandReceiverActivity.COMMAND_SET_KEYGUARD_DISABLED,
+ false))}));
+ }
// setLockTaskFeatures
final Intent lockTaskUiTestIntent = new Intent(this, LockTaskUiTestActivity.class);
@@ -540,11 +542,13 @@
createDisableNetworkLoggingIntent())}));
// Customize lock screen message
- adapter.add(TestListItem.newTest(this,
- R.string.device_owner_customize_lockscreen_message,
- LockscreenMessageTestActivity.class.getName(),
- new Intent(this, LockscreenMessageTestActivity.class),
- /* requiredFeatures */ null));
+ if (isSwipeToUnlockSupported()) {
+ adapter.add(TestListItem.newTest(this,
+ R.string.device_owner_customize_lockscreen_message,
+ LockscreenMessageTestActivity.class.getName(),
+ new Intent(this, LockscreenMessageTestActivity.class),
+ /* requiredFeatures */ null));
+ }
// setUsbDataSignalingEnabled
if (canUsbDataSignalingBeDisabled()) {
@@ -658,10 +662,22 @@
}
private boolean isStatusBarEnabled() {
- // Watches don't support the status bar so this is an ok proxy, but this is not the most
- // general test for that. TODO: add a test API to do a real check for status bar support.
- return !getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH) &&
- !getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ // Watches don't support the status bar so this is an ok proxy, but this is not the most
+ // general test for that. TODO: add a test API to do a real check for status bar support.
+ return !getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
+ && !isAutomotive();
+ }
+
+ private boolean isKeyguardShownWhenUserDoesntHaveCredentials() {
+ return !isAutomotive();
+ }
+
+ private boolean isSwipeToUnlockSupported() {
+ return !isAutomotive();
+ }
+
+ private boolean isAutomotive() {
+ return getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
private boolean canUsbDataSignalingBeDisabled() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
index 82612f7..44c20b8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/LockTaskUiTestActivity.java
@@ -338,6 +338,7 @@
.setContentTitle(getString(R.string.device_owner_lock_task_ui_test))
.setSmallIcon(android.R.drawable.sym_def_app_icon)
.setOngoing(true)
+ .extend(new Notification.TvExtender())
.build();
mNotifyMgr.notify(0, note);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
index 3037d98..1b751a6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java
@@ -94,6 +94,7 @@
R.string.device_owner_requesting_bugreport_tests))
.setContentText(msg)
.setStyle(new Notification.BigTextStyle().bigText(msg))
+ .extend(new Notification.TvExtender())
.build();
mNotificationManager.notify(notificationId, notification);
}
diff --git a/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/WifiManagerWrapper.java b/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/WifiManagerWrapper.java
index 6c6fb4c..5146bdd 100644
--- a/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/WifiManagerWrapper.java
+++ b/common/device-side/bedstead/dpmwrapper/src/main/java/com/android/bedstead/dpmwrapper/WifiManagerWrapper.java
@@ -58,7 +58,7 @@
try {
doReturn(spyString).when(spy).toString();
- // Used by WifiConfigCreator (whish is used by CtsVerifier)
+ // Used by WifiConfigCreator
doAnswer(answer).when(spy).addNetwork(any());
doAnswer(answer).when(spy).enableNetwork(anyInt(), anyBoolean());
doAnswer(answer).when(spy).removeNetwork(anyInt());
@@ -67,6 +67,9 @@
doAnswer(answer).when(spy).saveConfiguration();
doAnswer(answer).when(spy).isWifiEnabled();
doAnswer(answer).when(spy).setWifiEnabled(anyBoolean());
+
+ // Used by WifiNetworkConfigurationWithoutFineLocationPermissionTest
+ doAnswer(answer).when(spy).getCallerConfiguredNetworks();
} catch (Exception e) {
// Should never happen, but needs to be catch as some methods declare checked exceptions
Log.wtf("Exception setting mocks", e);
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/BedsteadJUnit4.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/BedsteadJUnit4.java
index 07d5314..e5be4d4 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/BedsteadJUnit4.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/BedsteadJUnit4.java
@@ -18,11 +18,14 @@
import androidx.annotation.Nullable;
+import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
import com.android.bedstead.harrier.annotations.enterprise.NegativePolicyTest;
import com.android.bedstead.harrier.annotations.enterprise.PositivePolicyTest;
import com.android.bedstead.harrier.annotations.meta.ParameterizedAnnotation;
+import com.android.bedstead.harrier.annotations.meta.RepeatingAnnotation;
import com.android.bedstead.harrier.annotations.parameterized.IncludeNone;
+import com.android.bedstead.nene.exceptions.NeneException;
import com.google.common.base.Objects;
@@ -34,6 +37,7 @@
import org.junit.runners.model.TestClass;
import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -57,6 +61,7 @@
static {
sIgnoredAnnotationPackages.add("java.lang.annotation");
sIgnoredAnnotationPackages.add("com.android.bedstead.harrier.annotations.meta");
+ sIgnoredAnnotationPackages.add("kotlin.*");
}
/**
@@ -82,8 +87,9 @@
}
private void calculateAnnotations() {
- List<Annotation> annotations = new ArrayList<>(
- Arrays.asList(getMethod().getAnnotations()));
+ List<Annotation> annotations =
+ new ArrayList<>(Arrays.asList(getDeclaringClass().getAnnotations()));
+ annotations.addAll(Arrays.asList(getMethod().getAnnotations()));
parseEnterpriseAnnotations(annotations);
@@ -151,14 +157,27 @@
@Nullable Class<? extends Annotation> parameterizedAnnotation) {
List<Annotation> replacementAnnotations = new ArrayList<>();
+ if (annotation.annotationType().getAnnotation(RepeatingAnnotation.class) != null) {
+ try {
+ Annotation[] annotations =
+ (Annotation[]) annotation.annotationType()
+ .getMethod("value").invoke(annotation);
+ Collections.addAll(replacementAnnotations, annotations);
+ return replacementAnnotations;
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new NeneException("Error expanding repeated annotations", e);
+ }
+
+ }
+
if (annotation.annotationType().getAnnotation(ParameterizedAnnotation.class) != null
&& !annotation.annotationType().equals(parameterizedAnnotation)) {
return replacementAnnotations;
}
for (Annotation indirectAnnotation : annotation.annotationType().getAnnotations()) {
- if (sIgnoredAnnotationPackages.contains(
- indirectAnnotation.annotationType().getPackage().getName())) {
+ String annotationPackage = indirectAnnotation.annotationType().getPackage().getName();
+ if (shouldSkipAnnotation(annotationPackage)) {
continue;
}
@@ -171,6 +190,21 @@
return replacementAnnotations;
}
+ private static boolean shouldSkipAnnotation(String annotationPackage) {
+ for (String ignoredPackage : sIgnoredAnnotationPackages) {
+ if (ignoredPackage.endsWith(".*")) {
+ if (annotationPackage.startsWith(
+ ignoredPackage.substring(0, ignoredPackage.length() - 2))) {
+ return true;
+ }
+ } else if (annotationPackage.equals(ignoredPackage)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public BedsteadJUnit4(Class<?> testClass) throws InitializationError {
super(testClass);
}
@@ -305,6 +339,17 @@
annotations.addAll(index, replacementAnnotations);
index += replacementAnnotations.size();
+ } else if (annotation instanceof CannotSetPolicyTest) {
+ annotations.remove(index);
+ Class<?> policy = ((CannotSetPolicyTest) annotation).policy();
+
+ EnterprisePolicy enterprisePolicy =
+ policy.getAnnotation(EnterprisePolicy.class);
+ List<Annotation> replacementAnnotations =
+ Policy.cannotSetPolicyStates(enterprisePolicy);
+
+ annotations.addAll(index, replacementAnnotations);
+ index += replacementAnnotations.size();
} else {
index++;
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
index e1790f8..847a8a8 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
@@ -18,6 +18,7 @@
import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME;
import static com.android.bedstead.nene.users.UserType.SECONDARY_USER_TYPE_NAME;
+import static com.android.bedstead.nene.utils.Versions.meetsSdkVersionRequirements;
import static com.android.bedstead.remotedpc.Configuration.REMOTE_DPC_COMPONENT_NAME;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -27,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@@ -36,9 +38,13 @@
import com.android.bedstead.harrier.annotations.EnsureDoesNotHavePermission;
import com.android.bedstead.harrier.annotations.EnsureHasPermission;
+import com.android.bedstead.harrier.annotations.EnsurePackageNotInstalled;
import com.android.bedstead.harrier.annotations.FailureMode;
-import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeatures;
-import com.android.bedstead.harrier.annotations.RequireFeatures;
+import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeature;
+import com.android.bedstead.harrier.annotations.RequireFeature;
+import com.android.bedstead.harrier.annotations.RequirePackageInstalled;
+import com.android.bedstead.harrier.annotations.RequirePackageNotInstalled;
+import com.android.bedstead.harrier.annotations.RequireSdkVersion;
import com.android.bedstead.harrier.annotations.RequireUserSupported;
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner;
@@ -58,11 +64,13 @@
import com.android.bedstead.nene.devicepolicy.ProfileOwner;
import com.android.bedstead.nene.exceptions.AdbException;
import com.android.bedstead.nene.exceptions.NeneException;
+import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.permissions.PermissionContextImpl;
import com.android.bedstead.nene.users.User;
import com.android.bedstead.nene.users.UserBuilder;
import com.android.bedstead.nene.users.UserReference;
import com.android.bedstead.nene.utils.ShellCommand;
+import com.android.bedstead.nene.utils.Versions;
import com.android.bedstead.remotedpc.RemoteDpc;
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
@@ -77,6 +85,7 @@
import java.lang.annotation.Annotation;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -148,7 +157,7 @@
PermissionContextImpl permissionContext = null;
- for (Annotation annotation : description.getAnnotations()) {
+ for (Annotation annotation : getAnnotations(description)) {
Class<? extends Annotation> annotationType = annotation.annotationType();
EnsureHasNoProfileAnnotation ensureHasNoProfileAnnotation =
@@ -157,6 +166,7 @@
UserType userType = (UserType) annotation.annotationType()
.getMethod("forUser").invoke(annotation);
ensureHasNoProfile(ensureHasNoProfileAnnotation.value(), userType);
+ continue;
}
EnsureHasProfileAnnotation ensureHasProfileAnnotation =
@@ -170,12 +180,14 @@
ensureHasProfile(
ensureHasProfileAnnotation.value(), installInstrumentedApp,
forUser);
+ continue;
}
EnsureHasNoUserAnnotation ensureHasNoUserAnnotation =
annotationType.getAnnotation(EnsureHasNoUserAnnotation.class);
if (ensureHasNoUserAnnotation != null) {
ensureHasNoUser(ensureHasNoUserAnnotation.value());
+ continue;
}
EnsureHasUserAnnotation ensureHasUserAnnotation =
@@ -185,12 +197,14 @@
annotation.getClass()
.getMethod("installInstrumentedApp").invoke(annotation);
ensureHasUser(ensureHasUserAnnotation.value(), installInstrumentedApp);
+ continue;
}
RequireRunOnUserAnnotation requireRunOnUserAnnotation =
annotationType.getAnnotation(RequireRunOnUserAnnotation.class);
if (requireRunOnUserAnnotation != null) {
requireRunOnUser(requireRunOnUserAnnotation.value());
+ continue;
}
RequireRunOnProfileAnnotation requireRunOnProfileAnnotation =
@@ -207,33 +221,37 @@
if (annotation instanceof EnsureHasDeviceOwner) {
EnsureHasDeviceOwner ensureHasDeviceOwnerAnnotation =
(EnsureHasDeviceOwner) annotation;
- ensureHasDeviceOwner(ensureHasDeviceOwnerAnnotation.onUser());
+ ensureHasDeviceOwner(ensureHasDeviceOwnerAnnotation.onUser(),
+ ensureHasDeviceOwnerAnnotation.failureMode(),
+ ensureHasDeviceOwnerAnnotation.isPrimary());
}
if (annotation instanceof EnsureHasNoDeviceOwner) {
ensureHasNoDeviceOwner();
}
- if (annotation instanceof RequireFeatures) {
- RequireFeatures requireFeaturesAnnotation = (RequireFeatures) annotation;
- for (String feature: requireFeaturesAnnotation.value()) {
- requireFeature(feature, requireFeaturesAnnotation.failureMode());
- }
+ if (annotation instanceof RequireFeature) {
+ RequireFeature requireFeatureAnnotation = (RequireFeature) annotation;
+ requireFeature(
+ requireFeatureAnnotation.value(),
+ requireFeatureAnnotation.failureMode());
+ continue;
}
- if (annotation instanceof RequireDoesNotHaveFeatures) {
- RequireDoesNotHaveFeatures requireDoesNotHaveFeaturesAnnotation =
- (RequireDoesNotHaveFeatures) annotation;
- for (String feature : requireDoesNotHaveFeaturesAnnotation.value()) {
- requireDoesNotHaveFeature(feature,
- requireDoesNotHaveFeaturesAnnotation.failureMode());
- }
+ if (annotation instanceof RequireDoesNotHaveFeature) {
+ RequireDoesNotHaveFeature requireDoesNotHaveFeatureAnnotation =
+ (RequireDoesNotHaveFeature) annotation;
+ requireDoesNotHaveFeature(
+ requireDoesNotHaveFeatureAnnotation.value(),
+ requireDoesNotHaveFeatureAnnotation.failureMode());
+ continue;
}
- if (annotationType.equals(EnsureHasProfileOwner.class)) {
+ if (annotation instanceof EnsureHasProfileOwner) {
EnsureHasProfileOwner ensureHasProfileOwnerAnnotation =
(EnsureHasProfileOwner) annotation;
- ensureHasProfileOwner(ensureHasProfileOwnerAnnotation.onUser());
+ ensureHasProfileOwner(ensureHasProfileOwnerAnnotation.onUser(),
+ ensureHasProfileOwnerAnnotation.isPrimary());
}
if (annotationType.equals(EnsureHasNoProfileOwner.class)) {
@@ -245,10 +263,52 @@
if (annotation instanceof RequireUserSupported) {
RequireUserSupported requireUserSupportedAnnotation =
(RequireUserSupported) annotation;
- for (String userType: requireUserSupportedAnnotation.value()) {
- requireUserSupported(
- userType, requireUserSupportedAnnotation.failureMode());
- }
+ requireUserSupported(
+ requireUserSupportedAnnotation.value(),
+ requireUserSupportedAnnotation.failureMode());
+ continue;
+ }
+
+ if (annotation instanceof RequireSdkVersion) {
+ RequireSdkVersion requireSdkVersionAnnotation =
+ (RequireSdkVersion) annotation;
+
+ requireSdkVersion(
+ requireSdkVersionAnnotation.min(),
+ requireSdkVersionAnnotation.max(),
+ requireSdkVersionAnnotation.failureMode());
+ continue;
+ }
+
+ if (annotation instanceof RequirePackageInstalled) {
+ RequirePackageInstalled requirePackageInstalledAnnotation =
+ (RequirePackageInstalled) annotation;
+ requirePackageInstalled(
+ requirePackageInstalledAnnotation.value(),
+ requirePackageInstalledAnnotation.onUser(),
+ requirePackageInstalledAnnotation.failureMode());
+ continue;
+ }
+
+ if (annotation instanceof RequirePackageNotInstalled) {
+ RequirePackageNotInstalled requirePackageNotInstalledAnnotation =
+ (RequirePackageNotInstalled) annotation;
+ requirePackageNotInstalled(
+ requirePackageNotInstalledAnnotation.value(),
+ requirePackageNotInstalledAnnotation.onUser(),
+ requirePackageNotInstalledAnnotation.failureMode()
+ );
+ continue;
+ }
+
+ if (annotation instanceof EnsurePackageNotInstalled) {
+ EnsurePackageNotInstalled ensurePackageNotInstalledAnnotation =
+ (EnsurePackageNotInstalled) annotation;
+ ensurePackageNotInstalled(
+ ensurePackageNotInstalledAnnotation.value(),
+ ensurePackageNotInstalledAnnotation.onUser()
+ );
+ continue;
}
if (annotation instanceof EnsureHasPermission) {
@@ -314,7 +374,10 @@
// Otherwise we should build a new collection by recursively gathering annotations
// if we find any which don't work without the runner we should error and fail the test
- List<Annotation> annotations = new ArrayList<>(description.getAnnotations());
+ List<Annotation> annotations =
+ new ArrayList<>(Arrays.asList(description.getTestClass().getAnnotations()));
+ annotations.addAll(description.getAnnotations());
+
checkAnnotations(annotations);
BedsteadJUnit4.resolveRecursiveAnnotations(annotations,
@@ -379,6 +442,14 @@
!sTestApis.packages().features().contains(feature), failureMode);
}
+ private void requireSdkVersion(int min, int max, FailureMode failureMode) {
+ checkFailOrSkip(
+ "Sdk version must be between " + min + " and " + max + " (inclusive)",
+ meetsSdkVersionRequirements(min, max),
+ failureMode
+ );
+ }
+
private com.android.bedstead.nene.users.UserType requireUserSupported(
String userType, FailureMode failureMode) {
com.android.bedstead.nene.users.UserType resolvedUserType =
@@ -413,6 +484,8 @@
}
public enum UserType {
+ /** Only to be used with annotations. */
+ ANY,
SYSTEM_USER,
CURRENT_USER,
PRIMARY_USER,
@@ -431,6 +504,7 @@
mProfiles = new HashMap<>();
private DevicePolicyController mDeviceOwner;
private Map<UserReference, DevicePolicyController> mProfileOwners = new HashMap<>();
+ private DevicePolicyController mPrimaryDpc;
private final List<UserReference> mCreatedUsers = new ArrayList<>();
private final List<UserBuilder> mRemovedUsers = new ArrayList<>();
@@ -743,6 +817,8 @@
return workProfile();
case TV_PROFILE:
return tvProfile();
+ case ANY:
+ throw new IllegalStateException("ANY UserType can not be used here");
default:
throw new IllegalArgumentException("Unknown user type " + userType);
}
@@ -756,6 +832,7 @@
broadcastReceiver.unregisterQuietly();
}
mRegisteredBroadcastReceivers.clear();
+ mPrimaryDpc = null;
}
private void teardownShareableState() {
@@ -773,13 +850,13 @@
mOriginalDeviceOwner = null;
}
- for (Map.Entry<UserReference, DevicePolicyController> profileOwner :
- mProfileOwners.entrySet()) {
+ for (Map.Entry<UserReference, DevicePolicyController> originalProfileOwner :
+ mChangedProfileOwners.entrySet()) {
ProfileOwner currentProfileOwner =
- sTestApis.devicePolicy().getProfileOwner(profileOwner.getKey());
+ sTestApis.devicePolicy().getProfileOwner(originalProfileOwner.getKey());
- if (Objects.equal(currentProfileOwner, profileOwner.getValue())) {
+ if (Objects.equal(currentProfileOwner, originalProfileOwner.getValue())) {
continue; // No need to restore
}
@@ -787,11 +864,12 @@
currentProfileOwner.remove();
}
- if (profileOwner.getValue() != null) {
- sTestApis.devicePolicy().setProfileOwner(profileOwner.getKey(),
- profileOwner.getValue().componentName());
+ if (originalProfileOwner.getValue() != null) {
+ sTestApis.devicePolicy().setProfileOwner(originalProfileOwner.getKey(),
+ originalProfileOwner.getValue().componentName());
}
}
+ mChangedProfileOwners.clear();
for (UserReference user : mCreatedUsers) {
user.remove();
@@ -845,9 +923,13 @@
}
}
- private void ensureHasDeviceOwner(UserType onUser) {
+ private void ensureHasDeviceOwner(UserType onUser, FailureMode failureMode, boolean isPrimary) {
// TODO(scottjonathan): Should support non-remotedpc device owner (default to remotedpc)
// TODO(scottjonathan): Should allow setting the device owner on a different user
+ if (isPrimary && mPrimaryDpc != null) {
+ throw new IllegalStateException("Only one DPC can be marked as primary per test");
+ }
+
DeviceOwner currentDeviceOwner = sTestApis.devicePolicy().getDeviceOwner();
if (currentDeviceOwner != null
@@ -857,15 +939,24 @@
UserReference instrumentedUser = sTestApis.users().instrumented();
- // TODO(scottjonathan): Consider if we should restore these users
- for (UserReference u : sTestApis.users().all()) {
- if (u.equals(instrumentedUser)) {
- continue;
- }
- removeAndRecordUser(u);
+ if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) {
+ // Prior to S we can't set device owner if there are other users on the device
+ for (UserReference u : sTestApis.users().all()) {
+ if (u.equals(instrumentedUser)) {
+ continue;
+ }
+ try {
+ removeAndRecordUser(u);
+ } catch (NeneException e) {
+ failOrSkip(
+ "Error removing user to prepare for DeviceOwner: " + e.toString(),
+ failureMode);
+ }
+ }
}
+
// TODO(scottjonathan): Remove accounts
ensureHasNoProfileOwner(onUser);
@@ -876,10 +967,18 @@
mDeviceOwner = RemoteDpc.setAsDeviceOwner(resolveUserTypeToUser(onUser))
.devicePolicyController();
+
+ if (isPrimary) {
+ mPrimaryDpc = mDeviceOwner;
+ }
}
- private void ensureHasProfileOwner(UserType onUser) {
+ private void ensureHasProfileOwner(UserType onUser, boolean isPrimary) {
// TODO(scottjonathan): Should support non-remotedpc profile owner (default to remotedpc)
+ if (isPrimary && mPrimaryDpc != null) {
+ throw new IllegalStateException("Only one DPC can be marked as primary per test");
+ }
+
UserReference user = resolveUserTypeToUser(onUser);
ProfileOwner currentProfileOwner = sTestApis.devicePolicy().getProfileOwner(user);
DeviceOwner currentDeviceOwner = sTestApis.devicePolicy().getDeviceOwner();
@@ -899,6 +998,10 @@
}
mProfileOwners.put(user, RemoteDpc.setAsProfileOwner(user).devicePolicyController());
+
+ if (isPrimary) {
+ mPrimaryDpc = mProfileOwners.get(user);
+ }
}
private void ensureHasNoDeviceOwner() {
@@ -1005,4 +1108,104 @@
return RemoteDpc.forDevicePolicyController(profileOwner);
}
+
+ private void requirePackageInstalled(
+ String packageName, UserType forUser, FailureMode failureMode) {
+
+ Package pkg = sTestApis.packages().find(packageName).resolve();
+ checkFailOrSkip(
+ packageName + " is required to be installed for " + forUser,
+ pkg != null,
+ failureMode);
+
+ if (forUser.equals(UserType.ANY)) {
+ checkFailOrSkip(
+ packageName + " is required to be installed",
+ !pkg.installedOnUsers().isEmpty(),
+ failureMode);
+ } else {
+ checkFailOrSkip(
+ packageName + " is required to be installed for " + forUser,
+ pkg.installedOnUsers().contains(resolveUserTypeToUser(forUser)),
+ failureMode);
+ }
+ }
+
+ private void requirePackageNotInstalled(
+ String packageName, UserType forUser, FailureMode failureMode) {
+ Package pkg = sTestApis.packages().find(packageName).resolve();
+ if (pkg == null) {
+ // Definitely not installed
+ return;
+ }
+
+ if (forUser.equals(UserType.ANY)) {
+ checkFailOrSkip(
+ packageName + " is required to be not installed",
+ pkg.installedOnUsers().isEmpty(),
+ failureMode);
+ } else {
+ checkFailOrSkip(
+ packageName + " is required to be not installed for " + forUser,
+ !pkg.installedOnUsers().contains(resolveUserTypeToUser(forUser)),
+ failureMode);
+ }
+ }
+
+ private void ensurePackageNotInstalled(
+ String packageName, UserType forUser) {
+
+ Package pkg = sTestApis.packages().find(packageName).resolve();
+ if (pkg == null) {
+ // Definitely not installed
+ return;
+ }
+
+ if (forUser.equals(UserType.ANY)) {
+ if (!pkg.installedOnUsers().isEmpty()) {
+ pkg.uninstallFromAllUsers();
+ }
+ } else {
+ UserReference user = resolveUserTypeToUser(forUser);
+ if (pkg.installedOnUsers().contains(user)) {
+ pkg.uninstall(user);
+ }
+ }
+ }
+
+ /**
+ * Get the most appropriate {@link RemoteDpc} instance for the device state.
+ *
+ * <p>This method should only be used by tests which are annotated with {@link PolicyTest}.
+ *
+ * <p>If no DPC is set as the "primary" DPC for the device state, then this method will first
+ * check for a profile owner in the current user, or else check for a device owner.
+ *
+ * <p>If no Harrier-managed profile owner or device owner exists, an exception will be thrown.
+ *
+ * <p>If the profile owner or device owner is not a RemoteDPC then an exception will be thrown.
+ */
+ public RemoteDpc dpc() {
+ if (mPrimaryDpc != null) {
+ return RemoteDpc.forDevicePolicyController(mPrimaryDpc);
+ }
+
+ if (mProfileOwners.containsKey(sTestApis.users().instrumented())) {
+ DevicePolicyController profileOwner =
+ mProfileOwners.get(sTestApis.users().instrumented());
+
+ if (profileOwner.componentName().equals(REMOTE_DPC_COMPONENT_NAME)) {
+ return RemoteDpc.forDevicePolicyController(profileOwner);
+ }
+ }
+
+ if (mDeviceOwner != null) {
+ if (mDeviceOwner.componentName().equals(REMOTE_DPC_COMPONENT_NAME)) {
+ return RemoteDpc.forDevicePolicyController(mDeviceOwner);
+ }
+
+ }
+
+ throw new IllegalStateException("No Harrier-managed profile owner or device owner.");
+ }
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/Policy.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/Policy.java
index 3496f86..2440198 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/Policy.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/Policy.java
@@ -20,6 +20,10 @@
import com.android.bedstead.harrier.annotations.parameterized.IncludeNone;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnDeviceOwnerUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser;
+import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfCorporateOwnedProfileOwner;
+import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfProfileOwner;
+import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnProfileOwner;
+import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
@@ -31,6 +35,10 @@
@IncludeNone
@IncludeRunOnDeviceOwnerUser
@IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser
+@IncludeRunOnProfileOwner
+@IncludeRunOnParentOfCorporateOwnedProfileOwner
+@IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner
+@IncludeRunOnParentOfProfileOwner
public final class Policy {
private Policy() {
@@ -44,6 +52,18 @@
private static final IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser
INCLUDE_RUN_ON_NON_AFFILIATED_DEVICE_OWNER_SECONDARY_USER =
Policy.class.getAnnotation(IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser.class);
+ private static final IncludeRunOnProfileOwner
+ INCLUDE_RUN_ON_PROFILE_OWNER =
+ Policy.class.getAnnotation(IncludeRunOnProfileOwner.class);
+ private static final IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner
+ INCLUDE_RUN_ON_SECONDARY_USER_IN_DIFFERENT_PROFILE_GROUP_TO_PROFILE_OWNER =
+ Policy.class.getAnnotation(
+ IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner.class);
+ private static final IncludeRunOnParentOfProfileOwner INCLUDE_RUN_ON_PARENT_OF_PROFILE_OWNER =
+ Policy.class.getAnnotation(IncludeRunOnParentOfProfileOwner.class);
+ private static final IncludeRunOnParentOfCorporateOwnedProfileOwner
+ INCLUDE_RUN_ON_PARENT_OF_CORPORATE_OWNED_PROFILE_OWNER =
+ Policy.class.getAnnotation(IncludeRunOnParentOfCorporateOwnedProfileOwner.class);
/**
* Get positive state annotations for the given policy.
@@ -53,8 +73,45 @@
public static List<Annotation> positiveStates(EnterprisePolicy enterprisePolicy) {
List<Annotation> annotations = new ArrayList<>();
- annotations.add(INCLUDE_RUN_ON_DEVICE_OWNER_USER);
- annotations.add(INCLUDE_RUN_ON_NON_AFFILIATED_DEVICE_OWNER_SECONDARY_USER);
+ switch (enterprisePolicy.deviceOwner()) {
+ case NO:
+ break;
+ case GLOBAL:
+ annotations.add(INCLUDE_RUN_ON_DEVICE_OWNER_USER);
+ annotations.add(INCLUDE_RUN_ON_NON_AFFILIATED_DEVICE_OWNER_SECONDARY_USER);
+ break;
+ case USER:
+ annotations.add(INCLUDE_RUN_ON_DEVICE_OWNER_USER);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unknown policy control: " + enterprisePolicy.deviceOwner());
+ }
+
+ switch (enterprisePolicy.profileOwner()) {
+ case NO:
+ break;
+ case PARENT:
+ annotations.add(INCLUDE_RUN_ON_PROFILE_OWNER);
+ annotations.add(INCLUDE_RUN_ON_PARENT_OF_PROFILE_OWNER);
+ break;
+ case COPE_PARENT:
+ annotations.add(INCLUDE_RUN_ON_PROFILE_OWNER);
+ // TODO(scottjonathan): Re-add when we can setup this state
+// annotations.add(INCLUDE_RUN_ON_PARENT_OF_CORPORATE_OWNED_PROFILE_OWNER);
+ break;
+ case PROFILE:
+ annotations.add(INCLUDE_RUN_ON_PROFILE_OWNER);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unknown policy control: " + enterprisePolicy.profileOwner());
+ }
+
+ if (annotations.isEmpty()) {
+ // Don't run the original test unparameterized
+ annotations.add(INCLUDE_NONE_ANNOTATION);
+ }
return annotations;
}
@@ -67,7 +124,92 @@
public static List<Annotation> negativeStates(EnterprisePolicy enterprisePolicy) {
List<Annotation> annotations = new ArrayList<>();
- annotations.add(INCLUDE_NONE_ANNOTATION);
+ switch (enterprisePolicy.deviceOwner()) {
+ case NO:
+ break;
+ case GLOBAL:
+ break;
+ case USER:
+ annotations.add(INCLUDE_RUN_ON_NON_AFFILIATED_DEVICE_OWNER_SECONDARY_USER);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unknown policy control: " + enterprisePolicy.deviceOwner());
+ }
+
+ switch (enterprisePolicy.profileOwner()) {
+ case NO:
+ break;
+ case PARENT:
+ annotations.add(
+ INCLUDE_RUN_ON_SECONDARY_USER_IN_DIFFERENT_PROFILE_GROUP_TO_PROFILE_OWNER);
+ break;
+ case COPE_PARENT:
+ annotations.add(
+ INCLUDE_RUN_ON_SECONDARY_USER_IN_DIFFERENT_PROFILE_GROUP_TO_PROFILE_OWNER);
+ annotations.add(
+ INCLUDE_RUN_ON_PARENT_OF_PROFILE_OWNER);
+ break;
+ case PROFILE:
+ annotations.add(
+ INCLUDE_RUN_ON_SECONDARY_USER_IN_DIFFERENT_PROFILE_GROUP_TO_PROFILE_OWNER);
+ // TODO(scottjonathan): Re-add when we can setup this state
+// annotations.add(
+// INCLUDE_RUN_ON_PARENT_OF_CORPORATE_OWNED_PROFILE_OWNER);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unknown policy control: " + enterprisePolicy.profileOwner());
+ }
+
+ if (annotations.isEmpty()) {
+ // Don't run the original test unparameterized
+ annotations.add(INCLUDE_NONE_ANNOTATION);
+ }
+
+ return annotations;
+ }
+
+ /**
+ * Get state annotations where the policy cannot be set for the given policy.
+ */
+ public static List<Annotation> cannotSetPolicyStates(EnterprisePolicy enterprisePolicy) {
+ List<Annotation> annotations = new ArrayList<>();
+
+ // TODO(scottjonathan): Always include a state without a dpc
+
+ switch (enterprisePolicy.deviceOwner()) {
+ case NO:
+ annotations.add(INCLUDE_RUN_ON_DEVICE_OWNER_USER);
+ break;
+ case GLOBAL:
+ break;
+ case USER:
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unknown policy control: " + enterprisePolicy.deviceOwner());
+ }
+
+ switch (enterprisePolicy.profileOwner()) {
+ case NO:
+ annotations.add(INCLUDE_RUN_ON_PROFILE_OWNER);
+ break;
+ case PARENT:
+ break;
+ case COPE_PARENT:
+ break;
+ case PROFILE:
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unknown policy control: " + enterprisePolicy.profileOwner());
+ }
+
+ if (annotations.isEmpty()) {
+ // Don't run the original test unparameterized
+ annotations.add(INCLUDE_NONE_ANNOTATION);
+ }
return annotations;
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureDoesNotHavePermission.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureDoesNotHavePermission.java
index 22e7d55..7aeafa1 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureDoesNotHavePermission.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureDoesNotHavePermission.java
@@ -24,7 +24,7 @@
/**
* Ensure that the given permission is denied before running the test.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnsureDoesNotHavePermission {
String[] value();
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoSecondaryUser.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoSecondaryUser.java
index e66a126..ef20a0c 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoSecondaryUser.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoSecondaryUser.java
@@ -32,7 +32,7 @@
* has no secondary user that is not the current user. Otherwise, you can use {@link DeviceState}
* to ensure that the device enters the correct state for the method.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@EnsureHasNoUserAnnotation("android.os.usertype.full.SECONDARY")
public @interface EnsureHasNoSecondaryUser {
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoTvProfile.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoTvProfile.java
index 29bd8ee..345d418 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoTvProfile.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoTvProfile.java
@@ -33,7 +33,7 @@
* no Tv profile. Otherwise, you can use {@link DeviceState} to ensure that the device enters
* the correct state for the method.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@EnsureHasNoProfileAnnotation("com.android.tv.profile")
public @interface EnsureHasNoTvProfile {
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoWorkProfile.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoWorkProfile.java
index 2a1e48e..6373625 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoWorkProfile.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoWorkProfile.java
@@ -33,7 +33,7 @@
* no work profile. Otherwise, you can use {@link DeviceState} to ensure that the device enters
* the correct state for the method.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@EnsureHasNoProfileAnnotation("android.os.usertype.profile.MANAGED")
public @interface EnsureHasNoWorkProfile {
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasPermission.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasPermission.java
index 00c20a5..8f87474 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasPermission.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasPermission.java
@@ -24,7 +24,7 @@
/**
* Ensure that the given permission is granted before running the test.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnsureHasPermission {
String[] value();
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasSecondaryUser.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasSecondaryUser.java
index 06d2dd8..8ee6924 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasSecondaryUser.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasSecondaryUser.java
@@ -37,7 +37,7 @@
* secondary user on the device, and the device does not support creating additional users, then
* the test will be skipped.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@EnsureHasUserAnnotation("android.os.usertype.full.SECONDARY")
public @interface EnsureHasSecondaryUser {
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasTvProfile.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasTvProfile.java
index 8db9cb8..c6020a1 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasTvProfile.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasTvProfile.java
@@ -35,7 +35,7 @@
* a Tv profile. Otherwise, you can use {@link DeviceState} to ensure that the device enters
* the correct state for the method.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@EnsureHasProfileAnnotation("com.android.tv.profile")
public @interface EnsureHasTvProfile {
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasWorkProfile.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasWorkProfile.java
index 7802733..309056c 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasWorkProfile.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasWorkProfile.java
@@ -33,15 +33,16 @@
* Mark that a test method should run on a user which has a work profile.
*
* <p>Use of this annotation implies
- * {@code RequireFeatures("android.software.managed_users", SKIP)}.
+ * {@code RequireFeature("android.software.managed_users", SKIP)}.
*
* <p>Your test configuration may be configured so that this test is only run on a user which has
* a work profile. Otherwise, you can use {@link DeviceState} to ensure that the device enters
* the correct state for the method.
*/
-@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@EnsureHasProfileAnnotation("android.os.usertype.profile.MANAGED")
+@RequireFeature("android.software.managed_users")
@EnsureHasNoDeviceOwner // TODO: This should only apply on Android R+
public @interface EnsureHasWorkProfile {
/** Which user type the work profile should be attached to. */
@@ -49,4 +50,12 @@
/** Whether the instrumented test app should be installed in the work profile. */
OptionalBoolean installInstrumentedApp() default TRUE;
+
+ /**
+ * Whether the profile owner's DPC should be returned by calls to {@link DeviceState#dpc()}.
+ *
+ * <p>Only one device policy controller per test should be marked as primary.
+ */
+ // TODO(scottjonathan): Enforce dpcIsPrimary
+ boolean dpcIsPrimary() default false;
}
\ No newline at end of file
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsurePackageNotInstalled.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsurePackageNotInstalled.java
new file mode 100644
index 0000000..5144dde
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsurePackageNotInstalled.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import com.android.bedstead.harrier.DeviceState;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark that a test method should run only when a given package is not installed.
+ *
+ * <p>You can guarantee that these methods do not run on devices with the package by
+ * using {@code DeviceState}.
+ *
+ * <p>Tests annotated with this will attempt to remove the package if it exists, or otherwise fail.
+ * If you'd rather skip or fail tests immediately without attempting to remove see
+ * {@link RequirePackageNotInstalled}.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(EnsurePackagesNotInstalled.class)
+public @interface EnsurePackageNotInstalled {
+ String value();
+ DeviceState.UserType onUser() default DeviceState.UserType.CURRENT_USER;
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsurePackagesNotInstalled.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsurePackagesNotInstalled.java
new file mode 100644
index 0000000..d3327a0
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsurePackagesNotInstalled.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import com.android.bedstead.harrier.annotations.meta.RepeatingAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@RepeatingAnnotation
+public @interface EnsurePackagesNotInstalled {
+ EnsurePackageNotInstalled[] value();
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/Postsubmit.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/Postsubmit.java
index bb9d248..92a8c15 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/Postsubmit.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/Postsubmit.java
@@ -33,8 +33,8 @@
* <p>It can also be used for tests which don't meet the requirements to be part of multi-user
* presubmits.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Postsubmit {
String reason();
-}
\ No newline at end of file
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireAospBuild.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireAospBuild.java
new file mode 100644
index 0000000..253aa3a
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireAospBuild.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import static com.android.bedstead.harrier.annotations.RequireAospBuild.GMS_CORE_PACKAGE;
+import static com.android.bedstead.harrier.annotations.RequireAospBuild.GSF_PACKAGE;
+import static com.android.bedstead.harrier.annotations.RequireAospBuild.PLAY_STORE_PACKAGE;
+
+import com.android.bedstead.harrier.DeviceState;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@RequirePackageNotInstalled(value = GMS_CORE_PACKAGE, onUser = DeviceState.UserType.ANY)
+@RequirePackageNotInstalled(value = PLAY_STORE_PACKAGE, onUser = DeviceState.UserType.ANY)
+@RequirePackageNotInstalled(value = GSF_PACKAGE, onUser = DeviceState.UserType.ANY)
+public @interface RequireAospBuild {
+ String GMS_CORE_PACKAGE = "com.google.android.gms";
+ String PLAY_STORE_PACKAGE = "com.android.vending";
+ String GSF_PACKAGE = "com.google.android.gsf";
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireCnGmsBuild.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireCnGmsBuild.java
new file mode 100644
index 0000000..e5e838b
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireCnGmsBuild.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import static com.android.bedstead.harrier.annotations.RequireCnGmsBuild.CHINA_GOOGLE_SERVICES_FEATURE;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@RequireFeature(CHINA_GOOGLE_SERVICES_FEATURE)
+public @interface RequireCnGmsBuild {
+ String CHINA_GOOGLE_SERVICES_FEATURE = "cn.google.services";
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireDoesNotHaveFeature.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireDoesNotHaveFeature.java
new file mode 100644
index 0000000..e9c816a
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireDoesNotHaveFeature.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark that a test method should run only when the device does not have a given feature.
+ *
+ * <p>You can guarantee that these methods do not run on devices with the feature by
+ * using {@code DeviceState}.
+ *
+ * <p>By default the test will be skipped if the feature is available. If you'd rather the test
+ * fail then use {@code failureMode = FailureMode.FAIL}.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RequireDoesNotHaveFeature {
+ String value();
+ FailureMode failureMode() default FailureMode.SKIP;
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireDoesNotHaveFeatures.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireDoesNotHaveFeatures.java
index 61a3854..58d2bd9 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireDoesNotHaveFeatures.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireDoesNotHaveFeatures.java
@@ -16,23 +16,16 @@
package com.android.bedstead.harrier.annotations;
+import com.android.bedstead.harrier.annotations.meta.RepeatingAnnotation;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * Mark that a test method should run only when the device does not have a given feature.
- *
- * <p>You can guarantee that these methods do not run on devices with the feature by
- * using {@code DeviceState}.
- *
- * <p>By default the test will be skipped if the feature is available. If you'd rather the test
- * fail then use {@code failureMode = FailureMode.FAIL}.
- */
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
+@RepeatingAnnotation
public @interface RequireDoesNotHaveFeatures {
- String[] value();
- FailureMode failureMode() default FailureMode.SKIP;
-}
+ RequireDoesNotHaveFeature[] value();
+}
\ No newline at end of file
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireFeature.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireFeature.java
new file mode 100644
index 0000000..4919acb
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireFeature.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 com.android.bedstead.harrier.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark that a test method should run only when the device has a given feature.
+ *
+ * <p>You can guarantee that these methods do not run on devices lacking the feature by
+ * using {@code DeviceState}.
+ *
+ * <p>By default the test will be skipped if the feature is not available. If you'd rather the test
+ * fail then use {@code failureMode = FailureMode.FAIL}.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(RequireFeatures.class)
+public @interface RequireFeature {
+ String value();
+ FailureMode failureMode() default FailureMode.SKIP;
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireFeatures.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireFeatures.java
index c44292e..827e613 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireFeatures.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireFeatures.java
@@ -16,23 +16,16 @@
package com.android.bedstead.harrier.annotations;
+import com.android.bedstead.harrier.annotations.meta.RepeatingAnnotation;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * Mark that a test method should run only when the device has a given feature.
- *
- * <p>You can guarantee that these methods do not run on devices lacking the feature by
- * using {@code DeviceState}.
- *
- * <p>By default the test will be skipped if the feature is not available. If you'd rather the test
- * fail then use {@code failureMode = FailureMode.FAIL}.
- */
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
+@RepeatingAnnotation
public @interface RequireFeatures {
- String[] value();
- FailureMode failureMode() default FailureMode.SKIP;
+ RequireFeature[] value();
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireGmsBuild.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireGmsBuild.java
new file mode 100644
index 0000000..fb0625d
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireGmsBuild.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import static com.android.bedstead.harrier.annotations.RequireAospBuild.GMS_CORE_PACKAGE;
+import static com.android.bedstead.harrier.annotations.RequireAospBuild.GSF_PACKAGE;
+import static com.android.bedstead.harrier.annotations.RequireAospBuild.PLAY_STORE_PACKAGE;
+
+import com.android.bedstead.harrier.DeviceState;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@RequirePackageInstalled(value = GMS_CORE_PACKAGE, onUser = DeviceState.UserType.ANY)
+@RequirePackageInstalled(value = PLAY_STORE_PACKAGE, onUser = DeviceState.UserType.ANY)
+@RequirePackageInstalled(value = GSF_PACKAGE, onUser = DeviceState.UserType.ANY)
+public @interface RequireGmsBuild {
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireNotCnGmsBuild.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireNotCnGmsBuild.java
new file mode 100644
index 0000000..3405411
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireNotCnGmsBuild.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 com.android.bedstead.harrier.annotations;
+
+import static com.android.bedstead.harrier.annotations.RequireCnGmsBuild.CHINA_GOOGLE_SERVICES_FEATURE;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@RequireDoesNotHaveFeature(CHINA_GOOGLE_SERVICES_FEATURE)
+public @interface RequireNotCnGmsBuild {
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackageInstalled.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackageInstalled.java
new file mode 100644
index 0000000..dd0f95b
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackageInstalled.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import com.android.bedstead.harrier.DeviceState;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark that a test method should run only when a given package is installed.
+ *
+ * <p>You can guarantee that these methods do not run on devices lacking the package by
+ * using {@code DeviceState}.
+ *
+ * <p>By default the test will be skipped if the package is not available. If you'd rather the test
+ * fail then use {@code failureMode = FailureMode.FAIL}.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(RequirePackagesInstalled.class)
+public @interface RequirePackageInstalled {
+ String value();
+ DeviceState.UserType onUser() default DeviceState.UserType.CURRENT_USER;
+ FailureMode failureMode() default FailureMode.SKIP;
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackageNotInstalled.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackageNotInstalled.java
new file mode 100644
index 0000000..db1a749
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackageNotInstalled.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import com.android.bedstead.harrier.DeviceState;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark that a test method should run only when a given package is not installed.
+ *
+ * <p>You can guarantee that these methods do not run on devices with the package by
+ * using {@code DeviceState}.
+ *
+ * <p>By default the test will be skipped if the package is available. If you'd rather the test
+ * fail then use {@code failureMode = FailureMode.FAIL}. If you'd like to uninstall the package if
+ * it is installed, see {@link EnsurePackageNotInstalled}.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(RequirePackagesNotInstalled.class)
+public @interface RequirePackageNotInstalled {
+ String value();
+ DeviceState.UserType onUser() default DeviceState.UserType.CURRENT_USER;
+ FailureMode failureMode() default FailureMode.SKIP;
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackagesInstalled.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackagesInstalled.java
new file mode 100644
index 0000000..aaef85c
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackagesInstalled.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import com.android.bedstead.harrier.annotations.meta.RepeatingAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@RepeatingAnnotation
+public @interface RequirePackagesInstalled {
+ RequirePackageInstalled[] value();
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackagesNotInstalled.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackagesNotInstalled.java
new file mode 100644
index 0000000..279506c
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequirePackagesNotInstalled.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import com.android.bedstead.harrier.annotations.meta.RepeatingAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@RepeatingAnnotation
+public @interface RequirePackagesNotInstalled {
+ RequirePackageNotInstalled[] value();
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnPrimaryUser.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnPrimaryUser.java
index 875f1e3..583e061 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnPrimaryUser.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnPrimaryUser.java
@@ -32,7 +32,7 @@
* <p>Optionally, you can guarantee that these methods do not run outside of the primary
* user by using {@link DeviceState}.
*/
-@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RequireRunOnUserAnnotation("android.os.usertype.full.SYSTEM")
public @interface RequireRunOnPrimaryUser {
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnSecondaryUser.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnSecondaryUser.java
index 9225e04..eaa7ee0 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnSecondaryUser.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnSecondaryUser.java
@@ -37,7 +37,7 @@
* annotated {@link Postsubmit} until they are shown to meet the multi-user presubmit
* requirements.
*/
-@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RequireRunOnUserAnnotation("android.os.usertype.full.SECONDARY")
public @interface RequireRunOnSecondaryUser {
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnTvProfile.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnTvProfile.java
index 9e20a9f..1f12524 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnTvProfile.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnTvProfile.java
@@ -16,8 +16,11 @@
package com.android.bedstead.harrier.annotations;
+import static com.android.bedstead.harrier.OptionalBoolean.ANY;
+
import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.meta.RequireRunOnUserAnnotation;
+import com.android.bedstead.harrier.OptionalBoolean;
+import com.android.bedstead.harrier.annotations.meta.RequireRunOnProfileAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -33,8 +36,9 @@
* <p>Optionally, you can guarantee that these methods do not run outside of a Tv
* profile by using {@link DeviceState}.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
-@RequireRunOnUserAnnotation("com.android.tv.profile")
+@RequireRunOnProfileAnnotation("com.android.tv.profile")
public @interface RequireRunOnTvProfile {
+ OptionalBoolean installInstrumentedAppInParent() default ANY;
}
\ No newline at end of file
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnWorkProfile.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnWorkProfile.java
index 7207d71..bf257a4 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnWorkProfile.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireRunOnWorkProfile.java
@@ -21,7 +21,7 @@
import com.android.bedstead.harrier.DeviceState;
import com.android.bedstead.harrier.OptionalBoolean;
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasProfileOwner;
-import com.android.bedstead.harrier.annotations.meta.RequireRunOnUserAnnotation;
+import com.android.bedstead.harrier.annotations.meta.RequireRunOnProfileAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -40,10 +40,18 @@
* <p>This annotation by default opts a test into multi-user presubmit. New tests should also be
* annotated {@link Postsubmit} until they are shown to meet the multi-user presubmit requirements.
*/
-@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
-@RequireRunOnUserAnnotation("android.os.usertype.profile.MANAGED")
+@RequireRunOnProfileAnnotation("android.os.usertype.profile.MANAGED")
@EnsureHasProfileOwner
public @interface RequireRunOnWorkProfile {
OptionalBoolean installInstrumentedAppInParent() default ANY;
+
+ /**
+ * Whether the profile owner's DPC should be returned by calls to {@link DeviceState#dpc()}.
+ *
+ * <p>Only one device policy controller per test should be marked as primary.
+ */
+ // TODO(scottjonathan): Enforce dpcIsPrimary
+ boolean dpcIsPrimary() default false;
}
\ No newline at end of file
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireSdkVersion.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireSdkVersion.java
new file mode 100644
index 0000000..7e7fd39
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireSdkVersion.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import com.android.bedstead.harrier.DeviceState;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark that a test method should only run on specified sdk versions.
+ *
+ * <p>Your test configuration may be configured so that this test is only run on a device with the
+ * given user. Otherwise, you can use {@link DeviceState} to ensure that the test is
+ * not run when the sdk version is not correct.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RequireSdkVersion {
+ int min() default 1;
+ int max() default Integer.MAX_VALUE;
+ FailureMode failureMode() default FailureMode.SKIP;
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireUserSupported.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireUserSupported.java
index deb06f1..f346b28 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireUserSupported.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireUserSupported.java
@@ -19,6 +19,7 @@
import com.android.bedstead.harrier.DeviceState;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -30,9 +31,10 @@
* supports the user types. Otherwise, you can use {@link DeviceState} to ensure that the test is
* not run when the user type is not supported.
*/
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(RequireUsersSupported.class)
public @interface RequireUserSupported {
- String[] value();
+ String value();
FailureMode failureMode() default FailureMode.SKIP;
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireUsersSupported.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireUsersSupported.java
new file mode 100644
index 0000000..2ee65cb
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/RequireUsersSupported.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations;
+
+import com.android.bedstead.harrier.annotations.meta.RepeatingAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@RepeatingAnnotation
+public @interface RequireUsersSupported {
+ RequireUserSupported[] value();
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/CannotSetPolicyTest.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/CannotSetPolicyTest.java
new file mode 100644
index 0000000..84b0282
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/CannotSetPolicyTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.annotations.enterprise;
+
+import com.android.bedstead.harrier.annotations.meta.RequiresBedsteadJUnit4;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Mark a test as testing the states where a policy is not allowed to be applied.
+ *
+ * <p>An example is running with a device owner for a policy only applicable to profile owners.
+ *
+ * <p>This will generate parameterized runs for all matching states.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@RequiresBedsteadJUnit4
+public @interface CannotSetPolicyTest {
+ /**
+ * The policy being tested.
+ *
+ * <p>This is used to calculate which states are required to be tested.
+ */
+ Class<?> policy();
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDeviceOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDeviceOwner.java
index 99863a9..f999e74 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDeviceOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasDeviceOwner.java
@@ -19,6 +19,7 @@
import static com.android.bedstead.harrier.DeviceState.UserType.SYSTEM_USER;
import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.FailureMode;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -36,11 +37,21 @@
* <p>If {@link DeviceState} is required to set the device owner (because there isn't one already)
* then all users and accounts may be removed from the device.
*/
-@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnsureHasDeviceOwner {
/** Which user type the device owner should be installed on. */
DeviceState.UserType onUser() default SYSTEM_USER;
+
+ /** Behaviour if the device owner cannot be set. */
+ FailureMode failureMode() default FailureMode.FAIL;
+
+ /**
+ * Whether this DPC should be returned by calls to {@link DeviceState#dpc()}.
+ *
+ * <p>Only one device policy controller per test should be marked as primary.
+ */
+ boolean isPrimary() default false;
}
// TODO(scottjonathan): Is there a feature or something that we need to check to make sure DO is
// supported?
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDeviceOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDeviceOwner.java
index d417bda..027e808 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDeviceOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoDeviceOwner.java
@@ -30,7 +30,7 @@
* no device owner. Otherwise, you can use {@link DeviceState} to ensure that the device enters
* the correct state for the method.
*/
-@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnsureHasNoDeviceOwner {
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoProfileOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoProfileOwner.java
index 0801977..18664fb 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoProfileOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasNoProfileOwner.java
@@ -31,7 +31,7 @@
* <p>You can use {@link DeviceState} to ensure that the device enters the correct state for the
* method.
*/
-@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnsureHasNoProfileOwner {
/** Which user type the profile owner should not be attached to. */
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasProfileOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasProfileOwner.java
index 4526630..05821ad 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasProfileOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/EnsureHasProfileOwner.java
@@ -32,11 +32,18 @@
* the correct state for the method. If using {@link DeviceState}, you can use
* {@link DeviceState#profileOwner()} to interact with the profile owner.
*/
-@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EnsureHasProfileOwner {
/** Which user type the work profile should be attached to. */
DeviceState.UserType onUser() default CURRENT_USER;
+
+ /**
+ * Whether this DPC should be returned by calls to {@link DeviceState#dpc()}.
+ *
+ * <p>Only one device policy controller per test should be marked as primary.
+ */
+ boolean isPrimary() default false;
}
// TODO(scottjonathan): Is there a feature or something that we need to check to make sure PO is
// supported?
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/NegativePolicyTest.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/NegativePolicyTest.java
index c5a9ef4..5e2564d 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/NegativePolicyTest.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/enterprise/NegativePolicyTest.java
@@ -27,7 +27,7 @@
* Mark a test as testing the states where a policy is applied (by a Device Owner or Profile Owner)
* and it should not apply to the user the test is running on.
*
- * <p>This will generated parameterized runs for all matching states.
+ * <p>This will generate parameterized runs for all matching states.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/client/ClientSuggestionsTest.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/meta/RepeatingAnnotation.java
similarity index 60%
copy from tests/autofillservice/src/android/autofillservice/cts/client/ClientSuggestionsTest.java
copy to common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/meta/RepeatingAnnotation.java
index 895ec3a..5556120 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/client/ClientSuggestionsTest.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/meta/RepeatingAnnotation.java
@@ -13,13 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.autofillservice.cts.client;
-import android.autofillservice.cts.commontests.ClientSuggestionsCommonTestCase;
+package com.android.bedstead.harrier.annotations.meta;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
/**
- * Tests client suggestions behaviors for the dropdown mode.
+ * Indicates that an annotation is a collection of repeated annotations.
*/
-public class ClientSuggestionsTest extends ClientSuggestionsCommonTestCase {
-
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RepeatingAnnotation {
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/meta/RequiresBedsteadJUnit4.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/meta/RequiresBedsteadJUnit4.java
index 9033b90..4bbd2d6 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/meta/RequiresBedsteadJUnit4.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/meta/RequiresBedsteadJUnit4.java
@@ -24,7 +24,7 @@
import java.lang.annotation.Target;
/**
- * Indicates that an annotation requires using the {@link BedsteadJUnit4} test runner
+ * Indicates that an annotation requires using the {@link BedsteadJUnit4} test runner.
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnDeviceOwnerUser.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnDeviceOwnerUser.java
index 75755e0..6d44e6e 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnDeviceOwnerUser.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnDeviceOwnerUser.java
@@ -32,6 +32,6 @@
@Retention(RetentionPolicy.RUNTIME)
@ParameterizedAnnotation
@RequireRunOnPrimaryUser
-@EnsureHasDeviceOwner
+@EnsureHasDeviceOwner(isPrimary = true)
public @interface IncludeRunOnDeviceOwnerUser {
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser.java
index d32f9ba..7bff643 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser.java
@@ -34,10 +34,7 @@
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ParameterizedAnnotation
-// TODO(scottjonathan): We can't set the device owner when we're already running on secondary user
-// so we need to have a different @RequireRunOn annotation for a secondary user when a DO is
-// already set
@RequireRunOnSecondaryUser
-@EnsureHasDeviceOwner(onUser = SYSTEM_USER)
+@EnsureHasDeviceOwner(onUser = SYSTEM_USER, isPrimary = true)
public @interface IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser {
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfProfileOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfProfileOwner.java
new file mode 100644
index 0000000..2eb8ab0
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfProfileOwner.java
@@ -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 com.android.bedstead.harrier.annotations.parameterized;
+
+import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
+import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
+import com.android.bedstead.harrier.annotations.meta.ParameterizedAnnotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Parameterize a test so that it runs on the parent of a profile owner.
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@ParameterizedAnnotation
+@RequireRunOnPrimaryUser
+@EnsureHasWorkProfile(dpcIsPrimary = true)
+public @interface IncludeRunOnParentOfProfileOwner {
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnProfileOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnProfileOwner.java
index 69af670..baa59fe 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnProfileOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnProfileOwner.java
@@ -30,6 +30,6 @@
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ParameterizedAnnotation
-@RequireRunOnWorkProfile
+@RequireRunOnWorkProfile(dpcIsPrimary = true)
public @interface IncludeRunOnProfileOwner {
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner.java
index 40f4b44..cf14dfb 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner.java
@@ -34,6 +34,6 @@
@Retention(RetentionPolicy.RUNTIME)
@ParameterizedAnnotation
@RequireRunOnSecondaryUser
-@EnsureHasWorkProfile(forUser = DeviceState.UserType.PRIMARY_USER)
+@EnsureHasWorkProfile(forUser = DeviceState.UserType.PRIMARY_USER, dpcIsPrimary = true)
public @interface IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner {
}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/LockTaskPackages.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/LockTaskPackages.java
new file mode 100644
index 0000000..3f6d18c
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/policies/LockTaskPackages.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier.policies;
+
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.DeviceOwnerControl.USER;
+import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.ProfileOwnerControl.NO;
+
+import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
+
+@EnterprisePolicy(deviceOwner = USER, profileOwner = NO)
+public class LockTaskPackages {
+}
diff --git a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateClassAnnotationTest.java b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateClassAnnotationTest.java
new file mode 100644
index 0000000..b2b0cc4
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateClassAnnotationTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier;
+
+import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
+import com.android.bedstead.nene.TestApis;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(BedsteadJUnit4.class)
+@EnsureHasWorkProfile
+public class DeviceStateClassAnnotationTest {
+
+ @ClassRule
+ @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
+ private static final TestApis sTestApis = new TestApis();
+
+ @Test
+ public void ensureHasWorkProfileAnnotationOnClass_workProfileExists() {
+ assertThat(sTestApis.users().findProfileOfType(
+ sTestApis.users().supportedType(MANAGED_PROFILE_TYPE_NAME),
+ sTestApis.users().instrumented())
+ ).isNotNull();
+ }
+}
diff --git a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateClassAnnotationWithoutBedsteadJunit4Test.java b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateClassAnnotationWithoutBedsteadJunit4Test.java
new file mode 100644
index 0000000..594a103
--- /dev/null
+++ b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateClassAnnotationWithoutBedsteadJunit4Test.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.harrier;
+
+import static com.android.bedstead.nene.users.UserType.SECONDARY_USER_TYPE_NAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
+import com.android.bedstead.nene.TestApis;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@EnsureHasSecondaryUser
+public class DeviceStateClassAnnotationWithoutBedsteadJunit4Test {
+
+ @ClassRule
+ @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
+ private static final TestApis sTestApis = new TestApis();
+
+ @Test
+ public void ensureHasSecondaryUserAnnotationOnClass_secondaryUserExists() {
+ assertThat(sTestApis.users().findUserOfType(
+ sTestApis.users().supportedType(SECONDARY_USER_TYPE_NAME))
+ ).isNotNull();
+ }
+}
diff --git a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
index 9c0d2aa..0a1f55a 100644
--- a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
+++ b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
@@ -21,13 +21,19 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static com.android.bedstead.harrier.DeviceState.UserType.ANY;
+import static com.android.bedstead.harrier.annotations.RequireAospBuild.GMS_CORE_PACKAGE;
+import static com.android.bedstead.harrier.annotations.RequireCnGmsBuild.CHINA_GOOGLE_SERVICES_FEATURE;
import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME;
import static com.android.bedstead.nene.users.UserType.SECONDARY_USER_TYPE_NAME;
+import static com.android.bedstead.nene.users.UserType.SYSTEM_USER_TYPE_NAME;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
+import android.os.Build;
+
import com.android.bedstead.harrier.annotations.EnsureDoesNotHavePermission;
import com.android.bedstead.harrier.annotations.EnsureHasNoSecondaryUser;
import com.android.bedstead.harrier.annotations.EnsureHasNoTvProfile;
@@ -36,8 +42,20 @@
import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
import com.android.bedstead.harrier.annotations.EnsureHasTvProfile;
import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
+import com.android.bedstead.harrier.annotations.EnsurePackageNotInstalled;
+import com.android.bedstead.harrier.annotations.RequireAospBuild;
+import com.android.bedstead.harrier.annotations.RequireCnGmsBuild;
+import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeature;
+import com.android.bedstead.harrier.annotations.RequireFeature;
+import com.android.bedstead.harrier.annotations.RequireGmsBuild;
+import com.android.bedstead.harrier.annotations.RequireNotCnGmsBuild;
+import com.android.bedstead.harrier.annotations.RequirePackageInstalled;
+import com.android.bedstead.harrier.annotations.RequirePackageNotInstalled;
+import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
import com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser;
+import com.android.bedstead.harrier.annotations.RequireRunOnTvProfile;
import com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile;
+import com.android.bedstead.harrier.annotations.RequireSdkVersion;
import com.android.bedstead.harrier.annotations.RequireUserSupported;
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner;
@@ -45,13 +63,15 @@
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasProfileOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnDeviceOwnerUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser;
+import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfProfileOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnProfileOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwner;
import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.users.UserReference;
-import com.android.bedstead.nene.users.UserType;
import org.junit.ClassRule;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -176,7 +196,7 @@
@EnsureHasNoSecondaryUser
public void secondaryUser_createdSecondaryUser_throwsException() {
try (UserReference secondaryUser = sTestApis.users().createUser()
- .type(sTestApis.users().supportedType(UserType.SECONDARY_USER_TYPE_NAME))
+ .type(sTestApis.users().supportedType(SECONDARY_USER_TYPE_NAME))
.create()) {
assertThrows(IllegalStateException.class, sDeviceState::secondaryUser);
}
@@ -186,7 +206,7 @@
@EnsureHasSecondaryUser
public void ensureHasSecondaryUserAnnotation_secondaryUserExists() {
assertThat(sTestApis.users().findUserOfType(
- sTestApis.users().supportedType(UserType.SECONDARY_USER_TYPE_NAME))
+ sTestApis.users().supportedType(SECONDARY_USER_TYPE_NAME))
).isNotNull();
}
@@ -197,7 +217,7 @@
@EnsureHasNoSecondaryUser
public void ensureHasNoSecondaryUserAnnotation_secondaryUserDoesNotExist() {
assertThat(sTestApis.users().findUserOfType(
- sTestApis.users().supportedType(UserType.SECONDARY_USER_TYPE_NAME))
+ sTestApis.users().supportedType(SECONDARY_USER_TYPE_NAME))
).isNull();
}
@@ -389,4 +409,129 @@
// TODO(scottjonathan): Assert profile groups are different
}
+
+ @RequirePackageInstalled(value = GMS_CORE_PACKAGE, onUser = ANY)
+ public void requirePackageInstalledAnnotation_anyUser_packageIsInstalled() {
+ assertThat(sTestApis.packages().find(GMS_CORE_PACKAGE).resolve()).isNotNull();
+ }
+
+ @Test
+ @RequirePackageInstalled(GMS_CORE_PACKAGE)
+ public void requirePackageInstalledAnnotation_currentUser_packageIsInstalled() {
+ assertThat(sTestApis.packages().find(GMS_CORE_PACKAGE).resolve().installedOnUsers())
+ .contains(sTestApis.users().instrumented());
+ }
+
+ @Test
+ @RequirePackageNotInstalled(value = GMS_CORE_PACKAGE, onUser = ANY)
+ public void requirePackageNotInstalledAnnotation_anyUser_packageIsNotInstalled() {
+ assertThat(sTestApis.packages().find(GMS_CORE_PACKAGE).resolve()).isNull();
+
+ }
+
+ @Test
+ @RequirePackageNotInstalled(GMS_CORE_PACKAGE)
+ public void requirePackageNotInstalledAnnotation_currentUser_packageIsNotInstalled() {
+ Package resolvedPackage = sTestApis.packages().find(GMS_CORE_PACKAGE).resolve();
+
+ if (resolvedPackage != null) {
+ assertThat(resolvedPackage.installedOnUsers())
+ .doesNotContain(sTestApis.users().instrumented());
+ }
+ }
+
+ @Test
+ @EnsurePackageNotInstalled(value = GMS_CORE_PACKAGE, onUser = ANY)
+ @Ignore // TODO(scottjonathan): Restore this with a package which can be uninstalled
+ public void ensurePackageNotInstalledAnnotation_anyUser_packageIsNotInstalled() {
+ assertThat(sTestApis.packages().find(GMS_CORE_PACKAGE).resolve()).isNull();
+ }
+
+ @Test
+ @EnsurePackageNotInstalled(GMS_CORE_PACKAGE)
+ @Ignore // TODO(scottjonathan): Restore this with a package which can be uninstalled
+ public void ensurePackageNotInstalledAnnotation_currentUser_packageIsNotInstalled() {
+ Package resolvedPackage = sTestApis.packages().find(GMS_CORE_PACKAGE).resolve();
+
+ if (resolvedPackage != null) {
+ assertThat(resolvedPackage.installedOnUsers())
+ .doesNotContain(sTestApis.users().instrumented());
+ }
+ }
+
+ @Test
+ @RequireAospBuild
+ public void requireAospBuildAnnotation_isRunningOnAospBuild() {
+ assertThat(sTestApis.packages().find(GMS_CORE_PACKAGE).resolve()).isNull();
+ }
+
+ @Test
+ @RequireGmsBuild
+ public void requireGmsBuildAnnotation_isRunningOnGmsbuild() {
+ assertThat(sTestApis.packages().find(GMS_CORE_PACKAGE).resolve()).isNotNull();
+ }
+
+ @Test
+ @RequireCnGmsBuild
+ public void requireCnGmsBuildAnnotation_isRunningOnCnGmsBuild() {
+ assertThat(sTestApis.packages().features()).contains(CHINA_GOOGLE_SERVICES_FEATURE);
+ }
+
+ @Test
+ @RequireNotCnGmsBuild
+ public void requireNotCnGmsBuildAnnotation_isNotRunningOnCnGmsBuild() {
+ assertThat(sTestApis.packages().features()).doesNotContain(CHINA_GOOGLE_SERVICES_FEATURE);
+
+ }
+
+ @Test
+ @RequireFeature(CHINA_GOOGLE_SERVICES_FEATURE)
+ public void requireHasFeatureAnnotation_doesNotHaveFeature() {
+ assertThat(sTestApis.packages().features()).contains(CHINA_GOOGLE_SERVICES_FEATURE);
+ }
+
+ @Test
+ @RequireDoesNotHaveFeature(CHINA_GOOGLE_SERVICES_FEATURE)
+ public void requireDoesNotHaveFeatureAnnotation_doesNotHaveFeature() {
+ assertThat(sTestApis.packages().features()).doesNotContain(CHINA_GOOGLE_SERVICES_FEATURE);
+ }
+
+ @Test
+ @RequireSdkVersion(min = 27)
+ public void requireSdkVersionAnnotation_min_minIsMet() {
+ assertThat(Build.VERSION.SDK_INT).isGreaterThan(26);
+ }
+
+ @Test
+ @RequireSdkVersion(max = 30)
+ public void requireSdkVersionAnnotation_max_maxIsMet() {
+ assertThat(Build.VERSION.SDK_INT).isLessThan(31);
+ }
+
+ @Test
+ @RequireSdkVersion(min = 27, max = 30)
+ public void requireSdkVersionAnnotation_minAndMax_bothAreMet() {
+ assertThat(Build.VERSION.SDK_INT).isGreaterThan(26);
+ assertThat(Build.VERSION.SDK_INT).isLessThan(31);
+ }
+
+ @Test
+ @RequireRunOnPrimaryUser
+ public void requireRunOnPrimaryUserAnnotation_isRunningOnPrimaryUser() {
+ assertThat(sTestApis.users().instrumented().resolve().type().name())
+ .isEqualTo(SYSTEM_USER_TYPE_NAME);
+ }
+
+ @Test
+ @RequireRunOnTvProfile
+ public void requireRunOnTvProfileAnnotation_isRunningOnTvProfile() {
+ assertThat(sTestApis.users().instrumented().resolve().type().name())
+ .isEqualTo(TV_PROFILE_TYPE_NAME);
+ }
+
+ @Test
+ @IncludeRunOnParentOfProfileOwner
+ public void includeRunOnParentOfProfileOwnerAnnotation_isRunningOnParentOfProfileOwner() {
+ assertThat(sDeviceState.workProfile()).isNotNull();
+ }
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/AdbDevicePolicyParser27.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/AdbDevicePolicyParser27.java
index 3a20f6c..de24e0d 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/AdbDevicePolicyParser27.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/AdbDevicePolicyParser27.java
@@ -71,7 +71,7 @@
"ComponentInfo\\{", 2)[1].split("\\}", 2)[0]);
int userId = Integer.parseInt(deviceOwnerSection.split(
"User ID: ", 2)[1].split("\n", 2)[0]);
- return new DeviceOwner(mTestApis.users().find(userId),
+ return new DeviceOwner(mTestApis, mTestApis.users().find(userId),
mTestApis.packages().find(componentName.getPackageName()), componentName);
}
@@ -114,7 +114,7 @@
"ComponentInfo\\{", 2)[1].split("\\}", 2)[0]);
int userId = Integer.parseInt(
profileOwnerSection.split("\\(User ", 2)[1].split("\\)", 2)[0]);
- return new ProfileOwner(mTestApis.users().find(userId),
+ return new ProfileOwner(mTestApis, mTestApis.users().find(userId),
mTestApis.packages().find(componentName.getPackageName()), componentName);
}
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DeviceOwner.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DeviceOwner.java
index 3d9a4ea..c46791b 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DeviceOwner.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DeviceOwner.java
@@ -18,6 +18,7 @@
import android.content.ComponentName;
+import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.exceptions.AdbException;
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.packages.PackageReference;
@@ -32,10 +33,11 @@
*/
public final class DeviceOwner extends DevicePolicyController {
- DeviceOwner(UserReference user,
+ DeviceOwner(TestApis testApis,
+ UserReference user,
PackageReference pkg,
ComponentName componentName) {
- super(user, pkg, componentName);
+ super(testApis, user, pkg, componentName);
}
@Override
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
index 8ed118e..1ef0146 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
@@ -16,13 +16,21 @@
package com.android.bedstead.nene.devicepolicy;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.os.Build.VERSION.SDK_INT;
+import static com.android.bedstead.nene.permissions.Permissions.MANAGE_DEVICE_ADMINS;
+import static com.android.bedstead.nene.permissions.Permissions.MANAGE_PROFILE_AND_DEVICE_OWNERS;
+
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.provider.Settings;
import androidx.annotation.Nullable;
@@ -36,10 +44,15 @@
import com.android.bedstead.nene.users.UserReference;
import com.android.bedstead.nene.utils.ShellCommand;
import com.android.bedstead.nene.utils.ShellCommandUtils;
+import com.android.bedstead.nene.utils.Versions;
+import com.android.compatibility.common.util.PollingCheck;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
@@ -47,6 +60,8 @@
*/
public final class DevicePolicy {
+ private static final String USER_SETUP_COMPLETE_KEY = "user_setup_complete";
+
private final TestApis mTestApis;
private final AdbDevicePolicyParser mParser;
@@ -75,49 +90,44 @@
.addOperand(profileOwnerComponent.flattenToShortString())
.validate(ShellCommandUtils::startsWithSuccess);
- try {
- command.execute();
- } catch (AdbException e) {
- // If it fails, we check for terminal failure states - and if not we retry because if
- // the profile owner was recently removed, it can take some time to be allowed to set
- // it again
-
- ProfileOwner profileOwner = getProfileOwner(user);
- if (profileOwner != null) {
- // TODO(scottjonathan): Should we actually fail here if the component name is the
- // same?
-
- throw new NeneException(
- "Could not set profile owner for user " + user
- + " as a profile owner is already set: " + profileOwner);
- }
-
- PackageReference pkg = mTestApis.packages().find(
- profileOwnerComponent.getPackageName());
- if (!mTestApis.packages().installedForUser(user).contains(pkg)) {
- throw new NeneException(
- "Could not set profile owner for user " + user
- + " as the package " + pkg + " is not installed");
- }
-
- if (!componentCanBeSetAsDeviceAdmin(profileOwnerComponent, user)) {
- throw new NeneException("Could not set profile owner for user "
- + user + " as component " + profileOwnerComponent + " is not valid");
- }
-
- try {
- command.executeUntilValid();
- } catch (AdbException | InterruptedException e2) {
- throw new NeneException("Could not set profile owner for user " + user
- + " component " + profileOwnerComponent, e2);
- }
- }
-
- return new ProfileOwner(user,
+ // TODO(b/187925230): If it fails, we check for terminal failure states - and if not
+ // we retry because if the profile owner was recently removed, it can take some time
+ // to be allowed to set it again
+ retryIfNotTerminal(
+ () -> command.executeOrThrowNeneException("Could not set profile owner for user "
+ + user + " component " + profileOwnerComponent),
+ () -> checkForTerminalProfileOwnerFailures(user, profileOwnerComponent));
+ return new ProfileOwner(mTestApis, user,
mTestApis.packages().find(
profileOwnerComponent.getPackageName()), profileOwnerComponent);
}
+ private void checkForTerminalProfileOwnerFailures(
+ UserReference user, ComponentName profileOwnerComponent) {
+ ProfileOwner profileOwner = getProfileOwner(user);
+ if (profileOwner != null) {
+ // TODO(scottjonathan): Should we actually fail here if the component name is the
+ // same?
+
+ throw new NeneException(
+ "Could not set profile owner for user " + user
+ + " as a profile owner is already set: " + profileOwner);
+ }
+
+ PackageReference pkg = mTestApis.packages().find(
+ profileOwnerComponent.getPackageName());
+ if (!mTestApis.packages().installedForUser(user).contains(pkg)) {
+ throw new NeneException(
+ "Could not set profile owner for user " + user
+ + " as the package " + pkg + " is not installed");
+ }
+
+ if (!componentCanBeSetAsDeviceAdmin(profileOwnerComponent, user)) {
+ throw new NeneException("Could not set profile owner for user "
+ + user + " as component " + profileOwnerComponent + " is not valid");
+ }
+ }
+
/**
* Get the profile owner for a given {@link UserReference}.
*/
@@ -137,43 +147,151 @@
throw new NullPointerException();
}
- // TODO: use setDeviceOwner on S+
+ if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) {
+ return setDeviceOwnerPreS(user, deviceOwnerComponent);
+ }
+ DevicePolicyManager devicePolicyManager =
+ mTestApis.context().instrumentedContext()
+ .getSystemService(DevicePolicyManager.class);
+
+ boolean userSetupComplete = getUserSetupComplete();
+
+ try {
+ setUserSetupComplete(false);
+
+ try (PermissionContext p =
+ mTestApis.permissions().withPermission(
+ MANAGE_PROFILE_AND_DEVICE_OWNERS, MANAGE_DEVICE_ADMINS,
+ INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS)) {
+ devicePolicyManager.setActiveAdmin(deviceOwnerComponent,
+ /* refreshing= */ true, user.id());
+
+ // TODO(b/187925230): If it fails, we check for terminal failure states - and if not
+ // we retry because if the DO/PO was recently removed, it can take some time
+ // to be allowed to set it again
+ retryIfNotTerminal(
+ () -> devicePolicyManager.setDeviceOwner(
+ deviceOwnerComponent, "Nene", user.id()),
+ () -> checkForTerminalDeviceOwnerFailures(
+ user, deviceOwnerComponent, /* allowAdditionalUsers= */ true));
+ } catch (IllegalArgumentException | IllegalStateException | SecurityException e) {
+ throw new NeneException("Error setting device owner", e);
+ }
+ } finally {
+ setUserSetupComplete(userSetupComplete);
+ }
+
+ return new DeviceOwner(mTestApis, user,
+ mTestApis.packages().find(
+ deviceOwnerComponent.getPackageName()), deviceOwnerComponent);
+ }
+
+ /**
+ * Runs {@code operation}. If it fails, runs {@code terminalCheck} and then retries
+ * {@code operation} until it does not fail or for a maximum of 30 seconds.
+ *
+ * <p>The {@code operation} is considered to be successful if it does not throw an exception
+ */
+ private void retryIfNotTerminal(
+ Runnable operation, Runnable terminalCheck,
+ Class<? extends RuntimeException>... exceptions) {
+ Set<Class<? extends RuntimeException>> exceptionSet =
+ new HashSet<>(Arrays.asList(exceptions));
+ try {
+ operation.run();
+ } catch (RuntimeException e) {
+ if (!exceptionSet.contains(e.getClass())) {
+ throw e;
+ }
+
+ terminalCheck.run();
+
+ try {
+ PollingCheck.waitFor(30_000, () -> {
+ try {
+ operation.run();
+ return true;
+ } catch (RuntimeException e2) {
+ if (!exceptionSet.contains(e2.getClass())) {
+ throw e2;
+ }
+ return false;
+ }
+ });
+ } catch (AssertionError e3) {
+ operation.run();
+ }
+ }
+ }
+
+
+ private void setUserSetupComplete(boolean complete) {
+ DevicePolicyManager devicePolicyManager =
+ mTestApis.context().instrumentedContext()
+ .getSystemService(DevicePolicyManager.class);
+ try (PermissionContext p = mTestApis.permissions().withPermission(
+ WRITE_SECURE_SETTINGS, MANAGE_PROFILE_AND_DEVICE_OWNERS,
+ INTERACT_ACROSS_USERS_FULL)) {
+ Settings.Secure.putInt(mTestApis.context().androidContextAsUser(
+ mTestApis.users().system()).getContentResolver(),
+ USER_SETUP_COMPLETE_KEY, complete ? 1 : 0);
+ devicePolicyManager.forceUpdateUserSetupComplete(mTestApis.users().system().id());
+ }
+ }
+
+ private boolean getUserSetupComplete() {
+ return Settings.Secure.getInt(
+ mTestApis.context().instrumentedContext().getContentResolver(),
+ USER_SETUP_COMPLETE_KEY, /* def= */ 0) == 1;
+ }
+
+ private DeviceOwner setDeviceOwnerPreS(UserReference user, ComponentName deviceOwnerComponent) {
ShellCommand.Builder command = ShellCommand.builderForUser(
user, "dpm set-device-owner")
.addOperand(deviceOwnerComponent.flattenToShortString())
.validate(ShellCommandUtils::startsWithSuccess);
- try {
- command.execute();
- } catch (AdbException e) {
- // If it fails, we check for terminal failure states - and if not we retry because if
- // the device owner was recently removed, it can take some time to be allowed to set
- // it again
+ // TODO(b/187925230): If it fails, we check for terminal failure states - and if not
+ // we retry because if the device owner was recently removed, it can take some time
+ // to be allowed to set it again
+ retryIfNotTerminal(
+ () -> command.executeOrThrowNeneException("Could not set device owner for user "
+ + user + " component " + deviceOwnerComponent),
+ () -> checkForTerminalDeviceOwnerFailures(
+ user, deviceOwnerComponent, /* allowAdditionalUsers= */ false));
- DeviceOwner deviceOwner = getDeviceOwner();
- if (deviceOwner != null) {
- // TODO(scottjonathan): Should we actually fail here if the component name is the
- // same?
+ return new DeviceOwner(mTestApis, user,
+ mTestApis.packages().find(
+ deviceOwnerComponent.getPackageName()), deviceOwnerComponent);
+ }
- throw new NeneException(
- "Could not set device owner for user " + user
- + " as a device owner is already set: " + deviceOwner);
- }
+ private void checkForTerminalDeviceOwnerFailures(
+ UserReference user, ComponentName deviceOwnerComponent, boolean allowAdditionalUsers) {
+ DeviceOwner deviceOwner = getDeviceOwner();
+ if (deviceOwner != null) {
+ // TODO(scottjonathan): Should we actually fail here if the component name is the
+ // same?
- PackageReference pkg = mTestApis.packages().find(
- deviceOwnerComponent.getPackageName());
- if (!mTestApis.packages().installedForUser(user).contains(pkg)) {
- throw new NeneException(
- "Could not set device owner for user " + user
- + " as the package " + pkg + " is not installed");
- }
+ throw new NeneException(
+ "Could not set device owner for user " + user
+ + " as a device owner is already set: " + deviceOwner);
+ }
- if (!componentCanBeSetAsDeviceAdmin(deviceOwnerComponent, user)) {
- throw new NeneException("Could not set device owner for user "
- + user + " as component " + deviceOwnerComponent + " is not valid");
- }
+ PackageReference pkg = mTestApis.packages().find(
+ deviceOwnerComponent.getPackageName());
+ if (!mTestApis.packages().installedForUser(user).contains(pkg)) {
+ throw new NeneException(
+ "Could not set device owner for user " + user
+ + " as the package " + pkg + " is not installed");
+ }
+ if (!componentCanBeSetAsDeviceAdmin(deviceOwnerComponent, user)) {
+ throw new NeneException("Could not set device owner for user "
+ + user + " as component " + deviceOwnerComponent + " is not valid");
+ }
+
+ if (!allowAdditionalUsers) {
Collection<User> users = mTestApis.users().all();
if (users.size() > 1) {
@@ -181,19 +299,8 @@
+ user + " as there are already additional users on the device: " + users);
}
- // TODO(scottjonathan): Check accounts
-
- try {
- command.executeUntilValid();
- } catch (AdbException | InterruptedException e2) {
- throw new NeneException("Could not set device owner for user " + user
- + " component " + deviceOwnerComponent, e2);
- }
}
-
- return new DeviceOwner(user,
- mTestApis.packages().find(
- deviceOwnerComponent.getPackageName()), deviceOwnerComponent);
+ // TODO(scottjonathan): Check accounts
}
private boolean componentCanBeSetAsDeviceAdmin(ComponentName component, UserReference user) {
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicyController.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicyController.java
index 9a8aff3..059712f 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicyController.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicyController.java
@@ -19,6 +19,7 @@
import android.app.admin.DeviceAdminReceiver;
import android.content.ComponentName;
+import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.packages.PackageReference;
import com.android.bedstead.nene.users.UserReference;
@@ -29,15 +30,18 @@
*/
public abstract class DevicePolicyController implements AutoCloseable {
+ protected final TestApis mTestApis;
protected final UserReference mUser;
protected final PackageReference mPackage;
protected final ComponentName mComponentName;
- DevicePolicyController(UserReference user, PackageReference pkg, ComponentName componentName) {
- if (user == null || pkg == null || componentName == null) {
+ DevicePolicyController(TestApis testApis,
+ UserReference user, PackageReference pkg, ComponentName componentName) {
+ if (testApis == null || user == null || pkg == null || componentName == null) {
throw new NullPointerException();
}
+ mTestApis = testApis;
mUser = user;
mPackage = pkg;
mComponentName = componentName;
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/ProfileOwner.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/ProfileOwner.java
index 7fb21a8..6074421 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/ProfileOwner.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/ProfileOwner.java
@@ -18,6 +18,7 @@
import android.content.ComponentName;
+import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.exceptions.AdbException;
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.packages.PackageReference;
@@ -31,10 +32,12 @@
* A reference to a Profile Owner.
*/
public final class ProfileOwner extends DevicePolicyController {
- ProfileOwner(UserReference user,
+
+ ProfileOwner(TestApis testApis,
+ UserReference user,
PackageReference pkg,
ComponentName componentName) {
- super(user, pkg, componentName);
+ super(testApis, user, pkg, componentName);
}
@Override
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
index 298db9d..3b6ec83 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
@@ -46,6 +46,11 @@
static final class MutableUserPackage {
Set<String> mGrantedPermissions;
+
+ @Override
+ public String toString() {
+ return "UserPackage{grantedPermissions=" + mGrantedPermissions + "}";
+ }
}
private final MutablePackage mMutablePackage;
@@ -102,7 +107,7 @@
public String toString() {
StringBuilder stringBuilder = new StringBuilder("Package{");
stringBuilder.append("packageName=" + mMutablePackage.mPackageName);
- stringBuilder.append("installedOnUsers=" + mMutablePackage.mInstalledOnUsers);
+ stringBuilder.append(", installedOnUsers=" + mMutablePackage.mInstalledOnUsers);
stringBuilder.append("}");
return stringBuilder.toString();
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/PackageReference.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/PackageReference.java
index f15d9ad..82b36a5 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/PackageReference.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/PackageReference.java
@@ -95,6 +95,24 @@
}
/**
+ * Uninstall the package for all users.
+ *
+ * <p>The package will no longer {@link #resolve()}.
+ */
+ public PackageReference uninstallFromAllUsers() {
+ Package pkg = resolve();
+ if (pkg == null) {
+ return this;
+ }
+
+ for (UserReference user : pkg.installedOnUsers()) {
+ pkg.uninstall(user);
+ }
+
+ return this;
+ }
+
+ /**
* Uninstall the package for the given user.
*
* <p>If this is the last user which has this package installed, then the package will no
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java
index 3d01d3c..b4f5d62 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java
@@ -126,10 +126,17 @@
throw new NullPointerException();
}
- if (Versions.isRunningOn(Versions.S, "S")) {
+ if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) {
return install(user, loadBytes(apkFile));
}
+ User resolvedUser = user.resolve();
+
+ if (resolvedUser == null || resolvedUser.state() != RUNNING_UNLOCKED) {
+ throw new NeneException("Packages can not be installed in non-started users "
+ + "(Trying to install into user " + resolvedUser + ")");
+ }
+
BlockingBroadcastReceiver broadcastReceiver = BlockingBroadcastReceiver.create(
mTestApis.context().instrumentedContext(), mPackageAddedIntentFilter);
broadcastReceiver.register();
@@ -144,12 +151,6 @@
return waitForPackageAddedBroadcast(broadcastReceiver);
} catch (AdbException e) {
- User resolvedUser = user.resolve();
-
- if (resolvedUser == null || resolvedUser.state() != RUNNING_UNLOCKED) {
- throw new NeneException("Packages can not be installed in non-started users "
- + "(Trying to install into user " + resolvedUser + ")");
- }
throw new NeneException("Could not install " + apkFile + " for user " + user, e);
} finally {
broadcastReceiver.unregisterQuietly();
@@ -192,7 +193,7 @@
throw new NullPointerException();
}
- // if (!Versions.isRunningOn(Versions.S, "S")) {
+ // if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) {
return installPreS(user, apkFile);
// }
@@ -303,7 +304,7 @@
@RequiresApi(Build.VERSION_CODES.S)
@CheckResult
public KeepUninstalledPackagesBuilder keepUninstalledPackages() {
- Versions.requireS();
+ Versions.requireMinimumVersion(Build.VERSION_CODES.S);
return new KeepUninstalledPackagesBuilder(mTestApis);
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java
index c0b8031..1ede7d8 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java
@@ -39,6 +39,11 @@
/** Permission manager for tests. */
public class Permissions {
+ public static final String MANAGE_PROFILE_AND_DEVICE_OWNERS =
+ "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS";
+
+ public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
+
private static final String LOG_TAG = "Permissions";
private List<PermissionContextImpl> mPermissionContexts = new ArrayList<>();
@@ -229,7 +234,7 @@
}
private void recordExistingPermissions() {
- if (!Versions.isRunningOn(Versions.S, "S")) {
+ if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) {
return;
}
@@ -237,7 +242,7 @@
}
private void restoreExistingPermissions() {
- if (!Versions.isRunningOn(Versions.S, "S")) {
+ if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) {
return;
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java
index 0bada6b..3245462 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java
@@ -20,6 +20,7 @@
import androidx.annotation.Nullable;
import com.android.bedstead.nene.exceptions.AdbException;
+import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.users.UserReference;
import java.util.function.Function;
@@ -128,6 +129,18 @@
return commandBuilder.toString();
}
+ /**
+ * See {@link #execute()} except that any {@link AdbException} is wrapped in a
+ * {@link NeneException} with the message {@code errorMessage}.
+ */
+ public String executeOrThrowNeneException(String errorMessage) throws NeneException {
+ try {
+ return execute();
+ } catch (AdbException e) {
+ throw new NeneException(errorMessage, e);
+ }
+ }
+
/** See {@link ShellCommandUtils#executeCommand(java.lang.String)}. */
public String execute() throws AdbException {
if (mOutputSuccessChecker != null) {
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommandUtils.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommandUtils.java
index 9995e3d..894667d 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommandUtils.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommandUtils.java
@@ -64,7 +64,7 @@
throws AdbException {
logCommand(command, allowEmptyOutput, stdInBytes);
- if (!Versions.isRunningOn(S, "S")) {
+ if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
return executeCommandPreS(command, allowEmptyOutput, stdInBytes);
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Versions.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Versions.java
index bc33792..e9b7922 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Versions.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Versions.java
@@ -16,30 +16,85 @@
package com.android.bedstead.nene.utils;
-import static android.os.Build.VERSION.SDK_INT;
-
import android.os.Build;
-/** Version constants used when VERSION_CODES is not final. */
+import java.lang.reflect.Field;
+
+/** SDK Version checks. */
public final class Versions {
- // TODO(scottjonathan): Replace once S version is final
- public static final int S = 31;
+
+ /** Any version. */
+ public static final int ANY = -1;
+
+ private static final String DEVELOPMENT_CODENAME = "S";
private Versions() {
}
- /** Require that this is running on Android S or above. */
- public static void requireS() {
- if (!isRunningOn(S, "S")) {
+ /**
+ * Throw a {@link UnsupportedOperationException} if the minimum version requirement is not met.
+ */
+ public static void requireMinimumVersion(int min) {
+ if (!meetsSdkVersionRequirements(min, ANY)) {
throw new UnsupportedOperationException(
- "keepUninstalledPackages is only available on S+ (currently "
- + Build.VERSION.CODENAME + ")");
+ "Thie feature is only available on "
+ + versionToLetter(min)
+ + "+ (currently " + Build.VERSION.CODENAME + ")");
}
}
- /** True if the app is running on the given Android version or above. */
- public static boolean isRunningOn(int version, String codename) {
- return (SDK_INT >= version || Build.VERSION.CODENAME.equals(codename));
+ private static String versionToLetter(int version) {
+ for (Field field : Build.VERSION_CODES.class.getFields()) {
+ if (!java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
+ continue;
+ }
+ if (!field.getType().equals(int.class)) {
+ continue;
+ }
+ try {
+ int fieldValue = (int) field.get(null);
+
+ if (fieldValue == version) {
+ return field.getName();
+ }
+ } catch (IllegalAccessException e) {
+ // Couldn't access this variable - ignore
+ }
+ }
+
+ throw new IllegalStateException("Could not find version with code " + version);
+ }
+
+ /**
+ * {@code true} if the minimum version requirement is met.
+ */
+ public static boolean meetsMinimumSdkVersionRequirement(int min) {
+ return meetsSdkVersionRequirements(min, ANY);
+ }
+
+ /**
+ * {@code true} if the minimum and maximum version requirements are met.
+ *
+ * <p>Use {@link #ANY} to accept any version.
+ */
+ public static boolean meetsSdkVersionRequirements(int min, int max) {
+ if (min != ANY) {
+ if (min == Build.VERSION_CODES.CUR_DEVELOPMENT) {
+ if (!Build.VERSION.CODENAME.equals(DEVELOPMENT_CODENAME)) {
+ return false;
+ }
+ } else if (min > Build.VERSION.SDK_INT) {
+ return false;
+ }
+ }
+
+ if (max != ANY
+ && max != Build.VERSION_CODES.CUR_DEVELOPMENT
+ && max < Build.VERSION.SDK_INT) {
+ return false;
+ }
+
+ return true;
}
}
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/devicepolicy/DevicePolicyTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/devicepolicy/DevicePolicyTest.java
index d26fb2c..8da7da3 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/devicepolicy/DevicePolicyTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/devicepolicy/DevicePolicyTest.java
@@ -18,28 +18,44 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.testng.Assert.assertThrows;
import android.content.ComponentName;
+import android.os.Build;
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.EnsureHasNoSecondaryUser;
+import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile;
+import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner;
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoProfileOwner;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.users.UserReference;
import com.android.bedstead.nene.users.UserType;
+import com.android.bedstead.nene.utils.Versions;
import com.android.bedstead.testapp.TestApp;
import com.android.bedstead.testapp.TestAppProvider;
import com.android.eventlib.premade.EventLibDeviceAdminReceiver;
import org.junit.AfterClass;
import org.junit.BeforeClass;
+import org.junit.ClassRule;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-@RunWith(JUnit4.class)
+@RunWith(BedsteadJUnit4.class)
public class DevicePolicyTest {
+ @ClassRule @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
// TODO(180478924): We shouldn't need to hardcode this
private static final String DEVICE_ADMIN_TESTAPP_PACKAGE_NAME = "android.DeviceAdminTestApp";
private static final ComponentName DPC_COMPONENT_NAME =
@@ -70,6 +86,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoWorkProfile
public void setProfileOwner_profileOwnerIsSet() {
UserReference profile = sTestApis.users().createUser()
.parent(sUser)
@@ -88,6 +106,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoWorkProfile
public void setProfileOwner_profileOwnerIsAlreadySet_throwsException() {
UserReference profile = sTestApis.users().createUser()
.parent(sUser)
@@ -106,6 +126,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoWorkProfile
public void setProfileOwner_componentNameNotInstalled_throwsException() {
UserReference profile = sTestApis.users().createUser()
.parent(sUser)
@@ -120,12 +142,16 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setProfileOwner_componentNameIsNotDPC_throwsException() {
assertThrows(NeneException.class,
() -> sTestApis.devicePolicy().setProfileOwner(sUser, NOT_DPC_COMPONENT_NAME));
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setProfileOwner_nullUser_throwsException() {
assertThrows(NullPointerException.class,
() -> sTestApis.devicePolicy().setProfileOwner(
@@ -133,6 +159,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setProfileOwner_nullComponentName_throwsException() {
assertThrows(NullPointerException.class,
() -> sTestApis.devicePolicy().setProfileOwner(
@@ -140,6 +168,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setProfileOwner_userDoesNotExist_throwsException() {
assertThrows(NeneException.class,
() -> sTestApis.devicePolicy().setProfileOwner(
@@ -147,6 +177,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoWorkProfile
public void getProfileOwner_returnsProfileOwner() {
UserReference profile = sTestApis.users().createUser()
.parent(sUser)
@@ -165,6 +197,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoWorkProfile
public void getProfileOwner_noProfileOwner_returnsNull() {
UserReference profile = sTestApis.users().createUser()
.parent(sUser)
@@ -186,6 +220,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setDeviceOwner_deviceOwnerIsSet() {
DeviceOwner deviceOwner =
sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME);
@@ -198,19 +234,15 @@
}
@Test
+ @EnsureHasDeviceOwner
public void setDeviceOwner_deviceOwnerIsAlreadySet_throwsException() {
- DeviceOwner deviceOwner =
- sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME);
-
- try {
- assertThrows(NeneException.class,
- () -> sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME));
- } finally {
- deviceOwner.remove();
- }
+ assertThrows(NeneException.class,
+ () -> sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME));
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setDeviceOwner_componentNameNotInstalled_throwsException() {
sTestApp.reference().uninstall(sUser);
try {
@@ -222,20 +254,40 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setDeviceOwner_componentNameIsNotDPC_throwsException() {
assertThrows(NeneException.class,
() -> sTestApis.devicePolicy().setDeviceOwner(sUser, NOT_DPC_COMPONENT_NAME));
}
@Test
- public void setDeviceOwner_userAlreadyOnDevice_throwsException() {
- UserReference user = sTestApis.users().createUser().create();
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
+ @EnsureHasSecondaryUser
+ public void setDeviceOwner_preS_userAlreadyOnDevice_throwsException() {
+ assumeFalse("After S, device owner can be set with users on the device",
+ Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S));
+
+ assertThrows(NeneException.class,
+ () -> sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME));
+ }
+
+ @Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
+ @EnsureHasSecondaryUser
+ public void setDeviceOwner_sPlus_userAlreadyOnDevice_deviceOwnerIsSet() {
+ assumeTrue("After S, device owner can be set with users on the device",
+ Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S));
+
+ DeviceOwner deviceOwner =
+ sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME);
try {
- assertThrows(NeneException.class,
- () -> sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME));
+ assertThat(sTestApis.devicePolicy().getDeviceOwner()).isNotNull();
} finally {
- user.remove();
+ deviceOwner.remove();
}
}
@@ -245,6 +297,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setDeviceOwner_nullUser_throwsException() {
assertThrows(NullPointerException.class,
() -> sTestApis.devicePolicy().setDeviceOwner(
@@ -252,6 +306,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setDeviceOwner_nullComponentName_throwsException() {
assertThrows(NullPointerException.class,
() -> sTestApis.devicePolicy().setDeviceOwner(
@@ -259,6 +315,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void setDeviceOwner_userDoesNotExist_throwsException() {
assertThrows(NeneException.class,
() -> sTestApis.devicePolicy().setDeviceOwner(
@@ -266,6 +324,11 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
+ // TODO(scottjonathan): This could be made more generic by requiring no additional users
+ @EnsureHasNoSecondaryUser
+ @EnsureHasNoWorkProfile
public void getDeviceOwner_returnsDeviceOwner() {
DeviceOwner deviceOwner =
sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME);
@@ -278,13 +341,14 @@
}
@Test
+ @EnsureHasNoDeviceOwner
public void getDeviceOwner_noDeviceOwner_returnsNull() {
- // We must assume no device owner entering the test
- // TODO(scottjonathan): Encode this assumption in the annotations when Harrier supports
assertThat(sTestApis.devicePolicy().getDeviceOwner()).isNull();
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void profileOwner_autoclose_removesProfileOwner() {
try (ProfileOwner profileOwner =
sTestApis.devicePolicy().setProfileOwner(sUser, DPC_COMPONENT_NAME)) {
@@ -295,6 +359,8 @@
}
@Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
public void deviceOwner_autoclose_removesDeviceOwner() {
try (DeviceOwner deviceOwner =
sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME)) {
@@ -303,4 +369,48 @@
assertThat(sTestApis.devicePolicy().getDeviceOwner()).isNull();
}
+
+ @Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
+ public void setDeviceOwner_recentlyUnsetProfileOwner_sets() {
+ sTestApis.devicePolicy().setProfileOwner(sUser, DPC_COMPONENT_NAME).remove();
+
+ sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME);
+
+ assertThat(sTestApis.devicePolicy().getDeviceOwner()).isNotNull();
+ }
+
+ @Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
+ public void setDeviceOwner_recentlyUnsetDeviceOwner_sets() {
+ sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME).remove();
+
+ sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME);
+
+ assertThat(sTestApis.devicePolicy().getDeviceOwner()).isNotNull();
+ }
+
+ @Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
+ public void setProfileOwner_recentlyUnsetProfileOwner_sets() {
+ sTestApis.devicePolicy().setProfileOwner(sUser, DPC_COMPONENT_NAME).remove();
+
+ sTestApis.devicePolicy().setProfileOwner(sUser, DPC_COMPONENT_NAME);
+
+ assertThat(sTestApis.devicePolicy().getProfileOwner(sUser)).isNotNull();
+ }
+
+ @Test
+ @EnsureHasNoDeviceOwner
+ @EnsureHasNoProfileOwner
+ public void setProfileOwner_recentlyUnsetDeviceOwner_sets() {
+ sTestApis.devicePolicy().setDeviceOwner(sUser, DPC_COMPONENT_NAME).remove();
+
+ sTestApis.devicePolicy().setProfileOwner(sUser, DPC_COMPONENT_NAME);
+
+ assertThat(sTestApis.devicePolicy().getProfileOwner(sUser)).isNotNull();
+ }
}
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageReferenceTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageReferenceTest.java
index 60f1cab..bceea8f 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageReferenceTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageReferenceTest.java
@@ -100,6 +100,25 @@
}
@Test
+ public void uninstallForAllUsers_isUninstalledForAllUsers() {
+ PackageReference pkg = sTestApis.packages().install(sUser, TEST_APP_APK_FILE);
+ try {
+ sTestApis.packages().install(sOtherUser, TEST_APP_APK_FILE);
+
+ mTestAppReference.uninstallFromAllUsers();
+
+ Package resolvedPackage = mTestAppReference.resolve();
+ // Might be null or might still resolve depending on device timing
+ if (resolvedPackage != null) {
+ assertThat(resolvedPackage.installedOnUsers()).isEmpty();
+ }
+ } finally {
+ pkg.uninstall(sUser);
+ pkg.uninstall(sOtherUser);
+ }
+ }
+
+ @Test
public void uninstall_packageIsInstalledForDifferentUser_isUninstalledForUser() {
PackageReference pkg = sTestApis.packages().install(sUser, TEST_APP_APK_FILE);
try {
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackagesTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackagesTest.java
index b9535ef..c02f84a 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackagesTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackagesTest.java
@@ -21,6 +21,8 @@
import static org.junit.Assume.assumeTrue;
import static org.testng.Assert.assertThrows;
+import android.os.Build;
+
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.users.UserReference;
@@ -215,13 +217,9 @@
@Test
public void install_userNotStarted_throwsException() {
- UserReference user = mTestApis.users().createUser().create().stop();
-
- try {
+ try (UserReference user = mTestApis.users().createUser().create().stop()) {
assertThrows(NeneException.class, () -> mTestApis.packages().install(user,
TEST_APP_APK_FILE));
- } finally {
- user.remove();
}
}
@@ -311,7 +309,7 @@
@Test
public void keepUninstalledPackages_packageIsUninstalled_packageStillResolves() {
assumeTrue("keepUninstalledPackages is only supported on S+",
- Versions.isRunningOn(Versions.S, "S"));
+ Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S));
mTestApis.packages().install(mUser, TEST_APP_APK_FILE);
mTestApis.packages().keepUninstalledPackages()
@@ -331,7 +329,7 @@
@Ignore("While using adb calls this is not reliable, enable once we use framework calls for uninstall")
public void keepUninstalledPackages_packageRemovedFromList_packageIsUninstalled_packageDoesNotResolve() {
assumeTrue("keepUninstalledPackages is only supported on S+",
- Versions.isRunningOn(Versions.S, "S"));
+ Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S));
mTestApis.packages().install(mUser, TEST_APP_APK_FILE);
mTestApis.packages().keepUninstalledPackages()
@@ -354,7 +352,7 @@
@Ignore("While using adb calls this is not reliable, enable once we use framework calls for uninstall")
public void keepUninstalledPackages_cleared_packageIsUninstalled_packageDoesNotResolve() {
assumeTrue("keepUninstalledPackages is only supported on S+",
- Versions.isRunningOn(Versions.S, "S"));
+ Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S));
mTestApis.packages().install(mUser, TEST_APP_APK_FILE);
@@ -376,7 +374,7 @@
@Ignore("While using adb calls this is not reliable, enable once we use framework calls for uninstall")
public void keepUninstalledPackages_packageRemovedFromList_packageAlreadyUninstalled_packageDoesNotResolve() {
assumeTrue("keepUninstalledPackages is only supported on S+",
- Versions.isRunningOn(Versions.S, "S"));
+ Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S));
mTestApis.packages().install(mUser, TEST_APP_APK_FILE);
mTestApis.packages().keepUninstalledPackages().add(mTestAppReference).commit();
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java
index 18eeed7..fad6bd6 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java
@@ -127,7 +127,7 @@
assumeTrue("assume shell identity is only available on Q+",
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q);
assumeFalse("After S, all available permissions are held by shell",
- Versions.isRunningOn(Versions.S, "S"));
+ Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S));
try (PermissionContext p =
sTestApis.permissions().withoutPermission(
@@ -213,32 +213,9 @@
}
@Test
- public void withPermissions_androidSAndAbove_restoresPreviousPermissionContext() {
- assumeTrue("restoring permissions is only available on S+",
- Versions.isRunningOn(Versions.S, "S"));
- assumeFalse("After S, all available permissions are held by shell",
- Versions.isRunningOn(Versions.S, "S"));
-
- ShellCommandUtils.uiAutomation()
- .adoptShellPermissionIdentity(DECLARED_PERMISSION_NOT_HELD_BY_SHELL_PRE_S);
-
- try {
- PermissionContext p =
- sTestApis.permissions()
- .withPermission(DECLARED_PERMISSION_NOT_HELD_BY_SHELL_PRE_S);
- p.close();
-
- assertThat(sContext.checkSelfPermission(DECLARED_PERMISSION_NOT_HELD_BY_SHELL_PRE_S))
- .isEqualTo(PERMISSION_DENIED);
- } finally {
- ShellCommandUtils.uiAutomation().dropShellPermissionIdentity();
- }
- }
-
- @Test
public void withoutPermission_androidSAndAbove_restoresPreviousPermissionContext() {
assumeTrue("restoring permissions is only available on S+",
- Versions.isRunningOn(Versions.S, "S"));
+ Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S));
ShellCommandUtils.uiAutomation().adoptShellPermissionIdentity(PERMISSION_HELD_BY_SHELL);
diff --git a/common/device-side/bedstead/remotedpc/src/communication/main/java/com/android/bedstead/remotedpc/managers/RemoteDevicePolicyManager.java b/common/device-side/bedstead/remotedpc/src/communication/main/java/com/android/bedstead/remotedpc/managers/RemoteDevicePolicyManager.java
index f1b79bb..d51063a 100644
--- a/common/device-side/bedstead/remotedpc/src/communication/main/java/com/android/bedstead/remotedpc/managers/RemoteDevicePolicyManager.java
+++ b/common/device-side/bedstead/remotedpc/src/communication/main/java/com/android/bedstead/remotedpc/managers/RemoteDevicePolicyManager.java
@@ -50,4 +50,16 @@
/** See {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()}. */
int getCurrentFailedPasswordAttempts();
+
+
+ /** See {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])}. */
+ void setLockTaskPackages(@NonNull ComponentName admin, @NonNull String[] packages);
+ /** See {@link DevicePolicyManager#setLockTaskPackages(ComponentName, String[])}. */
+ @RemoteDpcAutomaticAdmin void setLockTaskPackages(@NonNull String[] packages);
+
+ /** See {@link DevicePolicyManager#getLockTaskPackages(ComponentName)}. */
+ @NonNull String[] getLockTaskPackages(@NonNull ComponentName admin);
+ /** See {@link DevicePolicyManager#getLockTaskPackages(ComponentName)}. */
+ @RemoteDpcAutomaticAdmin @NonNull String[] getLockTaskPackages();
+
}
diff --git a/common/device-side/bedstead/remotedpc/src/dpc/main/AndroidManifest.xml b/common/device-side/bedstead/remotedpc/src/dpc/main/AndroidManifest.xml
index 2ff01e1..6ab5599 100644
--- a/common/device-side/bedstead/remotedpc/src/dpc/main/AndroidManifest.xml
+++ b/common/device-side/bedstead/remotedpc/src/dpc/main/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.bedstead.remotedpc.dpc">
- <uses-sdk android:minSdkVersion="27" />
+ <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="30" />
<application android:testOnly="true">
<service android:name="com.google.android.enterprise.connectedapps.CrossProfileConnector_Service" android:exported="true" />
</application>
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
index d13e998..f74214b 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/BlockingBroadcastReceiver.java
@@ -61,7 +61,7 @@
public class BlockingBroadcastReceiver extends BroadcastReceiver implements AutoCloseable {
private static final String TAG = "BlockingBroadcast";
- private static final int DEFAULT_TIMEOUT_SECONDS = 60;
+ private static final int DEFAULT_TIMEOUT_SECONDS = 240;
private Intent mReceivedIntent = null;
private final BlockingQueue<Intent> mBlockingQueue;
@@ -215,7 +215,7 @@
@Override
public void close() {
try {
- awaitForBroadcast();
+ awaitForBroadcastOrFail();
} finally {
unregisterQuietly();
}
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 185e714..70c9362 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
@@ -82,19 +82,10 @@
* Return the first API level for this product. If the read-only property is unset,
* this means the first API level is the current API level, and the current API level
* is returned.
- * If vendor partition has older API level than the first API level, this is a GRF
- * device and returns the vendor API level, instead.
*/
public static int getFirstApiLevel() {
int firstApiLevel = getPropertyInt(FIRST_API_LEVEL);
- if (firstApiLevel == INT_VALUE_IF_UNSET) {
- return Build.VERSION.SDK_INT;
- }
- int vendorApiLevel = getVendorApiLevel();
- if (firstApiLevel > vendorApiLevel) {
- return vendorApiLevel;
- }
- return firstApiLevel;
+ return (firstApiLevel == INT_VALUE_IF_UNSET) ? Build.VERSION.SDK_INT : firstApiLevel;
}
/**
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/WifiConfigCreator.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/WifiConfigCreator.java
index 6e35fe8..5aa36c9 100755
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/WifiConfigCreator.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/WifiConfigCreator.java
@@ -34,6 +34,7 @@
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
/**
* A simple activity to create and manage wifi configurations.
@@ -108,6 +109,7 @@
WifiConfiguration conf = getWifiConfigurationBySsid(ssid);
retrievedPacProxyUrl = getPacProxyUrl(conf);
+ Log.d(TAG, "calling removeNetwork(" + netId + ")");
if (!mWifiManager.removeNetwork(netId)) {
throw new IllegalStateException("Failed to remove WifiConfiguration: " + ssid);
}
@@ -132,7 +134,9 @@
conf.setHttpProxy(ProxyInfo.buildPacProxy(Uri.parse(pacProxyUrl)));
}
+ Log.d(TAG, "addNetworkWithProxy(ssid=" + ssid + ", pacProxyUrl=" + pacProxyUrl);
int netId = mWifiManager.addNetwork(conf);
+ Log.d(TAG, "added: netId=" + netId);
if (netId == -1) {
throw new IllegalStateException("Failed to addNetwork: " + ssid);
}
@@ -142,12 +146,13 @@
private WifiConfiguration getWifiConfigurationBySsid(String ssid) {
WifiConfiguration wifiConfiguration = null;
String expectedSsid = wrapInQuotes(ssid);
-
- for (final WifiConfiguration w : mWifiManager.getConfiguredNetworks()) {
+ List<WifiConfiguration> configuredNetworks = getConfiguredNetworksWithLogging();
+ for (WifiConfiguration w : configuredNetworks) {
if (w.SSID.equals(expectedSsid)) {
wifiConfiguration = w;
break;
}
+ Log.v(TAG, "skipping " + w.SSID);
}
if (wifiConfiguration == null) {
throw new IllegalStateException("Failed to get WifiConfiguration for: " + ssid);
@@ -172,8 +177,11 @@
int newNetId = mWifiManager.updateNetwork(conf);
if (newNetId != -1) {
+ Log.v(TAG, "calling saveConfiguration()");
mWifiManager.saveConfiguration();
+ Log.v(TAG, "calling enableNetwork(" + newNetId + ")");
mWifiManager.enableNetwork(newNetId, true);
+ Log.v(TAG, "enabled");
} else {
Log.w(TAG, "Unable to update SSID '" + ssid + "': netId = " + newNetId);
}
@@ -186,21 +194,26 @@
*/
public int updateNetwork(int netId, String ssid, boolean hidden,
int securityType, String password) throws InterruptedException, SecurityException {
+ Log.d(TAG, "updateNetwork(): netId= " + netId + ", ssid=" + ssid + ", hidden=" + hidden);
checkAndEnableWifi();
WifiConfiguration wifiConf = null;
- List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ List<WifiConfiguration> configs = getConfiguredNetworksWithLogging();
for (WifiConfiguration config : configs) {
if (config.networkId == netId) {
wifiConf = config;
break;
}
+ Log.v(TAG, "skipping " + config.networkId);
}
return updateNetwork(wifiConf, ssid, hidden, securityType, password);
}
public boolean removeNetwork(int netId) {
- return mWifiManager.removeNetwork(netId);
+ Log.v(TAG, "calling removeNetwork(" + netId + ")");
+ boolean removed = mWifiManager.removeNetwork(netId);
+ Log.v(TAG, "removed: " + removed);
+ return removed;
}
/**
@@ -264,6 +277,7 @@
final BroadcastReceiver watcher = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Received intent " + intent.getAction());
if (intent.getIntExtra(EXTRA_WIFI_STATE, -1) == WIFI_STATE_ENABLED) {
enabledLatch.countDown();
}
@@ -274,7 +288,9 @@
try {
// In case wifi is not already enabled, wait for it to come up
if (!mWifiManager.isWifiEnabled()) {
+ Log.v(TAG, "Calling setWifiEnabled(true)");
mWifiManager.setWifiEnabled(true);
+ Log.v(TAG, "enabled");
enabledLatch.await(ENABLE_WIFI_WAIT_SEC, TimeUnit.SECONDS);
}
} finally {
@@ -285,5 +301,17 @@
private String wrapInQuotes(String ssid) {
return '"' + ssid + '"';
}
+
+ private List<WifiConfiguration> getConfiguredNetworksWithLogging() {
+ Log.d(TAG, "calling getConfiguredNetworks()");
+ List<WifiConfiguration> configuredNetworks = getConfiguredNetworks();
+ Log.d(TAG, "Got " + configuredNetworks.size() + " networks: "
+ + configuredNetworks.stream().map((c) -> c.SSID).collect(Collectors.toList()));
+ return configuredNetworks;
+ }
+
+ public List<WifiConfiguration> getConfiguredNetworks() {
+ return mWifiManager.getConfiguredNetworks();
+ }
}
diff --git a/hostsidetests/accounts/Android.bp b/hostsidetests/accounts/Android.bp
index 0f79f1f..52a9d0c 100644
--- a/hostsidetests/accounts/Android.bp
+++ b/hostsidetests/accounts/Android.bp
@@ -27,6 +27,7 @@
"platform-test-annotations-host",
"compatibility-host-util",
"truth-prebuilt",
+ "device-policy-log-verifier-util",
],
// tag this module as a cts test artifact
test_suites: [
diff --git a/hostsidetests/accounts/src/android/host/accounts/AccountManagerHostSideTest.java b/hostsidetests/accounts/src/android/host/accounts/AccountManagerHostSideTest.java
new file mode 100644
index 0000000..67335a8
--- /dev/null
+++ b/hostsidetests/accounts/src/android/host/accounts/AccountManagerHostSideTest.java
@@ -0,0 +1,209 @@
+/*
+ * 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.host.accounts;
+
+import static android.host.accounts.AccountManagerXUserTest.runDeviceTests;
+
+import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
+
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.SystemUserOnly;
+import android.stats.devicepolicy.EventId;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.cts.devicepolicy.metrics.DevicePolicyEventWrapper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Host-side tests for {@link android.accounts.AccountManager}
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+@SystemUserOnly
+@AppModeFull(reason = "instant applications cannot see any other application")
+// TODO(b/187939873): Migrate to device side tests.
+public class AccountManagerHostSideTest implements IDeviceTest, IBuildReceiver {
+
+ private static final String TEST_WITH_PERMISSION_APK =
+ "CtsAccountManagerCrossUserApp.apk";
+ private static final String TEST_WITH_PERMISSION_PKG =
+ "com.android.cts.accountmanager";
+ private static final String ACCOUNT_TYPE =
+ "com.android.cts.accountmanager";
+ private static final String AUTH_TOKEN_TYPE = "testAuthTokenType";
+ private static final String REQUIRED_FEATURES_STR = "testRequiredFeature1;testRequiredFeature2";
+
+ private String mOldVerifierValue;
+ private IBuildInfo mCtsBuild;
+ private ITestDevice mDevice;
+ private int mCurrentUser;
+
+ @Override
+ public void setDevice(ITestDevice device) {
+ mDevice = Objects.requireNonNull(device);
+ }
+
+ @Override
+ public ITestDevice getDevice() {
+ return mDevice;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mCurrentUser = getDevice().getCurrentUser();
+
+ mOldVerifierValue =
+ getDevice().executeShellCommand("settings get global package_verifier_enable");
+ getDevice().executeShellCommand("settings put global package_verifier_enable 0");
+
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+ File apkFile = buildHelper.getTestFile(TEST_WITH_PERMISSION_APK);
+ getDevice().installPackageForUser(
+ apkFile,
+ /* reinstall= */ true,
+ /* grantPermissions= */ true,
+ mCurrentUser,
+ /* extraArgs= */"-t");
+ waitForBroadcastIdle();
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ getDevice().uninstallPackage(TEST_WITH_PERMISSION_PKG);
+ getDevice().executeShellCommand("settings put global package_verifier_enable "
+ + mOldVerifierValue);
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ @Test
+ public void tesAddAccount_logsMetrics() throws Exception {
+ assertMetricsLogged(getDevice(), () -> {
+ runDeviceTests(
+ getDevice(),
+ TEST_WITH_PERMISSION_PKG,
+ ".AccountManagerCrossUserTest",
+ "testAccountManager_addMockAccountForCurrentUser",
+ mCurrentUser,
+ /* testArgs= */ null,
+ /* timeout= */ 60L,
+ TimeUnit.SECONDS);
+ }, new DevicePolicyEventWrapper.Builder(EventId.ADD_ACCOUNT_VALUE)
+ .setStrings(ACCOUNT_TYPE,
+ TEST_WITH_PERMISSION_PKG,
+ AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES_STR)
+ .build());
+ }
+
+ @Test
+ public void testStartAddAccountSession_logsMetrics() throws Exception {
+ assertMetricsLogged(getDevice(), () -> {
+ runDeviceTests(
+ getDevice(),
+ TEST_WITH_PERMISSION_PKG,
+ ".AccountManagerCrossUserTest",
+ "testAccountManager_startAddAccountSessionForCurrentUser",
+ mCurrentUser,
+ /* testArgs= */ null,
+ /* timeout= */ 60L,
+ TimeUnit.SECONDS);
+ }, new DevicePolicyEventWrapper.Builder(EventId.ADD_ACCOUNT_VALUE)
+ .setStrings(ACCOUNT_TYPE,
+ TEST_WITH_PERMISSION_PKG,
+ AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES_STR)
+ .build());
+ }
+
+ @Test
+ public void tesAddAccountExplicitly_logsMetrics() throws Exception {
+ final String[] expectedVisibilityStr = new String[]{"0:", "1:", "2:", "3:", "4:"};
+ assertMetricsLogged(getDevice(), () -> {
+ runDeviceTests(
+ getDevice(),
+ TEST_WITH_PERMISSION_PKG,
+ ".AccountManagerCrossUserTest",
+ "testAccountManager_addAccountExplicitlyForCurrentUser",
+ mCurrentUser,
+ /* testArgs= */ null,
+ /* timeout= */ 60L,
+ TimeUnit.SECONDS);
+ }, new DevicePolicyEventWrapper.Builder(EventId.ADD_ACCOUNT_EXPLICITLY_VALUE)
+ .setStrings(ACCOUNT_TYPE,
+ TEST_WITH_PERMISSION_PKG,
+ expectedVisibilityStr)
+ .build());
+ }
+
+ @Test
+ public void tesGetAuthToken_logsMetrics() throws Exception {
+ assertMetricsLogged(getDevice(), () -> {
+ runDeviceTests(
+ getDevice(),
+ TEST_WITH_PERMISSION_PKG,
+ ".AccountManagerCrossUserTest",
+ "testAccountManager_getAuthTokenForCurrentUser",
+ mCurrentUser,
+ /* testArgs= */ null,
+ /* timeout= */ 60L,
+ TimeUnit.SECONDS);
+ }, new DevicePolicyEventWrapper.Builder(EventId.GET_ACCOUNT_AUTH_TOKEN_VALUE)
+ .setStrings(ACCOUNT_TYPE,
+ TEST_WITH_PERMISSION_PKG)
+ .build());
+ }
+
+ protected void waitForBroadcastIdle() throws DeviceNotAvailableException {
+ final CollectingOutputReceiver receiver = new CollectingOutputReceiver();
+ // We allow 8min for the command to complete and 4min for the command to start to
+ // output something.
+ getDevice().executeShellCommand(
+ /* command= */ "am wait-for-broadcast-idle",
+ receiver,
+ /* maxTimeoutForCommand= */ 8,
+ /* maxTimeToOutputShellResponse= */ 4,
+ TimeUnit.MINUTES,
+ /* retryAttempts= */ 0);
+ final String output = receiver.getOutput();
+ if (!output.contains("All broadcast queues are idle!")) {
+ LogUtil.CLog.e("Output from 'am wait-for-broadcast-idle': %s", output);
+ fail("'am wait-for-broadcase-idle' did not complete.");
+ }
+ }
+}
diff --git a/hostsidetests/accounts/test-apps/AccountManagerCrossUserApp/src/com/android/cts/accountmanager/AccountManagerCrossUserTest.java b/hostsidetests/accounts/test-apps/AccountManagerCrossUserApp/src/com/android/cts/accountmanager/AccountManagerCrossUserTest.java
index e7cfda3..d278329 100644
--- a/hostsidetests/accounts/test-apps/AccountManagerCrossUserApp/src/com/android/cts/accountmanager/AccountManagerCrossUserTest.java
+++ b/hostsidetests/accounts/test-apps/AccountManagerCrossUserApp/src/com/android/cts/accountmanager/AccountManagerCrossUserTest.java
@@ -25,10 +25,8 @@
import android.accounts.AccountManagerFuture;
import android.app.UiAutomation;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
-import android.os.Process;
import android.os.UserHandle;
import androidx.test.InstrumentationRegistry;
@@ -44,6 +42,9 @@
private static final Account TEST_ACCOUNT = new Account(MockAuthenticator.ACCOUNT_NAME,
MockAuthenticator.ACCOUNT_TYPE);
+ private static final String AUTH_TOKEN_TYPE = "testAuthTokenType";
+ private static final String[] REQUIRED_FEATURES =
+ new String[]{"testRequiredFeature1", "testRequiredFeature2"};
private Context mContext;
private UiAutomation uiAutomation;
@@ -109,6 +110,20 @@
}
@Test
+ public void testAccountManager_addMockAccountForCurrentUser() throws Exception {
+ AccountManager accountManager = mContext.getSystemService(AccountManager.class);
+
+ accountManager.addAccount(
+ MockAuthenticator.ACCOUNT_TYPE,
+ AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES,
+ /* addAccountOptions= */ null,
+ /* activity= */ null,
+ /* callback= */ null,
+ /* handler= */ null);
+ }
+
+ @Test
public void testAccountManager_getAccountsForTestUser() throws Exception {
UserHandle profileHandle = UserHandle.of(getTestUser());
@@ -191,6 +206,31 @@
assertThat(accountManagerAsUser).isNotNull();
}
+ @Test
+ public void testAccountManager_getAuthTokenForCurrentUser() throws Exception {
+ AccountManager accountManager = mContext.getSystemService(AccountManager.class);
+ MockAuthenticator.addTestAccount(mContext);
+ accountManager.getAuthToken(TEST_ACCOUNT,
+ AUTH_TOKEN_TYPE,
+ /* options= */ null,
+ /* activity= */ null,
+ /* callback= */ null,
+ /* handler= */ null);
+ }
+
+ @Test
+ public void testAccountManager_startAddAccountSessionForCurrentUser() throws Exception {
+ AccountManager accountManager = mContext.getSystemService(AccountManager.class);
+ accountManager.startAddAccountSession(
+ MockAuthenticator.ACCOUNT_TYPE,
+ AUTH_TOKEN_TYPE,
+ REQUIRED_FEATURES,
+ /* options= */ null,
+ /* activity= */ null,
+ /* callback= */ null,
+ /* handler= */ null);
+ }
+
private void assertPermissionRevoked(String permission) throws Exception {
assertThat(mContext.getPackageManager().checkPermission(permission,
mContext.getPackageName())).isEqualTo(PackageManager.PERMISSION_DENIED);
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 73ba922..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
@@ -105,51 +105,51 @@
}
@Test
- public void setOverride_securityExceptionForNonOverridableChangeId() {
+ public void putPackageOverrides_securityExceptionForNonOverridableChangeId() {
SecurityException e = assertThrows(SecurityException.class,
- () -> CompatChanges.addPackageOverrides(OVERRIDE_PACKAGE,
+ () -> 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 setOverride_success() {
- CompatChanges.addPackageOverrides(OVERRIDE_PACKAGE,
+ public void putPackageOverrides_success() {
+ CompatChanges.putPackageOverrides(OVERRIDE_PACKAGE,
Collections.singletonMap(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID,
new PackageOverride.Builder().setEnabled(true).build()));
}
@Test
- public void setOverride_fromVersion2() {
- CompatChanges.addPackageOverrides(OVERRIDE_PACKAGE,
+ 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 setOverride_untilVersion1() {
- CompatChanges.addPackageOverrides(OVERRIDE_PACKAGE,
+ 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 setOverride_securityExceptionForNotHoldingPermission() {
+ 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.addPackageOverrides(OVERRIDE_PACKAGE,
+ () -> 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 clearOverride_securityExceptionForNonOverridableChangeId() {
+ public void removePackageOverrides_securityExceptionForNonOverridableChangeId() {
SecurityException e = assertThrows(SecurityException.class,
() -> CompatChanges.removePackageOverrides(OVERRIDE_PACKAGE,
Collections.singleton(CTS_SYSTEM_API_CHANGEID)));
@@ -157,14 +157,14 @@
}
@Test
- public void clearOverride_doesNothingIfOverrideNotPresent() {
+ public void removePackageOverrides_doesNothingIfOverrideNotPresent() {
CompatChanges.removePackageOverrides(OVERRIDE_PACKAGE,
Collections.singleton(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID));
}
@Test
- public void clearOverride_overridePresentSuccess() {
- CompatChanges.addPackageOverrides(OVERRIDE_PACKAGE,
+ 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,
@@ -172,8 +172,8 @@
}
@Test
- public void clearOverride_securityExceptionForNotHoldingPermission() {
- CompatChanges.addPackageOverrides(OVERRIDE_PACKAGE,
+ public void removePackageOverrides_securityExceptionForNotHoldingPermission() {
+ CompatChanges.putPackageOverrides(OVERRIDE_PACKAGE,
Collections.singletonMap(CTS_SYSTEM_API_OVERRIDABLE_CHANGEID,
new PackageOverride.Builder().setEnabled(true).build()));
diff --git a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesOverrideOnReleaseBuildTest.java b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesOverrideOnReleaseBuildTest.java
index 956d05e..70ba008 100644
--- a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesOverrideOnReleaseBuildTest.java
+++ b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesOverrideOnReleaseBuildTest.java
@@ -48,11 +48,11 @@
runCommand("settings put global force_non_debuggable_final_build_for_compat 0");
}
- public void testSetOverrideSecurityExceptionNonOverridableChangeId() throws Exception {
+ public void testPutPackageOverridesSecurityExceptionNonOverridableChangeId() throws Exception {
installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
- "setOverride_securityExceptionForNonOverridableChangeId",
+ "putPackageOverrides_securityExceptionForNonOverridableChangeId",
/*enabledChanges*/ImmutableSet.of(),
/*disabledChanges*/ ImmutableSet.of());
@@ -63,11 +63,11 @@
assertThat(ctsChange.hasOverrides).isFalse();
}
- public void testSetOverrideSecurityExceptionMissingPermission() throws Exception {
+ public void testPutPackageOverridesSecurityExceptionMissingPermission() throws Exception {
installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
- "setOverride_securityExceptionForNotHoldingPermission",
+ "putPackageOverrides_securityExceptionForNotHoldingPermission",
/*enabledChanges*/ImmutableSet.of(),
/*disabledChanges*/ ImmutableSet.of());
@@ -78,11 +78,11 @@
assertThat(ctsChange.hasOverrides).isFalse();
}
- public void testSetOverrideForAllVersions() throws Exception {
+ public void testPutPackageOverridesForAllVersions() throws Exception {
installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
- "setOverride_success",
+ "putPackageOverrides_success",
/*enabledChanges*/ImmutableSet.of(),
/*disabledChanges*/ ImmutableSet.of());
@@ -106,11 +106,11 @@
assertThat(ctsChange.overridesStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
}
- public void testSetOverrideForNewerVersion() throws Exception {
+ public void testPutPackageOverridesForNewerVersion() throws Exception {
installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
- "setOverride_fromVersion2",
+ "putPackageOverrides_fromVersion2",
/*enabledChanges*/ImmutableSet.of(),
/*disabledChanges*/ ImmutableSet.of());
@@ -135,11 +135,11 @@
assertThat(ctsChange.overridesStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
}
- public void testSetOverrideForOlderVersion() throws Exception {
+ public void testPutPackageOverridesForOlderVersion() throws Exception {
installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
- "setOverride_untilVersion1",
+ "putPackageOverrides_untilVersion1",
/*enabledChanges*/ImmutableSet.of(),
/*disabledChanges*/ ImmutableSet.of());
@@ -163,20 +163,21 @@
assertThat(ctsChange.hasOverrides).isFalse();
}
- public void testClearOverrideSecurityExceptionNonOverridableChangeId() throws Exception {
+ public void testRemovePackageOverridesSecurityExceptionNonOverridableChangeId()
+ throws Exception {
installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
- "clearOverride_securityExceptionForNonOverridableChangeId",
+ "removePackageOverrides_securityExceptionForNonOverridableChangeId",
/*enabledChanges*/ImmutableSet.of(),
/*disabledChanges*/ ImmutableSet.of());
}
- public void testClearOverrideSecurityExceptionMissingPermission() throws Exception {
+ public void testRemovePackageOverridesSecurityExceptionMissingPermission() throws Exception {
installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
- "clearOverride_securityExceptionForNotHoldingPermission",
+ "removePackageOverrides_securityExceptionForNotHoldingPermission",
/*enabledChanges*/ImmutableSet.of(),
/*disabledChanges*/ ImmutableSet.of());
@@ -187,11 +188,11 @@
assertThat(ctsChange.rawOverrideStr).isEqualTo("{" + OVERRIDE_PKG + "=true}");
}
- public void testClearOverrideWhenOverrideNotPresent() throws Exception {
+ public void testRemovePackageOverridesWhenOverrideNotPresent() throws Exception {
installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
- "clearOverride_doesNothingIfOverrideNotPresent",
+ "removePackageOverrides_doesNothingIfOverrideNotPresent",
/*enabledChanges*/ImmutableSet.of(),
/*disabledChanges*/ ImmutableSet.of());
@@ -202,11 +203,11 @@
assertThat(ctsChange.hasOverrides).isFalse();
}
- public void testClearOverrideWhenOverridePresent() throws Exception {
+ public void testRemovePackageOverridesWhenOverridePresent() throws Exception {
installPackage("appcompat_preinstall_override_versioncode1_release.apk", false);
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest",
- "clearOverride_overridePresentSuccess",
+ "removePackageOverrides_overridePresentSuccess",
/*enabledChanges*/ImmutableSet.of(),
/*disabledChanges*/ ImmutableSet.of());
diff --git a/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchHostTestBase.java b/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchHostTestBase.java
index 468997f..c4c76de 100644
--- a/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchHostTestBase.java
+++ b/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchHostTestBase.java
@@ -16,7 +16,14 @@
package android.appsearch.cts;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+
+import java.util.Map;
+
+import javax.annotation.Nonnull;
public abstract class AppSearchHostTestBase extends BaseHostJUnit4Test {
protected static final String TARGET_APK_A = "CtsAppSearchHostTestHelperA.apk";
@@ -26,15 +33,35 @@
protected static final String TARGET_PKG_B = "android.appsearch.app.b";
protected static final String TEST_CLASS_B = TARGET_PKG_B + ".AppSearchDeviceTest";
+ protected static final String USER_ID_KEY = "userId";
+
protected static final long DEFAULT_INSTRUMENTATION_TIMEOUT_MS = 600_000; // 10min
- protected void runDeviceTestAsUserInPkgA(String testMethod, int userId) throws Exception {
- runDeviceTests(getDevice(), TARGET_PKG_A, TEST_CLASS_A, testMethod, userId,
- DEFAULT_INSTRUMENTATION_TIMEOUT_MS);
+ protected void runDeviceTestAsUserInPkgA(@Nonnull String testMethod, int userId)
+ throws Exception {
+ assertWithMessage(testMethod + " failed").that(
+ runDeviceTests(getDevice(), TARGET_PKG_A, TEST_CLASS_A, testMethod, userId,
+ DEFAULT_INSTRUMENTATION_TIMEOUT_MS)).isTrue();
}
- protected void runDeviceTestAsUserInPkgB(String testMethod, int userId) throws Exception {
- runDeviceTests(getDevice(), TARGET_PKG_B, TEST_CLASS_B, testMethod, userId,
- DEFAULT_INSTRUMENTATION_TIMEOUT_MS);
+ protected void runDeviceTestAsUserInPkgA(@Nonnull String testMethod, int userId,
+ @Nonnull Map<String, String> args) throws Exception {
+ DeviceTestRunOptions deviceTestRunOptions = new DeviceTestRunOptions(TARGET_PKG_A)
+ .setTestClassName(TEST_CLASS_A)
+ .setTestMethodName(testMethod)
+ .setMaxInstrumentationTimeoutMs(DEFAULT_INSTRUMENTATION_TIMEOUT_MS)
+ .setUserId(userId);
+ for (Map.Entry<String, String> entry : args.entrySet()) {
+ deviceTestRunOptions.addInstrumentationArg(entry.getKey(), entry.getValue());
+ }
+ assertWithMessage(testMethod + " failed").that(
+ runDeviceTests(deviceTestRunOptions)).isTrue();
+ }
+
+ protected void runDeviceTestAsUserInPkgB(@Nonnull String testMethod, int userId)
+ throws Exception {
+ assertWithMessage(testMethod + " failed").that(
+ runDeviceTests(getDevice(), TARGET_PKG_B, TEST_CLASS_B, testMethod, userId,
+ DEFAULT_INSTRUMENTATION_TIMEOUT_MS)).isTrue();
}
}
diff --git a/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchMultiUserTestBase.java b/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchMultiUserTest.java
similarity index 68%
rename from hostsidetests/appsearch/src/android/appsearch/cts/AppSearchMultiUserTestBase.java
rename to hostsidetests/appsearch/src/android/appsearch/cts/AppSearchMultiUserTest.java
index 8696b69..6990ace 100644
--- a/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchMultiUserTestBase.java
+++ b/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchMultiUserTest.java
@@ -17,7 +17,6 @@
package android.appsearch.cts;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assume.assumeTrue;
@@ -28,18 +27,23 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
+import java.util.Map;
+
/**
- * Test to mock multi-user interacting with AppSearch.
+ * Test to cover multi-user interacting with AppSearch.
*
* <p>This test is split into two distinct parts: The first part is the test-apps that runs on the
- * device and interactive with AppSearch.This class is the second part that runs on the host and
+ * device and interactive with AppSearch. This class is the second part that runs on the host and
* triggers tests in the first part for different users.
*
* <p>To trigger a device test, call runDeviceTestAsUser with a specific the test name and specific
* user.
+ *
+ * <p>Unlock your device when test locally.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
-public class AppSearchMultiUserTestBase extends AppSearchHostTestBase {
+public class AppSearchMultiUserTest extends AppSearchHostTestBase {
private int mInitialUserId;
private int mSecondaryUserId;
@@ -75,4 +79,21 @@
// Cannot get the document from another user.
runDeviceTestAsUserInPkgA("testGetDocuments_nonexist", mInitialUserId);
}
+
+ @Test
+ public void testCreateSessionInStoppedUser() throws Exception {
+ Map<String, String> args =
+ Collections.singletonMap(USER_ID_KEY, String.valueOf(mSecondaryUserId));
+ getDevice().stopUser(mSecondaryUserId, /*waitFlag=*/true, /*forceFlag=*/true);
+ runDeviceTestAsUserInPkgA("createSessionInStoppedUser", mInitialUserId, args);
+ }
+
+ @Test
+ public void testStopUser_persistData() throws Exception {
+ runDeviceTestAsUserInPkgA("testPutDocuments", mSecondaryUserId);
+ runDeviceTestAsUserInPkgA("testGetDocuments_exist", mSecondaryUserId);
+ getDevice().stopUser(mSecondaryUserId, /*waitFlag=*/true, /*forceFlag=*/true);
+ getDevice().startUser(mSecondaryUserId, /*waitFlag=*/true);
+ runDeviceTestAsUserInPkgA("testGetDocuments_exist", mSecondaryUserId);
+ }
}
diff --git a/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchPackageTestBase.java b/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchPackageTest.java
similarity index 76%
rename from hostsidetests/appsearch/src/android/appsearch/cts/AppSearchPackageTestBase.java
rename to hostsidetests/appsearch/src/android/appsearch/cts/AppSearchPackageTest.java
index ef1d089..45f4f70 100644
--- a/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchPackageTestBase.java
+++ b/hostsidetests/appsearch/src/android/appsearch/cts/AppSearchPackageTest.java
@@ -16,18 +16,26 @@
package android.appsearch.cts;
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assume.assumeTrue;
-
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Test to cover install and uninstall packages with AppSearch.
+ *
+ * <p>This test is split into two distinct parts: The first part is the test-apps that runs on the
+ * device and interactive with AppSearch. This class is the second part that runs on the host and
+ * triggers tests in the first part for different users.
+ *
+ * <p>To trigger a device test, call runDeviceTestAsUser with a specific the test name and specific
+ * user.
+ *
+ * <p>Unlock your device when test locally.
+ */
@RunWith(DeviceJUnit4ClassRunner.class)
-public class AppSearchPackageTestBase extends AppSearchHostTestBase {
+public class AppSearchPackageTest extends AppSearchHostTestBase {
private int mPrimaryUserId;
diff --git a/hostsidetests/appsearch/test-apps/AppSearchHostTestHelperA/src/android/appsearch/app/a/AppSearchDeviceTest.java b/hostsidetests/appsearch/test-apps/AppSearchHostTestHelperA/src/android/appsearch/app/a/AppSearchDeviceTest.java
index 479a613..212f0a3 100644
--- a/hostsidetests/appsearch/test-apps/AppSearchHostTestHelperA/src/android/appsearch/app/a/AppSearchDeviceTest.java
+++ b/hostsidetests/appsearch/test-apps/AppSearchHostTestHelperA/src/android/appsearch/app/a/AppSearchDeviceTest.java
@@ -21,6 +21,9 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.expectThrows;
+
+import android.app.UiAutomation;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
@@ -31,8 +34,10 @@
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.PutDocumentsRequest;
import android.app.appsearch.SetSchemaRequest;
+import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.appsearch.testing.AppSearchSessionShimImpl;
@@ -43,12 +48,15 @@
import org.junit.runner.RunWith;
import java.util.List;
+import java.util.concurrent.ExecutionException;
+
@RunWith(AndroidJUnit4.class)
public class AppSearchDeviceTest {
private static final String DB_NAME = "";
private static final String NAMESPACE = "namespace";
private static final String ID = "id";
+ private static final String USER_ID_KEY = "userId";
private static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder("testSchema")
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder("subject")
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
@@ -77,11 +85,13 @@
"3D7A1AAE7AE8B9949BE93E071F3702AA38695B0F99B5FC4B2E8B364AC78FFDB2");
private AppSearchSessionShim mDb;
+ private UiAutomation mUiAutomation;
@Before
public void setUp() throws Exception {
mDb = AppSearchSessionShimImpl.createSearchSession(
new AppSearchManager.SearchContext.Builder(DB_NAME).build()).get();
+ mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
}
@Test
@@ -122,4 +132,21 @@
public void clearTestData() throws Exception {
mDb.setSchema(new SetSchemaRequest.Builder().setForceOverride(true).build()).get();
}
+
+ @Test
+ public void createSessionInStoppedUser() {
+ mUiAutomation.adoptShellPermissionIdentity(
+ "android.permission.INTERACT_ACROSS_USERS_FULL");
+ try {
+ Bundle args = InstrumentationRegistry.getArguments();
+ int userId = Integer.parseInt(args.getString(USER_ID_KEY));
+ ExecutionException exception = expectThrows(ExecutionException.class, () ->
+ AppSearchSessionShimImpl.createSearchSession(
+ new AppSearchManager.SearchContext.Builder(DB_NAME).build(),
+ userId).get());
+ assertThat(exception.getMessage()).contains("is locked or not running.");
+ } finally {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
+ }
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
index 15e1279..4ff0976 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
@@ -24,7 +24,7 @@
import com.android.compatibility.common.util.CddTest;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.DeviceParameterizedRunner;
import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
import org.junit.After;
@@ -32,10 +32,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.FileNotFoundException;
import java.util.HashMap;
-@RunWith(DeviceJUnit4ClassRunner.class)
+import junitparams.Parameters;
+
+@RunWith(DeviceParameterizedRunner.class)
@AppModeFull
public final class ApkVerityInstallTest extends BaseAppSecurityTest {
@@ -48,8 +49,15 @@
private static final String BAD_BASE_APK = "CtsApkVerityTestApp2Prebuilt.apk";
private static final String BAD_BASE_APK_DM = "CtsApkVerityTestApp2Prebuilt.dm";
private static final String FSV_SIG_SUFFIX = ".fsv_sig";
+ private static final String ID_SIG_SUFFIX = ".idsig";
private static final String APK_VERITY_STANDARD_MODE = "2";
+ private static final boolean INCREMENTAL = true;
+ private static final boolean NON_INCREMENTAL = false;
+
+ private static final boolean SUPPORTED = true;
+ private static final boolean UNSUPPORTED = false;
+
private static final HashMap<String, String> ORIGINAL_TO_INSTALL_NAME = new HashMap<>() {{
put(BASE_APK, "base.apk");
put(BASE_APK_DM, "base.dm");
@@ -59,6 +67,24 @@
private boolean mDmRequireFsVerity;
+ private static final Object[] installSingle() {
+ // Non-Incremental and Incremental.
+ return new Boolean[][]{{NON_INCREMENTAL}, {INCREMENTAL}};
+ }
+
+ private static final Object[] installAndUpdate() {
+ // Non-Incremental -> Non-Incremental: supported
+ // Incremental -> Non-Incremental: supported
+ // Incremental -> Incremental: supported
+ // Non-Incremental -> Incremental: unsupported
+ return new Boolean[][]{
+ {NON_INCREMENTAL, NON_INCREMENTAL, SUPPORTED},
+ {INCREMENTAL, NON_INCREMENTAL, SUPPORTED},
+ {INCREMENTAL, INCREMENTAL, SUPPORTED},
+ {NON_INCREMENTAL, INCREMENTAL, UNSUPPORTED}
+ };
+ }
+
@Before
public void setUp() throws DeviceNotAvailableException {
ITestDevice device = getDevice();
@@ -66,6 +92,7 @@
assumeTrue(device.getLaunchApiLevel() >= 30
|| APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
mDmRequireFsVerity = "true".equals(device.getProperty("pm.dexopt.dm.require_fsverity"));
+ assumeSecurityModelCompat();
}
@After
@@ -73,60 +100,66 @@
getDevice().uninstallPackage(PACKAGE_NAME);
}
- @CddTest(requirement="9.10/C-0-3")
+ @CddTest(requirement = "9.10/C-0-3")
@Test
- public void testInstallBase() throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installSingle")
+ public void testInstallBase(boolean incremental) throws Exception {
+ assumePreconditions(incremental);
+ new InstallMultiple(incremental)
.addFile(BASE_APK)
.addFile(BASE_APK + FSV_SIG_SUFFIX)
.run();
- verifyFsverityInstall(BASE_APK);
+ verifyFsverityInstall(incremental, BASE_APK);
}
- @CddTest(requirement="9.10/C-0-3")
+ @CddTest(requirement = "9.10/C-0-3")
@Test
- public void testInstallBaseWithWrongSignature()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installSingle")
+ public void testInstallBaseWithWrongSignature(boolean incremental) throws Exception {
+ assumePreconditions(incremental);
+ InstallMultiple install = new InstallMultiple(incremental)
.addFile(BAD_BASE_APK)
- .addFile(BAD_BASE_APK + FSV_SIG_SUFFIX)
- .runExpectingFailure();
+ .addFile(BAD_BASE_APK + FSV_SIG_SUFFIX);
+
+ // S with IncFsV1 silently skips fs-verity signatures.
+ boolean expectingSuccess = incremental && !isIncrementalDeliveryV2Feature();
+ install.run(expectingSuccess);
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallBaseWithSplit()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installSingle")
+ public void testInstallBaseWithSplit(boolean incremental) throws Exception {
+ assumePreconditions(incremental);
+ new InstallMultiple(incremental)
.addFile(BASE_APK)
.addFile(BASE_APK + FSV_SIG_SUFFIX)
.addFile(SPLIT_APK)
.addFile(SPLIT_APK + FSV_SIG_SUFFIX)
.run();
- verifyFsverityInstall(BASE_APK, SPLIT_APK);
+ verifyFsverityInstall(incremental, BASE_APK, SPLIT_APK);
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallBaseWithDm() throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installSingle")
+ public void testInstallBaseWithDm(boolean incremental) throws Exception {
+ assumePreconditions(incremental);
+ new InstallMultiple(incremental)
.addFile(BASE_APK)
.addFile(BASE_APK + FSV_SIG_SUFFIX)
.addFile(BASE_APK_DM)
.addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
.run();
- verifyFsverityInstall(BASE_APK, BASE_APK_DM);
+ verifyFsverityInstall(incremental, BASE_APK, BASE_APK_DM);
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallEverything() throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installSingle")
+ public void testInstallEverything(boolean incremental) throws Exception {
+ assumePreconditions(incremental);
+ new InstallMultiple(incremental)
.addFile(BASE_APK)
.addFile(BASE_APK + FSV_SIG_SUFFIX)
.addFile(BASE_APK_DM)
@@ -136,68 +169,121 @@
.addFile(SPLIT_APK_DM)
.addFile(SPLIT_APK_DM + FSV_SIG_SUFFIX)
.run();
- verifyFsverityInstall(BASE_APK, BASE_APK_DM, SPLIT_APK, SPLIT_APK_DM);
+ verifyFsverityInstall(incremental, BASE_APK, BASE_APK_DM, SPLIT_APK, SPLIT_APK_DM);
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallSplitOnly()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installAndUpdate")
+ public void testInstallSplitOnly(boolean installIncremental, boolean updateIncremental,
+ boolean isSupported) throws Exception {
+ assumePreconditions(installIncremental || updateIncremental);
+ new InstallMultiple(installIncremental)
.addFile(BASE_APK)
.addFile(BASE_APK + FSV_SIG_SUFFIX)
.run();
- verifyFsverityInstall(BASE_APK);
+ verifyFsverityInstall(installIncremental, BASE_APK);
- new InstallMultiple()
+ new InstallMultiple(updateIncremental)
.inheritFrom(PACKAGE_NAME)
.addFile(SPLIT_APK)
.addFile(SPLIT_APK + FSV_SIG_SUFFIX)
- .run();
- verifyFsverityInstall(BASE_APK, SPLIT_APK);
+ .run(isSupported);
+ if (isSupported) {
+ verifyFsverityInstall(updateIncremental, BASE_APK, SPLIT_APK);
+ }
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallSplitOnlyMissingSignature()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installAndUpdate")
+ public void testInstallSplitOnlyMissingSignature(boolean installIncremental,
+ boolean updateIncremental, boolean isSupported) throws Exception {
+ assumePreconditions(installIncremental || updateIncremental);
+ new InstallMultiple(installIncremental)
.addFile(BASE_APK)
.addFile(BASE_APK + FSV_SIG_SUFFIX)
.run();
- verifyFsverityInstall(BASE_APK);
+ verifyFsverityInstall(installIncremental, BASE_APK);
- new InstallMultiple()
+ InstallMultiple install = new InstallMultiple(updateIncremental)
.inheritFrom(PACKAGE_NAME)
- .addFile(SPLIT_APK)
- .runExpectingFailure();
+ .addFile(SPLIT_APK);
+
+ // S with IncFsV1 silently skips fs-verity signatures.
+ boolean expectingSuccess =
+ isSupported && installIncremental && !isIncrementalDeliveryV2Feature();
+ install.run(expectingSuccess);
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallSplitOnlyWithoutBaseSignature()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installAndUpdate")
+ public void testInstallSplitOnlyWithoutBaseSignature(boolean installIncremental,
+ boolean updateIncremental, boolean isSupported) throws Exception {
+ assumePreconditions(installIncremental || updateIncremental);
+ new InstallMultiple(installIncremental)
.addFile(BASE_APK)
.run();
- new InstallMultiple()
+ new InstallMultiple(updateIncremental)
.inheritFrom(PACKAGE_NAME)
.addFile(SPLIT_APK)
.addFile(SPLIT_APK + FSV_SIG_SUFFIX)
- .run();
- verifyFsverityInstall(SPLIT_APK);
+ .run(isSupported);
+ if (isSupported) {
+ verifyFsverityInstall(updateIncremental, SPLIT_APK);
+ }
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallBaseWithFsvSigAndSplitWithout()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installAndUpdate")
+ public void testInstallSplitAndSignatureForBase(boolean installIncremental,
+ boolean updateIncremental, boolean isSupported) throws Exception {
+ assumePreconditions(installIncremental || updateIncremental);
+ new InstallMultiple(installIncremental)
+ .addFile(BASE_APK)
+ .run();
+
+ new InstallMultiple(updateIncremental)
+ .inheritFrom(PACKAGE_NAME)
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .addFile(SPLIT_APK)
+ .addFile(SPLIT_APK + FSV_SIG_SUFFIX)
+ .run(isSupported);
+ if (isSupported) {
+ verifyFsverityInstall(updateIncremental, BASE_APK);
+ }
+ }
+
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
+ @Test
+ @Parameters(method = "installAndUpdate")
+ public void testUpdateBaseWithSignature(boolean installIncremental, boolean updateIncremental,
+ boolean isSupported) throws Exception {
+ assumePreconditions(installIncremental || updateIncremental);
+ new InstallMultiple(installIncremental)
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .run();
+ verifyFsverityInstall(installIncremental, BASE_APK);
+
+ new InstallMultiple(updateIncremental)
+ .inheritFrom(PACKAGE_NAME)
+ .addFile(BASE_APK)
+ .addFile(BASE_APK + FSV_SIG_SUFFIX)
+ .run(isSupported);
+ verifyFsverityInstall(updateIncremental, BASE_APK);
+ }
+
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
+ @Test
+ @Parameters(method = "installSingle")
+ public void testInstallBaseWithFsvSigAndSplitWithout(boolean incremental) throws Exception {
+ assumePreconditions(incremental);
+ new InstallMultiple(incremental)
.addFile(BASE_APK)
.addFile(BASE_APK + FSV_SIG_SUFFIX)
.addFile(BASE_APK_DM)
@@ -208,12 +294,12 @@
.runExpectingFailure();
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallDmWithFsvSig()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installSingle")
+ public void testInstallDmWithFsvSig(boolean incremental) throws Exception {
+ assumePreconditions(incremental);
+ new InstallMultiple(incremental)
.addFile(BASE_APK)
.addFile(BASE_APK_DM)
.addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
@@ -221,15 +307,15 @@
.addFile(SPLIT_APK_DM)
.addFile(SPLIT_APK_DM + FSV_SIG_SUFFIX)
.run();
- verifyFsverityInstall(BASE_APK_DM, SPLIT_APK_DM);
+ verifyFsverityInstall(incremental, BASE_APK_DM, SPLIT_APK_DM);
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallDmWithMissingFsvSig()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- InstallMultiple installer = new InstallMultiple()
+ @Parameters(method = "installSingle")
+ public void testInstallDmWithMissingFsvSig(boolean incremental) throws Exception {
+ assumePreconditions(incremental);
+ InstallMultiple installer = new InstallMultiple(incremental)
.addFile(BASE_APK)
.addFile(BASE_APK_DM)
.addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
@@ -239,16 +325,16 @@
installer.runExpectingFailure();
} else {
installer.run();
- verifyFsverityInstall(BASE_APK_DM);
+ verifyFsverityInstall(incremental, BASE_APK_DM);
}
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallSplitWithFsvSigAndBaseWithout()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- InstallMultiple installer = new InstallMultiple()
+ @Parameters(method = "installSingle")
+ public void testInstallSplitWithFsvSigAndBaseWithout(boolean incremental) throws Exception {
+ assumePreconditions(incremental);
+ InstallMultiple installer = new InstallMultiple(incremental)
.addFile(BASE_APK)
.addFile(BASE_APK_DM)
.addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
@@ -259,73 +345,49 @@
installer.runExpectingFailure();
} else {
installer.run();
- verifyFsverityInstall(BASE_APK_DM, SPLIT_APK_DM);
+ verifyFsverityInstall(incremental, BASE_APK_DM, SPLIT_APK_DM);
}
}
- @CddTest(requirement="9.10/C-0-3,C-0-5")
+ @CddTest(requirement = "9.10/C-0-3,C-0-5")
@Test
- public void testInstallBaseWithFsvSigThenSplitWithout()
- throws DeviceNotAvailableException, FileNotFoundException {
- assumeSecurityModelCompat();
- new InstallMultiple()
+ @Parameters(method = "installAndUpdate")
+ public void testInstallBaseWithFsvSigThenSplitWithout(boolean installIncremental,
+ boolean updateIncremental, boolean isSupported) throws Exception {
+ assumePreconditions(installIncremental || updateIncremental);
+ new InstallMultiple(installIncremental)
.addFile(BASE_APK)
.addFile(BASE_APK + FSV_SIG_SUFFIX)
.run();
- verifyFsverityInstall(BASE_APK);
+ verifyFsverityInstall(installIncremental, BASE_APK);
- new InstallMultiple()
+ new InstallMultiple(updateIncremental)
.addFile(SPLIT_APK)
.runExpectingFailure();
}
@Test
public void testInstallBaseIncrementally() throws Exception {
- assumeIncrementalDeliveryFeature();
- new InstallMultiple()
- .useIncremental()
+ assumeTrue(hasIncrementalDeliveryFeature());
+ new InstallMultiple(/*incremental=*/true)
.addFile(BASE_APK)
.run();
}
- @Test
- public void testInstallBaseWithFsvSigIncrementally() throws Exception {
- assumeSecurityModelCompat();
- assumeIncrementalDeliveryFeature();
- new InstallMultiple()
- .useIncremental()
- .addFile(BASE_APK)
- .addFile(BASE_APK + FSV_SIG_SUFFIX)
- .run();
- assumeIncrementalDeliveryV2Feature();
- verifyFsverityInstall(BASE_APK);
+ private void assumePreconditions(boolean requiresIncremental) throws Exception {
+ if (requiresIncremental) {
+ assumeTrue(hasIncrementalDeliveryFeature());
+ }
}
- @Test
- public void testInstallEverythingWithFsvSigIncrementally() throws Exception {
- assumeSecurityModelCompat();
- assumeIncrementalDeliveryFeature();
- new InstallMultiple()
- .useIncremental()
- .addFile(BASE_APK)
- .addFile(BASE_APK_DM)
- .addFile(BASE_APK_DM + FSV_SIG_SUFFIX)
- .addFile(SPLIT_APK)
- .addFile(SPLIT_APK_DM)
- .addFile(SPLIT_APK_DM + FSV_SIG_SUFFIX)
- .run();
- assumeIncrementalDeliveryV2Feature();
- verifyFsverityInstall(BASE_APK_DM, SPLIT_APK_DM);
+ private boolean hasIncrementalDeliveryFeature() throws Exception {
+ return "true\n".equals(getDevice().executeShellCommand(
+ "pm has-feature android.software.incremental_delivery"));
}
- private void assumeIncrementalDeliveryFeature() throws Exception {
- assumeTrue("true\n".equals(getDevice().executeShellCommand(
- "pm has-feature android.software.incremental_delivery")));
- }
-
- private void assumeIncrementalDeliveryV2Feature() throws Exception {
- assumeTrue("true\n".equals(getDevice().executeShellCommand(
- "pm has-feature android.software.incremental_delivery 2")));
+ private boolean isIncrementalDeliveryV2Feature() throws Exception {
+ return "true\n".equals(getDevice().executeShellCommand(
+ "pm has-feature android.software.incremental_delivery 2"));
}
private void assumeSecurityModelCompat() throws DeviceNotAvailableException {
@@ -333,7 +395,11 @@
getDevice().hasFeature("feature:android.hardware.security.model.compatible"));
}
- void verifyFsverityInstall(String... files) throws DeviceNotAvailableException {
+ void verifyFsverityInstall(boolean incremental, String... files) throws Exception {
+ if (incremental && !isIncrementalDeliveryV2Feature()) {
+ return;
+ }
+
DeviceTestRunOptions options = new DeviceTestRunOptions(PACKAGE_NAME);
options.setTestClassName(PACKAGE_NAME + ".InstalledFilesCheck");
options.setTestMethodName("testFilesHaveFsverity");
@@ -350,8 +416,11 @@
}
private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
- InstallMultiple() {
+ InstallMultiple(boolean incremental) throws Exception {
super(getDevice(), getBuild(), getAbi());
+ if (incremental) {
+ useIncremental();
+ }
}
@Override
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
index 8657c23..77b059e 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppDataIsolationTests.java
@@ -315,6 +315,8 @@
@Test
public void testNormalProcessCannotAccessOtherAppExternalDataDir() throws Exception {
+ assumeThatFuseDataIsolationIsEnabled(getDevice());
+
new InstallMultiple().addFile(APPA_APK).run();
new InstallMultiple().addFile(APPB_APK).run();
@@ -415,6 +417,13 @@
+ " " + packageName));
}
+ private static void assumeThatFuseDataIsolationIsEnabled(ITestDevice device)
+ throws DeviceNotAvailableException {
+ assumeThat(device.executeShellCommand(
+ "getprop persist.sys.vold_app_data_isolation_enabled").trim(),
+ is("true"));
+ }
+
private boolean isFbeModeEmulated() throws Exception {
String mode = getDevice().executeShellCommand("sm get-fbe-mode").trim();
if (mode.equals(FBE_MODE_EMULATED)) {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
index 3c52427..094f372 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseInstallMultiple.java
@@ -140,6 +140,10 @@
run(true, null);
}
+ void run(boolean expectingSuccess) throws DeviceNotAvailableException {
+ run(expectingSuccess, null);
+ }
+
void runExpectingFailure() throws DeviceNotAvailableException {
run(false, null);
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ReadableSettingsFieldsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ReadableSettingsFieldsTest.java
index e38eb0a..67fba8ace 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ReadableSettingsFieldsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ReadableSettingsFieldsTest.java
@@ -91,6 +91,13 @@
}
@Test
+ public void testGlobalHiddenSettingsKeyNotReadableWithoutPermissions() throws
+ DeviceNotAvailableException {
+ runDeviceTests(TEST_PACKAGE, TEST_CLASS,
+ "testGlobalHiddenSettingsKeyNotReadableWithoutPermissions");
+ }
+
+ @Test
public void testSecureHiddenSettingsKeysNotReadableWithoutAnnotation()
throws DeviceNotAvailableException {
runDeviceTests(TEST_PACKAGE, TEST_CLASS,
diff --git a/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk b/hostsidetests/appsecurity/test-apps/PrivilegedUpdateApp/apk/arm/CtsShimPrivUpgrade.apk
index 9b7eae3..bbb312f 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 b6baab3..d2bf4e4 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 b34d7a0..9f4ea15 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 7e8d3a7..4ce0f37 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/ReadSettingsFieldsApp/src/com/android/cts/readsettingsfieldsapp/ReadSettingsFieldsTest.java b/hostsidetests/appsecurity/test-apps/ReadSettingsFieldsApp/src/com/android/cts/readsettingsfieldsapp/ReadSettingsFieldsTest.java
index e7c6638..c28032f 100644
--- a/hostsidetests/appsecurity/test-apps/ReadSettingsFieldsApp/src/com/android/cts/readsettingsfieldsapp/ReadSettingsFieldsTest.java
+++ b/hostsidetests/appsecurity/test-apps/ReadSettingsFieldsApp/src/com/android/cts/readsettingsfieldsapp/ReadSettingsFieldsTest.java
@@ -49,6 +49,10 @@
if (isSettingsDeprecated(ex)) {
continue;
}
+ /** b/174151290 skip it due to it's @hide but also @TestApi */
+ if (key.equals(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT)) {
+ continue;
+ }
fail("Reading public " + settingsClass.getSimpleName() + " settings key <" + key
+ "> should not raise exception! "
+ "Did you forget to add @Readable annotation?\n" + ex.getMessage());
@@ -156,6 +160,23 @@
publicSettingsKeys, hiddenSettingsKeys);
}
+ // test the cases that hidden keys are marked with readable annotation but access should be
+ // protected by additional permission check.
+ public void testGlobalHiddenSettingsKeyNotReadableWithoutPermissions() {
+ final String[] hiddenSettingsKeysRequiresPermissions = {"multi_sim_data_call"};
+ for (String key : hiddenSettingsKeysRequiresPermissions) {
+ try {
+ // Verify that the hidden keys can't be accessed due to lack of permissions.
+ callGetStringMethod(Settings.Global.class, key);
+ } catch (SecurityException ex) {
+ assertTrue(ex.getMessage().contains("permission"));
+ continue;
+ }
+ fail("Reading hidden " + Settings.Global.class.getSimpleName() + " settings key <" + key
+ + "> should be protected with permission!");
+ }
+ }
+
private <T extends Settings.NameValueTable>
void testHiddenSettingsKeysNotReadableWithoutAnnotation(
Class<T> settingsClass, ArraySet<String> publicKeys, String[] targetKeys) {
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
index 4ff919b..64a9cbc 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java
@@ -264,10 +264,21 @@
}
}
+ private boolean isFuseDataIsolationIsEnabled() throws IOException {
+ return UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+ "getprop persist.sys.vold_app_data_isolation_enabled").trim().equals("true");
+ }
+
/**
* Verify that .nomedia is created correctly.
*/
public void testVerifyNoMediaCreated() throws Exception {
+ boolean expectNoMediaFileExists = true;
+ if (BuildCompat.isAtLeastS() && isFuseDataIsolationIsEnabled()) {
+ // All package specific paths will be in app's mount namespace, and it cannot
+ // access its parent to check .nomedia file.
+ expectNoMediaFileExists = false;
+ }
for (File file : getAllPackageSpecificPathsExceptMedia(getContext())) {
deleteContents(file);
}
@@ -276,14 +287,9 @@
for (File path : paths) {
MediaStore.scanFile(getContext().getContentResolver(), path);
}
+
// Require that .nomedia was created somewhere above each dir
for (File path : paths) {
- boolean expectNoMediaFileExists = true;
- if (BuildCompat.isAtLeastS() && Environment.isExternalStorageEmulated(path)) {
- // All package specific paths will be in app's mount namespace, and it cannot
- // access its parent to check .nomedia file.
- expectNoMediaFileExists = false;
- }
assertNotNull("Valid media must be inserted during CTS", path);
assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED,
Environment.getExternalStorageState(path));
diff --git a/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java b/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java
index 6a7f414..c199b5b 100644
--- a/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java
+++ b/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java
@@ -56,6 +56,7 @@
testHelper.checkCurrentPolicy(PowerPolicyDef.IdSet.NO_USER_INTERACTION);
testHelper.checkSilentModeStatus(true);
testHelper.checkSilentModeFull(SilentModeInfo.FORCED_SILENT);
+ testHelper.checkCurrentPowerComponents(PowerPolicyDef.PolicySet.NO_USER_INTERACT);
teststep = "restore to normal mode";
restoreFromForcedSilentMode();
@@ -64,8 +65,15 @@
testHelper.checkCurrentPolicy(PowerPolicyDef.IdSet.DEFAULT_ALL_ON);
testHelper.checkSilentModeStatus(false);
testHelper.checkSilentModeFull(SilentModeInfo.NO_SILENT);
+ testHelper.checkCurrentPowerComponents(PowerPolicyDef.PolicySet.DEFAULT_ALL_ON);
}
+ /**
+ * Tests the error conditions for CPMS at the ON state.
+ *
+ * <p>All other VHAL events but {@code SHUTDOWN_PREPARE} shall not have any impact
+ * to CPMS power state.
+ */
@Test
public void testDefaultStateMachineAtONState() throws Exception {
String testcase = "testDefaultStateMachineAtONState:";
@@ -80,8 +88,6 @@
PowerPolicyConstants.VhalPowerStateReq.FINISHED
};
- // CPMS is at the ON state. All other VHAL events but SHUTDOWN_PREPARE
- // will not have any impact to CPMS
for (int i = 0; i < stepNames.length; i++) {
triggerVhalPowerStateReq(vhalReqs[i], PowerPolicyConstants.ShutdownParam.NOT_USED);
PowerPolicyTestHelper testHelper = getTestHelper(testcase, i + 1, stepNames[i]);
diff --git a/hostsidetests/car/src/android/car/cts/PreCreateUsersHostTest.java b/hostsidetests/car/src/android/car/cts/PreCreateUsersHostTest.java
index 05acc3d..e24419c 100644
--- a/hostsidetests/car/src/android/car/cts/PreCreateUsersHostTest.java
+++ b/hostsidetests/car/src/android/car/cts/PreCreateUsersHostTest.java
@@ -24,7 +24,6 @@
import android.platform.test.annotations.Presubmit;
-import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -43,7 +42,6 @@
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public final class PreCreateUsersHostTest extends CarHostJUnit4TestCase {
- private static final int DEFAULT_TIMEOUT_SEC = 20;
private static int sNumberCreateadUsers;
/**
@@ -108,7 +106,7 @@
assertAppNotInstalledForUser(APP_PKG, preCreatedUserId);
if (afterReboot) {
- restartSystemWithOnePreCreatedUserOrGuest(isGuest);
+ restartSystem();
// Checks again
assertAppInstalledForUser(APP_PKG, initialUserId);
@@ -163,6 +161,8 @@
int referenceUserId = isGuest
? createGuestUser("PreCreatedUsersTest_Reference_Guest")
: createFullUser("PreCreatedUsersTest_Reference_User");
+ // Some permissions (e.g. Role permission) are given only after initialization.
+ switchUser(referenceUserId);
waitUntilUserPermissionsIsReady(referenceUserId);
Map<String, List<String>> refPkgMap = getPackagesAndPermissionsForUser(referenceUserId);
@@ -172,14 +172,15 @@
removeUser(referenceUserId);
}
- int initialUserId = getCurrentUserId();
int preCreatedUserId = preCreateUser(isGuest);
if (afterReboot) {
- restartSystemWithOnePreCreatedUserOrGuest(isGuest);
+ restartSystem();
}
convertPreCreatedUser(isGuest, preCreatedUserId);
+ // Some permissions (e.g. Role permission) are given only after initialization.
+ switchUser(preCreatedUserId);
waitUntilUserPermissionsIsReady(preCreatedUserId);
Map<String, List<String>> actualPkgMap = getPackagesAndPermissionsForUser(preCreatedUserId);
@@ -257,14 +258,6 @@
}
}
- private void setPreCreatedUsersProperties(int value) throws DeviceNotAvailableException {
- getDevice().setProperty("android.car.number_pre_created_users", Integer.toString(value));
- }
-
- private void setPreCreatedGuestsProperties(int value) throws DeviceNotAvailableException {
- getDevice().setProperty("android.car.number_pre_created_guests", Integer.toString(value));
- }
-
private void convertPreCreatedUser(boolean isGuest, int expectedId) throws Exception {
assertHasPreCreatedUser(expectedId);
String type = isGuest ? "guest" : "user";
@@ -280,23 +273,7 @@
+ "id " + expectedId);
}
- private void restartSystemWithOnePreCreatedUserOrGuest(boolean isGuest) throws Exception {
- List<Integer> ids = getPreCreatedUsers();
- CLog.d("Pre-created users before boot: %s", ids);
- assertWithMessage("Should have just 1 pre-created user before boot").that(ids).hasSize(1);
- assertUserInitialized(ids.get(0));
-
- // CarUserService creates / remove pre-created users on boot to keep the pool constant,
- // based on system properties. We need to tune then so the pre-created users set by this
- // test are not changed when the system restarts.
- if (isGuest) {
- setPreCreatedGuestsProperties(1);
- setPreCreatedUsersProperties(0);
- } else {
- setPreCreatedUsersProperties(1);
- setPreCreatedGuestsProperties(0);
- }
-
+ private void restartSystem() throws Exception {
// Restart the system to make sure PackageManager preserves the installed bit
restartSystemServer();
}
diff --git a/hostsidetests/car/src/android/car/cts/powerpolicy/CpmsFrameworkLayerStateInfo.java b/hostsidetests/car/src/android/car/cts/powerpolicy/CpmsFrameworkLayerStateInfo.java
index 3d3f176..33ac06a 100644
--- a/hostsidetests/car/src/android/car/cts/powerpolicy/CpmsFrameworkLayerStateInfo.java
+++ b/hostsidetests/car/src/android/car/cts/powerpolicy/CpmsFrameworkLayerStateInfo.java
@@ -34,7 +34,8 @@
public static final String PENDING_POLICY_ID_HDR = "mPendingPowerPolicyId:";
public static final String CURRENT_POLICY_GROUP_ID_HDR = "mCurrentPowerPolicyGroupId:";
public static final String COMPONENT_STATE_HDR = "Power components state:";
- public static final String COMPONENT_CONTROLLED_HDR = "Components controlled by user:";
+ public static final String COMPONENT_CONTROLLED_HDR =
+ "Components powered off by power policy:";
public static final String COMPONENT_CHANGED_HDR = "Components changed by the last policy:";
public static final String MONITORING_HW_HDR = "Monitoring HW state signal:";
public static final String SILENT_MODE_BY_HW_HDR = "Silent mode by HW state signal:";
@@ -94,6 +95,14 @@
return mForcedSilentMode;
}
+ public PowerPolicyDef.PowerComponent[] getCurrentEnabledComponents() {
+ return PowerPolicyDef.PowerComponent.asComponentArray(mEnables);
+ }
+
+ public PowerPolicyDef.PowerComponent[] getCurrentDisabledComponents() {
+ return PowerPolicyDef.PowerComponent.asComponentArray(mDisables);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(STRING_BUILDER_BUF_SIZE);
@@ -170,14 +179,16 @@
currentPolicyGroupId = parser.getStringData(CURRENT_POLICY_GROUP_ID_HDR);
break;
case COMPONENT_STATE_HDR:
- parser.parseComponentStates(COMPONENT_STATE_HDR, COMPONENT_CONTROLLED_HDR);
+ parser.parseComponentStates(COMPONENT_STATE_HDR,
+ COMPONENT_CONTROLLED_HDR, true);
enables = parser.getEnables();
disables = parser.getDisables();
Collections.sort(enables);
Collections.sort(disables);
break;
case COMPONENT_CONTROLLED_HDR:
- parser.parseComponentStates(COMPONENT_CONTROLLED_HDR, COMPONENT_CHANGED_HDR);
+ parser.parseComponentStates(COMPONENT_CONTROLLED_HDR,
+ COMPONENT_CHANGED_HDR, false);
controlledEnables = parser.getEnables();
controlledDisables = parser.getDisables();
Collections.sort(controlledEnables);
@@ -269,13 +280,14 @@
return val;
}
- private void parseComponentStates(String startHdr, String endHdr) throws Exception {
+ private void parseComponentStates(String startHdr, String endHdr,
+ boolean hasStateInfo) throws Exception {
mEnables = new ArrayList<String>();
mDisables = new ArrayList<String>();
while (mIdx < (mLines.length - 1) && !mLines[++mIdx].contains(endHdr)) {
String stateStr = mLines[mIdx].trim();
String[] vals = stateStr.split(":\\s");
- if (vals.length != 2) {
+ if (hasStateInfo && vals.length != 2) {
String errMsg = String.format("wrong format at %d in: %s ", mIdx, stateStr);
CLog.e(errMsg);
throw new IllegalArgumentException(errMsg);
@@ -292,15 +304,20 @@
throw new IllegalArgumentException(errMsg);
}
- if (vals[1].startsWith("on")) {
- mEnables.add(vals[0]);
- } else if (vals[1].startsWith("off")) {
- mDisables.add(vals[0]);
+ if (hasStateInfo) {
+ if (vals[1].startsWith("on")) {
+ mEnables.add(vals[0]);
+ } else if (vals[1].startsWith("off")) {
+ mDisables.add(vals[0]);
+ } else {
+ String errMsg =
+ String.format("wrong state value at %d with (%s, %s) in: %s",
+ mIdx, vals[0], vals[1], stateStr);
+ CLog.e(errMsg);
+ throw new IllegalArgumentException(errMsg);
+ }
} else {
- String errMsg = String.format("wrong state value at %d with (%s, %s) in: %s",
- mIdx, vals[0], vals[1], stateStr);
- CLog.e(errMsg);
- throw new IllegalArgumentException(errMsg);
+ mDisables.add(vals[0]);
}
}
mIdx--;
diff --git a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyDef.java b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyDef.java
index 1904785..70d7354 100644
--- a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyDef.java
+++ b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyDef.java
@@ -19,6 +19,7 @@
import com.android.tradefed.log.LogUtil.CLog;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
import java.util.StringTokenizer;
@@ -43,10 +44,20 @@
return mPolicyId;
}
+ public PowerComponent[] getEnables() {
+ return mEnables;
+ }
+
+ public PowerComponent[] getDisables() {
+ return mDisables;
+ }
+
@Override
public String toString() {
- String[] enables = Arrays.stream(mEnables).map(c -> c.value).toArray(i -> new String[i]);
- String[] disables = Arrays.stream(mDisables).map(c -> c.value).toArray(i -> new String[i]);
+ String[] enables = Arrays.stream(mEnables).map(PowerComponent::getValue)
+ .toArray(String[]::new);
+ String[] disables = Arrays.stream(mDisables).map(PowerComponent::getValue)
+ .toArray(String[]::new);
StringBuilder str = new StringBuilder();
str.append(mPolicyId);
if (!enables[0].equals("none")) {
@@ -110,12 +121,7 @@
throw new IllegalArgumentException("malformatted disabled headers string: "
+ policyDefStr);
}
- PowerComponent[] enabledComps = null;
- if (enables != null) {
- normalizeComponentName(enables);
- enabledComps = Arrays.stream(enables)
- .map(e -> PowerComponent.valueOf(e)).toArray(n -> new PowerComponent[n]);
- }
+ PowerComponent[] enabledComps = PowerComponent.asComponentArray(enables);
String[] disables = null;
tmpStr = tokens.nextToken().trim();
@@ -123,34 +129,11 @@
if (!tmpStr.isEmpty()) {
disables = tmpStr.split(",\\s*");
}
- PowerComponent[] disabledComps = null;
- if (disables != null) {
- normalizeComponentName(disables);
- disabledComps = Arrays.stream(disables)
- .map(e -> PowerComponent.valueOf(e)).toArray(n -> new PowerComponent[n]);
- }
+ PowerComponent[] disabledComps = PowerComponent.asComponentArray(disables);
return new PowerPolicyDef(policyId, enabledComps, disabledComps);
}
- private static void normalizeComponentName(String[] comps) throws Exception {
- for (int i = 0; i < comps.length; i++) {
- try {
- PowerComponent.valueOf(comps[i]);
- } catch (Exception e) {
- if (comps[i] == null || comps[i].isEmpty()) {
- throw new IllegalArgumentException("empty PowerComponent name at " + i);
- }
-
- if (comps[i].equals("none")) {
- comps[i] = "NONE";
- } else {
- throw new IllegalArgumentException("unknown PowerComponent: " + comps[i]);
- }
- }
- }
- }
-
private static boolean search(String[] strList, String str) {
return Arrays.stream(strList).anyMatch(s -> str.contains(s));
}
@@ -186,10 +169,44 @@
MICROPHONE("MICROPHONE"),
CPU("CPU");
- public final String value;
+ private final String mValue;
PowerComponent(String v) {
- value = v;
+ mValue = v;
+ }
+
+ public String getValue() {
+ return mValue;
+ }
+
+ public static PowerComponent[] asComponentArray(String[] componentNames) {
+ if (componentNames == null) {
+ return new PowerComponent[0];
+ }
+ normalizeComponentName(componentNames);
+ PowerComponent[] compArray = Arrays.stream(componentNames)
+ .map(PowerComponent::valueOf).toArray(PowerComponent[]::new);
+ Arrays.sort(compArray);
+ return compArray;
+ }
+
+ public static PowerComponent[] asComponentArray(List<String> nameList) {
+ if (nameList == null) {
+ return new PowerComponent[0];
+ }
+ return asComponentArray(nameList.toArray(new String[0]));
+ }
+
+ private static void normalizeComponentName(String[] comps) {
+ for (int i = 0; i < comps.length; i++) {
+ try {
+ PowerComponent.valueOf(comps[i]);
+ } catch (Exception e) {
+ if (comps[i] != null && comps[i].equals("none")) {
+ comps[i] = "NONE";
+ }
+ }
+ }
}
}
diff --git a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestHelper.java b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestHelper.java
index 327ca1c..0e61af9 100644
--- a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestHelper.java
+++ b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestHelper.java
@@ -16,6 +16,7 @@
package android.car.cts.powerpolicy;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import com.android.tradefed.log.LogUtil.CLog;
@@ -29,6 +30,7 @@
public static final String CURRENT_STATE_ASSERT_MSG = "current state";
public static final String CURRENT_POLICY_ASSERT_MSG = "current policy";
+ public static final String CURRENT_POWER_COMPONENT_ASSERT_MSG = "current power components";
public static final String REGISTERED_POLICY_ASSERT_MSG = "registered policy";
public static final String SILENT_MODE_FULL_ASSERT_MSG = "silent mode in full";
public static final String SILENT_MODE_STATUS_ASSERT_MSG = "silent mode status";
@@ -118,4 +120,11 @@
assertWithMessage(TOTAL_REGISTERED_POLICIES_ASSERT_MSG)
.that(mSystemCpms.getRegisteredPolicies().size() == totalNum).isTrue();
}
+
+ public void checkCurrentPowerComponents(PowerPolicyDef expected) throws Exception {
+ assertThat(mFrameCpms.getCurrentEnabledComponents()).asList()
+ .containsExactlyElementsIn(expected.getEnables());
+ assertThat(mFrameCpms.getCurrentDisabledComponents()).asList()
+ .containsExactlyElementsIn(expected.getDisables());
+ }
}
diff --git a/hostsidetests/car/src/android/car/cts/powerpolicy/SystemInfoParser.java b/hostsidetests/car/src/android/car/cts/powerpolicy/SystemInfoParser.java
index 39f83b8..09506e2 100644
--- a/hostsidetests/car/src/android/car/cts/powerpolicy/SystemInfoParser.java
+++ b/hostsidetests/car/src/android/car/cts/powerpolicy/SystemInfoParser.java
@@ -35,7 +35,7 @@
Object ret = m.invoke(null, cmdOutput);
t = mType.cast(ret);
} catch (Exception e) {
- CLog.e("%s: %s", mType.getSimpleName(), e.toString());
+ CLog.wtf("ERROR: Apply failed with " + cmdOutput, e);
}
return t;
}
diff --git a/hostsidetests/classloaders/OWNERS b/hostsidetests/classloaders/OWNERS
index 137abb9..e7c6bf7 100644
--- a/hostsidetests/classloaders/OWNERS
+++ b/hostsidetests/classloaders/OWNERS
@@ -1,4 +1,6 @@
# Bug component: 86431
calin@google.com
ngeoffray@google.com
-sehr@google.com
+oth@google.com
+skvadrik@google.com
+vmarko@google.com
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/Android.bp b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/Android.bp
index 2315719..dc719d4 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/Android.bp
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/Android.bp
@@ -28,8 +28,9 @@
"androidx.test.rules",
"truth-prebuilt",
"ub-uiautomator",
+ "Nene",
],
- min_sdk_version: "21",
+ sdk_version: "test_current",
// tag this module as a cts test artifact
test_suites: [
"cts",
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsPermissionToInteractTest.java b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsPermissionToInteractTest.java
index c020e36..71aa554 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsPermissionToInteractTest.java
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileAppsTest/src/com/android/cts/crossprofileappstest/CrossProfileAppsPermissionToInteractTest.java
@@ -26,13 +26,18 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.CrossProfileApps;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Bundle;
-import android.provider.Settings;
+import android.os.UserHandle;
+import android.os.UserManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.nene.permissions.PermissionContext;
+
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,11 +54,14 @@
"android.permission.INTERACT_ACROSS_USERS";
public static final String INTERACT_ACROSS_USERS_FULL_PERMISSION =
"android.permission.INTERACT_ACROSS_USERS_FULL";
+ public static final String ACTION_MANAGE_CROSS_PROFILE_ACCESS =
+ "android.settings.MANAGE_CROSS_PROFILE_ACCESS";
private static final ComponentName ADMIN_RECEIVER_COMPONENT =
new ComponentName(
AdminReceiver.class.getPackage().getName(), AdminReceiver.class.getName());
private static final String PARAM_CROSS_PROFILE_PACKAGE = "crossProfilePackage";
+ private static final TestApis sTestApis = new TestApis();
private final Context mContext = InstrumentationRegistry.getContext();
private final CrossProfileApps mCrossProfileApps =
@@ -78,54 +86,69 @@
@Test
public void testCanInteractAcrossProfiles_withAppOpEnabled_returnsTrue() {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(MANAGE_APP_OPS_MODES_PERMISSION);
- mAppOpsManager.setMode(AppOpsManager.OP_INTERACT_ACROSS_PROFILES,
- Binder.getCallingUid(), mContext.getPackageName(), AppOpsManager.MODE_ALLOWED);
+ setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED);
assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
}
@Test
public void testCanInteractAcrossProfiles_withCrossProfilesPermission_returnsTrue() {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(INTERACT_ACROSS_PROFILES_PERMISSION);
+ // Ideally we want to grant the permission in the other profile instead of allowing the
+ // appop, however UiAutomation#adoptShellPermission can't be used for multiple UIDs.
+ setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED, /* includeCallingProfile= */ false);
+ setAppOpOnCurrentProfile(AppOpsManager.MODE_IGNORED);
- assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
+ try (PermissionContext p = sTestApis.permissions().withPermission(
+ INTERACT_ACROSS_PROFILES_PERMISSION)) {
+ assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
+ }
}
@Test
public void testCanInteractAcrossProfiles_withCrossUsersPermission_returnsTrue() {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(INTERACT_ACROSS_USERS_PERMISSION);
+ // Ideally we want to grant the permission in the other profile instead of allowing the
+ // appop, however UiAutomation#adoptShellPermission can't be used for multiple UIDs.
+ setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED, /* includeCallingProfile= */ false);
+ setAppOpOnCurrentProfile(AppOpsManager.MODE_IGNORED);
- assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
+ try (PermissionContext p = sTestApis.permissions().withPermission(
+ INTERACT_ACROSS_USERS_PERMISSION)) {
+ assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
+ }
}
@Test
public void testCanInteractAcrossProfiles_withCrossUsersFullPermission_returnsTrue() {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(INTERACT_ACROSS_USERS_FULL_PERMISSION);
+ // Ideally we want to grant the permission in the other profile instead of allowing the
+ // appop, however UiAutomation#adoptShellPermission can't be used for multiple UIDs.
+ setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED, /* includeCallingProfile= */ false);
+ setAppOpOnCurrentProfile(AppOpsManager.MODE_IGNORED);
- assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
+ try (PermissionContext p = sTestApis.permissions().withPermission(
+ INTERACT_ACROSS_USERS_FULL_PERMISSION)) {
+ assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isTrue();
+ }
}
@Test
- public void testCanInteractAcrossProfiles_withAppOpDisabled_returnsFalse() {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(MANAGE_APP_OPS_MODES_PERMISSION);
- mAppOpsManager.setMode(AppOpsManager.OP_INTERACT_ACROSS_PROFILES,
- Binder.getCallingUid(), mContext.getPackageName(), AppOpsManager.MODE_IGNORED);
+ public void testCanInteractAcrossProfiles_withAppOpDisabledOnCallingProfile_returnsFalse() {
+ setAppOpOnAllProfiles(AppOpsManager.MODE_ALLOWED, /* includeCallingProfile= */ false);
+ setAppOpOnCurrentProfile(AppOpsManager.MODE_IGNORED);
+
+ assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isFalse();
+ }
+
+ @Test
+ public void testCanInteractAcrossProfiles_withAppOpDisabledOnOtherProfiles_returnsFalse() {
+ setAppOpOnAllProfiles(AppOpsManager.MODE_IGNORED, /* includeCallingProfile= */ false);
+ setAppOpOnCurrentProfile(AppOpsManager.MODE_ALLOWED);
assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isFalse();
}
@Test
public void testCanInteractAcrossProfiles_withNoOtherProfile_returnsFalse() {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .adoptShellPermissionIdentity(MANAGE_APP_OPS_MODES_PERMISSION);
- mAppOpsManager.setMode(AppOpsManager.OP_INTERACT_ACROSS_PROFILES,
- Binder.getCallingUid(), mContext.getPackageName(), AppOpsManager.MODE_ALLOWED);
+ setAppOpOnCurrentProfile(AppOpsManager.MODE_ALLOWED);
assertThat(mCrossProfileApps.canInteractAcrossProfiles()).isFalse();
}
@@ -135,7 +158,7 @@
Intent intent = mCrossProfileApps.createRequestInteractAcrossProfilesIntent();
assertThat(intent).isNotNull();
- assertThat(intent.getAction()).isEqualTo(Settings.ACTION_MANAGE_CROSS_PROFILE_ACCESS);
+ assertThat(intent.getAction()).isEqualTo(ACTION_MANAGE_CROSS_PROFILE_ACCESS);
assertThat(intent.getData()).isNotNull();
assertThat(intent.getData().getSchemeSpecificPart()).isEqualTo(mContext.getPackageName());
}
@@ -178,4 +201,37 @@
fail("cross profile package param not found.");
return null;
}
+
+ private void setAppOpOnCurrentProfile(int mode) {
+ try (PermissionContext p = sTestApis.permissions().withPermission(
+ MANAGE_APP_OPS_MODES_PERMISSION)) {
+ mAppOpsManager.setMode(AppOpsManager.OPSTR_INTERACT_ACROSS_PROFILES,
+ Binder.getCallingUid(), mContext.getPackageName(), mode);
+ }
+ }
+
+ private void setAppOpOnAllProfiles(int mode) {
+ setAppOpOnAllProfiles(mode, /* includeCallingProfile= */ true);
+ }
+
+ private void setAppOpOnAllProfiles(int mode, boolean includeCallingProfile) {
+ try (PermissionContext p = sTestApis.permissions().withPermission(
+ MANAGE_APP_OPS_MODES_PERMISSION, INTERACT_ACROSS_USERS_PERMISSION)) {
+ for (UserHandle profile : mContext.getSystemService(
+ UserManager.class).getAllProfiles()) {
+ if (!includeCallingProfile && profile.getIdentifier() == mContext.getUserId()) {
+ continue;
+ }
+ try {
+ final int uid = mContext.createContextAsUser(profile, /* flags= */ 0)
+ .getPackageManager().getPackageUid(
+ mContext.getPackageName(), /* flags= */ 0);
+ mAppOpsManager.setMode(AppOpsManager.OPSTR_INTERACT_ACROSS_PROFILES,
+ uid, mContext.getPackageName(), mode);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Do nothing
+ }
+ }
+ }
+ }
}
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DelegateApp/AndroidManifest.xml
index 9618e9b..d196e70 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DelegateApp/AndroidManifest.xml
@@ -24,11 +24,12 @@
<activity android:name="com.android.cts.delegate.DelegatedScopesReceiverActivity"
android:exported="true">
</activity>
- <receiver android:name=".DelegateTestUtils$NetworkLogsReceiver"
+ <receiver android:name=".DelegateTestUtils$DelegatedLogsReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN"
android:exported="true">
<intent-filter>
<action android:name="android.app.action.NETWORK_LOGS_AVAILABLE"/>
+ <action android:name="android.app.action.SECURITY_LOGS_AVAILABLE"/>
</intent-filter>
</receiver>
</application>
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/DelegateTestUtils.java b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/DelegateTestUtils.java
index 78dd9b0..0480566 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/DelegateTestUtils.java
+++ b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/DelegateTestUtils.java
@@ -62,17 +62,18 @@
}
/**
- * A receiver for listening for network logs.
+ * A receiver for listening for network and security logs.
*
* To use this the sBatchCountDown must be assigned before generating logs.
* The receiver will ignore events until sBatchCountDown is assigned.
*/
- public static class NetworkLogsReceiver extends DelegatedAdminReceiver {
+ public static class DelegatedLogsReceiver extends DelegatedAdminReceiver {
private static final long TIMEOUT_MIN = 3;
static CountDownLatch sBatchCountDown;
static ArrayList<NetworkEvent> sNetworkEvents = new ArrayList<>();
+ static ArrayList<SecurityEvent> sSecurityEvents = new ArrayList<>();
@Override
public void onNetworkLogsAvailable(Context context, Intent intent, long batchToken,
@@ -93,16 +94,38 @@
}
}
+ @Override
+ public void onSecurityLogsAvailable(Context context, Intent intent) {
+ if (sBatchCountDown == null) {
+ // If the latch is not set then nothing will be using the receiver to examine
+ // the logs. Leave the logs unread.
+ return;
+ }
+
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ final List<SecurityEvent> events = dpm.retrieveSecurityLogs(null);
+ if (events == null || events.size() == 0) {
+ fail("Failed to retrieve security logs");
+ } else {
+ sSecurityEvents.addAll(events);
+ sBatchCountDown.countDown();
+ }
+ }
+
public static void waitForBroadcast() throws InterruptedException {
sBatchCountDown.await(TIMEOUT_MIN, TimeUnit.MINUTES);
if (sBatchCountDown.getCount() > 0) {
- fail("Did not get DelegateAdminReceiver#onNetworkLogsAvailable callback");
+ fail("Did not get DelegateAdminReceiver callback");
}
}
public static List<NetworkEvent> getNetworkEvents() {
return sNetworkEvents;
}
+
+ public static List<SecurityEvent> getSecurityEvents() {
+ return sSecurityEvents;
+ }
}
public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
@@ -141,23 +164,6 @@
ks.deleteEntry(keyAlias);
}
-
- /**
- * Fetches the available security events
- */
- public static List<SecurityEvent> getSecurityEvents(DevicePolicyManager dpm)
- throws Exception {
- List<SecurityEvent> events = null;
- // Retry once after seeping for 1 second, in case "dpm force-security-logs" hasn't taken
- // effect just yet.
- for (int i = 0; i < 5 && events == null; i++) {
- events = dpm.retrieveSecurityLogs(null);
- if (events == null) Thread.sleep(1000);
- }
-
- return events;
- }
-
/**
* Verifies that the expected keystore events generated by {@link #testGenerateKey} are
* present
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/NetworkLoggingDelegateTest.java b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/NetworkLoggingDelegateTest.java
index 8e838cb..2f5adeb 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/NetworkLoggingDelegateTest.java
+++ b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/NetworkLoggingDelegateTest.java
@@ -62,7 +62,7 @@
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mContext = getInstrumentation().getContext();
mDpm = mContext.getSystemService(DevicePolicyManager.class);
- DelegateTestUtils.NetworkLogsReceiver.sBatchCountDown = new CountDownLatch(1);
+ DelegateTestUtils.DelegatedLogsReceiver.sBatchCountDown = new CountDownLatch(1);
}
public void testCanAccessApis() throws Throwable {
@@ -92,7 +92,7 @@
}
mDevice.executeShellCommand("dpm force-network-logs");
- DelegateTestUtils.NetworkLogsReceiver.waitForBroadcast();
+ DelegateTestUtils.DelegatedLogsReceiver.waitForBroadcast();
} finally {
mDpm.setNetworkLoggingEnabled(null, false);
assertFalse(mDpm.isNetworkLoggingEnabled(null));
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/SecurityLoggingDelegateTest.java b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/SecurityLoggingDelegateTest.java
index e924d41..82c99ab 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/SecurityLoggingDelegateTest.java
+++ b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/SecurityLoggingDelegateTest.java
@@ -28,6 +28,7 @@
import androidx.test.InstrumentationRegistry;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
/**
* Tests that a delegate app with DELEGATION_SECURITY_LOGGING is able to control and access
@@ -50,6 +51,7 @@
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mContext = getInstrumentation().getContext();
mDpm = mContext.getSystemService(DevicePolicyManager.class);
+ DelegateTestUtils.DelegatedLogsReceiver.sBatchCountDown = new CountDownLatch(1);
}
public void testCannotAccessApis()throws Exception {
@@ -89,7 +91,11 @@
* side actions and by {@link #testGenerateLogs()} are there.
*/
public void testVerifyGeneratedLogs() throws Exception {
- final List<SecurityEvent> events = DelegateTestUtils.getSecurityEvents(mDpm);
+ mDevice.executeShellCommand("dpm force-security-logs");
+ DelegateTestUtils.DelegatedLogsReceiver.waitForBroadcast();
+
+ final List<SecurityEvent> events =
+ DelegateTestUtils.DelegatedLogsReceiver.getSecurityEvents();
DelegateTestUtils.verifyKeystoreEventsPresent(GENERATED_KEY_ALIAS, events);
}
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/WorkProfileNetworkLoggingDelegateTest.java b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/WorkProfileNetworkLoggingDelegateTest.java
index 0b26658..a18c4056 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/WorkProfileNetworkLoggingDelegateTest.java
+++ b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/WorkProfileNetworkLoggingDelegateTest.java
@@ -86,7 +86,7 @@
mContext = InstrumentationRegistry.getContext();
mDpm = mContext.getSystemService(DevicePolicyManager.class);
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- DelegateTestUtils.NetworkLogsReceiver.sBatchCountDown = new CountDownLatch(1);
+ DelegateTestUtils.DelegatedLogsReceiver.sBatchCountDown = new CountDownLatch(1);
}
@Test
@@ -125,9 +125,9 @@
@Test
public void testRetrieveNetworkLogs_forceNetworkLogs_receiveNetworkLogs() throws Exception {
mDevice.executeShellCommand("dpm force-network-logs");
- DelegateTestUtils.NetworkLogsReceiver.waitForBroadcast();
+ DelegateTestUtils.DelegatedLogsReceiver.waitForBroadcast();
- verifyNetworkLogs(DelegateTestUtils.NetworkLogsReceiver.getNetworkEvents());
+ verifyNetworkLogs(DelegateTestUtils.DelegatedLogsReceiver.getNetworkEvents());
}
private void verifyNetworkLogs(List<NetworkEvent> networkEvents) {
diff --git a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/WorkProfileSecurityLoggingDelegateTest.java b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/WorkProfileSecurityLoggingDelegateTest.java
index e660d1b..165d38c 100644
--- a/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/WorkProfileSecurityLoggingDelegateTest.java
+++ b/hostsidetests/devicepolicy/app/DelegateApp/src/com/android/cts/delegate/WorkProfileSecurityLoggingDelegateTest.java
@@ -23,6 +23,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.SecurityLog.SecurityEvent;
import android.content.Context;
+import android.support.test.uiautomator.UiDevice;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -32,6 +33,7 @@
import org.junit.runner.RunWith;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
@RunWith(AndroidJUnit4.class)
public class WorkProfileSecurityLoggingDelegateTest {
@@ -42,11 +44,14 @@
private Context mContext;
private DevicePolicyManager mDpm;
+ private UiDevice mDevice;
@Before
public void setUp() {
mContext = InstrumentationRegistry.getContext();
mDpm = mContext.getSystemService(DevicePolicyManager.class);
+ mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ DelegateTestUtils.DelegatedLogsReceiver.sBatchCountDown = new CountDownLatch(1);
}
@Test
@@ -90,7 +95,11 @@
*/
@Test
public void testVerifyGeneratedLogs() throws Exception {
- final List<SecurityEvent> events = DelegateTestUtils.getSecurityEvents(mDpm);
+ mDevice.executeShellCommand("dpm force-security-logs");
+ DelegateTestUtils.DelegatedLogsReceiver.waitForBroadcast();
+
+ final List<SecurityEvent> events =
+ DelegateTestUtils.DelegatedLogsReceiver.getSecurityEvents();
DelegateTestUtils.verifyKeystoreEventsPresent(GENERATED_KEY_ALIAS, events);
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdminService/src/com/android/cts/deviceadminservice/ComponentController.java b/hostsidetests/devicepolicy/app/DeviceAdminService/src/com/android/cts/deviceadminservice/ComponentController.java
index fe93de6..e63e562d 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdminService/src/com/android/cts/deviceadminservice/ComponentController.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdminService/src/com/android/cts/deviceadminservice/ComponentController.java
@@ -17,6 +17,7 @@
import android.content.ComponentName;
import android.content.pm.PackageManager;
+import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -28,7 +29,8 @@
(enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
PackageManager.COMPONENT_ENABLED_STATE_DISABLED)
, PackageManager.DONT_KILL_APP);
- Log.i(TAG, "setComponentEnabledSetting: " + cn + " enabled=" + enabled);
+ Log.i(TAG, "enableComponent(): user=" + UserHandle.myUserId() + ", component=" + cn
+ + ", enabled=" + enabled);
}
public void testEnableService1() {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
index 35e3ee2..4897559 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
@@ -26,7 +26,6 @@
import static com.google.common.truth.Truth.assertWithMessage;
-import static org.junit.Assert.assertArrayEquals;
import static org.testng.Assert.assertThrows;
import android.app.ActivityManager;
@@ -170,32 +169,6 @@
mContext.unregisterReceiver(mReceiver);
}
- // Setting and unsetting the lock task packages.
- public void testSetLockTaskPackages() {
- final String[] packages = new String[] { TEST_PACKAGE, "some.other.package" };
- mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, packages);
- assertArrayEquals(packages, mDevicePolicyManager.getLockTaskPackages(ADMIN_COMPONENT));
- assertTrue(mDevicePolicyManager.isLockTaskPermitted(TEST_PACKAGE));
-
- mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[0]);
- assertEquals(0, mDevicePolicyManager.getLockTaskPackages(ADMIN_COMPONENT).length);
- assertFalse(mDevicePolicyManager.isLockTaskPermitted(TEST_PACKAGE));
- }
-
- // When OEM defines policy-exempt apps, they are permitted on lock task mode
- public void testIsLockTaskPermittedIncludesPolicyExemptApps() {
- Set<String> policyExemptApps = mDevicePolicyManager.getPolicyExemptApps();
- if (policyExemptApps.isEmpty()) {
- Log.v(TAG, "OEM doesn't define any policy-exempt app");
- return;
- }
-
- for (String app : policyExemptApps) {
- assertWithMessage("isLockTaskPermitted(%s)", app)
- .that(mDevicePolicyManager.isLockTaskPermitted(app)).isTrue();
- }
- }
-
// Setting and unsetting the lock task packages when the OEM defines policy-exempt apps
public void testSetLockTaskPackagesIgnoresExemptApps() {
Set<String> policyExemptApps = mDevicePolicyManager.getPolicyExemptApps();
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
index 1a67710..d58430b 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecurityLoggingTest.java
@@ -73,6 +73,7 @@
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.support.test.uiautomator.UiDevice;
import android.text.TextUtils;
import android.util.Log;
@@ -220,7 +221,11 @@
* side actions and by {@link #testGenerateLogs()} are there.
*/
public void testVerifyGeneratedLogs() throws Exception {
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ .executeShellCommand("dpm force-security-logs");
+
final List<SecurityEvent> events = getEvents();
+
verifyAutomaticEventsPresent(events);
verifyKeystoreEventsPresent(events);
verifyKeyChainEventsPresent(events);
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 01d8572..75bcdf1 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
@@ -54,6 +54,7 @@
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.
@@ -65,7 +66,7 @@
super.setUp();
mContext = getInstrumentation().getContext();
mContentResolver = mContext.getContentResolver();
- mUiAutomation = getInstrumentation().getUiAutomation();
+ mUiAutomation = getUiAutomation();
mDevicePolicyManager = (DevicePolicyManager)
mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
@@ -87,6 +88,18 @@
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);
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
index 9185164..61f7645 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
@@ -70,7 +70,9 @@
UserManager.DISALLOW_DATA_ROAMING,
UserManager.DISALLOW_SET_USER_ICON,
UserManager.DISALLOW_BLUETOOTH,
- UserManager.DISALLOW_BLUETOOTH_SHARING
+ UserManager.DISALLOW_BLUETOOTH_SHARING,
+ UserManager.DISALLOW_CAMERA_TOGGLE,
+ UserManager.DISALLOW_MICROPHONE_TOGGLE,
};
/**
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
index 2d17b0d..93b6817 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
@@ -61,6 +61,8 @@
UserManager.DISALLOW_CONTENT_CAPTURE,
UserManager.DISALLOW_CONTENT_SUGGESTIONS,
UserManager.DISALLOW_UNIFIED_PASSWORD,
+ UserManager.DISALLOW_CAMERA_TOGGLE,
+ UserManager.DISALLOW_MICROPHONE_TOGGLE,
};
public static final String[] DISALLOWED = new String[] {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
index e9edba1..e09fc54 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
@@ -23,6 +23,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.pm.PackageManager;
+import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.UserHandle;
import android.os.UserManager;
@@ -33,6 +34,9 @@
import androidx.test.InstrumentationRegistry;
import com.android.bedstead.dpmwrapper.TestAppSystemServiceFactory;
+import com.android.compatibility.common.util.WifiConfigCreator;
+
+import java.util.List;
/**
* Base class for device-owner based tests.
@@ -48,6 +52,7 @@
protected DevicePolicyManager mDevicePolicyManager;
protected WifiManager mWifiManager;
+ protected WifiConfigCreator mWifiConfigCreator;
protected Instrumentation mInstrumentation;
protected UiDevice mDevice;
protected boolean mHasSecureLockScreen;
@@ -66,6 +71,16 @@
BasicAdminReceiver.class);
mWifiManager = TestAppSystemServiceFactory.getWifiManager(mContext,
BasicAdminReceiver.class);
+ WifiManager currentUserWifiManager = mContext.getSystemService(WifiManager.class);
+ mWifiConfigCreator = new WifiConfigCreator(mContext, mWifiManager) {
+ @Override
+ public List<WifiConfiguration> getConfiguredNetworks() {
+ // Must always use the current user's wifi manager, otherwise it would fail on
+ // headless system user (as the device owner is not the current user).
+ return currentUserWifiManager.getConfiguredNetworks();
+ }
+ };
+
mHasSecureLockScreen = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SECURE_LOCK_SCREEN);
mHasTelephonyFeature = mContext.getPackageManager().hasSystemFeature(
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SensorToggleRestrictionTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SensorToggleRestrictionTest.java
new file mode 100755
index 0000000..76b3f0a
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SensorToggleRestrictionTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.deviceowner;
+
+import android.hardware.SensorPrivacyManager;
+import android.os.UserManager;
+
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
+/**
+ * Test interaction between user restriction controlling sensor toggles (
+ * {@link UserManager#DISALLOW_CAMERA_TOGGLE} and {@link UserManager#DISALLOW_MICROPHONE_TOGGLE})
+ * and {@link android.hardware.SensorPrivacyManager}.
+ */
+public class SensorToggleRestrictionTest extends BaseDeviceOwnerTest {
+
+ private static final long RESTRICTION_WAITING_TIMEOUT_NANO = 30_000_000_000L;
+
+ private SensorPrivacyManager mSensorPrivacyManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mSensorPrivacyManager = mContext.getSystemService(SensorPrivacyManager.class);
+
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDevicePolicyManager.clearUserRestriction(getWho(), UserManager.DISALLOW_CAMERA_TOGGLE);
+ mDevicePolicyManager.clearUserRestriction(getWho(), UserManager.DISALLOW_MICROPHONE_TOGGLE);
+ super.tearDown();
+ }
+
+ public void testCameraToggle_RestrictionSet_CannotChangeSensorPrivacy() {
+ if (!mSensorPrivacyManager.supportsSensorToggle(SensorPrivacyManager.Sensors.CAMERA)) {
+ return;
+ }
+ assertFalse("Camera sensor privacy should be off by default",
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mSensorPrivacyManager,
+ m -> m.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA)));
+
+ mDevicePolicyManager.addUserRestriction(getWho(), UserManager.DISALLOW_CAMERA_TOGGLE);
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSensorPrivacyManager,
+ m -> m.setSensorPrivacy(SensorPrivacyManager.Sensors.CAMERA, true));
+
+ assertFalse("Camera sensor privacy should not be enabled given admin restriction",
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mSensorPrivacyManager,
+ m -> m.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA)));
+ }
+
+ public void testMicrophoneToggle_RestrictionSet_CannotChangeSensorPrivacy() {
+ if (!mSensorPrivacyManager.supportsSensorToggle(SensorPrivacyManager.Sensors.MICROPHONE)) {
+ return;
+ }
+ assertFalse("Microphone sensor privacy should be off by default",
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mSensorPrivacyManager,
+ m -> m.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.MICROPHONE)));
+
+ mDevicePolicyManager.addUserRestriction(getWho(), UserManager.DISALLOW_MICROPHONE_TOGGLE);
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSensorPrivacyManager,
+ m -> m.setSensorPrivacy(SensorPrivacyManager.Sensors.MICROPHONE, true));
+
+ assertFalse("Microphone sensor privacy should not be enabled given admin restriction",
+ ShellIdentityUtils.invokeMethodWithShellPermissions(mSensorPrivacyManager,
+ m -> m.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.MICROPHONE)));
+ }
+
+ public void testCameraToggle_RestrictionSet_ResetSensorPrivacy() {
+ if (!mSensorPrivacyManager.supportsSensorToggle(SensorPrivacyManager.Sensors.CAMERA)) {
+ return;
+ }
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSensorPrivacyManager,
+ m -> m.setSensorPrivacy(SensorPrivacyManager.Sensors.CAMERA, true));
+
+ mDevicePolicyManager.addUserRestriction(getWho(), UserManager.DISALLOW_CAMERA_TOGGLE);
+
+ long deadline = System.nanoTime() + RESTRICTION_WAITING_TIMEOUT_NANO;
+ while (System.nanoTime() < deadline) {
+ if (!ShellIdentityUtils.invokeMethodWithShellPermissions(mSensorPrivacyManager,
+ m -> m.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA))) {
+ return;
+ }
+ }
+ fail("Camera sensor privacy did not get reset in time");
+ }
+
+ public void testMicrophoneToggle_RestrictionSet_ResetSensorPrivacy() {
+ if (!mSensorPrivacyManager.supportsSensorToggle(SensorPrivacyManager.Sensors.MICROPHONE)) {
+ return;
+ }
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSensorPrivacyManager,
+ m -> m.setSensorPrivacy(SensorPrivacyManager.Sensors.MICROPHONE, true));
+
+ mDevicePolicyManager.addUserRestriction(getWho(), UserManager.DISALLOW_MICROPHONE_TOGGLE);
+
+ long deadline = System.nanoTime() + RESTRICTION_WAITING_TIMEOUT_NANO;
+ while (System.nanoTime() < deadline) {
+ if (!ShellIdentityUtils.invokeMethodWithShellPermissions(mSensorPrivacyManager,
+ m -> m.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.MICROPHONE))) {
+ return;
+ }
+ }
+ fail("Microphone sensor privacy did not get reset in time");
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetLocationEnabledTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetLocationEnabledTest.java
index 20a160a..60500d7 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetLocationEnabledTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetLocationEnabledTest.java
@@ -15,12 +15,15 @@
*/
package com.android.cts.deviceowner;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
+import android.util.Log;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -28,41 +31,90 @@
/**
* Test {@link DevicePolicyManager#setLocationEnabled}.
*/
-public class SetLocationEnabledTest extends BaseDeviceOwnerTest {
+public final class SetLocationEnabledTest extends BaseDeviceOwnerTest {
+
+ private static final String TAG = SetLocationEnabledTest.class.getSimpleName();
+
private static final long TIMEOUT_MS = 5000;
- public void testSetLocationEnabled() throws Exception {
- LocationManager locationManager = mContext.getSystemService(LocationManager.class);
- boolean enabled = locationManager.isLocationEnabled();
+ private static final boolean ENABLED = true;
+ private static final boolean DISABLED = false;
- setLocationEnabledAndWaitIfNecessary(!enabled);
+ private LocationManager mLocationManager;
- boolean expected = mIsAutomotive ? enabled : !enabled;
- assertEquals(expected, locationManager.isLocationEnabled());
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
- setLocationEnabledAndWaitIfNecessary(enabled);
- assertEquals(enabled, locationManager.isLocationEnabled());
+ mLocationManager = mContext.getSystemService(LocationManager.class);
}
- private void setLocationEnabledAndWaitIfNecessary(boolean enabled) throws Exception {
- if (mIsAutomotive) {
+ public void testSetLocationEnabled() throws Exception {
+ boolean enabled = mLocationManager.isLocationEnabled();
+ if (enabled) {
+ Log.d(TAG, "location is initially enabled for user " + mUserId);
+ runDisableLocationFirst();
+ } else {
+ Log.d(TAG, "location is initially disabled for user " + mUserId);
+ runEnableLocationFirst();
+ }
+ }
+
+ private void runDisableLocationFirst() throws Exception {
+ Log.v(TAG, "runDisableLocationFirst()");
+
+ setLocationEnabledAndWaitIfNecessary(DISABLED, /* wait= */ !mIsAutomotive);
+ assertWithMessage("isLocationEnabled()").that(mLocationManager.isLocationEnabled())
+ .isEqualTo(mIsAutomotive ? ENABLED : DISABLED);
+
+ setLocationEnabledAndWaitIfNecessary(ENABLED, /* wait= */ !mIsAutomotive);
+ assertWithMessage("isLocationEnabled()").that(mLocationManager.isLocationEnabled())
+ .isTrue();
+ }
+
+ private void runEnableLocationFirst() throws Exception {
+ Log.v(TAG, "runEnableLocationFirst()");
+
+ setLocationEnabledAndWaitIfNecessary(ENABLED, /* wait= */ true);
+ assertWithMessage("isLocationEnabled()").that(mLocationManager.isLocationEnabled())
+ .isTrue();
+
+ setLocationEnabledAndWaitIfNecessary(DISABLED, /* wait= */ !mIsAutomotive);
+ assertWithMessage("isLocationEnabled()").that(mLocationManager.isLocationEnabled())
+ .isEqualTo(mIsAutomotive ? ENABLED : DISABLED);
+ }
+
+ private void setLocationEnabledAndWaitIfNecessary(boolean enabled, boolean wait)
+ throws Exception {
+ if (!wait) {
+ Log.d(TAG, "setting location to " + enabled);
mDevicePolicyManager.setLocationEnabled(getWho(), enabled);
+ Log.d(TAG, "not waiting for " + LocationManager.MODE_CHANGED_ACTION + " intent");
return;
}
- final CountDownLatch latch = new CountDownLatch(1);
- final BroadcastReceiver receiver = new BroadcastReceiver() {
+ CountDownLatch latch = new CountDownLatch(1);
+ BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ boolean actualEnabled = intent
+ .getBooleanExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled);
+ Log.d(TAG, "received intent " + intent.getAction() + ": enabled=" + actualEnabled);
+ if (actualEnabled != enabled) {
+ Log.e(TAG, "Invalid value on extra " + LocationManager.EXTRA_LOCATION_ENABLED
+ + ": " + actualEnabled);
+ return;
+ }
latch.countDown();
}
};
mContext.registerReceiver(receiver, new IntentFilter(LocationManager.MODE_CHANGED_ACTION));
-
try {
+ Log.d(TAG, "setting location to " + enabled);
mDevicePolicyManager.setLocationEnabled(getWho(), enabled);
- assertTrue("timed out waiting for location mode change broadcast",
- latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ Log.d(TAG, "Waiting for " + LocationManager.MODE_CHANGED_ACTION + " intent");
+ assertWithMessage("%s intent reveiced in %sms", LocationManager.MODE_CHANGED_ACTION,
+ TIMEOUT_MS).that(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
} finally {
mContext.unregisterReceiver(receiver);
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
index 927520a..3630cee 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
@@ -37,27 +37,30 @@
private static final String TEST_APP_APK = "CtsEmptyTestApp.apk";
private static final String TEST_APP_PKG = "android.packageinstaller.emptytestapp.cts";
- private static final String SIMPLE_APP_APK ="CtsSimpleApp.apk";
+ private static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
private static final String SIMPLE_APP_PKG = "com.android.cts.launcherapps.simpleapp";
private static final String SIMPLE_APP_ACTIVITY =
"com.android.cts.launcherapps.simpleapp.SimpleActivityImmediateExit";
public void testSetUserControlDisabledPackages() throws Exception {
- ArrayList<String> protectedPackages= new ArrayList<>();
+ ArrayList<String> protectedPackages = new ArrayList<>();
protectedPackages.add(SIMPLE_APP_PKG);
mDevicePolicyManager.setUserControlDisabledPackages(getWho(), protectedPackages);
+ }
+
+ public void testLaunchActivity() throws Exception {
// Launch an activity so that the app exits stopped state.
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName(SIMPLE_APP_PKG, SIMPLE_APP_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- Log.d(TAG, "Starting " + intent + " on user " + mUserId);
- mContext.startActivity(intent);
+ Log.d(TAG, "Starting " + intent + " on user " + getCurrentUser().getIdentifier());
+ mContext.startActivityAsUser(intent, getCurrentUser());
}
public void testForceStopWithUserControlDisabled() throws Exception {
final ArrayList<String> pkgs = new ArrayList<>();
pkgs.add(SIMPLE_APP_PKG);
- // Check if package is part of UserControlDisabledPackages before checking if
+ // Check if package is part of UserControlDisabledPackages before checking if
// package is stopped since it is a necessary condition to prevent stopping of
// package
@@ -79,16 +82,19 @@
private boolean isPackageStopped(String packageName) throws Exception {
PackageInfo packageInfo = mContext.getPackageManager()
- .getPackageInfo(packageName, PackageManager.GET_META_DATA);
+ .getPackageInfoAsUser(packageName, PackageManager.GET_META_DATA,
+ getCurrentUser().getIdentifier());
boolean stopped = (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED)
== ApplicationInfo.FLAG_STOPPED;
- Log.d(TAG, "Application flags for " + packageName + " on user " + mUserId + " = "
+ Log.d(TAG, "Application flags for " + packageName + " on user "
+ + getCurrentUser().getIdentifier() + " = "
+ Integer.toHexString(packageInfo.applicationInfo.flags) + ". Stopped: " + stopped);
return stopped;
}
private void assertPackageStopped(boolean stopped) throws Exception {
- assertWithMessage("Package %s stopped for user %s", SIMPLE_APP_PKG, mUserId)
+ assertWithMessage("Package %s stopped for user %s", SIMPLE_APP_PKG,
+ getCurrentUser().getIdentifier())
.that(isPackageStopped(SIMPLE_APP_PKG)).isEqualTo(stopped);
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
index 79ebaaf..22bd3f6 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
@@ -33,8 +33,6 @@
import android.provider.Settings;
import android.util.Log;
-import com.android.compatibility.common.util.WifiConfigCreator;
-
import java.util.List;
import java.util.stream.Collectors;
@@ -48,15 +46,13 @@
private static final String ORIGINAL_REGULAR_SSID = "RegularCTSTest";
private static final String CHANGED_REGULAR_SSID = "RegularChangedCTSTest";
private static final String ORIGINAL_PASSWORD = "originalpassword";
- private WifiConfigCreator mConfigCreator;
@Override
protected void setUp() throws Exception {
super.setUp();
- mConfigCreator = new WifiConfigCreator(mContext, mWifiManager);
mDevicePolicyManager.setGlobalSetting(getWho(),
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, "1");
- mConfigCreator.addNetwork(ORIGINAL_DEVICE_OWNER_SSID, true, SECURITY_TYPE_WPA,
+ mWifiConfigCreator.addNetwork(ORIGINAL_DEVICE_OWNER_SSID, true, SECURITY_TYPE_WPA,
ORIGINAL_PASSWORD);
startRegularActivity(ACTION_CREATE_WIFI_CONFIG, -1, ORIGINAL_REGULAR_SSID,
SECURITY_TYPE_WPA, ORIGINAL_PASSWORD);
@@ -66,7 +62,7 @@
protected void tearDown() throws Exception {
mDevicePolicyManager.setGlobalSetting(getWho(),
Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, "0");
- List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ List<WifiConfiguration> configs = mWifiConfigCreator.getConfiguredNetworks();
logConfigs("tearDown()", configs);
for (WifiConfiguration config : configs) {
if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID) ||
@@ -81,13 +77,13 @@
}
public void testDeviceOwnerCanUpdateConfig() throws Exception {
- List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ List<WifiConfiguration> configs = mWifiConfigCreator.getConfiguredNetworks();
logConfigs("testDeviceOwnerCanUpdateConfig()", configs);
int updateCount = 0;
for (WifiConfiguration config : configs) {
Log.d(TAG, "testDeviceOwnerCanUpdateConfig(): testing " + config.SSID);
if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID)) {
- int netId = mConfigCreator.updateNetwork(config,
+ int netId = mWifiConfigCreator.updateNetwork(config,
CHANGED_DEVICE_OWNER_SSID, true, SECURITY_TYPE_NONE, null);
Log.d(TAG, "netid after updateNetwork(REGULAR_SSID):" + netId);
assertWithMessage("netid after updateNetwork(%s, DO_SSID)", config.SSID)
@@ -95,7 +91,7 @@
++updateCount;
}
if (areMatchingSsids(ORIGINAL_REGULAR_SSID, config.SSID)) {
- int netId = mConfigCreator.updateNetwork(config,
+ int netId = mWifiConfigCreator.updateNetwork(config,
CHANGED_REGULAR_SSID, true, SECURITY_TYPE_NONE, null);
Log.d(TAG, "netid after updateNetwork(REGULAR_SSID):" + netId);
assertWithMessage("netid after updateNetwork(%s, REGULAR_SSID)", config.SSID)
@@ -108,7 +104,7 @@
}
public void testRegularAppCannotUpdateDeviceOwnerConfig() throws Exception {
- List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ List<WifiConfiguration> configs = mWifiConfigCreator.getConfiguredNetworks();
logConfigs("testRegularAppCannotUpdateDeviceOwnerConfig()", configs);
int updateCount = 0;
for (WifiConfiguration config : configs) {
@@ -123,7 +119,7 @@
.that(updateCount).isEqualTo(1);
// Assert nothing has changed
- configs = mWifiManager.getConfiguredNetworks();
+ configs = mWifiConfigCreator.getConfiguredNetworks();
int notChangedCount = 0;
for (WifiConfiguration config : configs) {
Log.d(TAG, "testRegularAppCannotUpdateDeviceOwnerConfig(): testing " + config.SSID);
@@ -137,7 +133,7 @@
}
public void testRegularAppCannotRemoveDeviceOwnerConfig() throws Exception {
- List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ List<WifiConfiguration> configs = mWifiConfigCreator.getConfiguredNetworks();
logConfigs("testRegularAppCannotUpdateDeviceOwnerConfig()", configs);
int removeCount = 0;
for (WifiConfiguration config : configs) {
@@ -153,7 +149,7 @@
.that(removeCount).isEqualTo(1);
// Assert nothing has changed
- configs = mWifiManager.getConfiguredNetworks();
+ configs = mWifiConfigCreator.getConfiguredNetworks();
int notChangedCount = 0;
for (WifiConfiguration config : configs) {
Log.d(TAG, "testRegularAppCannotRemoveDeviceOwnerConfig(): testing " + config.SSID);
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiNetworkConfigurationWithoutFineLocationPermissionTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiNetworkConfigurationWithoutFineLocationPermissionTest.java
index b69e2df..9189e50 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiNetworkConfigurationWithoutFineLocationPermissionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiNetworkConfigurationWithoutFineLocationPermissionTest.java
@@ -16,17 +16,17 @@
package com.android.cts.deviceowner;
-
import static com.android.compatibility.common.util.WifiConfigCreator.SECURITY_TYPE_NONE;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import android.Manifest;
import android.content.pm.PackageManager;
import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager;
import android.os.SystemClock;
+import android.util.Log;
import com.android.compatibility.common.util.SystemUtil;
-import com.android.compatibility.common.util.WifiConfigCreator;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -42,37 +42,39 @@
private static final long UPDATE_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(5);
private static final long UPDATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(1);
- private WifiManager mWifiManager;
- private WifiConfigCreator mWifiConfigCreator;
-
@Override
public void setUp() throws Exception {
super.setUp();
- mWifiConfigCreator = new WifiConfigCreator(getContext());
- mWifiManager = getContext().getSystemService(WifiManager.class);
// WiFi is supposed to be a prerequisite of CTS but sometimes it's not enabled
// for some unknown reason. Check it here just in case.
if (!mWifiManager.isWifiEnabled()) {
+ Log.d(TAG, "Enabling wifi using shell");
SystemUtil.runShellCommand("svc wifi enable");
awaitWifiEnabled();
+ Log.d(TAG, "Done: " + mWifiManager.isWifiEnabled());
}
}
public void testAddAndRetrieveCallerConfiguredNetworks() throws Exception {
- assertTrue("WiFi is not enabled", mWifiManager.isWifiEnabled());
- assertEquals(PackageManager.PERMISSION_DENIED,
- mContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION));
+ assertWithMessage("wifi is enabled").that(mWifiManager.isWifiEnabled()).isTrue();
+ assertWithMessage("permission status (denied=%s) for %s on user %s",
+ PackageManager.PERMISSION_DENIED, Manifest.permission.ACCESS_FINE_LOCATION, mUserId)
+ .that(mContext.checkSelfPermission(
+ Manifest.permission.ACCESS_FINE_LOCATION))
+ .isEqualTo(PackageManager.PERMISSION_DENIED);
int netId = mWifiConfigCreator.addNetwork(NETWORK_SSID, /* hidden */ false,
SECURITY_TYPE_NONE, /* password */ null);
- assertNotSame("Failed to add network", INVALID_NETWORK_ID, netId);
+ assertWithMessage("id of added network").that(netId).isNotEqualTo(INVALID_NETWORK_ID);
try {
List<WifiConfiguration> configs = mWifiManager.getCallerConfiguredNetworks();
- assertEquals(1, configs.size());
- assertEquals('"' + NETWORK_SSID + '"', configs.get(0).SSID);
+ assertWithMessage("configured networks").that(configs).hasSize(1);
+ assertWithMessage("SSID of configured networks").that(configs.get(0).SSID)
+ .isEqualTo('"' + NETWORK_SSID + '"');
} finally {
+ Log.d(TAG, "Removing network " + netId);
mWifiManager.removeNetwork(netId);
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiSetHttpProxyTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiSetHttpProxyTest.java
index cddc732..333fabd 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiSetHttpProxyTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiSetHttpProxyTest.java
@@ -16,9 +16,9 @@
package com.android.cts.deviceowner;
-import android.content.pm.PackageManager;
+import static com.google.common.truth.Truth.assertWithMessage;
-import com.android.compatibility.common.util.WifiConfigCreator;
+import android.content.pm.PackageManager;
/**
* Tests that DeviceOwner can add WifiConfigurations containing a HttpProxy
@@ -42,9 +42,9 @@
// skip the test if WiFi is not supported
return;
}
- WifiConfigCreator configCreator = new WifiConfigCreator(mContext, mWifiManager);
- String retreievedPacProxyUrl = configCreator.addHttpProxyNetworkVerifyAndRemove(
+ String retrievedPacProxyUrl = mWifiConfigCreator.addHttpProxyNetworkVerifyAndRemove(
TEST_SSID, TEST_PAC_URL);
- assertEquals(TEST_PAC_URL, retreievedPacProxyUrl);
+ assertWithMessage("pacProxyUrl for SSID %s", TEST_SSID).that(retrievedPacProxyUrl)
+ .isEqualTo(TEST_PAC_URL);
}
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java
index 4d7ddeb..322bf71 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java
@@ -16,13 +16,17 @@
package com.android.cts.managedprofile;
+import static com.google.common.truth.Truth.assertThat;
+
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
+import android.content.Intent;
import android.test.AndroidTestCase;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
import java.io.IOException;
-import java.util.Set;
import java.util.UUID;
/**
@@ -36,11 +40,6 @@
* TODO: Merge the primary and managed profile tests into one.
*/
public class BluetoothTest extends AndroidTestCase {
- private static final int DISABLE_TIMEOUT_MS = 8000;
- private static final int ENABLE_TIMEOUT_MS = 10000;
- private static final int POLL_TIME_MS = 400;
- private static final int CHECK_WAIT_TIME_MS = 1000;
-
private BluetoothAdapter mAdapter;
private boolean mBtWasEnabled;
@@ -133,32 +132,21 @@
* Behavior of getState() and isEnabled() are validated along the way.
*/
private void disable() {
- sleep(CHECK_WAIT_TIME_MS);
if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) {
assertFalse(mAdapter.isEnabled());
return;
}
- assertEquals(BluetoothAdapter.STATE_ON, mAdapter.getState());
- assertTrue(mAdapter.isEnabled());
- assertTrue(mAdapter.disable());
- boolean turnOff = false;
- for (int i=0; i<DISABLE_TIMEOUT_MS/POLL_TIME_MS; i++) {
- sleep(POLL_TIME_MS);
- int state = mAdapter.getState();
- switch (state) {
- case BluetoothAdapter.STATE_OFF:
- assertFalse(mAdapter.isEnabled());
- return;
- default:
- if (state != BluetoothAdapter.STATE_ON || turnOff) {
- assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
- turnOff = true;
- }
- break;
- }
+ assertThat(mAdapter.getState()).isEqualTo(BluetoothAdapter.STATE_ON);
+ assertThat(mAdapter.isEnabled()).isTrue();
+ assertThat(mAdapter.disable()).isTrue();
+ try (BlockingBroadcastReceiver r = new BlockingBroadcastReceiver(
+ mContext,
+ BluetoothAdapter.ACTION_STATE_CHANGED,
+ this::isStateDisabled).register()) {
+ assertThat(mAdapter.disable()).isTrue();
}
- fail("disable() timeout");
+ assertThat(mAdapter.isEnabled()).isFalse();
}
/**
@@ -167,37 +155,29 @@
* Behavior of getState() and isEnabled() are validated along the way.
*/
private void enable() {
- sleep(CHECK_WAIT_TIME_MS);
if (mAdapter.getState() == BluetoothAdapter.STATE_ON) {
assertTrue(mAdapter.isEnabled());
return;
}
- assertEquals(BluetoothAdapter.STATE_OFF, mAdapter.getState());
- assertFalse(mAdapter.isEnabled());
- assertTrue(mAdapter.enable());
- boolean turnOn = false;
- for (int i=0; i<ENABLE_TIMEOUT_MS/POLL_TIME_MS; i++) {
- sleep(POLL_TIME_MS);
- int state = mAdapter.getState();
- switch (state) {
- case BluetoothAdapter.STATE_ON:
- assertTrue(mAdapter.isEnabled());
- return;
- default:
- if (state != BluetoothAdapter.STATE_OFF || turnOn) {
- assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
- turnOn = true;
- }
- break;
- }
+ assertThat(mAdapter.getState()).isEqualTo(BluetoothAdapter.STATE_OFF);
+ assertThat(mAdapter.isEnabled()).isFalse();
+ try (BlockingBroadcastReceiver r = new BlockingBroadcastReceiver(
+ mContext,
+ BluetoothAdapter.ACTION_STATE_CHANGED,
+ this::isStateEnabled).register()) {
+ assertThat(mAdapter.enable()).isTrue();
}
- fail("enable() timeout");
+ assertThat(mAdapter.isEnabled()).isTrue();
}
- private void sleep(long t) {
- try {
- Thread.sleep(t);
- } catch (InterruptedException e) {}
+ private boolean isStateEnabled(Intent intent) {
+ return intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)
+ == BluetoothAdapter.STATE_ON;
+ }
+
+ private boolean isStateDisabled(Intent intent) {
+ return intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)
+ == BluetoothAdapter.STATE_OFF;
}
}
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
index f96615c..1dd888c 100644
--- a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
@@ -27,13 +27,17 @@
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
+import android.util.Log;
import java.util.regex.Pattern;
/**
* This class tests manual package install and uninstall by a device owner.
*/
-public class ManualPackageInstallTest extends BasePackageInstallTest {
+public final class ManualPackageInstallTest extends BasePackageInstallTest {
+
+ private static final String TAG = ManualPackageInstallTest.class.getSimpleName();
+
private static final int AUTOMATOR_WAIT_TIMEOUT = 5000;
private static final int INSTALL_WAIT_TIME = 5000;
@@ -41,11 +45,13 @@
Pattern.CASE_INSENSITIVE));
private UiAutomation mUiAutomation;
+ private boolean mIsAutomotive;
@Override
protected void setUp() throws Exception {
super.setUp();
mUiAutomation = getInstrumentation().getUiAutomation();
+ mIsAutomotive = mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
public void testManualInstallSucceeded() throws Exception {
@@ -58,17 +64,21 @@
mCallbackStatus = PACKAGE_INSTALLER_STATUS_UNDEFINED;
}
// Calls the original installPackage which does not click through the install button.
+ Log.d(TAG, "Installing " + TEST_APP_LOCATION);
super.installPackage(TEST_APP_LOCATION);
synchronized (mPackageInstallerTimeoutLock) {
try {
mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS);
} catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.e(TAG, "Interrupted", e);
}
assertTrue(mCallbackReceived);
assertEquals(PackageInstaller.STATUS_PENDING_USER_ACTION, mCallbackStatus);
}
mCallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Log.d(TAG, "Starting " + mCallbackIntent + " on user " + UserHandle.myUserId());
mContext.startActivity(mCallbackIntent);
automateDismissInstallBlockedDialog();
@@ -111,9 +121,10 @@
}
private void automateDismissInstallBlockedDialog() {
- mDevice.wait(Until.hasObject(getPopUpImageSelector()), AUTOMATOR_WAIT_TIMEOUT);
- UiObject2 icon = mDevice.findObject(getPopUpImageSelector());
- assertNotNull("Policy transparency dialog icon not found", icon);
+ BySelector selector = getPopUpImageSelector();
+ mDevice.wait(Until.hasObject(selector), AUTOMATOR_WAIT_TIMEOUT);
+ UiObject2 icon = mDevice.findObject(selector);
+ assertNotNull("Policy transparency dialog icon not found: " + selector, icon);
// "OK" button only present in the dialog if it is blocked by policy.
UiObject2 button = mDevice.findObject(getPopUpButtonSelector());
assertNotNull("OK button not found", button);
@@ -121,9 +132,11 @@
}
private String getSettingsPackageName() {
- String settingsPackageName = "com.android.settings";
+ String settingsPackageName = mIsAutomotive
+ ? "com.android.car.settings"
+ : "com.android.settings";
+ mUiAutomation.adoptShellPermissionIdentity("android.permission.INTERACT_ACROSS_USERS");
try {
- mUiAutomation.adoptShellPermissionIdentity("android.permission.INTERACT_ACROSS_USERS");
ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(
new Intent(Settings.ACTION_SETTINGS), PackageManager.MATCH_SYSTEM_ONLY,
UserHandle.USER_SYSTEM);
@@ -133,6 +146,7 @@
} finally {
mUiAutomation.dropShellPermissionIdentity();
}
+ Log.d(TAG, "getSettingsPackageName(): returning " + settingsPackageName);
return settingsPackageName;
}
@@ -144,8 +158,9 @@
private BySelector getPopUpImageSelector() {
final String settingsPackageName = getSettingsPackageName();
+ final String resId = mIsAutomotive ? "car_ui_alert_icon" : "admin_support_icon";
return By.clazz(android.widget.ImageView.class.getName())
- .res(settingsPackageName + ":id/admin_support_icon")
+ .res(settingsPackageName + ":id/" + resId)
.pkg(settingsPackageName);
}
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java
index 6dd628e..85a8c9b 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AdbProvisioningTests.java
@@ -31,19 +31,19 @@
@Override
public void setUp() throws Exception {
super.setUp();
- installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
+ installDeviceOwnerApp(DEVICE_ADMIN_APK);
}
@Override
public void tearDown() throws Exception {
super.tearDown();
- getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
+ removeDeviceOwnerAdmin(DEVICE_ADMIN_PKG);
}
@Test
public void testAdbDeviceOwnerLogged() throws Exception {
assertMetricsLogged(getDevice(), () -> {
- setDeviceOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mPrimaryUserId,
+ setDeviceOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mDeviceOwnerUserId,
/* expectFailure */ false);
}, new DevicePolicyEventWrapper.Builder(EventId.PROVISIONING_ENTRY_POINT_ADB_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
index 1f55f23..00228ef 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
@@ -70,7 +70,7 @@
protected void withRetry(RunnableWithThrowable test) throws Throwable {
final long until = System.nanoTime() + TimeUnit.SECONDS.toNanos(TIMEOUT_SECONDS);
- Thread.sleep(500);
+ sleep(500);
Throwable lastThrowable = null;
while (System.nanoTime() < until) {
@@ -81,57 +81,61 @@
} catch (Throwable th) {
lastThrowable = th;
}
- Thread.sleep(3000);
+ sleep(3000);
}
if (lastThrowable != null) {
throw lastThrowable;
}
- fail("Internal error: test didn't run, exception not thrown.");
+ fail("Internal error: test " + test + " didn't run, exception not thrown.");
}
+ protected abstract void installOwnerApp(String apk) throws Exception;
+
+ protected abstract void removeAdmin(String ownerComponent) throws Exception;
+
protected abstract void setAsOwnerOrFail(String component) throws Exception;
@Test
public void testAll() throws Throwable {
// Install
- CLog.i("Installing apk1...");
- installAppAsUser(OWNER_APK_1, getUserId());
+ CLog.i("Installing apk1 (%s)...", OWNER_APK_1);
+ installOwnerApp(OWNER_APK_1);
- CLog.i("Making it a device/profile owner...");
+ CLog.i("Making it (%s) a device/profile owner...", OWNER_COMPONENT);
setAsOwnerOrFail(OWNER_COMPONENT);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
// Remove admin.
CLog.i("Removing admin...");
- removeAdmin(OWNER_COMPONENT, getUserId());
+ removeAdmin(OWNER_COMPONENT);
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Overwrite -> update.
CLog.i("Re-installing apk1...");
- installAppAsUser(OWNER_APK_1, getUserId());
+ installOwnerApp(OWNER_APK_1);
CLog.i("Making it a device/profile owner...");
setAsOwnerOrFail(OWNER_COMPONENT);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
- CLog.i("Installing apk2...");
- installAppAsUser(OWNER_APK_2, getUserId());
+ CLog.i("Installing apk2 (%s)...", OWNER_APK_2);
+ installOwnerApp(OWNER_APK_2);
withRetry(() -> assertServiceBound(OWNER_SERVICE)); // Should still be bound.
// Service exported -> not bound.
- CLog.i("Installing apk3...");
- installAppAsUser(OWNER_APK_3, getUserId());
+ CLog.i("Installing apk3 (%s)...", OWNER_APK_3);
+ installOwnerApp(OWNER_APK_3);
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Recover.
CLog.i("Installing apk2 again...");
- installAppAsUser(OWNER_APK_2, getUserId());
+ installOwnerApp(OWNER_APK_2);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
// Multiple service found -> not bound.
- CLog.i("Installing apk4...");
- installAppAsUser(OWNER_APK_4, getUserId());
+ CLog.i("Installing apk4 (%s)...", OWNER_APK_4);
+ installOwnerApp(OWNER_APK_4);
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
withRetry(() -> assertServiceNotBound(OWNER_SERVICE2));
@@ -158,25 +162,25 @@
// Remove admin.
CLog.i("Removing admin again...");
- removeAdmin(OWNER_COMPONENT, getUserId());
+ removeAdmin(OWNER_COMPONENT);
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Retry with package 1 and remove admin.
CLog.i("Installing apk1 again...");
- installAppAsUser(OWNER_APK_1, getUserId());
+ installOwnerApp(OWNER_APK_1);
CLog.i("Making it a device/profile owner again...");
setAsOwnerOrFail(OWNER_COMPONENT);
withRetry(() -> assertServiceBound(OWNER_SERVICE));
CLog.i("Removing admin again...");
- removeAdmin(OWNER_COMPONENT, getUserId());
+ removeAdmin(OWNER_COMPONENT);
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
// Now install package B and make it the owner. OWNER_APK_1 still exists, but it shouldn't
// interfere.
- CLog.i("Installing apk B...");
- installAppAsUser(OWNER_APK_B, getUserId());
+ CLog.i("Installing apk B (%s)...", OWNER_APK_B);
+ installOwnerApp(OWNER_APK_B);
CLog.i("Making it a device/profile owner...");
setAsOwnerOrFail(OWNER_COMPONENT_B);
@@ -187,7 +191,7 @@
private String rumpDumpSysService(String component) throws Exception {
final String command = "dumpsys activity services " + component;
final String commandOutput = getDevice().executeShellCommand(command);
- CLog.d("Output for command " + command + ":\n" + commandOutput);
+ CLog.d("Output for command %s: \n%s", command, commandOutput);
return commandOutput;
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceOwnerTest.java
index e08f44c..a967d3a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceOwnerTest.java
@@ -40,7 +40,8 @@
public void setUp() throws Exception {
super.setUp();
- installAppAsUser(DEVICE_OWNER_APK, mDeviceOwnerUserId);
+ installDeviceOwnerApp(DEVICE_OWNER_APK);
+
mDeviceOwnerSet = setDeviceOwner(DEVICE_OWNER_COMPONENT, mDeviceOwnerUserId,
/*expectFailure= */ false);
@@ -52,7 +53,6 @@
if (isHeadlessSystemUserMode()) {
affiliateUsers(DEVICE_OWNER_PKG, mDeviceOwnerUserId, mPrimaryUserId);
- grantDpmWrapperPermissions(DEVICE_OWNER_PKG, mPrimaryUserId);
}
// Enable the notification listener
@@ -62,11 +62,14 @@
@Override
public void tearDown() throws Exception {
- if (mDeviceOwnerSet && !removeAdmin(DEVICE_OWNER_COMPONENT, mDeviceOwnerUserId)) {
- // Don't fail as it could hide the real failure from the test method
- CLog.e("Failed to remove device owner on user " + mDeviceOwnerUserId);
+ if (mDeviceOwnerSet) {
+ removeDeviceOwnerAdmin(DEVICE_OWNER_COMPONENT);
}
- getDevice().uninstallPackage(DEVICE_OWNER_PKG);
+
+ String status = getDevice().uninstallPackage(DEVICE_OWNER_PKG);
+ if (status != null) {
+ CLog.e("Could not uninstall package %s: %s", DEVICE_OWNER_PKG, status);
+ }
super.tearDown();
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index c3a036d..7832b6d 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -108,7 +108,7 @@
protected static final int USER_SYSTEM = 0; // From the UserHandle class.
- protected static final int USER_OWNER = 0;
+ protected static final int USER_OWNER = USER_SYSTEM;
private static final long TIMEOUT_USER_REMOVED_MILLIS = TimeUnit.SECONDS.toMillis(15);
private static final long WAIT_SAMPLE_INTERVAL_MILLIS = 200;
@@ -348,6 +348,28 @@
assertEquals("Success\n", installResult);
}
+ protected void installDeviceOwnerApp(String apk) throws Exception {
+ installAppAsUser(apk, mDeviceOwnerUserId);
+
+ if (isHeadlessSystemUserMode()) {
+ // Need to explicitly install the device owner app for the current user (rather than
+ // relying on DPMS) so it has the same privileges (like INTERACT_ACROSS_USERS) as the
+ // app running on system user, otherwise some tests might fail
+ installAppAsUser(apk, mPrimaryUserId);
+ }
+ }
+
+ protected void removeDeviceOwnerAdmin(String componentName) throws DeviceNotAvailableException {
+ // Don't fail as it could hide the real failure from the test method
+ if (!removeAdmin(componentName, mDeviceOwnerUserId)) {
+ CLog.e("Failed to remove device owner %s on user %d", componentName,
+ mDeviceOwnerUserId);
+ }
+ if (isHeadlessSystemUserMode() && !removeAdmin(componentName, mPrimaryUserId)) {
+ CLog.e("Failed to remove profile owner %s on user %d", componentName, mPrimaryUserId);
+ }
+ }
+
protected void forceStopPackageForUser(String packageName, int userId) throws Exception {
// TODO Move this logic to ITestDevice
executeShellCommand("am force-stop --user " + userId + " " + packageName);
@@ -650,17 +672,18 @@
protected int createUser(int flags) throws Exception {
boolean guest = FLAG_GUEST == (flags & FLAG_GUEST);
boolean ephemeral = FLAG_EPHEMERAL == (flags & FLAG_EPHEMERAL);
+ CLog.i("Creating user with flags %d: guest=%b, ephemeral=%b", flags, guest, ephemeral);
// TODO Use ITestDevice.createUser() when guest and ephemeral is available
String command ="pm create-user " + (guest ? "--guest " : "")
+ (ephemeral ? "--ephemeral " : "") + "TestUser_" + System.currentTimeMillis();
- CLog.d("Starting command " + command);
+ CLog.d("Starting command %s", command);
String commandOutput = getDevice().executeShellCommand(command);
- CLog.d("Output for command " + command + ": " + commandOutput);
+ CLog.d("Output for command %s: %s", command, commandOutput);
// Extract the id of the new user.
String[] tokens = commandOutput.split("\\s+");
assertTrue(tokens.length > 0);
- assertEquals("Success:", tokens[0]);
+ assertEquals("Command '" + command + "' failed: " + commandOutput, "Success:", tokens[0]);
return Integer.parseInt(tokens[tokens.length-1]);
}
@@ -1205,17 +1228,16 @@
protected void grantDpmWrapperPermissions(String deviceAdminPkg, int userId) throws Exception {
// TODO(b/176993670): INTERACT_ACROSS_USERS is needed by DevicePolicyManagerWrapper to
- // get the current user; the permission is available on mDeviceOwnerUserId because it
- // was installed with -g, but not on mPrimaryUserId as the app is intalled by code
- // (DPMS.manageUserUnchecked(), which don't grant it (as this is a privileged permission
+ // send ordered broadcasts to the test user. The permission is already available to the
+ // packages installed by the host side test (as they're installed with -g), but need to be
+ // granted for users created by the test, as the package is intalled by code
+ // (DPMS.manageUserUnchecked(), which doesn't grant it (as this is a privileged permission
// that's not available to 3rd party apps). If we get rid of DevicePolicyManagerWrapper,
// we won't need to grant it anymore.
grantPermission(deviceAdminPkg, PERMISSION_INTERACT_ACROSS_USERS, userId, "its PO needs to "
+ "send ordered broadcasts to user 0");
- grantPermission(deviceAdminPkg, "android.permission.WRITE_SECURE_SETTINGS", userId,
- "some tests need it");
-
+ // Probably not needed anymore, but it doesn't hurt to keep...
CLog.i("Granting ALLOW_TEST_API_ACCESS to package %s", deviceAdminPkg);
executeShellCommand("am compat enable ALLOW_TEST_API_ACCESS %s", deviceAdminPkg);
}
@@ -1319,4 +1341,9 @@
return false;
}
}
+
+ void sleep(int timeMs) throws InterruptedException {
+ CLog.d("Sleeping %d ms");
+ Thread.sleep(timeMs);
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsPermissionHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsPermissionHostSideTest.java
index dc65e75..e8b6f49 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsPermissionHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/CrossProfileAppsPermissionHostSideTest.java
@@ -287,6 +287,32 @@
}
@Test
+ public void testCanInteractAcrossProfiles_withAppOpDisabledOnCallingProfile_returnsFalse()
+ throws Exception {
+ installAppAsUser(TEST_WITH_REQUESTED_PERMISSION_APK, mPrimaryUserId);
+
+ runDeviceTestsAsUser(
+ TEST_WITH_REQUESTED_PERMISSION_PACKAGE,
+ TEST_WITH_REQUESTED_PERMISSION_CLASS,
+ "testCanInteractAcrossProfiles_withAppOpDisabledOnCallingProfile_returnsFalse",
+ mPrimaryUserId,
+ Collections.EMPTY_MAP);
+ }
+
+ @Test
+ public void testCanInteractAcrossProfiles_withAppOpDisabledOnOtherProfiles_returnsFalse()
+ throws Exception {
+ installAppAsUser(TEST_WITH_REQUESTED_PERMISSION_APK, mPrimaryUserId);
+
+ runDeviceTestsAsUser(
+ TEST_WITH_REQUESTED_PERMISSION_PACKAGE,
+ TEST_WITH_REQUESTED_PERMISSION_CLASS,
+ "testCanInteractAcrossProfiles_withAppOpDisabledOnOtherProfiles_returnsFalse",
+ mPrimaryUserId,
+ Collections.EMPTY_MAP);
+ }
+
+ @Test
public void testCreateRequestInteractAcrossProfilesIntent_canRequestInteraction_returnsIntent()
throws Exception {
installAppAsUser(TEST_WITH_REQUESTED_PERMISSION_APK, mPrimaryUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceDeviceOwnerTest.java
index 216238f..5b9833d 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceDeviceOwnerTest.java
@@ -15,13 +15,28 @@
*/
package com.android.cts.devicepolicy;
-public class DeviceAdminServiceDeviceOwnerTest extends BaseDeviceAdminServiceTest {
+import com.android.tradefed.log.LogUtil.CLog;
+
+public final class DeviceAdminServiceDeviceOwnerTest extends BaseDeviceAdminServiceTest {
+
@Override
protected int getUserId() {
return USER_OWNER;
}
@Override
+ protected void installOwnerApp(String apk) throws Exception {
+ CLog.i("Installing owner app %s...", apk);
+ installDeviceOwnerApp(apk);
+ }
+
+ @Override
+ protected void removeAdmin(String component) throws Exception {
+ CLog.i("Removing admin %s...", component);
+ removeDeviceOwnerAdmin(component);
+ }
+
+ @Override
protected void setAsOwnerOrFail(String component) throws Exception {
setDeviceOwnerOrFail(component, getUserId());
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
index 55cd092..aa57480 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminServiceProfileOwnerTest.java
@@ -17,9 +17,11 @@
import android.platform.test.annotations.LargeTest;
+import com.android.tradefed.log.LogUtil.CLog;
+
import org.junit.Test;
-public class DeviceAdminServiceProfileOwnerTest extends BaseDeviceAdminServiceTest {
+public final class DeviceAdminServiceProfileOwnerTest extends BaseDeviceAdminServiceTest {
private int mUserId;
@@ -38,8 +40,20 @@
}
@Override
+ protected void installOwnerApp(String apk) throws Exception {
+ CLog.i("Installing %s on user %d...", apk, mUserId);
+ installAppAsUser(apk, mUserId);
+ }
+
+ @Override
+ protected void removeAdmin(String component) throws Exception {
+ CLog.i("Removing admin %s from user %d...", component, mUserId);
+ removeAdmin(component, mUserId);
+ }
+
+ @Override
protected void setAsOwnerOrFail(String component) throws Exception {
- setProfileOwnerOrFail(component, getUserId());
+ setProfileOwnerOrFail(component, mUserId);
}
@Override
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index dec9f9d..b7abcf9 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -430,6 +430,7 @@
}
@Test
+ @FlakyTest(bugId = 187862351)
public void testGrantOfSensorsRelatedPermissions() throws Exception {
installAppPermissionAppAsUser();
executeDeviceTestMethod(".PermissionsTest", "testSensorsRelatedPermissionsCannotBeGranted");
@@ -441,6 +442,7 @@
}
@Test
+ @FlakyTest(bugId = 187862351)
public void testSensorsRelatedPermissionsNotGrantedViaPolicy() throws Exception {
installAppPermissionAppAsUser();
executeDeviceTestMethod(".PermissionsTest",
@@ -448,6 +450,7 @@
}
@Test
+ @FlakyTest(bugId = 187862351)
public void testStateOfSensorsRelatedPermissionsCannotBeRead() throws Exception {
installAppPermissionAppAsUser();
executeDeviceTestMethod(".PermissionsTest",
@@ -600,6 +603,7 @@
}
@Test
+ @FlakyTest(bugId = 187862351)
public void testPermissionPrompts() throws Exception {
installAppPermissionAppAsUser();
executeDeviceTestMethod(".PermissionsTest", "testPermissionPrompts");
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 15d41c6..603db30 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -39,6 +39,7 @@
import org.junit.Test;
import java.io.File;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -551,6 +552,8 @@
.build());
}
+ @TemporaryIgnoreOnHeadlessSystemUserMode(bugId = "132360087",
+ reason = "need more investigation / decide how to support it")
@Test
public void testBluetoothRestriction() throws Exception {
executeDeviceOwnerTest("BluetoothRestrictionTest");
@@ -721,6 +724,11 @@
}
@Test
+ public void testSensorToggleRestriction() throws Exception {
+ executeDeviceOwnerTest("SensorToggleRestrictionTest");
+ }
+
+ @Test
public void testOverrideApn() throws Exception {
assumeHasTelephonyFeature();
@@ -813,11 +821,13 @@
}
@Test
- public void testSetUserControlDisabledPackages() throws Exception {
+ public void testSetUserControlDisabledPackages_singleUser_verifyMetricIsLogged()
+ throws Exception {
+ final List<Integer> otherUserIds = new ArrayList<>();
try {
- installAppAsUser(SIMPLE_APP_APK, mPrimaryUserId);
- // launch the app once before starting the test.
- startActivityAsUser(mPrimaryUserId, SIMPLE_APP_PKG, SIMPLE_APP_ACTIVITY);
+ setupDeviceForSetUserControlDisabledPackagesTesting(otherUserIds);
+
+ // Set the package under test as a protected package.
assertMetricsLogged(getDevice(),
() -> executeDeviceTestMethod(".UserControlDisabledPackagesTest",
"testSetUserControlDisabledPackages"),
@@ -826,24 +836,229 @@
.setAdminPackageName(DEVICE_OWNER_PKG)
.setStrings(new String[] {SIMPLE_APP_PKG})
.build());
- forceStopPackageForUser(SIMPLE_APP_PKG, mPrimaryUserId);
+ } finally {
+ cleanupProtectedPackage(otherUserIds);
+ getDevice().uninstallPackageForUser(SIMPLE_APP_APK, mPrimaryUserId);
+ }
+ }
+
+ @Test
+ public void testSetUserControlDisabledPackages_singleUser_verifyPackageNotStopped()
+ throws Exception {
+ final List<Integer> otherUserIds = new ArrayList<>();
+ try {
+ setupDeviceForSetUserControlDisabledPackagesTesting(otherUserIds);
+ // Set the package under test as a protected package.
executeDeviceTestMethod(".UserControlDisabledPackagesTest",
- "testForceStopWithUserControlDisabled");
+ "testSetUserControlDisabledPackages");
+
+ // Try to stop the package on the primary user.
+ tryStoppingProtectedPackage(otherUserIds, /* canUserStopPackage= */ false);
+ } finally {
+ cleanupProtectedPackage(otherUserIds);
+ getDevice().uninstallPackageForUser(SIMPLE_APP_APK, mPrimaryUserId);
+ }
+ }
+
+ @Test
+ public void testSetUserControlDisabledPackages_singleUser_reboot_verifyPackageNotStopped()
+ throws Exception {
+ final List<Integer> otherUserIds = new ArrayList<>();
+ try {
+ setupDeviceForSetUserControlDisabledPackagesTesting(otherUserIds);
+ // Set the package under test as a protected package.
+ executeDeviceTestMethod(".UserControlDisabledPackagesTest",
+ "testSetUserControlDisabledPackages");
+
// Reboot and verify protected packages are persisted
rebootAndWaitUntilReady();
+
// The simple app package seems to be set into stopped state on reboot.
- // Launch the activity again to get it out of stopped state.
- startActivityAsUser(mPrimaryUserId, SIMPLE_APP_PKG, SIMPLE_APP_ACTIVITY);
- forceStopPackageForUser(SIMPLE_APP_PKG, mDeviceOwnerUserId);
+ // Launch the activity again to get it out of stopped state on the primary user.
+ startProtectedPackage(otherUserIds);
+ // Try to stop the package on the primary user.
+ tryStoppingProtectedPackage(otherUserIds, /* canUserStopPackage= */ false);
+ } finally {
+ cleanupProtectedPackage(otherUserIds);
+ getDevice().uninstallPackageForUser(SIMPLE_APP_APK, mPrimaryUserId);
+ }
+ }
+
+ @Test
+ public void testSetUserControlDisabledPackages_multiUser_verifyMetricIsLogged()
+ throws Exception {
+ assumeCanCreateAdditionalUsers(1);
+ final int userId = createUser();
+ final List<Integer> otherUserIds = new ArrayList<>();
+ otherUserIds.add(userId);
+ try {
+ setupDeviceForSetUserControlDisabledPackagesTesting(otherUserIds);
+
+ // Set the package under test as a protected package.
+ assertMetricsLogged(getDevice(),
+ () -> executeDeviceTestMethod(".UserControlDisabledPackagesTest",
+ "testSetUserControlDisabledPackages"),
+ new DevicePolicyEventWrapper.Builder(
+ EventId.SET_USER_CONTROL_DISABLED_PACKAGES_VALUE)
+ .setAdminPackageName(DEVICE_OWNER_PKG)
+ .setStrings(new String[] {SIMPLE_APP_PKG})
+ .build());
+ } finally {
+ cleanupProtectedPackage(otherUserIds);
+ getDevice().uninstallPackageForUser(SIMPLE_APP_APK, mPrimaryUserId);
+ getDevice().uninstallPackageForUser(SIMPLE_APP_APK, userId);
+ removeUser(userId);
+ }
+ }
+
+ @Test
+ public void testSetUserControlDisabledPackages_multiUser_verifyPackageNotStopped()
+ throws Exception {
+ assumeCanCreateAdditionalUsers(1);
+ final int userId = createUser();
+ final List<Integer> otherUserIds = new ArrayList<>();
+ otherUserIds.add(userId);
+ try {
+ setupDeviceForSetUserControlDisabledPackagesTesting(otherUserIds);
+ // Set the package under test as a protected package.
executeDeviceTestMethod(".UserControlDisabledPackagesTest",
- "testForceStopWithUserControlDisabled");
+ "testSetUserControlDisabledPackages");
+
+ // Try to stop the package under test on all users.
+ tryStoppingProtectedPackage(otherUserIds, /* canUserStopPackage= */ false);
+ } finally {
+ cleanupProtectedPackage(otherUserIds);
+ getDevice().uninstallPackageForUser(SIMPLE_APP_APK, mPrimaryUserId);
+ getDevice().uninstallPackageForUser(SIMPLE_APP_APK, userId);
+ removeUser(userId);
+ }
+ }
+
+ @Test
+ public void testSetUserControlDisabledPackages_multiUser_reboot_verifyPackageNotStopped()
+ throws Exception {
+ assumeCanCreateAdditionalUsers(1);
+ final int userId = createUser();
+ final List<Integer> otherUserIds = new ArrayList<>();
+ otherUserIds.add(userId);
+ try {
+ setupDeviceForSetUserControlDisabledPackagesTesting(otherUserIds);
+ // Set the package under test as a protected package.
executeDeviceTestMethod(".UserControlDisabledPackagesTest",
- "testClearSetUserControlDisabledPackages");
- forceStopPackageForUser(SIMPLE_APP_PKG, mPrimaryUserId);
+ "testSetUserControlDisabledPackages");
+
+ // Reboot and verify protected packages are persisted.
+ rebootAndWaitUntilReady();
+
+ // The simple app package seems to be set into stopped state on reboot.
+ // Launch the activity again to get it out of stopped state for all users.
+ startProtectedPackage(otherUserIds);
+ // Try to stop the package under test on all users.
+ tryStoppingProtectedPackage(otherUserIds, /* canUserStopPackage= */ false);
+ } finally {
+ cleanupProtectedPackage(otherUserIds);
+ getDevice().uninstallPackageForUser(SIMPLE_APP_APK, mPrimaryUserId);
+ getDevice().uninstallPackageForUser(SIMPLE_APP_APK, userId);
+ removeUser(userId);
+ }
+ }
+
+ /**
+ * Helper when testing {@link DevicePolicyManager#setUserControlDisabledPackages} API that
+ * installs the app and starts the activity for the package that is under test for the primary
+ * user and provided users.
+ * @param otherUserIds The user Ids apart from the primary user that were created
+ */
+ private void setupDeviceForSetUserControlDisabledPackagesTesting(List<Integer> otherUserIds)
+ throws Exception {
+ // Install app on the primary user and other users.
+ installAppAsUser(SIMPLE_APP_APK, mPrimaryUserId);
+ if (!otherUserIds.isEmpty()) {
+ for (Integer userId : otherUserIds) {
+ installAppAsUser(SIMPLE_APP_APK, userId);
+ }
+ }
+
+ // Start the activity of the package under test on the primary user and other users.
+ startProtectedPackage(otherUserIds);
+ }
+
+ /**
+ * Helper when testing {@link DevicePolicyManager#setUserControlDisabledPackages} API that
+ * starts the activity for the package that is under test for the primary user and provided
+ * users.
+ * @param otherUserIds The user Ids apart from the primary user that were created
+ */
+ private void startProtectedPackage(List<Integer> otherUserIds) throws Exception {
+ startProtectedPackage(mPrimaryUserId);
+ if (!otherUserIds.isEmpty()) {
+ for (Integer userId : otherUserIds) {
+ switchUser(userId);
+ startProtectedPackage(userId);
+ }
+ switchUser(mPrimaryUserId);
+ }
+ }
+
+ /**
+ * Helper when testing {@link DevicePolicyManager#setUserControlDisabledPackages} API that
+ * starts the activity for the package that is under test for a given user.
+ * @param userId The user Id to start the package for
+ */
+ private void startProtectedPackage(int userId) throws Exception {
+ // Launch the app once before starting the test.
+ startActivityAsUser(userId, SIMPLE_APP_PKG, SIMPLE_APP_ACTIVITY);
+ executeDeviceTestMethod(".UserControlDisabledPackagesTest",
+ "testLaunchActivity");
+ }
+
+ /**
+ * Helper when testing {@link DevicePolicyManager#setUserControlDisabledPackages} API that
+ * removes the package under test as a protected package and stops the package under test for
+ * the primary user and provided users.
+ * @param otherUserIds The user Ids apart from the primary user that were created
+ */
+ private void cleanupProtectedPackage(List<Integer> otherUserIds) throws Exception {
+ executeDeviceTestMethod(".UserControlDisabledPackagesTest",
+ "testClearSetUserControlDisabledPackages");
+ tryStoppingProtectedPackage(otherUserIds, /* canUserStopPackage= */ true);
+ }
+
+ /**
+ * Helper when testing {@link DevicePolicyManager#setUserControlDisabledPackages} API that
+ * attempts to stop protected package under test for the primary user and provided users.
+ * @param otherUserIds The user Ids apart from the primary user that were created
+ * @param canUserStopPackage Whether the user can force stop the protected package
+ */
+ private void tryStoppingProtectedPackage(List<Integer> otherUserIds, boolean canUserStopPackage)
+ throws Exception {
+ if (!otherUserIds.isEmpty()) {
+ for (Integer userId : otherUserIds) {
+ // TODO(b/188464764): Run device tests on the required user instead of switching
+ // users
+ switchUser(userId);
+ tryStoppingProtectedPackage(userId, canUserStopPackage);
+ }
+ switchUser(mPrimaryUserId);
+ }
+ tryStoppingProtectedPackage(mPrimaryUserId, canUserStopPackage);
+ }
+
+ /**
+ * Helper when testing {@link DevicePolicyManager#setUserControlDisabledPackages} API that
+ * attempts to stop the protected package under test for a given user.
+ * @param userId The user Id to stop the package for
+ * @param canUserStopPackage Whether the user can force stop the protected package
+ */
+ private void tryStoppingProtectedPackage(int userId, boolean canUserStopPackage)
+ throws Exception {
+ forceStopPackageForUser(SIMPLE_APP_PKG, userId);
+ if (canUserStopPackage) {
executeDeviceTestMethod(".UserControlDisabledPackagesTest",
"testForceStopWithUserControlEnabled");
- } finally {
- getDevice().uninstallPackage(SIMPLE_APP_APK);
+ } else {
+ executeDeviceTestMethod(".UserControlDisabledPackagesTest",
+ "testForceStopWithUserControlDisabled");
}
}
@@ -935,8 +1150,8 @@
@Test
public void testWifiNetworkConfigurationWithoutFineLocationPermission() throws Exception {
- getDevice().executeShellCommand(String.format(
- "pm revoke %s android.permission.ACCESS_FINE_LOCATION", DEVICE_OWNER_PKG));
+ executeShellCommand("pm revoke --user %d %s android.permission.ACCESS_FINE_LOCATION",
+ mPrimaryUserId, DEVICE_OWNER_PKG);
executeDeviceOwnerTest("WifiNetworkConfigurationWithoutFineLocationPermissionTest");
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 0637354..96254f2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -43,7 +43,7 @@
* Set of tests for device owner use cases that also apply to profile owners.
* Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTest.
*/
-public class MixedDeviceOwnerTest extends DeviceAndProfileOwnerTest {
+public final class MixedDeviceOwnerTest extends DeviceAndProfileOwnerTest {
private static final String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
@@ -63,26 +63,24 @@
+ "mDeviceOwnerUserId=%d", getClass(), mUserId, mPrimaryUserId, mInitialUserId,
mDeviceOwnerUserId);
- installAppAsUser(DEVICE_ADMIN_APK, mDeviceOwnerUserId);
+ installDeviceOwnerApp(DEVICE_ADMIN_APK);
mDeviceOwnerSet = setDeviceOwner(DEVICE_ADMIN_COMPONENT_FLATTENED, mDeviceOwnerUserId,
/*expectFailure= */ false);
if (!mDeviceOwnerSet) {
- removeAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED, mUserId);
+ removeDeviceOwnerAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED);
getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
fail("Failed to set device owner on user " + mDeviceOwnerUserId);
}
if (isHeadlessSystemUserMode()) {
affiliateUsers(DEVICE_ADMIN_PKG, mDeviceOwnerUserId, mPrimaryUserId);
- grantDpmWrapperPermissions(DEVICE_ADMIN_PKG, mPrimaryUserId);
}
}
@Override
public void tearDown() throws Exception {
- if (mDeviceOwnerSet && !removeAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED, mDeviceOwnerUserId)) {
- // Don't fail as it could hide the real failure from the test method
- CLog.e("Failed to remove device owner on user " + mDeviceOwnerUserId);
+ if (mDeviceOwnerSet) {
+ removeDeviceOwnerAdmin(DEVICE_ADMIN_COMPONENT_FLATTENED);
}
super.tearDown();
@@ -120,18 +118,6 @@
}
@Test
- public void testIsLockTaskPermitted_includesPolicyExemptApps() throws Exception {
- runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".LockTaskTest",
- "testIsLockTaskPermittedIncludesPolicyExemptApps", mDeviceOwnerUserId);
- }
-
- @Test
- public void testLockTask_policyExemptApps() throws Exception {
- runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".LockTaskTest",
- "testSetLockTaskPackagesIgnoresExemptApps", mDeviceOwnerUserId);
- }
-
- @Test
public void testDelegatedCertInstallerDeviceIdAttestation() throws Exception {
setUpDelegatedCertInstallerAndRunTests(() ->
runDeviceTestsAsUser("com.android.cts.certinstaller",
@@ -283,7 +269,11 @@
pushUpdateFileToDevice("wrongPayload.zip");
pushUpdateFileToDevice("wrongHash.zip");
pushUpdateFileToDevice("wrongSize.zip");
- executeDeviceTestClass(".systemupdate.InstallUpdateTest");
+
+ // This test will run as user 0 since there will be {@link InstallSystemUpdateCallback}
+ // in the test and it's not necessary to run from secondary user.
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".systemupdate.InstallUpdateTest",
+ mDeviceOwnerUserId);
}
@Test
@@ -433,7 +423,6 @@
runDeviceTestsAsUser(packageName, testClassName,
"testGenerateLogs", mUserId);
getDevice().executeShellCommand("whoami"); // Generate adb command securty event
- getDevice().executeShellCommand("dpm force-security-logs");
runDeviceTestsAsUser(packageName, testClassName,
"testVerifyGeneratedLogs", mUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
index ba2eda4..dc57601 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
@@ -23,6 +23,7 @@
import android.platform.test.annotations.FlakyTest;
import android.platform.test.annotations.LargeTest;
+import com.android.cts.devicepolicy.DeviceAdminFeaturesCheckerRule.RequiresProfileOwnerSupport;
import com.android.tradefed.log.LogUtil.CLog;
import org.junit.Ignore;
@@ -32,6 +33,7 @@
* Set of tests for pure (non-managed) profile owner use cases that also apply to device owners.
* Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTest.
*/
+@RequiresProfileOwnerSupport
public final class MixedProfileOwnerTest extends DeviceAndProfileOwnerTest {
@Override
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTestApi25.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTestApi25.java
index 179e505..aec12dd 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTestApi25.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTestApi25.java
@@ -19,10 +19,13 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.cts.devicepolicy.DeviceAdminFeaturesCheckerRule.RequiresProfileOwnerSupport;
+
/**
* Set of tests for pure (non-managed) profile owner use cases that also apply to device owners.
* Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTestApi25.
*/
+@RequiresProfileOwnerSupport
public class MixedProfileOwnerTestApi25 extends DeviceAndProfileOwnerTestApi25 {
@Override
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
index 408ca489..01d720f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
@@ -266,8 +266,7 @@
// Generate various types of events on device side and check that they are logged.
runDeviceTestsAsUser(packageName, testClassName,
"testGenerateLogs", mUserId);
- getDevice().executeShellCommand("whoami"); // Generate adb command securty event
- getDevice().executeShellCommand("dpm force-security-logs");
+ getDevice().executeShellCommand("whoami"); // Generate adb command security event
runDeviceTestsAsUser(packageName, testClassName,
"testVerifyGeneratedLogs", mUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
index 301e1e4..1cb086a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
@@ -21,6 +21,7 @@
import com.android.cts.devicepolicy.DeviceAdminFeaturesCheckerRule.RequiresAdditionalFeatures;
import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
import org.junit.Test;
@@ -57,10 +58,15 @@
@Override
public void tearDown() throws Exception {
if (mRemoveOwnerInTearDown) {
- assertTrue("Failed to clear owner",
- removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
- mDeviceOwnerUserId));
+ String componentName = DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS;
+ assertTrue("Failed to clear owner", removeAdmin(componentName, mDeviceOwnerUserId));
runTests("userrestrictions.CheckNoOwnerRestrictionsTest", mDeviceOwnerUserId);
+ if (isHeadlessSystemUserMode()) {
+ boolean removed = removeAdmin(componentName, mPrimaryUserId);
+ if (!removed) {
+ CLog.e("Failed to remove %s on user %d", componentName, mPrimaryUserId);
+ }
+ }
}
// DO/PO might have set DISALLOW_REMOVE_USER, so it needs to be done after removing
@@ -275,7 +281,8 @@
/** Installs admin package and makes it a device owner. */
private void setDo() throws Exception {
- installAppAsUser(DEVICE_ADMIN_APK, mDeviceOwnerUserId);
+ installDeviceOwnerApp(DEVICE_ADMIN_APK);
+
assertTrue("Failed to set device owner",
setDeviceOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
mDeviceOwnerUserId, /*expectFailure*/ false));
@@ -283,7 +290,6 @@
if (isHeadlessSystemUserMode()) {
affiliateUsers(DEVICE_ADMIN_PKG, mDeviceOwnerUserId, mPrimaryUserId);
- grantDpmWrapperPermissions(DEVICE_ADMIN_PKG, mPrimaryUserId);
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
index b906c0d..6646a1f 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/BaseHdmiCecCtsTest.java
@@ -269,4 +269,23 @@
"getprop ro.hdmi.set_menu_language");
return val.trim().equals("true") ? true : false;
}
+
+ public static String getSettingsValue(ITestDevice device, String setting) throws Exception {
+ return device.executeShellCommand("cmd hdmi_control cec_setting get " + setting)
+ .replace(setting + " = ", "").trim();
+ }
+
+ public String getSettingsValue(String setting) throws Exception {
+ return getSettingsValue(getDevice(), setting);
+ }
+
+ public static void setSettingsValue(ITestDevice device, String setting, String value)
+ throws Exception {
+ device.executeShellCommand("cmd hdmi_control cec_setting set " + setting + " " +
+ value);
+ }
+
+ public void setSettingsValue(String setting, String value) throws Exception {
+ setSettingsValue(getDevice(), setting, value);
+ }
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
index 712e56d..c059110 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecOperand.java
@@ -55,6 +55,7 @@
REPORT_FEATURES(0xa6),
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/HdmiCecConstants.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
index 9cd9eda..6570edb 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecConstants.java
@@ -60,6 +60,7 @@
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;
+ public static final int CEC_DEVICE_TYPE_VIDEO_PROCESSOR = 7;
/** Feature Abort Reasons */
public static final int ABORT_UNRECOGNIZED_MODE = 0;
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecDeviceTypeTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecDeviceTypeTest.java
index 548170b..5f2df84 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecDeviceTypeTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/common/HdmiCecDeviceTypeTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.fail;
+
import android.hdmicec.cts.BaseHdmiCecCtsTest.CecRules;
import android.hdmicec.cts.HdmiCecConstants;
@@ -92,8 +94,13 @@
private int getAllDeviceTypes(ITestDevice device) {
int deviceTypes = 0;
String deviceType = "";
+ boolean usingStringDeviceTypes = true;
try {
- deviceType = device.executeShellCommand("getprop ro.hdmi.device_type").trim();
+ deviceType = device.executeShellCommand("getprop ro.hdmi.cec_device_types").trim();
+ if (deviceType.isEmpty()) {
+ usingStringDeviceTypes = false;
+ deviceType = device.executeShellCommand("getprop ro.hdmi.device_type").trim();
+ }
} catch (DeviceNotAvailableException dnae) {
return 0;
}
@@ -101,7 +108,11 @@
String[] cecDevices = deviceType.split(",");
for (String cecDevice : cecDevices) {
if (!cecDevice.equals("")) {
- deviceTypes |= setBit(Integer.parseInt(cecDevice));
+ if (usingStringDeviceTypes) {
+ deviceTypes |= setBit(stringToIntDeviceType(cecDevice));
+ } else {
+ deviceTypes |= setBit(Integer.parseInt(cecDevice));
+ }
}
}
@@ -111,4 +122,28 @@
private int setBit(int value) {
return (1 << value);
}
+
+ private int stringToIntDeviceType(String value) {
+ switch (value) {
+ case "tv":
+ return HdmiCecConstants.CEC_DEVICE_TYPE_TV;
+ case "recording_device":
+ return HdmiCecConstants.CEC_DEVICE_TYPE_RECORDER;
+ case "reserved":
+ return HdmiCecConstants.CEC_DEVICE_TYPE_RESERVED;
+ case "tuner":
+ return HdmiCecConstants.CEC_DEVICE_TYPE_TUNER;
+ case "playback_device":
+ return HdmiCecConstants.CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
+ case "audio_system":
+ return HdmiCecConstants.CEC_DEVICE_TYPE_AUDIO_SYSTEM;
+ case "pure_cec_switch":
+ return HdmiCecConstants.CEC_DEVICE_TYPE_SWITCH;
+ case "video_processor":
+ return HdmiCecConstants.CEC_DEVICE_TYPE_VIDEO_PROCESSOR;
+ default:
+ fail("Unrecognized device type: " + value);
+ return 0; // Prevent compiler error
+ }
+ }
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
index c2654cf4..2310eb1 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecRoutingControlTest.java
@@ -39,6 +39,10 @@
public final class HdmiCecRoutingControlTest extends BaseHdmiCecCtsTest {
private static final int PHYSICAL_ADDRESS = 0x1000;
+ private static final String POWER_CONTROL_MODE =
+ "power_control_mode";
+ private static final String POWER_CONTROL_MODE_NONE =
+ "none";
public HdmiCecRoutingControlTest() {
super(LogicalAddress.PLAYBACK_1);
@@ -52,6 +56,12 @@
.around(CecRules.requiresDeviceType(this, LogicalAddress.PLAYBACK_1))
.around(hdmiCecClient);
+ private String setPowerControlMode(String valToSet) throws Exception {
+ String val = getSettingsValue(POWER_CONTROL_MODE);
+ setSettingsValue(POWER_CONTROL_MODE, valToSet);
+ return val;
+ }
+
/**
* Test 11.1.2-2, HF4-7-2
*
@@ -132,6 +142,7 @@
@Test
public void cect_11_2_2_4_InactiveSourceOnStandby() throws Exception {
ITestDevice device = getDevice();
+ String previousPowerControlMode = setPowerControlMode(POWER_CONTROL_MODE_NONE);
try {
int dumpsysPhysicalAddress = getDumpsysPhysicalAddress();
hdmiCecClient.sendCecMessage(
@@ -147,6 +158,7 @@
} finally {
/* Wake up the device */
device.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ setPowerControlMode(previousPowerControlMode);
}
}
}
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/targetprep/CecPortDiscoverer.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/targetprep/CecPortDiscoverer.java
index 3d5cc4b..1fdac27 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/targetprep/CecPortDiscoverer.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/targetprep/CecPortDiscoverer.java
@@ -87,6 +87,7 @@
HdmiCecClientWrapper cecClientWrapper = new HdmiCecClientWrapper();
launchCommand.add("cec-client");
+ String serialNo = "";
try {
List<String> comPorts = cecClientWrapper.getValidCecClientPorts();
@@ -106,7 +107,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
@@ -177,15 +178,21 @@
"Caught "
+ e.getClass().getSimpleName()
+ ". "
- + "Could not get adapter mapping.", e);
+ + "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.", generic);
+ + "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/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 f927646..69512de 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -241,6 +241,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/packagemanager/boottest/Android.bp b/hostsidetests/packagemanager/boottest/Android.bp
new file mode 100644
index 0000000..9d1f96e
--- /dev/null
+++ b/hostsidetests/packagemanager/boottest/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+ name: "CtsPackageManagerBootTestCases",
+ defaults: ["cts_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ libs: [
+ "cts-tradefed",
+ "tradefed",
+ "compatibility-host-util",
+ ],
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/boottest/AndroidTest.xml b/hostsidetests/packagemanager/boottest/AndroidTest.xml
new file mode 100644
index 0000000..9889a52
--- /dev/null
+++ b/hostsidetests/packagemanager/boottest/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?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 the CTS PackageManager boot tests">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <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" />
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsPackageManagerBootTestCases.jar" />
+ <option name="runtime-hint" value="3m" />
+ </test>
+
+</configuration>
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/boottest/app/Android.bp b/hostsidetests/packagemanager/boottest/app/Android.bp
new file mode 100644
index 0000000..d1a3271
--- /dev/null
+++ b/hostsidetests/packagemanager/boottest/app/Android.bp
@@ -0,0 +1,31 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CtsPackageManagerBootTestStubApp",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ sdk_version: "current",
+ manifest: "AndroidManifest.xml",
+ compile_multilib: "both",
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/boottest/app/AndroidManifest.xml b/hostsidetests/packagemanager/boottest/app/AndroidManifest.xml
new file mode 100644
index 0000000..06b1e34
--- /dev/null
+++ b/hostsidetests/packagemanager/boottest/app/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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.packagemanager.boottest.stub">
+
+ <application>
+ <activity android:name=".StubActivity"
+ android:exported="true"/>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/boottest/src/android/packagemanager/boot/cts/BootTest.java b/hostsidetests/packagemanager/boottest/src/android/packagemanager/boot/cts/BootTest.java
new file mode 100644
index 0000000..f86f5cf
--- /dev/null
+++ b/hostsidetests/packagemanager/boottest/src/android/packagemanager/boot/cts/BootTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.packagemanager.boot.cts;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class BootTest extends BaseHostJUnit4Test {
+ private static final String TEST_APK = "CtsPackageManagerBootTestStubApp.apk";
+ private static final String TEST_PACKAGE = "android.packagemanager.boottest.stub";
+
+ @Before
+ public void setUp() throws Exception {
+ installPackage(TEST_APK);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uninstallPackage(getDevice(), TEST_PACKAGE);
+ }
+
+ @Test
+ public void testUninstallPackageWithKeepDataAndReboot() throws Exception {
+ Assert.assertTrue(isPackageInstalled(TEST_PACKAGE));
+ uninstallPackageWithKeepData(TEST_PACKAGE);
+ getDevice().rebootUntilOnline();
+ waitForBootCompleted();
+ }
+
+ private void uninstallPackageWithKeepData(String packageName)
+ throws DeviceNotAvailableException {
+ getDevice().executeShellCommand("pm uninstall -k " + packageName);
+ }
+
+ private void waitForBootCompleted() throws Exception {
+ for (int i = 0; i < 45; i++) {
+ if (isBootCompleted()) {
+ return;
+ }
+ Thread.sleep(1000);
+ }
+ throw new AssertionError("System failed to become ready!");
+ }
+
+ private boolean isBootCompleted() throws Exception {
+ return "1".equals(getDevice().executeShellCommand("getprop sys.boot_completed").trim());
+ }
+}
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/extractnativelibs/apps/app_extract/src/com/android/cts/extractnativelibs/app/extract/MainActivity.java b/hostsidetests/packagemanager/extractnativelibs/apps/app_extract/src/com/android/cts/extractnativelibs/app/extract/MainActivity.java
index fbcc783..58868d4 100644
--- a/hostsidetests/packagemanager/extractnativelibs/apps/app_extract/src/com/android/cts/extractnativelibs/app/extract/MainActivity.java
+++ b/hostsidetests/packagemanager/extractnativelibs/apps/app_extract/src/com/android/cts/extractnativelibs/app/extract/MainActivity.java
@@ -30,6 +30,7 @@
@Override
public void onCreate(Bundle savedOnstanceState) {
+ super.onCreate(savedOnstanceState);
// The native lib should have been loaded already
Intent intent = new Intent(
getApplicationContext().getPackageName() + ".NativeLibLoaded");
diff --git a/hostsidetests/packagemanager/extractnativelibs/apps/app_no_extract/src/com/android/cts/extractnativelibs/app/noextract/MainActivity.java b/hostsidetests/packagemanager/extractnativelibs/apps/app_no_extract/src/com/android/cts/extractnativelibs/app/noextract/MainActivity.java
index 9200260..72367c4 100644
--- a/hostsidetests/packagemanager/extractnativelibs/apps/app_no_extract/src/com/android/cts/extractnativelibs/app/noextract/MainActivity.java
+++ b/hostsidetests/packagemanager/extractnativelibs/apps/app_no_extract/src/com/android/cts/extractnativelibs/app/noextract/MainActivity.java
@@ -30,6 +30,7 @@
@Override
public void onCreate(Bundle savedOnstanceState) {
+ super.onCreate(savedOnstanceState);
// The native lib should have been loaded already
Intent intent = new Intent(
getApplicationContext().getPackageName() + ".NativeLibLoaded");
diff --git a/hostsidetests/packagemanager/multiuser/src/com/android/tests/packagemanager/multiuser/host/PackageManagerMultiUserTestBase.java b/hostsidetests/packagemanager/multiuser/src/com/android/tests/packagemanager/multiuser/host/PackageManagerMultiUserTestBase.java
index 40c9b26..7866896 100644
--- a/hostsidetests/packagemanager/multiuser/src/com/android/tests/packagemanager/multiuser/host/PackageManagerMultiUserTestBase.java
+++ b/hostsidetests/packagemanager/multiuser/src/com/android/tests/packagemanager/multiuser/host/PackageManagerMultiUserTestBase.java
@@ -54,6 +54,9 @@
/** Remove created users after tests. */
@After
public void tearDown() throws Exception {
+ if (mCreatedUsers == null) {
+ return;
+ }
for (int userId : mCreatedUsers) {
removeUser(userId);
}
diff --git a/hostsidetests/packagemanager/parsing/OWNERS b/hostsidetests/packagemanager/parsing/OWNERS
new file mode 100644
index 0000000..541ab2e
--- /dev/null
+++ b/hostsidetests/packagemanager/parsing/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+chiuwinson@google.com
+patb@google.com
+toddke@google.com
+
diff --git a/hostsidetests/packagemanager/parsing/host/Android.bp b/hostsidetests/packagemanager/parsing/host/Android.bp
new file mode 100644
index 0000000..a5233fe
--- /dev/null
+++ b/hostsidetests/packagemanager/parsing/host/Android.bp
@@ -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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_test_host {
+ name: "CtsPackageManagerParsingHostTestCases",
+ defaults: ["cts_defaults"],
+ srcs: ["src/**/*.kt"],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+ libs: [
+ "cts-tradefed",
+ "tradefed",
+ "compatibility-host-util",
+ ],
+ static_libs: [
+ "cts-host-utils",
+ "CtsPackageManagerParsingAnnotationProcessorApi",
+ ],
+ java_resources: [
+ ":CtsPackageManagerParsingHostTestGen",
+ ],
+}
+
+genrule {
+ name: "CtsPackageManagerParsingHostTestGen",
+ defaults: [ "CtsPackageManagerParsingApkGenerator" ],
+ srcs: [ "src/**/*.kt" ],
+ out: [ "GeneratedApks.zip" ],
+}
diff --git a/hostsidetests/packagemanager/parsing/host/AndroidTest.xml b/hostsidetests/packagemanager/parsing/host/AndroidTest.xml
new file mode 100644
index 0000000..2a82513
--- /dev/null
+++ b/hostsidetests/packagemanager/parsing/host/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?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 package manager metrics host test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsPackageManagerParsingHostTestCases.jar" />
+ </test>
+</configuration>
+
diff --git a/hostsidetests/packagemanager/parsing/host/src/android/content/pm/parsing/cts/host/UsesSdkTest.kt b/hostsidetests/packagemanager/parsing/host/src/android/content/pm/parsing/cts/host/UsesSdkTest.kt
new file mode 100644
index 0000000..aa5e813
--- /dev/null
+++ b/hostsidetests/packagemanager/parsing/host/src/android/content/pm/parsing/cts/host/UsesSdkTest.kt
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.cts.host
+
+import android.content.pm.parsing.cts.generator.api.AndroidManifestXml
+import android.content.pm.parsing.cts.generator.api.ApkGenerator
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.After
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class UsesSdkTest : BaseHostJUnit4Test() {
+
+ companion object {
+
+ @JvmField
+ @ClassRule
+ val tempFolder = TemporaryFolder()
+
+ @JvmStatic
+ @BeforeClass
+ fun preloadApks() {
+ ApkGenerator.initialize(tempFolder)
+ }
+ }
+
+ @Before
+ @After
+ fun uninstall() {
+ ApkGenerator.uninstallAll(device, tempFolder)
+ }
+
+ @Test
+ fun defaultsNoTag() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, min = 0, target = 0)
+ }
+
+ @Test
+ fun defaultsWithTag() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest>
+ <uses-sdk/>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, min = 1, target = 1)
+ }
+
+ @Test
+ fun missingMinDefaults() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:targetSdkVersion="29"/>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, min = 1, target = 29)
+ }
+
+ @Test
+ fun missingTargetCoercedToMin() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:minSdkVersion="29"/>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, min = 29, target = 29)
+ }
+
+ @Test
+ fun takeAndroidTargetSdk() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk targetSdkVersion="27" android:targetSdkVersion="27">
+ <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/>
+ </uses-sdk>
+ <uses-sdk android:targetSdkVersion="29" targetSdkVersion="27">
+ <extension-sdk android:sdkVersion="31" android:minExtensionVersion="0"/>
+ </uses-sdk>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, target = 29, extensions = mapOf(31 to 0))
+ }
+
+ @Test
+ fun emptyUsesSdk() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:targetSdkVersion="29">
+ <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/>
+ </uses-sdk>
+ <uses-sdk targetSdkVersion="27"/>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, min = 1, target = 1, extensions = emptyMap())
+ }
+
+ @Test
+ fun takeLastTargetSdk() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:targetSdkVersion="28"/>
+ <uses-sdk android:targetSdkVersion="29"/>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, target = 29)
+ }
+
+ @Test
+ fun takeLastMinSdk() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:minSdkVersion="28"/>
+ <uses-sdk android:minSdkVersion="29"/>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ // Target gets coerced to 29 since it's not possible to target below min
+ assertSdks(result, min = 29, target = 29)
+ }
+
+ @Test
+ fun takeLastExtensionSdk() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk>
+ <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/>
+ </uses-sdk>
+ <uses-sdk>
+ <extension-sdk android:sdkVersion="31" android:minExtensionVersion="0"/>
+ </uses-sdk>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, min = 1, target = 1, extensions = mapOf(31 to 0))
+ }
+
+ @Test
+ fun lastDeclarationOverridesAllPrevious() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:minSdkVersion="21">
+ <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/>
+ </uses-sdk>
+ <uses-sdk android:targetSdkVersion="30"/>
+ <application/>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, min = 1, target = 30, extensions = emptyMap())
+ }
+
+ @Test
+ fun takeLastAfterAppTag() {
+ // language=XML
+ @AndroidManifestXml
+ val xml = """
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29">
+ <extension-sdk android:sdkVersion="30" android:minExtensionVersion="0"/>
+ </uses-sdk>
+ <application/>
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30">
+ <extension-sdk android:sdkVersion="31" android:minExtensionVersion="0"/>
+ </uses-sdk>
+ </manifest>
+ """
+ val result = ApkGenerator.install(device, xml, tempFolder)
+ assertThat(result.error).isEmpty()
+ assertSdks(result, min = 30, target = 30, extensions = mapOf(31 to 0))
+ }
+
+ private fun assertSdks(
+ result: ApkGenerator.InstallResult,
+ min: Int = 1,
+ target: Int = 1,
+ extensions: Map<Int, Int> = emptyMap()
+ ) {
+ val output = device.executeShellCommand("dumpsys package ${result.packageName}")
+ assertThat(output).contains("minSdk=$min")
+ assertThat(output).contains("targetSdk=$target")
+
+ val minExtensionVersions = output.lineSequence()
+ .dropWhile { !it.startsWith("Packages:") }
+ .map { it.trim() }
+ .first { it.startsWith("minExtensionVersions=") }
+ .removePrefix("minExtensionVersions=[")
+ .removeSuffix("]")
+ .trim()
+ .takeIf(String::isNotEmpty)
+ ?.split(",")
+ ?.associate {
+ it.split("=")
+ .map(String::trim)
+ .map(String::toInt)
+ .let { it.first() to it.last() }
+ }
+ ?: emptyMap()
+
+ assertWithMessage(output).that(minExtensionVersions).containsExactlyEntriesIn(extensions)
+ }
+}
diff --git a/hostsidetests/packagemanager/parsing/processing/Android.bp b/hostsidetests/packagemanager/parsing/processing/Android.bp
new file mode 100644
index 0000000..284dda3
--- /dev/null
+++ b/hostsidetests/packagemanager/parsing/processing/Android.bp
@@ -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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library_host {
+ name: "CtsPackageManagerParsingAnnotationProcessorApi",
+ srcs: [ "api/src/**/*.kt" ],
+ defaults: ["cts_defaults"],
+ libs: [
+ "compatibility-host-util",
+ "cts-tradefed",
+ "tradefed",
+ ],
+ static_libs: [
+ "cts-host-utils",
+ "jetbrains-annotations",
+ "junit",
+ ],
+}
+
+filegroup {
+ name: "CtsPackageManagerApkGeneratorSrc",
+ srcs: [ "ApkGenerator.py" ],
+}
+
+filegroup {
+ name: "CtsPackageManagerApkGeneratorJks",
+ srcs: [ "ApkGenerator.jks" ],
+}
+
+genrule_defaults {
+ name: "CtsPackageManagerParsingApkGenerator",
+ tool_files: [
+ ":CtsPackageManagerApkGeneratorSrc",
+ ":CtsPackageManagerApkGeneratorJks",
+ ":framework-res",
+ ],
+ tools: [
+ "aapt2",
+ "apksigner",
+ "soong_zip",
+ ],
+ cmd: "python3 $(location :CtsPackageManagerApkGeneratorSrc) \"$(location aapt2)\" " +
+ "\"$(location :framework-res)\" \"$(location apksigner)\" " +
+ "\"$(location :CtsPackageManagerApkGeneratorJks)\" " +
+ "\"$(genDir)\" $(in) && $(location soong_zip) -o $(out) -L 0 -C $(genDir)/out -D $(genDir)/out"
+}
diff --git a/hostsidetests/packagemanager/parsing/processing/ApkGenerator.jks b/hostsidetests/packagemanager/parsing/processing/ApkGenerator.jks
new file mode 100644
index 0000000..cec2979
--- /dev/null
+++ b/hostsidetests/packagemanager/parsing/processing/ApkGenerator.jks
Binary files differ
diff --git a/hostsidetests/packagemanager/parsing/processing/ApkGenerator.py b/hostsidetests/packagemanager/parsing/processing/ApkGenerator.py
new file mode 100644
index 0000000..dbbf010
--- /dev/null
+++ b/hostsidetests/packagemanager/parsing/processing/ApkGenerator.py
@@ -0,0 +1,110 @@
+"""
+Parses @AndroidManifestXml annotations to pre-generate APKs for tests
+
+Assumes a Kotlin triple quoted string literal which is prepended by a custom annotation. See the
+AndroidManifestXml annotation for more information.
+"""
+
+import subprocess
+import sys
+import os
+
+ANNOTATION = "@AndroidManifestXml"
+TRIPLE_QUOTE = "\"\"\""
+PACKAGE_NAME_PREFIX = "android.content.pm.parsing.cts.generated"
+GENERATED_APK_PACKAGE_NAMES_FILE = "GeneratedApkPackageNames.txt"
+ANDROID_NAMESPACE = "xmlns:android=\"http://schemas.android.com/apk/res/android\""
+
+def java_string_hashcode(string):
+ """
+ Simulates Java's hashCode so that APKs can be looked up at runtime by using the manifest
+ hashCode as the file name.
+ """
+ hash = 0
+ for char in string:
+ hash = int((((31 * hash + ord(char)) ^ 0x80000000) & 0xFFFFFFFF) - 0x80000000)
+ return str(abs(hash))
+
+aapt2 = sys.argv[1]
+frameworkRes = sys.argv[2]
+apkSigner = sys.argv[3]
+keyStore = sys.argv[4]
+genDir = sys.argv[5]
+inputFiles = sys.argv[6:]
+
+tempDir = f"{genDir}/temp"
+outDir = f"{genDir}/out"
+
+os.makedirs(tempDir, exist_ok=True)
+os.makedirs(outDir, exist_ok=True)
+
+packageNamesOutput = open(f"{outDir}/{GENERATED_APK_PACKAGE_NAMES_FILE}", "w")
+
+usedHashCodes = {}
+
+for inputFile in inputFiles:
+ text = open(inputFile, "r").read()
+
+ annotationIndex = 0
+ while True:
+ try:
+ annotationIndex = text.index(ANNOTATION, annotationIndex)
+ if annotationIndex < 0:
+ break
+ except:
+ break
+
+ annotationIndex += len(ANNOTATION)
+ startIndex = text.index(TRIPLE_QUOTE, annotationIndex)
+ if startIndex < 0:
+ continue
+
+ endIndex = text.index(TRIPLE_QUOTE, startIndex + len(TRIPLE_QUOTE))
+ if endIndex < 0:
+ continue
+
+ string = text[startIndex + len(TRIPLE_QUOTE): endIndex]
+ hashCode = java_string_hashcode(string)
+
+ if hashCode in usedHashCodes:
+ if usedHashCodes[hashCode] != string:
+ sys.exit(f"Manifest XML with hash code {hashCode} already used: {string}")
+ usedHashCodes[hashCode] = string
+
+ if ANDROID_NAMESPACE not in string:
+ string = string.replace("<manifest", f"<manifest {ANDROID_NAMESPACE}\n", 1)
+
+ packageName = PACKAGE_NAME_PREFIX + hashCode
+ string = string.replace(">", f"\npackage=\"{packageName}\"\n>", 1)
+ string = string.replace("<application", "<application\nandroid:hasCode=\"false\"\n")
+
+ outputPath = f"{tempDir}/{hashCode}.xml"
+ outputFile = open(outputPath, "w")
+ outputFile.write(string)
+ outputFile.close()
+
+ packageNamesOutput.write(packageName)
+ packageNamesOutput.write("\n")
+
+ apkPath = f"{outDir}/{hashCode}.apk"
+
+ subprocess.run([
+ aapt2, "link",
+ "-I", frameworkRes,
+ "--manifest", outputPath,
+ "--warn-manifest-validation",
+ "--rename-manifest-package", packageName,
+ "-o", apkPath
+ ], check = True)
+
+ subprocess.run([
+ apkSigner, "sign",
+ "--ks", keyStore,
+ "--ks-pass", "pass:password",
+ apkPath
+ ], check = True)
+
+ # apksigner will generate an idsig file, but it's not useful for the test, so get rid of it
+ idsigPath = f"{outDir}/{hashCode}.idsig"
+ if os.path.isfile(idsigPath):
+ os.remove(idsigPath)
diff --git a/hostsidetests/packagemanager/parsing/processing/api/src/android/content/pm/parsing/cts/generator/api/AndroidManifestXml.kt b/hostsidetests/packagemanager/parsing/processing/api/src/android/content/pm/parsing/cts/generator/api/AndroidManifestXml.kt
new file mode 100644
index 0000000..cfab38e
--- /dev/null
+++ b/hostsidetests/packagemanager/parsing/processing/api/src/android/content/pm/parsing/cts/generator/api/AndroidManifestXml.kt
@@ -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.content.pm.parsing.cts.generator.api
+
+/**
+ * Used to annotate a triple quoted string literal in order to automatically build an APK host side
+ * so that tests don't need to configure individual test apps for each iteration.
+ *
+ * Note that this only supports actual string constants, with no inline format or variations. The
+ * string in the source file must exactly match the string that will be passed to [ApkGenerator],
+ * which will look up the appropriate APK by hash code.
+ *
+ * Sample usage:
+ * ```kotlin
+ * fun someTestMethod() {
+ * @AndroidManifestXml
+ * val xml = """
+ * <manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ * <uses-sdk android:targetSdkVersion="30"/>
+ * <application/>
+ * </manifest>
+ * """
+ * val result = ApkGenerator.install(device, xml, tempFolder)
+ * ...
+ * }
+ * ```
+ *
+ * **Note:** The string *cannot* end with [String.trimIndent] as this will alter the hash code.
+ */
+@Target(
+ AnnotationTarget.EXPRESSION,
+ AnnotationTarget.LOCAL_VARIABLE,
+ AnnotationTarget.TYPE_PARAMETER,
+ AnnotationTarget.VALUE_PARAMETER,
+)
+@Retention(AnnotationRetention.SOURCE)
+annotation class AndroidManifestXml
diff --git a/hostsidetests/packagemanager/parsing/processing/api/src/android/content/pm/parsing/cts/generator/api/ApkGenerator.kt b/hostsidetests/packagemanager/parsing/processing/api/src/android/content/pm/parsing/cts/generator/api/ApkGenerator.kt
new file mode 100644
index 0000000..76df7356
--- /dev/null
+++ b/hostsidetests/packagemanager/parsing/processing/api/src/android/content/pm/parsing/cts/generator/api/ApkGenerator.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.parsing.cts.generator.api
+
+import com.android.tradefed.device.ITestDevice
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.rules.TemporaryFolder
+import java.io.File
+import java.io.FileOutputStream
+import java.util.zip.ZipFile
+import kotlin.math.absoluteValue
+
+/**
+ * Handles installing APKs previously generated via [AndroidManifestXml] declarations. Supports
+ * installing APKs from the annotated strings and cleaning up all packages afterwards.
+ */
+object ApkGenerator {
+
+ private const val PACKAGE_NAME_PREFIX = "android.content.pm.parsing.cts.generated"
+ private const val GENERATED_APKS_FILE = "GeneratedApks.zip"
+ private const val GENERATED_PACKAGE_NAMES_FILE = "GeneratedApkPackageNames.txt"
+
+ private lateinit var hostZipFile: File
+
+ /**
+ * Call in a @[BeforeClass] method to preload the zip file to the host. Note that this will
+ * require that the [TemporaryFolder] be initialized as a @[ClassRule].
+ */
+ fun initialize(tempFolder: TemporaryFolder) {
+ copyHostZipFileIfNeeded(tempFolder)
+ }
+
+ fun uninstallAll(device: ITestDevice, tempFolder: TemporaryFolder) {
+ getFileFromZip(tempFolder, GENERATED_PACKAGE_NAMES_FILE).readLines().forEach {
+ try {
+ device.uninstallPackage(it)
+ } catch (ignored: Exception) {
+ }
+ }
+ }
+
+ fun install(
+ device: ITestDevice,
+ @AndroidManifestXml
+ @org.intellij.lang.annotations.Language("XML")
+ androidManifestXml: String,
+ tempFolder: TemporaryFolder
+ ): InstallResult {
+ val hashCode = androidManifestXml.hashCode().absoluteValue
+ val apkFile = getFileFromZip(tempFolder, "$hashCode.apk")
+ val error = device.installPackage(apkFile, false)
+ return InstallResult(
+ packageName = "$PACKAGE_NAME_PREFIX$hashCode",
+ error = error.orEmpty(),
+ )
+ }
+
+ private fun getFileFromZip(tempFolder: TemporaryFolder, fileName: String): File {
+ val zipFile = ZipFile(copyHostZipFileIfNeeded(tempFolder))
+ val entry = zipFile.getEntry(fileName)
+ return tempFolder.newFile().apply {
+ zipFile.getInputStream(entry).use { input ->
+ FileOutputStream(this).use { output ->
+ input.copyTo(output)
+ }
+ }
+ }
+ }
+
+ private fun copyHostZipFileIfNeeded(tempFolder: TemporaryFolder): File {
+ if (this::hostZipFile.isInitialized && hostZipFile.exists()) {
+ return hostZipFile
+ }
+
+ val outputFile = tempFolder.newFile()
+ ApkGenerator::class.java.classLoader!!.getResource(GENERATED_APKS_FILE)!!.openStream()
+ .use { input ->
+ FileOutputStream(outputFile).use { output ->
+ input.copyTo(output)
+ }
+ }
+
+ hostZipFile = outputFile
+ return hostZipFile
+ }
+
+ data class InstallResult(
+ val packageName: String,
+ val error: String,
+ )
+}
diff --git a/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageInstallerV2StatsTests.java b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageInstallerV2StatsTests.java
index 369c983..bcac138 100644
--- a/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageInstallerV2StatsTests.java
+++ b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageInstallerV2StatsTests.java
@@ -32,6 +32,8 @@
import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class PackageInstallerV2StatsTests extends DeviceTestCase implements IBuildReceiver {
private static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
@@ -80,6 +82,8 @@
assertEquals(1, report.getReturnCode());
assertTrue(report.getDurationMillis() > 0);
assertEquals(getTestFileSize(TEST_INSTALL_APK), report.getApksSizeBytes());
+ assertTrue(report.getUid() != 0);
+ assertEquals(getAppUid(TEST_INSTALL_PACKAGE), report.getUid());
}
public void testPackageInstallerV2MetricsReportedForSplits() throws Throwable {
@@ -94,6 +98,8 @@
assertEquals(
getTestFileSize(TEST_INSTALL_APK_BASE) + getTestFileSize(TEST_INSTALL_APK_SPLIT),
report.getApksSizeBytes());
+ assertTrue(report.getUid() != 0);
+ assertEquals(getAppUid(TEST_INSTALL_PACKAGE), report.getUid());
}
private long getTestFileSize(String fileName) throws Exception {
@@ -147,4 +153,17 @@
return remoteApkPath;
}
+ protected int getAppUid(String pkgName) throws Exception {
+ final int currentUser = getDevice().getCurrentUser();
+ final String uidLine = getDevice().executeShellCommand(
+ "cmd package list packages -U --user " + currentUser + " " + pkgName);
+ final Pattern pattern = Pattern.compile("package:" + pkgName + " uid:(\\d+)");
+ final Matcher matcher = pattern.matcher(uidLine);
+ if (matcher.find()) {
+ return Integer.parseInt(matcher.group(1));
+ } else {
+ return -1;
+ }
+ }
+
}
diff --git a/hostsidetests/scopedstorage/TEST_MAPPING b/hostsidetests/scopedstorage/TEST_MAPPING
index 8d9d418..899b9c1 100644
--- a/hostsidetests/scopedstorage/TEST_MAPPING
+++ b/hostsidetests/scopedstorage/TEST_MAPPING
@@ -5,14 +5,14 @@
},
{
"name": "CtsScopedStorageHostTest"
+ },
+ {
+ "name": "CtsScopedStorageDeviceOnlyTest"
}
],
"presubmit-large": [
{
"name": "CtsScopedStoragePublicVolumeHostTest"
- },
- {
- "name": "CtsScopedStorageDeviceOnlyTest"
}
]
}
diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
index e2424b9..8f3022b 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
@@ -1076,6 +1076,7 @@
}
@Test
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
public void testDefaultNoIsolatedStorageFlag() throws Exception {
assertThat(Environment.isExternalStorageLegacy()).isFalse();
}
diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
index 090b125..eeee104 100644
--- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
+++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java
@@ -536,7 +536,9 @@
@Test
public void testAndroidMedia() throws Exception {
// Check that the app does not have legacy external storage access
- assertThat(Environment.isExternalStorageLegacy()).isFalse();
+ if (BuildCompat.isAtLeastS()) {
+ assertThat(Environment.isExternalStorageLegacy()).isFalse();
+ }
pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
@@ -686,7 +688,9 @@
@Test
public void testNoIsolatedStorageCanCreateFilesAnywhere() throws Exception {
- assertThat(Environment.isExternalStorageLegacy()).isTrue();
+ if (BuildCompat.isAtLeastS()) {
+ assertThat(Environment.isExternalStorageLegacy()).isTrue();
+ }
final File topLevelPdf = new File(getExternalStorageDir(), NONMEDIA_FILE_NAME);
final File musicFileInMovies = new File(getMoviesDir(), AUDIO_FILE_NAME);
final File imageFileInDcim = new File(getDcimDir(), IMAGE_FILE_NAME);
@@ -701,7 +705,9 @@
@Test
public void testNoIsolatedStorageCantReadWriteOtherAppExternalDir() throws Exception {
- assertThat(Environment.isExternalStorageLegacy()).isTrue();
+ if (BuildCompat.isAtLeastS()) {
+ assertThat(Environment.isExternalStorageLegacy()).isTrue();
+ }
// Let app A create a file in its data dir
final File otherAppExternalDataDir = new File(getExternalFilesDir().getPath().replace(
THIS_PACKAGE_NAME, APP_A_HAS_RES.getPackageName()));
@@ -725,7 +731,9 @@
@Test
public void testNoIsolatedStorageStorageReaddir() throws Exception {
- assertThat(Environment.isExternalStorageLegacy()).isTrue();
+ if (BuildCompat.isAtLeastS()) {
+ assertThat(Environment.isExternalStorageLegacy()).isTrue();
+ }
final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
@@ -752,7 +760,9 @@
@Test
public void testNoIsolatedStorageQueryOtherAppsFile() throws Exception {
- assertThat(Environment.isExternalStorageLegacy()).isTrue();
+ if (BuildCompat.isAtLeastS()) {
+ assertThat(Environment.isExternalStorageLegacy()).isTrue();
+ }
final File otherAppPdf = new File(getDownloadDir(), "other" + NONMEDIA_FILE_NAME);
final File otherAppImg = new File(getDcimDir(), "other" + IMAGE_FILE_NAME);
final File otherAppMusic = new File(getMusicDir(), "other" + AUDIO_FILE_NAME);
@@ -816,7 +826,9 @@
@Test
public void testClearPackageData() throws Exception {
// Check that the app does not have legacy external storage access
- assertThat(Environment.isExternalStorageLegacy()).isFalse();
+ if (BuildCompat.isAtLeastS()) {
+ assertThat(Environment.isExternalStorageLegacy()).isFalse();
+ }
pollForPermission(Manifest.permission.READ_EXTERNAL_STORAGE, /*granted*/ true);
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index db2b666..4b8287f 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -1011,6 +1011,28 @@
}
/**
+ * Tests that tracefs files(/sys/kernel/tracing and /d/tracing) are correctly labeled.
+ *
+ * @throws Exception
+ */
+ @Test
+ 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
+ */
+ @Test
+ 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.
diff --git a/hostsidetests/securitybulletin/Android.bp b/hostsidetests/securitybulletin/Android.bp
index 39f7eda..d3e6ea7 100644
--- a/hostsidetests/securitybulletin/Android.bp
+++ b/hostsidetests/securitybulletin/Android.bp
@@ -55,6 +55,7 @@
test_suites: [
"cts",
"sts",
+ "general-tests",
],
cflags: [
"-Wall",
diff --git a/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
index adbde0f..efc45c4 100755
--- a/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
+++ b/hostsidetests/securitybulletin/securityPatch/Bug-115739809/poc.cpp
@@ -125,6 +125,10 @@
outMsg->body.motion.xCursorPosition = msg.body.motion.xCursorPosition;
// float yCursorPosition
outMsg->body.motion.yCursorPosition = msg.body.motion.yCursorPosition;
+ // int32_t displayW
+ outMsg->body.motion.displayWidth = msg.body.motion.displayWidth;
+ // int32_t displayH
+ outMsg->body.motion.displayHeight = msg.body.motion.displayHeight;
// uint32_t pointerCount
outMsg->body.motion.pointerCount = msg.body.motion.pointerCount;
//struct Pointer pointers[MAX_POINTERS]
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/Android.bp
index 8d95e36..2187f92 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2022/Android.bp
@@ -15,6 +15,10 @@
*
*/
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_test {
name: "CVE-2019-2022",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2038/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2038/Android.bp
index d343438..032f4cd 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2038/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2038/Android.bp
@@ -15,6 +15,10 @@
*
*/
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_test {
name: "CVE-2019-2038",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2039/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2039/Android.bp
index 48fb497..485c92a 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2039/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2039/Android.bp
@@ -15,6 +15,10 @@
*
*/
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_test {
name: "CVE-2019-2039",
defaults: ["cts_hostsidetests_securitybulletin_defaults"],
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 6392952..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-2021-0484/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0484/Android.bp
index 9912e5e..c24f1af 100644
--- a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0484/Android.bp
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0484/Android.bp
@@ -15,6 +15,10 @@
*
*/
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_test {
name: "CVE-2021-0484",
defaults: [
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11164.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11164.java
new file mode 100644
index 0000000..4132917
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11164.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.security.cts;
+
+import static org.junit.Assert.*;
+
+import android.platform.test.annotations.SecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2020_11164 extends SecurityTestCase {
+
+ /**
+ * CVE-2020-11164
+ */
+ @SecurityTest(minPatchLevel = "2020-10")
+ @Test
+ public void testPocCVE_2020_11164() throws Exception {
+ String result =
+ AdbUtils.runCommandLine("pm list package com.qualcomm.qti.perfdump", getDevice());
+ assertFalse(result.contains("com.qualcomm.qti.perfdump"));
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
index 3d4ae46..65cd58a 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
@@ -131,21 +131,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/silentupdate/src/com/android/tests/hostside/silentupdate/SilentUpdateHostsideTests.java b/hostsidetests/silentupdate/src/com/android/tests/hostside/silentupdate/SilentUpdateHostsideTests.java
index 3d6143f..fb8ad92 100644
--- a/hostsidetests/silentupdate/src/com/android/tests/hostside/silentupdate/SilentUpdateHostsideTests.java
+++ b/hostsidetests/silentupdate/src/com/android/tests/hostside/silentupdate/SilentUpdateHostsideTests.java
@@ -115,6 +115,26 @@
runDeviceTests(TEST_PKG, TEST_CLS, "setRequireUserAction_throwsOnIllegalArgument");
}
+ @Test
+ public void silentInstallRepeatedly_RequiresUserAction() throws Exception {
+ install(CURRENT_APK, TEST_PKG);
+ runDeviceTests(TEST_PKG, TEST_CLS, "silentInstallRepeatedly_RequiresUserAction");
+ }
+
+ @Test
+ public void silentInstallRepeatedly_withUnlimitedSilentUpdates_succeed() throws Exception {
+ install(CURRENT_APK, TEST_PKG);
+ runDeviceTests(TEST_PKG, TEST_CLS,
+ "silentInstallRepeatedly_withUnlimitedSilentUpdates_succeed");
+ }
+
+ @Test
+ public void silentInstallRepeatedly_waitForThrottleTime_succeed() throws Exception {
+ install(CURRENT_APK, TEST_PKG);
+ runDeviceTests(TEST_PKG, TEST_CLS,
+ "silentInstallRepeatedly_waitForThrottleTime_succeed");
+ }
+
private void waitForPathChange(String packageName, String originalCodePath, long timeout)
throws DeviceNotAvailableException, InterruptedException {
long startTime = System.currentTimeMillis();
diff --git a/hostsidetests/silentupdate/testapp/Android.bp b/hostsidetests/silentupdate/testapp/Android.bp
index 9204dfc..d3417e94 100644
--- a/hostsidetests/silentupdate/testapp/Android.bp
+++ b/hostsidetests/silentupdate/testapp/Android.bp
@@ -22,6 +22,7 @@
srcs: ["src/**/*.java", "src/**/*.kt"],
static_libs: [
+ "compatibility-device-util-axt",
"androidx.annotation_annotation",
"androidx.core_core",
"androidx.test.runner",
diff --git a/hostsidetests/silentupdate/testapp/src/com/android/tests/silentupdate/SilentUpdateTests.java b/hostsidetests/silentupdate/testapp/src/com/android/tests/silentupdate/SilentUpdateTests.java
index dd7293d..da1d5ee 100644
--- a/hostsidetests/silentupdate/testapp/src/com/android/tests/silentupdate/SilentUpdateTests.java
+++ b/hostsidetests/silentupdate/testapp/src/com/android/tests/silentupdate/SilentUpdateTests.java
@@ -22,7 +22,7 @@
import static android.content.pm.PackageInstaller.SessionParams.USER_ACTION_REQUIRED;
import static android.content.pm.PackageInstaller.SessionParams.USER_ACTION_UNSPECIFIED;
-import static com.google.common.truth.Truth.assertWithMessage;
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import static org.junit.Assert.fail;
@@ -36,11 +36,10 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
-import android.util.Log;
+import android.os.SystemClock;
import androidx.test.platform.app.InstrumentationRegistry;
-
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
@@ -55,6 +54,7 @@
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
@@ -65,11 +65,18 @@
private static final String CURRENT_APK = "SilentInstallCurrent.apk";
private static final String P_APK = "SilentInstallP.apk";
private static final String Q_APK = "SilentInstallQ.apk";
+ private static final String INSTALLER_PACKAGE_NAME = "com.android.tests.silentupdate";
+ static final long SILENT_UPDATE_THROTTLE_TIME_MS = TimeUnit.SECONDS.toMillis(30);
private static Context getContext() {
return InstrumentationRegistry.getInstrumentation().getContext();
}
+ @After
+ public void tearDown() {
+ setUnlimitedSilentUpdates(null);
+ }
+
@Test
public void newInstall_RequiresUserAction() throws Exception {
Assert.assertEquals("New install should require user action",
@@ -167,6 +174,39 @@
}
}
+ @Test
+ public void silentInstallRepeatedly_RequiresUserAction() throws Exception {
+ Assert.assertEquals("The first silent update should succeed",
+ PackageInstaller.STATUS_SUCCESS,
+ silentInstallResource(CURRENT_APK));
+ Assert.assertEquals("The repeated silent install invoked within the throttle time "
+ + "should require user action",
+ PackageInstaller.STATUS_PENDING_USER_ACTION,
+ silentInstallResource(CURRENT_APK));
+ }
+
+ @Test
+ public void silentInstallRepeatedly_withUnlimitedSilentUpdates_succeed() throws Exception {
+ setUnlimitedSilentUpdates(INSTALLER_PACKAGE_NAME);
+ Assert.assertEquals("The first silent update should succeed",
+ PackageInstaller.STATUS_SUCCESS,
+ silentInstallResource(CURRENT_APK));
+ Assert.assertEquals("The repeated silent update should succeed",
+ PackageInstaller.STATUS_SUCCESS,
+ silentInstallResource(CURRENT_APK));
+ }
+
+ @Test
+ public void silentInstallRepeatedly_waitForThrottleTime_succeed() throws Exception {
+ Assert.assertEquals("The first silent update should succeed",
+ PackageInstaller.STATUS_SUCCESS,
+ silentInstallResource(CURRENT_APK));
+ SystemClock.sleep(SILENT_UPDATE_THROTTLE_TIME_MS);
+ Assert.assertEquals("The repeated silent update should succeed",
+ PackageInstaller.STATUS_SUCCESS,
+ silentInstallResource(CURRENT_APK));
+ }
+
private int silentInstallResource(String resourceName) throws Exception {
return install(resourceSupplier(resourceName), false);
}
@@ -251,6 +291,16 @@
return lastUpdateTime;
}
+ private void setUnlimitedSilentUpdates(String installerPackageName) {
+ final StringBuilder cmd = new StringBuilder("pm allow-unlimited-silent-updates ");
+ if (installerPackageName == null) {
+ cmd.append("--reset");
+ } else {
+ cmd.append(installerPackageName);
+ }
+ runShellCommand(cmd.toString());
+ }
+
public static class InstallStatusListener extends BroadcastReceiver {
private final BlockingQueue<Intent> mResults = new LinkedBlockingQueue<>();
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 4a69184..9ba9448 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -17,7 +17,6 @@
package com.android.tests.stagedinstall;
import static com.android.cts.install.lib.InstallUtils.assertStatusSuccess;
-import static com.android.cts.install.lib.InstallUtils.getInstalledVersion;
import static com.android.cts.install.lib.InstallUtils.getPackageInstaller;
import static com.android.cts.shim.lib.ShimPackage.DIFFERENT_APEX_PACKAGE_NAME;
import static com.android.cts.shim.lib.ShimPackage.NOT_PRE_INSTALL_APEX_PACKAGE_NAME;
@@ -162,6 +161,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() {
@@ -1191,6 +1193,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 fd9b0f9..08da8a1 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -649,6 +649,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
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 3019791..b9ba412 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 258eadc..381c03e 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 f72afda..ab0bd3b 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 f9aa433..49eb049 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 97da742..7a3c698 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 876ca57..549ef2f 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 4e801f8..7f6c3f7 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 b76f639..22af8a6 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 d672bc2..0ca48f6 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..a9b6105
--- /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 bf7d8c4..4d8b380 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 ac0d06e..a520f63 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 405ed92..8a86a33 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 1a524fb..9ee1bbb 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 5d294ae..3352c03 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 3f1cde1..d23c81c 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 cd3e8ba..ac71363 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 9f30c4f..0673492 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 aaa0d36..1de3948 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 fdc764b..8643df9 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 b4bafaf..742395a 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/x86/com.android.apex.cts.shim.v1.apex b/hostsidetests/stagedinstall/testdata/apex/x86/com.android.apex.cts.shim.v1.apex
index fc2a7f8..ac9ddb6 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 29daf35..c7051e8 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 f72afda..ab0bd3b 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 f9aa433..49eb049 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 97da742..7a3c698 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 876ca57..549ef2f 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 bf5dee0..dc517ab 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 145e974..207083c 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 458dc15..459cdd7d 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..a9b6105
--- /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 1607d35..fa289c0 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 31f08a1..9d09714 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 0432704..2b9e42f 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 0f10c19..cfc8c0e 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 5d294ae..3352c03 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 3f1cde1..d23c81c 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 cd3e8ba..ac71363 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 9f30c4f..0673492 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 57a4e8e..66ad34e 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 fe0be5b..21ba7bb 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 1bd82d0..a1087fc 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/apk/CtsShimTargetPSdk/arm/CtsShimTargetPSdk.apk b/hostsidetests/stagedinstall/testdata/apk/CtsShimTargetPSdk/arm/CtsShimTargetPSdk.apk
index e852251..cbd5a8b 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 e852251..cbd5a8b 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/statsdatom/src/android/cts/statsdatom/apphibernation/AppHibernationStatsTest.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/apphibernation/AppHibernationStatsTest.java
index c397583..8564d494 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/apphibernation/AppHibernationStatsTest.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/apphibernation/AppHibernationStatsTest.java
@@ -123,6 +123,22 @@
fail(String.format("Did not find a matching atom for user %d", userId));
}
+ public void testGlobalHibernatedApps() throws Exception {
+ ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
+ AtomsProto.Atom.GLOBAL_HIBERNATED_APPS_FIELD_NUMBER);
+ getDevice().executeShellCommand(
+ getHibernationCommand(DeviceUtils.STATSD_ATOM_TEST_PKG,
+ /* isGlobal */ true, /* isHibernating */ true));
+
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
+ Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
+
+ final List<AtomsProto.Atom> atoms = ReportUtils.getGaugeMetricAtoms(getDevice());
+ assertThat(atoms.size()).isEqualTo(1);
+ AtomsProto.GlobalHibernatedApps apps = atoms.get(0).getGlobalHibernatedApps();
+ assertThat(apps.getHibernatedAppCount()).isAtLeast(1);
+ }
+
private static void assertUserLevelHibernationStateChangedEvent(
List<StatsLog.EventMetricData> data, boolean isHibernating) {
for (StatsLog.EventMetricData d : data) {
diff --git a/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetector.java b/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetector.java
index 7265d85..63b0362 100644
--- a/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetector.java
+++ b/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetector.java
@@ -43,6 +43,13 @@
String SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED = "set_auto_detection_enabled";
/**
+ * A shell command that prints whether the telephony-based time zone detection feature is
+ * supported on the device.
+ * @hide
+ */
+ String SHELL_COMMAND_IS_TELEPHONY_DETECTION_SUPPORTED = "is_telephony_detection_supported";
+
+ /**
* A shell command that prints whether the geolocation-based time zone detection feature is
* supported on the device.
* @hide
diff --git a/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetectorHostHelper.java b/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetectorHostHelper.java
index e3201fe..321cbf2 100644
--- a/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetectorHostHelper.java
+++ b/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetectorHostHelper.java
@@ -101,6 +101,12 @@
return parseShellCommandBytesAsBoolean(result);
}
+ boolean isTelephonyDetectionSupported() throws Exception {
+ byte[] result = executeTimeZoneDetectorCommand(
+ TimeZoneDetector.SHELL_COMMAND_IS_TELEPHONY_DETECTION_SUPPORTED);
+ return parseShellCommandBytesAsBoolean(result);
+ }
+
void clearSystemTimeDeviceConfigKey(String deviceConfigKey) throws Exception {
executeDeviceConfigCommand("delete %s %s", NAMESPACE, deviceConfigKey);
}
diff --git a/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetectorStatsTest.java b/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetectorStatsTest.java
index 5e029ed5..e62af49 100644
--- a/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetectorStatsTest.java
+++ b/hostsidetests/time/host/src/android/time/cts/host/TimeZoneDetectorStatsTest.java
@@ -96,11 +96,15 @@
assertThat(state.getAutoDetectionSetting())
.isEqualTo(autoDetectionEnabledFromShell);
+ boolean telephonyDetectionSupportedFromShell =
+ mTimeZoneDetectorHostHelper.isTelephonyDetectionSupported();
+ boolean noAutoDetectionSupported =
+ !(telephonyDetectionSupportedFromShell || geoDetectionSupportedFromShell);
// The atom reports the functional state for "detection mode", which is derived from
// device config and settings. This logic basically repeats the logic used on the
// device.
DetectionMode expectedDetectionMode;
- if (!autoDetectionEnabledFromShell) {
+ if (noAutoDetectionSupported || !autoDetectionEnabledFromShell) {
expectedDetectionMode = DetectionMode.MANUAL;
} else {
boolean geoDetectionSettingEnabledFromShell =
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java b/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java
index aa415cd..f0fc793 100644
--- a/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import android.app.AlarmManager;
import android.app.AppOpsManager;
@@ -45,6 +46,7 @@
import com.android.compatibility.common.util.AppOpsUtils;
import com.android.compatibility.common.util.AppStandbyUtils;
+import com.android.compatibility.common.util.FeatureUtil;
import com.android.compatibility.common.util.SystemUtil;
import org.junit.After;
@@ -363,6 +365,9 @@
@Test
public void alarmClockGrantsWhitelist() throws Exception {
+ // no device idle in auto
+ assumeFalse(FeatureUtil.isAutomotive());
+
final int id = mIdGenerator.nextInt();
final AlarmManager.AlarmClockInfo alarmClock = new AlarmManager.AlarmClockInfo(
System.currentTimeMillis() + 100, null);
@@ -375,6 +380,9 @@
@Test
public void exactAwiGrantsWhitelist() throws Exception {
+ // no device idle in auto
+ assumeFalse(FeatureUtil.isAutomotive());
+
reclaimQuota(1);
final int id = mIdGenerator.nextInt();
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
@@ -387,6 +395,9 @@
@Test
public void activityToRequestPermissionExists() {
+ // TODO(b/188070398) Remove this when auto supports the ACTION_REQUEST_SCHEDULE_EXACT_ALARM
+ assumeFalse(FeatureUtil.isAutomotive());
+
final Intent request = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
final PackageManager pm = sContext.getPackageManager();
diff --git a/tests/MediaProviderTranscode/src/android/mediaprovidertranscode/cts/TranscodeTest.java b/tests/MediaProviderTranscode/src/android/mediaprovidertranscode/cts/TranscodeTest.java
index 8f34997..ed2495d 100644
--- a/tests/MediaProviderTranscode/src/android/mediaprovidertranscode/cts/TranscodeTest.java
+++ b/tests/MediaProviderTranscode/src/android/mediaprovidertranscode/cts/TranscodeTest.java
@@ -20,6 +20,7 @@
import static android.mediaprovidertranscode.cts.TranscodeTestUtils.assertFileContent;
import static android.mediaprovidertranscode.cts.TranscodeTestUtils.assertTranscode;
+import static android.mediaprovidertranscode.cts.TranscodeTestUtils.executeShellCommand;
import static android.mediaprovidertranscode.cts.TranscodeTestUtils.installAppWithStoragePermissions;
import static android.mediaprovidertranscode.cts.TranscodeTestUtils.isAppIoBlocked;
import static android.mediaprovidertranscode.cts.TranscodeTestUtils.open;
@@ -905,6 +906,8 @@
// Trigger transcoding so that the transcoded file gets added to cache.
assertTranscode(modernFile, true);
+ // To make the cache clearing logic easier to verify, ignore any cache reserved space.
+ executeShellCommand("settings put global sys_storage_cache_max_bytes 0");
// Invoke StorageManager to free maximum allocatable bytes, so that it tries to clear
// all available caches.
StorageManager storageManager = getContext().getSystemService(StorageManager.class);
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 038af75..adf2f4f 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -57,6 +57,10 @@
android:supportsPictureInPicture="true"
android:screenOrientation="locked"/>
+ <activity android:label="@string/non_default_display_activity"
+ android:name=".activities.NonDefaultDisplayActivity"
+ android:screenOrientation="locked"/>
+
<activity android:label="Full screen activity for gesture dispatch testing"
android:name=".AccessibilityGestureDispatchTest$GestureDispatchActivity"
android:theme="@style/Theme_NoSwipeDismiss"
@@ -65,15 +69,6 @@
<activity android:label="@string/accessibility_soft_keyboard_modes_activity"
android:name=".AccessibilitySoftKeyboardModesTest$SoftKeyboardModesActivity"/>
- <activity android:label="@string/accessibility_embedded_display_test_parent_activity"
- android:name=".AccessibilityEmbeddedDisplayTest$EmbeddedDisplayParentActivity"
- android:theme="@android:style/Theme.Dialog"
- android:screenOrientation="locked"/>
-
- <activity android:label="@string/accessibility_embedded_display_test_activity"
- android:name=".AccessibilityEmbeddedDisplayTest$EmbeddedDisplayActivity"
- android:screenOrientation="locked"/>
-
<activity android:label="@string/accessibility_embedded_hierarchy_test_activity"
android:name=".AccessibilityEmbeddedHierarchyTest$AccessibilityEmbeddedHierarchyActivity"
android:theme="@android:style/Theme.Dialog"
diff --git a/tests/accessibilityservice/res/layout/accessibility_embedded_display_test.xml b/tests/accessibilityservice/res/layout/non_default_display_activity.xml
similarity index 100%
rename from tests/accessibilityservice/res/layout/accessibility_embedded_display_test.xml
rename to tests/accessibilityservice/res/layout/non_default_display_activity.xml
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index 03de62d..747ce54 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -178,13 +178,7 @@
<!-- Description of the accessibility service -->
<string name="soft_keyboard_modes_accessibility_service_description">This Accessibility Service was installed for testing purposes. It can be uninstalled by going to Settings > Apps > android.view.accessibilityservice.services and selecting \"Uninstall\".</string>
- <!-- AccessibilityEmbeddedDisplayTest -->
-
- <!-- String title of accessibility embedded display test parent window activity -->
- <string name="accessibility_embedded_display_test_parent_activity">Embedded display test parent</string>
-
- <!-- String title of accessibility embedded display test activity -->
- <string name="accessibility_embedded_display_test_activity">Embedded display test</string>
+ <!-- AccessibilityTextActionTest -->
<!-- String the default label of AccessibilityAction ACTION_IME_ENTER -->
<string name="accessibility_action_ime_enter_label">Enter</string>
@@ -196,4 +190,9 @@
<string name="stub_focus_indicator_service_description">com.android.accessibilityservice.cts.StubFocusIndicatorService</string>
+ <!-- AccessibilityWindowQueryTest and AccessibilityReportingTest -->
+
+ <!-- String title of embedded display activity -->
+ <string name="non_default_display_activity">Non default display activity</string>
+
</resources>
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
deleted file mode 100644
index 03324b8..0000000
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedDisplayTest.java
+++ /dev/null
@@ -1,274 +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.accessibilityservice.cts;
-
-import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangeTypesAndWindowTitle;
-import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
-import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
-import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.supportsMultiDisplay;
-import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
-import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
-import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ADDED;
-import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_BOUNDS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-
-import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
-import android.accessibilityservice.AccessibilityServiceInfo;
-import android.accessibilityservice.cts.activities.AccessibilityTestActivity;
-import android.app.Activity;
-import android.app.ActivityView;
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.platform.test.annotations.Presubmit;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityWindowInfo;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-/**
- * Tests that AccessibilityWindowInfos and AccessibilityNodeInfos from a window on an embedded
- * display that is re-parented to another window are properly populated.
- */
-@RunWith(AndroidJUnit4.class)
-public class AccessibilityEmbeddedDisplayTest {
- private static Instrumentation sInstrumentation;
- private static UiAutomation sUiAutomation;
-
- private EmbeddedDisplayParentActivity mActivity;
- private EmbeddedDisplayActivity mEmbeddedDisplayActivity;
- private ActivityView mActivityView;
- private Context mContext;
-
- private String mParentActivityTitle;
- private String mActivityTitle;
-
- private final ActivityTestRule<EmbeddedDisplayParentActivity> mActivityRule =
- new ActivityTestRule<>(EmbeddedDisplayParentActivity.class, false, false);
-
- private final AccessibilityDumpOnFailureRule mDumpOnFailureRule =
- new AccessibilityDumpOnFailureRule();
-
- @Rule
- public final RuleChain mRuleChain = RuleChain
- .outerRule(mActivityRule)
- .around(mDumpOnFailureRule);
-
- @BeforeClass
- public static void oneTimeSetup() {
- sInstrumentation = InstrumentationRegistry.getInstrumentation();
- sUiAutomation = sInstrumentation.getUiAutomation();
- }
-
- @AfterClass
- public static void postTestTearDown() {
- sUiAutomation.destroy();
- }
-
- @Before
- public void setUp() throws Exception {
- mContext = sInstrumentation.getContext();
- assumeTrue(supportsMultiDisplay(mContext));
-
- mParentActivityTitle = mContext.getString(
- R.string.accessibility_embedded_display_test_parent_activity);
- mActivityTitle = mContext.getString(R.string.accessibility_embedded_display_test_activity);
-
- SystemUtil.runWithShellPermissionIdentity(() -> {
- mActivity = launchActivityAndWaitForItToBeOnscreen(
- sInstrumentation, sUiAutomation, mActivityRule);
- mActivityView = mActivity.getActivityView();
- });
-
- launchActivityInActivityView();
- }
-
- @After
- public void tearDown() throws Exception {
- mEmbeddedDisplayActivity = null;
- if (mActivityView != null) {
- SystemUtil.runWithShellPermissionIdentity(() -> mActivityView.release());
- }
- }
-
- @Presubmit
- @Test
- public void testA11yWindowInfoHasCorrectLayer() {
- final AccessibilityWindowInfo parentActivityWindow =
- findWindowByTitle(sUiAutomation, mParentActivityTitle);
- final AccessibilityWindowInfo activityWindow =
- findWindowByTitle(sUiAutomation, mActivityTitle);
-
- assertNotNull(parentActivityWindow);
- assertNotNull(activityWindow);
- assertTrue(parentActivityWindow.getLayer() > activityWindow.getLayer());
- }
-
- @Presubmit
- @Test
- public void testA11yWindowInfoAndA11yNodeInfoHasCorrectBoundsInScreen() {
- final AccessibilityWindowInfo parentActivityWindow =
- findWindowByTitle(sUiAutomation, mParentActivityTitle);
- final AccessibilityWindowInfo activityWindow =
- findWindowByTitle(sUiAutomation, mActivityTitle);
- final AccessibilityNodeInfo button = findWindowByTitle(sUiAutomation,
- mActivityTitle).getRoot().findAccessibilityNodeInfosByViewId(
- "android.accessibilityservice.cts:id/button").get(0);
-
- assertNotNull(parentActivityWindow);
- assertNotNull(activityWindow);
- assertNotNull(button);
-
- final Rect parentActivityBound = new Rect();
- final Rect activityBound = new Rect();
- final Rect buttonBound = new Rect();
- parentActivityWindow.getBoundsInScreen(parentActivityBound);
- activityWindow.getBoundsInScreen(activityBound);
- button.getBoundsInScreen(buttonBound);
-
- assertTrue("parentActivityBound" + parentActivityBound.toShortString()
- + " doesn't contain activityBound" + activityBound.toShortString(),
- parentActivityBound.contains(activityBound));
- assertTrue("parentActivityBound" + parentActivityBound.toShortString()
- + " doesn't contain buttonBound" + buttonBound.toShortString(),
- parentActivityBound.contains(buttonBound));
- }
-
- @Test
- public void testA11yWindowNotifyWhenResizeWindowInActivityView() throws Exception {
- testA11yWindowNotifyAfterResizeWindowInActivityView();
- }
-
- @Test
- public void testA11yWindowNotifyWhenResizeWindowInActivityViewAfterServiceOffAndOn()
- throws Exception {
- // Clears window access flag to disable the A11y window tracking.
- AccessibilityServiceInfo info = sUiAutomation.getServiceInfo();
- info.flags &= ~AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
- sUiAutomation.setServiceInfo(info);
-
- // Only needs to make sure the windows cannot be accessed for UiAutomation service
- // because other A11y services had been disabled when calling the method, getUiAutomation().
- assertTrue(sUiAutomation.getWindows().isEmpty());
-
- // Sets window access flag to enable the A11y window tracking.
- sUiAutomation.executeAndWaitForEvent(
- () -> sInstrumentation.runOnMainSync(() -> {
- // Make sure we get window events, so we'll know when the window appears
- info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
- sUiAutomation.setServiceInfo(info);
- }),
- filterWindowsChangeTypesAndWindowTitle(sUiAutomation, WINDOWS_CHANGE_ADDED,
- mActivityTitle),
- DEFAULT_TIMEOUT_MS);
-
- testA11yWindowNotifyAfterResizeWindowInActivityView();
- }
-
- private void testA11yWindowNotifyAfterResizeWindowInActivityView() throws Exception {
- final AccessibilityWindowInfo oldActivityWindow =
- findWindowByTitle(sUiAutomation, mActivityTitle);
- final Rect activityBound = new Rect();
- oldActivityWindow.getBoundsInScreen(activityBound);
-
- final int width = activityBound.width() / 2;
- final int height = activityBound.height() / 2;
- sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync(
- () -> mEmbeddedDisplayActivity.getWindow().setLayout(width, height)),
- filterWindowsChangeTypesAndWindowTitle(sUiAutomation, WINDOWS_CHANGE_BOUNDS,
- mActivityTitle),
- DEFAULT_TIMEOUT_MS);
-
- final AccessibilityWindowInfo newActivityWindow =
- findWindowByTitle(sUiAutomation, mActivityTitle);
- newActivityWindow.getBoundsInScreen(activityBound);
-
- assertEquals(height, activityBound.height());
- assertEquals(width, activityBound.width());
- }
-
- private void launchActivityInActivityView() throws Exception {
- final Instrumentation.ActivityMonitor am = sInstrumentation.addMonitor(
- EmbeddedDisplayActivity.class.getName(), null, false);
- final Rect bounds = new Rect();
- sUiAutomation.executeAndWaitForEvent(
- () -> sInstrumentation.runOnMainSync(() -> {
- Intent intent = new Intent(mContext, EmbeddedDisplayActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- SystemUtil.runWithShellPermissionIdentity(
- () -> mActivityView.startActivity(intent));
- }),
- (event) -> {
- // Ensure the target activity is shown
- final AccessibilityWindowInfo window =
- findWindowByTitle(sUiAutomation, mActivityTitle);
- if (window == null) {
- return false;
- }
- window.getBoundsInScreen(bounds);
- return !bounds.isEmpty();
- }, DEFAULT_TIMEOUT_MS);
- mEmbeddedDisplayActivity = (EmbeddedDisplayActivity)
- am.waitForActivityWithTimeout(DEFAULT_TIMEOUT_MS);
- assertNotNull(mEmbeddedDisplayActivity);
- }
-
- public static class EmbeddedDisplayParentActivity extends AccessibilityTestActivity {
- private ActivityView mActivityView;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mActivityView = new ActivityView.Builder(this)
- .setUsePublicVirtualDisplay(true).build();
- setContentView(mActivityView);
- }
-
- ActivityView getActivityView() {
- return mActivityView;
- }
- }
-
- public static class EmbeddedDisplayActivity extends AccessibilityTestActivity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.accessibility_embedded_display_test);
- }
- }
-}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index a4dd8ab..571c0d9 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -21,7 +21,6 @@
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangTypesAndWindowId;
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangeTypesAndWindowTitle;
import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes;
-import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen;
import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.supportsMultiDisplay;
@@ -63,11 +62,11 @@
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity;
+import android.accessibilityservice.cts.activities.NonDefaultDisplayActivity;
import android.app.Activity;
import android.app.ActivityTaskManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
-import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.platform.test.annotations.AppModeFull;
import android.test.suitebuilder.annotation.MediumTest;
@@ -665,7 +664,7 @@
// Launches an activity on virtual display.
mActivityOnVirtualDisplay = launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(
sInstrumentation, sUiAutomation,
- AccessibilityEmbeddedDisplayTest.EmbeddedDisplayActivity.class,
+ NonDefaultDisplayActivity.class,
virtualDisplayId);
// Adds two app panel windows on activity of virtual display.
addTwoAppPanelWindows(mActivityOnVirtualDisplay);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
index 416d5ca..94791de 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java
@@ -47,13 +47,12 @@
import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.cts.activities.AccessibilityWindowReportingActivity;
-import android.accessibilityservice.cts.utils.DisplayUtils;
+import android.accessibilityservice.cts.activities.NonDefaultDisplayActivity;
import android.app.Activity;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.graphics.Rect;
import android.os.SystemClock;
-import android.view.Display;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -265,7 +264,7 @@
final Activity activityOnVirtualDisplay =
launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(sInstrumentation,
sUiAutomation,
- AccessibilityEmbeddedDisplayTest.EmbeddedDisplayActivity.class,
+ NonDefaultDisplayActivity.class,
virtualDisplayId);
final CharSequence activityTitle = getActivityTitle(sInstrumentation,
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NonDefaultDisplayActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NonDefaultDisplayActivity.java
new file mode 100644
index 0000000..11b763f
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NonDefaultDisplayActivity.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.accessibilityservice.cts.activities;
+
+import android.accessibilityservice.cts.R;
+import android.os.Bundle;
+
+/**
+ * Activity used by AccessibilityWindowQueryTest and AccessibilityWindowReportingTest
+ */
+public class NonDefaultDisplayActivity extends AccessibilityTestActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.non_default_display_activity);
+ }
+}
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/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 6cd8bbd..542567d 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -41,6 +41,12 @@
<package android:name="com.android.test.notificationtrampoline.api30" />
</queries>
+ <attribution android:tag="localActivity" android:label="@string/sample_text" />
+ <attribution android:tag="localActivityAlias" android:label="@string/sample_text" />
+ <attribution android:tag="localService" android:label="@string/sample_text" />
+ <attribution android:tag="localProvider" android:label="@string/sample_text" />
+ <attribution android:tag="localReceiver" android:label="@string/sample_text" />
+
<uses-permission android:name="android.app.stubs.permission.TEST_GRANTED"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
@@ -143,6 +149,7 @@
</activity>
<service android:name="android.app.stubs.LocalService"
+ android:attributionTags="localService"
android:exported="true">
<intent-filter>
<action android:name="android.app.stubs.activity.SERVICE_LOCAL"/>
@@ -245,7 +252,9 @@
</activity>
<activity android:name="android.app.stubs.LocalActivity"
- android:multiprocess="true">
+ android:attributionTags="localActivity"
+ android:multiprocess="true"
+ android:exported="true">
<meta-data android:name="android.app.stubs.string"
android:value="foo"/>
<meta-data android:name="android.app.stubs.boolean"
@@ -260,6 +269,11 @@
android:resource="@xml/metadata"/>
</activity>
+ <activity-alias android:name="android.app.stubs.LocalActivityAlias"
+ android:targetActivity="android.app.stubs.LocalActivity"
+ android:attributionTags="localActivityAlias"
+ android:exported="true" />
+
<activity android:name="android.app.stubs.TestedActivity"
android:process=":remoteActivity">
</activity>
@@ -515,6 +529,15 @@
<service android:name=".CloseSystemDialogsTestService"
android:exported="true" />
+
+ <receiver android:name="android.app.stubs.LocalReceiver"
+ android:attributionTags="localReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.app.stubs.activity.RECEIVER_LOCAL"/>
+ </intent-filter>
+ </receiver>
+
</application>
</manifest>
diff --git a/tests/app/app/ProviderAndroidManifest.xml b/tests/app/app/ProviderAndroidManifest.xml
index 2aef16d..d4092e0 100644
--- a/tests/app/app/ProviderAndroidManifest.xml
+++ b/tests/app/app/ProviderAndroidManifest.xml
@@ -27,6 +27,15 @@
android:grantUriPermissions="true"
/>
+ <provider android:name="android.app.stubs.LocalProvider"
+ android:attributionTags="localProvider"
+ android:authorities="android.app.stubs.provider"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.app.stubs.activity.PROVIDER_LOCAL"/>
+ </intent-filter>
+ </provider>
+
</application>
</manifest>
diff --git a/tests/app/app/src/android/app/stubs/LocalActivity.java b/tests/app/app/src/android/app/stubs/LocalActivity.java
index b279d3b..6ab6166 100644
--- a/tests/app/app/src/android/app/stubs/LocalActivity.java
+++ b/tests/app/app/src/android/app/stubs/LocalActivity.java
@@ -16,7 +16,22 @@
package android.app.stubs;
+import android.os.Bundle;
+
+import java.util.concurrent.atomic.AtomicReference;
+
public class LocalActivity extends TestedActivity {
+ private static final AtomicReference<String> sLastAttributionTag = new AtomicReference<>();
+
public LocalActivity() {
}
+
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ sLastAttributionTag.set(getAttributionTag());
+ }
+
+ public static String getAndClearLastAttributionTag() {
+ return sLastAttributionTag.getAndSet(null);
+ }
}
diff --git a/tests/app/app/src/android/app/stubs/LocalProvider.java b/tests/app/app/src/android/app/stubs/LocalProvider.java
new file mode 100644
index 0000000..bb76a76
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/LocalProvider.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.stubs;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+public class LocalProvider extends ContentProvider {
+ private static final AtomicReference<String> sLastAttributionTag = new AtomicReference<>();
+
+ @Override
+ public boolean onCreate() {
+ sLastAttributionTag.set(getContext().getAttributionTag());
+ return true;
+ }
+
+ public static String getAndClearLastAttributionTag() {
+ return sLastAttributionTag.getAndSet(null);
+ }
+
+ @Override
+ public Bundle call(String authority, String method, String arg, Bundle extras) {
+ final String tag;
+ switch (method) {
+ case "activity": tag = LocalActivity.getAndClearLastAttributionTag(); break;
+ case "service": tag = LocalService.getAndClearLastAttributionTag(); break;
+ case "provider": tag = LocalProvider.getAndClearLastAttributionTag(); break;
+ case "receiver": tag = LocalReceiver.getAndClearLastAttributionTag(); break;
+ default: throw new IllegalArgumentException();
+ }
+
+ final Bundle res = new Bundle();
+ res.putString(Intent.EXTRA_TITLE, tag);
+ return res;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/tests/app/app/src/android/app/stubs/LocalReceiver.java b/tests/app/app/src/android/app/stubs/LocalReceiver.java
new file mode 100644
index 0000000..cb581fd
--- /dev/null
+++ b/tests/app/app/src/android/app/stubs/LocalReceiver.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.stubs;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+public class LocalReceiver extends BroadcastReceiver {
+ private static final AtomicReference<String> sLastAttributionTag = new AtomicReference<>();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ sLastAttributionTag.set(context.getAttributionTag());
+ }
+
+ public static String getAndClearLastAttributionTag() {
+ return sLastAttributionTag.getAndSet(null);
+ }
+}
diff --git a/tests/app/app/src/android/app/stubs/LocalService.java b/tests/app/app/src/android/app/stubs/LocalService.java
index f270582..9fe18bd 100644
--- a/tests/app/app/src/android/app/stubs/LocalService.java
+++ b/tests/app/app/src/android/app/stubs/LocalService.java
@@ -27,6 +27,8 @@
import com.android.compatibility.common.util.IBinderParcelable;
+import java.util.concurrent.atomic.AtomicReference;
+
public class LocalService extends Service {
public static final String SERVICE_LOCAL =
"android.app.cts.activity.SERVICE_LOCAL";
@@ -54,6 +56,8 @@
public static Context sServiceContext = null;
+ private static final AtomicReference<String> sLastAttributionTag = new AtomicReference<>();
+
private IBinder mReportObject;
private int mStartCount = 1;
private int mValue = 0;
@@ -109,6 +113,16 @@
}
@Override
+ public void onCreate() {
+ super.onCreate();
+ sLastAttributionTag.set(getAttributionTag());
+ }
+
+ public static String getAndClearLastAttributionTag() {
+ return sLastAttributionTag.getAndSet(null);
+ }
+
+ @Override
public void onStart(Intent intent, int startId) {
mStartId = startId;
if (intent.getExtras() != null) {
diff --git a/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt b/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt
index b890636..3d48dcc 100644
--- a/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt
+++ b/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt
@@ -18,6 +18,8 @@
import android.R
import android.app.stubs.shared.NotificationHostActivity
import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.Color
import android.test.AndroidTestCase
import android.view.View
import android.view.ViewGroup
@@ -65,6 +67,11 @@
}
}
+ protected fun createBitmap(width: Int, height: Int): Bitmap =
+ Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also {
+ it.eraseColor(Color.GRAY)
+ }
+
protected fun makeCustomContent(): RemoteViews {
val customContent = RemoteViews(mContext.packageName, R.layout.simple_list_item_1)
val textId = getAndroidRId("text1")
diff --git a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
index d0afb64..09faefe 100644
--- a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
@@ -491,6 +491,85 @@
}
}
+ @Test
+ public void testUpdateUidProcState() throws Exception {
+ ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
+ PACKAGE_NAME_APP1, 0);
+ WatchUidRunner uid1Watcher = new WatchUidRunner(mInstrumentation, app1Info.uid,
+ WAITFOR_MSEC);
+ ApplicationInfo app2Info = mContext.getPackageManager().getApplicationInfo(
+ PACKAGE_NAME_APP2, 0);
+ WatchUidRunner uid2Watcher = new WatchUidRunner(mInstrumentation, app2Info.uid,
+ WAITFOR_MSEC);
+ ApplicationInfo app3Info = mContext.getPackageManager().getApplicationInfo(
+ PACKAGE_NAME_APP3, 0);
+ WatchUidRunner uid3Watcher = new WatchUidRunner(mInstrumentation, app3Info.uid,
+ WAITFOR_MSEC);
+
+ try {
+ WaitForBroadcast waiter = new WaitForBroadcast(mInstrumentation.getTargetContext());
+ waiter.prepare(ACTION_START_FGS_RESULT);
+
+ enableFgsRestriction(false, true, null);
+
+ // START FGS in APP2.
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_START_FOREGROUND_SERVICE,
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP2, 0, null);
+ // APP2 proc state is 4.
+ uid2Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_FG_SERVICE);
+ waiter.doWait(WAITFOR_MSEC);
+
+ // APP2 binds to APP1.
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_BIND_SERVICE,
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, Context.BIND_INCLUDE_CAPABILITIES, null);
+ // APP1 gets proc state 4.
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE,
+ WatchUidRunner.STATE_FG_SERVICE);
+
+ // Start activity in APP3, this put APP3 in TOP state.
+ allowBgActivityStart(PACKAGE_NAME_APP3, true);
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_START_ACTIVITY,
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP3, 0, null);
+ // APP3 gets proc state 2.
+ uid3Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP);
+
+ // APP3 repeatedly bind/unbind with APP2, observer APP1 proc state change.
+ // Observe updateUidProcState() call latency.
+ for (int i = 0; i < 10; ++i) {
+ // APP3 bind to APP2
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_BIND_SERVICE,
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP2, Context.BIND_INCLUDE_CAPABILITIES,
+ null);
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_BOUND_TOP);
+
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP2, 0, null);
+ uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
+ }
+
+ // unbind service.
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_STOP_ACTIVITY,
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP3, 0, null);
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+ PACKAGE_NAME_APP3, PACKAGE_NAME_APP2, 0, null);
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_UNBIND_SERVICE,
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, 0, null);
+ CommandReceiver.sendCommand(mContext,
+ CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE,
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP2, 0, null);
+
+ } finally {
+ uid1Watcher.finish();
+ uid2Watcher.finish();
+ uid3Watcher.finish();
+ allowBgActivityStart(PACKAGE_NAME_APP3, false);
+ }
+ }
+
/**
* Test FGS background startForeground() restriction, use DeviceConfig to turn on restriction.
* @throws Exception
diff --git a/tests/app/src/android/app/cts/AttributionTagsTest.java b/tests/app/src/android/app/cts/AttributionTagsTest.java
new file mode 100644
index 0000000..4fd1598
--- /dev/null
+++ b/tests/app/src/android/app/cts/AttributionTagsTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.stubs.LocalActivity;
+import android.app.stubs.LocalProvider;
+import android.app.stubs.LocalReceiver;
+import android.app.stubs.LocalService;
+import android.content.ComponentName;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Verify that {@code android:attributionTags} manifest attributes are honored
+ * when associated components are instantiated.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AttributionTagsTest {
+ private static final String TAG = "AttributionTagsTest";
+ private static final String TEST_AUTHORITY = "android.app.stubs.provider";
+
+ private Context mContext;
+ private Context mTargetContext;
+ private PackageManager mPackageManager;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ mTargetContext = InstrumentationRegistry.getTargetContext();
+ mPackageManager = mContext.getPackageManager();
+ }
+
+ @Test
+ public void testActivity() throws Exception {
+ // Verify manifest parsing
+ final Intent intent = new Intent(mTargetContext, LocalActivity.class);
+ assertEquals("localActivity",
+ mPackageManager.getActivityInfo(intent.getComponent(), 0).attributionTags[0]);
+
+ // Verify live behavior
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ assertAttributionTag("activity", "localActivity");
+ }
+
+ @Test
+ public void testActivityAlias() throws Exception {
+ // Verify manifest parsing
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName(mTargetContext.getPackageName(),
+ "android.app.stubs.LocalActivityAlias"));
+ assertEquals("localActivityAlias",
+ mPackageManager.getActivityInfo(intent.getComponent(), 0).attributionTags[0]);
+
+ // Verify live behavior
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ assertAttributionTag("activity", "localActivityAlias");
+ }
+
+ @Test
+ public void testService() throws Exception {
+ // Verify manifest parsing
+ final Intent intent = new Intent(mTargetContext, LocalService.class);
+ assertEquals("localService",
+ mPackageManager.getServiceInfo(intent.getComponent(), 0).attributionTags[0]);
+
+ // Verify live behavior
+ mContext.startService(intent);
+ assertAttributionTag("service", "localService");
+ }
+
+ @Test
+ public void testProvider() throws Exception {
+ // Verify manifest parsing
+ final Intent intent = new Intent(mTargetContext, LocalProvider.class);
+ assertEquals("localProvider",
+ mPackageManager.getProviderInfo(intent.getComponent(), 0).attributionTags[0]);
+
+ // Probing for the attribution tag will itself spin up the provider as a
+ // side-effect, so we have no special pre-work needed here
+ assertAttributionTag("provider", "localProvider");
+ }
+
+ @Test
+ public void testReceiver() throws Exception {
+ // Verify manifest parsing
+ final Intent intent = new Intent(mTargetContext, LocalReceiver.class);
+ assertEquals("localReceiver",
+ mPackageManager.getReceiverInfo(intent.getComponent(), 0).attributionTags[0]);
+
+ // Verify live behavior
+ mContext.sendBroadcast(intent);
+ assertAttributionTag("receiver", "localReceiver");
+ }
+
+ private void assertAttributionTag(String method, String expected) throws Exception {
+ try (ContentProviderClient client = mContext.getContentResolver()
+ .acquireContentProviderClient(TEST_AUTHORITY)) {
+ for (int i = 0; i < 10; i++) {
+ final Bundle res = client.call(TEST_AUTHORITY, method, null, null);
+ final String actual = res.getString(Intent.EXTRA_TITLE);
+ if (actual == null) {
+ Log.v(TAG, "No attribution tag yet; waiting...");
+ SystemClock.sleep(500);
+ continue;
+ } else {
+ assertEquals(expected, actual);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 1a0e003..769e191 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -131,6 +131,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenPolicy;
+import android.support.test.uiautomator.UiDevice;
import android.test.AndroidTestCase;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -219,6 +220,7 @@
@Nullable
private List<String> mPreviousDefaultBrowser;
+ private Instrumentation mInstrumentation;
@Override
protected void setUp() throws Exception {
@@ -245,11 +247,10 @@
// ensure listener access isn't allowed before test runs (other tests could put
// TestListener in an unexpected state)
toggleListenerAccess(false);
- toggleNotificationPolicyAccess(mContext.getPackageName(),
- InstrumentationRegistry.getInstrumentation(), true);
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ toggleNotificationPolicyAccess(mContext.getPackageName(), mInstrumentation, true);
mNotificationManager.setInterruptionFilter(INTERRUPTION_FILTER_ALL);
- toggleNotificationPolicyAccess(mContext.getPackageName(),
- InstrumentationRegistry.getInstrumentation(), false);
+ toggleNotificationPolicyAccess(mContext.getPackageName(), mInstrumentation, false);
// This setting is forced on / off for certain tests, save it & restore what's on the
// device after tests are run
@@ -283,12 +284,10 @@
}
// Unsuspend package if it was suspended in the test
- suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(),
- false);
+ suspendPackage(mContext.getPackageName(), mInstrumentation, false);
toggleListenerAccess(false);
- toggleNotificationPolicyAccess(mContext.getPackageName(),
- InstrumentationRegistry.getInstrumentation(), false);
+ toggleNotificationPolicyAccess(mContext.getPackageName(), mInstrumentation, false);
List<NotificationChannelGroup> groups = mNotificationManager.getNotificationChannelGroups();
// Delete all groups.
@@ -964,6 +963,14 @@
restored.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
+ /**
+ * Previous tests could have started activities within the grace period, so go home to avoid
+ * allowing background activity starts due to this exemption.
+ */
+ private void deactivateGracePeriod() {
+ UiDevice.getInstance(mInstrumentation).pressHome();
+ }
+
public void testConsolidatedNotificationPolicy() throws Exception {
final int originalFilter = mNotificationManager.getCurrentInterruptionFilter();
Policy origPolicy = mNotificationManager.getNotificationPolicy();
@@ -4219,6 +4226,7 @@
}
public void testActivityStartOnBroadcastTrampoline_isBlocked() throws Exception {
+ deactivateGracePeriod();
setUpNotifListener();
mListener.addTestPackage(TRAMPOLINE_APP);
EventCallback callback = new EventCallback();
@@ -4238,6 +4246,7 @@
}
public void testActivityStartOnServiceTrampoline_isBlocked() throws Exception {
+ deactivateGracePeriod();
setUpNotifListener();
mListener.addTestPackage(TRAMPOLINE_APP);
EventCallback callback = new EventCallback();
@@ -4257,6 +4266,7 @@
}
public void testActivityStartOnBroadcastTrampoline_whenApi30_isAllowed() throws Exception {
+ deactivateGracePeriod();
setUpNotifListener();
mListener.addTestPackage(TRAMPOLINE_APP_API_30);
EventCallback callback = new EventCallback();
@@ -4276,6 +4286,7 @@
}
public void testActivityStartOnServiceTrampoline_whenApi30_isAllowed() throws Exception {
+ deactivateGracePeriod();
setUpNotifListener();
mListener.addTestPackage(TRAMPOLINE_APP_API_30);
EventCallback callback = new EventCallback();
@@ -4296,6 +4307,7 @@
public void testActivityStartOnBroadcastTrampoline_whenDefaultBrowser_isAllowed()
throws Exception {
+ deactivateGracePeriod();
setDefaultBrowser(TRAMPOLINE_APP);
setUpNotifListener();
mListener.addTestPackage(TRAMPOLINE_APP);
@@ -4317,6 +4329,7 @@
public void testActivityStartOnServiceTrampoline_whenDefaultBrowser_isAllowed()
throws Exception {
+ deactivateGracePeriod();
setDefaultBrowser(TRAMPOLINE_APP);
setUpNotifListener();
mListener.addTestPackage(TRAMPOLINE_APP);
diff --git a/tests/app/src/android/app/cts/NotificationTemplateTest.kt b/tests/app/src/android/app/cts/NotificationTemplateTest.kt
index 0917767..338f431 100644
--- a/tests/app/src/android/app/cts/NotificationTemplateTest.kt
+++ b/tests/app/src/android/app/cts/NotificationTemplateTest.kt
@@ -38,7 +38,7 @@
class NotificationTemplateTest : NotificationTemplateTestBase() {
fun testWideIcon_inCollapsedState_cappedTo16By9() {
- val icon = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(200, 100)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -53,7 +53,7 @@
}
fun testWideIcon_inCollapsedState_canShowExact4By3() {
- val icon = Bitmap.createBitmap(400, 300, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(400, 300)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -84,7 +84,7 @@
}
fun testWideIcon_inCollapsedState_neverNarrowerThanSquare() {
- val icon = Bitmap.createBitmap(200, 300, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(200, 300)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -97,7 +97,7 @@
}
fun testWideIcon_inBigBaseState_cappedTo16By9() {
- val icon = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(200, 100)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -112,7 +112,7 @@
}
fun testWideIcon_inBigBaseState_canShowExact4By3() {
- val icon = Bitmap.createBitmap(400, 300, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(400, 300)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -127,7 +127,7 @@
}
fun testWideIcon_inBigBaseState_neverNarrowerThanSquare() {
- val icon = Bitmap.createBitmap(200, 300, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(200, 300)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -140,8 +140,8 @@
}
fun testWideIcon_inBigPicture_cappedTo16By9() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
- val icon = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
+ val icon = createBitmap(200, 100)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -157,8 +157,8 @@
}
fun testWideIcon_inBigPicture_canShowExact4By3() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
- val icon = Bitmap.createBitmap(400, 300, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
+ val icon = createBitmap(400, 300)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -174,8 +174,8 @@
}
fun testWideIcon_inBigPicture_neverNarrowerThanSquare() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
- val icon = Bitmap.createBitmap(200, 300, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
+ val icon = createBitmap(200, 300)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -189,7 +189,7 @@
}
fun testWideIcon_inBigText_cappedTo16By9() {
- val icon = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(200, 100)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -205,7 +205,7 @@
}
fun testWideIcon_inBigText_canShowExact4By3() {
- val icon = Bitmap.createBitmap(400, 300, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(400, 300)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -221,7 +221,7 @@
}
fun testWideIcon_inBigText_neverNarrowerThanSquare() {
- val icon = Bitmap.createBitmap(200, 300, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(200, 300)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -235,7 +235,7 @@
}
fun testBigPictureStyle_populatesExtrasCompatibly() {
- val bitmap = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
+ val bitmap = createBitmap(40, 30)
val uri = Uri.parse("content://android.app.stubs.assets/picture_400_by_300.png")
val iconWithUri = Icon.createWithContentUri(uri)
val iconWithBitmap = Icon.createWithBitmap(bitmap)
@@ -303,7 +303,7 @@
}
fun testPromoteBigPicture_withoutLargeIcon() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -325,8 +325,8 @@
}
fun testPromoteBigPicture_withLargeIcon() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
- val icon = Bitmap.createBitmap(80, 65, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
+ val icon = createBitmap(80, 65)
val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -354,8 +354,8 @@
}
fun testPromoteBigPicture_withBigLargeIcon() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
- val bigIcon = Bitmap.createBitmap(80, 75, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
+ val bigIcon = createBitmap(80, 75)
val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -761,7 +761,7 @@
private fun View.bgContainsColor(@ColorInt color: Int): Boolean {
val background = background ?: return false
- val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ val bitmap = createBitmap(width, height)
val canvas = Canvas(bitmap)
background.draw(canvas)
val maskedColor = color and 0x00ffffff
diff --git a/tests/appsearch/src/com/android/cts/appsearch/AppSearchSchemaMigrationCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/app/AppSearchSchemaMigrationCtsTest.java
similarity index 92%
rename from tests/appsearch/src/com/android/cts/appsearch/AppSearchSchemaMigrationCtsTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/app/AppSearchSchemaMigrationCtsTest.java
index 916375b..2c6e032 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/AppSearchSchemaMigrationCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/app/AppSearchSchemaMigrationCtsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open 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,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchSessionShim;
diff --git a/tests/appsearch/src/com/android/cts/appsearch/AppSearchSessionCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/app/AppSearchSessionCtsTest.java
similarity index 82%
rename from tests/appsearch/src/com/android/cts/appsearch/AppSearchSessionCtsTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/app/AppSearchSessionCtsTest.java
index 54c867d..559653cf 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/AppSearchSessionCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/app/AppSearchSessionCtsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open 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,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchSessionShim;
+import android.content.Context;
import androidx.annotation.NonNull;
+import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.testing.AppSearchSessionShimImpl;
@@ -36,7 +38,8 @@
@Override
protected ListenableFuture<AppSearchSessionShim> createSearchSession(
@NonNull String dbName, @NonNull ExecutorService executor) {
- return AppSearchSessionShimImpl.createSearchSession(
+ Context context = ApplicationProvider.getApplicationContext();
+ return AppSearchSessionShimImpl.createSearchSession(context,
new AppSearchManager.SearchContext.Builder(dbName).build(), executor);
}
}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/AppSearchSessionPlatformCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/app/AppSearchSessionPlatformCtsTest.java
similarity index 99%
rename from tests/appsearch/src/com/android/cts/appsearch/AppSearchSessionPlatformCtsTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/app/AppSearchSessionPlatformCtsTest.java
index 3569cba..9f42c74 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/AppSearchSessionPlatformCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/app/AppSearchSessionPlatformCtsTest.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+package android.app.appsearch.cts.app;
+
import static android.os.storage.StorageManager.UUID_DEFAULT;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/appsearch/src/com/android/cts/appsearch/GlobalSearchSessionCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionCtsTest.java
similarity index 93%
rename from tests/appsearch/src/com/android/cts/appsearch/GlobalSearchSessionCtsTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionCtsTest.java
index 7df7a51..ecdc9c0 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/GlobalSearchSessionCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionCtsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open 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,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchSessionShim;
diff --git a/tests/appsearch/src/com/android/cts/appsearch/GlobalSearchSessionPlatformCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java
similarity index 99%
rename from tests/appsearch/src/com/android/cts/appsearch/GlobalSearchSessionPlatformCtsTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java
index ee71acf..d8fbc84 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/GlobalSearchSessionPlatformCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/app/GlobalSearchSessionPlatformCtsTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import static com.android.server.appsearch.testing.AppSearchTestUtils.checkIsBatchResultSuccess;
@@ -58,7 +58,7 @@
import java.util.concurrent.TimeUnit;
/**
- * This doesn't extend the {@link android.app.appsearch.cts.GlobalSearchSessionCtsTestBase} since
+ * This doesn't extend {@link android.app.appsearch.cts.app.GlobalSearchSessionCtsTestBase} since
* these test cases can't be run in a non-platform environment.
*/
@AppModeFull(reason = "Can't bind to helper apps from instant mode")
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/SearchSpecCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/SearchSpecCtsTest.java
deleted file mode 100644
index 5eb18b3..0000000
--- a/tests/appsearch/src/com/android/cts/appsearch/external/SearchSpecCtsTest.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.appsearch.cts;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.appsearch.SearchSpec;
-
-import org.junit.Test;
-
-public class SearchSpecCtsTest {
- @Test
- public void testBuildSearchSpecWithoutTermMatch() {
- SearchSpec searchSpec = new SearchSpec.Builder().addFilterSchemas("testSchemaType").build();
- assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
- }
-
- @Test
- public void testBuildSearchSpec() {
- SearchSpec searchSpec =
- new SearchSpec.Builder()
- .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .addFilterNamespaces("namespace1", "namespace2")
- .addFilterSchemas("schemaTypes1", "schemaTypes2")
- .addFilterPackageNames("package1", "package2")
- .setSnippetCount(5)
- .setSnippetCountPerProperty(10)
- .setMaxSnippetSize(15)
- .setResultCountPerPage(42)
- .setOrder(SearchSpec.ORDER_ASCENDING)
- .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE)
- .setResultGrouping(
- SearchSpec.GROUPING_TYPE_PER_NAMESPACE
- | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
- /*limit=*/ 37)
- .build();
-
- assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
- assertThat(searchSpec.getFilterNamespaces())
- .containsExactly("namespace1", "namespace2")
- .inOrder();
- assertThat(searchSpec.getFilterSchemas())
- .containsExactly("schemaTypes1", "schemaTypes2")
- .inOrder();
- assertThat(searchSpec.getFilterPackageNames())
- .containsExactly("package1", "package2")
- .inOrder();
- assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
- assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
- assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15);
- assertThat(searchSpec.getResultCountPerPage()).isEqualTo(42);
- assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING);
- assertThat(searchSpec.getRankingStrategy())
- .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
- assertThat(searchSpec.getResultGroupingTypeFlags())
- .isEqualTo(
- SearchSpec.GROUPING_TYPE_PER_NAMESPACE
- | SearchSpec.GROUPING_TYPE_PER_PACKAGE);
- assertThat(searchSpec.getResultGroupingLimit()).isEqualTo(37);
- }
-}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/SetSchemaResponseCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/SetSchemaResponseCtsTest.java
deleted file mode 100644
index 4632fd7..0000000
--- a/tests/appsearch/src/com/android/cts/appsearch/external/SetSchemaResponseCtsTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.appsearch.cts;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.appsearch.AppSearchResult;
-import android.app.appsearch.SetSchemaResponse;
-
-import org.junit.Test;
-
-public class SetSchemaResponseCtsTest {
- @Test
- public void testRebuild() {
- SetSchemaResponse.MigrationFailure failure1 =
- new SetSchemaResponse.MigrationFailure(
- "namespace",
- "failure1",
- "schemaType",
- AppSearchResult.newFailedResult(
- AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage"));
- SetSchemaResponse.MigrationFailure failure2 =
- new SetSchemaResponse.MigrationFailure(
- "namespace",
- "failure2",
- "schemaType",
- AppSearchResult.newFailedResult(
- AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage"));
-
- SetSchemaResponse original =
- new SetSchemaResponse.Builder()
- .addDeletedType("delete1")
- .addIncompatibleType("incompatible1")
- .addMigratedType("migrated1")
- .addMigrationFailure(failure1)
- .build();
- assertThat(original.getDeletedTypes()).containsExactly("delete1");
- assertThat(original.getIncompatibleTypes()).containsExactly("incompatible1");
- assertThat(original.getMigratedTypes()).containsExactly("migrated1");
- assertThat(original.getMigrationFailures()).containsExactly(failure1);
-
- SetSchemaResponse rebuild =
- original.toBuilder()
- .addDeletedType("delete2")
- .addIncompatibleType("incompatible2")
- .addMigratedType("migrated2")
- .addMigrationFailure(failure2)
- .build();
-
- // rebuild won't effect the original object
- assertThat(original.getDeletedTypes()).containsExactly("delete1");
- assertThat(original.getIncompatibleTypes()).containsExactly("incompatible1");
- assertThat(original.getMigratedTypes()).containsExactly("migrated1");
- assertThat(original.getMigrationFailures()).containsExactly(failure1);
-
- assertThat(rebuild.getDeletedTypes()).containsExactly("delete1", "delete2");
- assertThat(rebuild.getIncompatibleTypes())
- .containsExactly("incompatible1", "incompatible2");
- assertThat(rebuild.getMigratedTypes()).containsExactly("migrated1", "migrated2");
- assertThat(rebuild.getMigrationFailures()).containsExactly(failure1, failure2);
- }
-}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchBatchResultCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchBatchResultCtsTest.java
similarity index 99%
rename from tests/appsearch/src/com/android/cts/appsearch/external/AppSearchBatchResultCtsTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchBatchResultCtsTest.java
index 0644ed4..aab7098 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchBatchResultCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchBatchResultCtsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchMigratorTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchMigratorTest.java
similarity index 99%
rename from tests/appsearch/src/com/android/cts/appsearch/external/AppSearchMigratorTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchMigratorTest.java
index 74af26d..f847769 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchMigratorTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchMigratorTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchResultCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchResultCtsTest.java
similarity index 98%
rename from tests/appsearch/src/com/android/cts/appsearch/external/AppSearchResultCtsTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchResultCtsTest.java
index 9c34b17..2691ecf 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchResultCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchResultCtsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSchemaCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaCtsTest.java
similarity index 68%
rename from tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSchemaCtsTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaCtsTest.java
index 191438c..65b006b 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSchemaCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaCtsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import static com.google.common.truth.Truth.assertThat;
@@ -25,8 +25,12 @@
import android.app.appsearch.AppSearchSchema.StringPropertyConfig;
import android.app.appsearch.exceptions.IllegalSchemaException;
+import com.android.server.appsearch.testing.AppSearchEmail;
+
import org.junit.Test;
+import java.util.List;
+
public class AppSearchSchemaCtsTest {
@Test
public void testInvalidEnums() {
@@ -187,4 +191,82 @@
assertThat(schema1).isNotEqualTo(schema2);
assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode());
}
+
+ @Test
+ public void testPropertyConfig() {
+ AppSearchSchema schema =
+ new AppSearchSchema.Builder("Test")
+ .addProperty(
+ new StringPropertyConfig.Builder("string")
+ .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+ .setIndexingType(
+ StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .addProperty(
+ new AppSearchSchema.LongPropertyConfig.Builder("long")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .addProperty(
+ new AppSearchSchema.DoublePropertyConfig.Builder("double")
+ .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+ .build())
+ .addProperty(
+ new AppSearchSchema.BooleanPropertyConfig.Builder("boolean")
+ .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+ .build())
+ .addProperty(
+ new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .addProperty(
+ new AppSearchSchema.DocumentPropertyConfig.Builder(
+ "document", AppSearchEmail.SCHEMA_TYPE)
+ .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+ .setShouldIndexNestedProperties(true)
+ .build())
+ .build();
+
+ assertThat(schema.getSchemaType()).isEqualTo("Test");
+ List<PropertyConfig> properties = schema.getProperties();
+ assertThat(properties).hasSize(6);
+
+ assertThat(properties.get(0).getName()).isEqualTo("string");
+ assertThat(properties.get(0).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_REQUIRED);
+ assertThat(((StringPropertyConfig) properties.get(0)).getIndexingType())
+ .isEqualTo(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS);
+ assertThat(((StringPropertyConfig) properties.get(0)).getTokenizerType())
+ .isEqualTo(StringPropertyConfig.TOKENIZER_TYPE_PLAIN);
+
+ assertThat(properties.get(1).getName()).isEqualTo("long");
+ assertThat(properties.get(1).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
+ assertThat(properties.get(1)).isInstanceOf(AppSearchSchema.LongPropertyConfig.class);
+
+ assertThat(properties.get(2).getName()).isEqualTo("double");
+ assertThat(properties.get(2).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_REPEATED);
+ assertThat(properties.get(2)).isInstanceOf(AppSearchSchema.DoublePropertyConfig.class);
+
+ assertThat(properties.get(3).getName()).isEqualTo("boolean");
+ assertThat(properties.get(3).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_REQUIRED);
+ assertThat(properties.get(3)).isInstanceOf(AppSearchSchema.BooleanPropertyConfig.class);
+
+ assertThat(properties.get(4).getName()).isEqualTo("bytes");
+ assertThat(properties.get(4).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
+ assertThat(properties.get(4)).isInstanceOf(AppSearchSchema.BytesPropertyConfig.class);
+
+ assertThat(properties.get(5).getName()).isEqualTo("document");
+ assertThat(properties.get(5).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_REPEATED);
+ assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(5)).getSchemaType())
+ .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
+ assertThat(
+ ((AppSearchSchema.DocumentPropertyConfig) properties.get(5))
+ .shouldIndexNestedProperties())
+ .isEqualTo(true);
+ }
}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSchemaMigrationCtsTestBase.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaMigrationCtsTestBase.java
similarity index 98%
rename from tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSchemaMigrationCtsTestBase.java
rename to tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaMigrationCtsTestBase.java
index d932e0d..830cc25 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSchemaMigrationCtsTestBase.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaMigrationCtsTestBase.java
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
+
+import static android.app.appsearch.AppSearchResult.RESULT_NOT_FOUND;
import static com.android.server.appsearch.testing.AppSearchTestUtils.checkIsBatchResultSuccess;
import static com.android.server.appsearch.testing.AppSearchTestUtils.convertSearchResultsToDocuments;
@@ -26,6 +28,7 @@
import android.annotation.NonNull;
import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.AppSearchSessionShim;
import android.app.appsearch.GenericDocument;
@@ -538,6 +541,12 @@
assertThat(migrationFailure.getNamespace()).isEqualTo("namespace");
assertThat(migrationFailure.getSchemaType()).isEqualTo("testSchema");
assertThat(migrationFailure.getDocumentId()).isEqualTo("id2");
+
+ AppSearchResult<Void> actualResult = migrationFailure.getAppSearchResult();
+ assertThat(actualResult.isSuccess()).isFalse();
+ assertThat(actualResult.getResultCode()).isEqualTo(RESULT_NOT_FOUND);
+ assertThat(actualResult.getErrorMessage())
+ .contains("Property config 'to' not found for key");
}
@Test
@@ -1202,7 +1211,7 @@
AppSearchSchema sourceSchema =
new AppSearchSchema.Builder("Person")
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("Age")
+ new AppSearchSchema.LongPropertyConfig.Builder("Age")
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
.build())
@@ -1273,7 +1282,7 @@
AppSearchSchema adultSchema =
new AppSearchSchema.Builder("Adult")
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("Age")
+ new AppSearchSchema.LongPropertyConfig.Builder("Age")
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
.build())
@@ -1281,7 +1290,7 @@
AppSearchSchema childSchema =
new AppSearchSchema.Builder("Child")
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("Age")
+ new AppSearchSchema.LongPropertyConfig.Builder("Age")
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
.build())
@@ -1324,7 +1333,7 @@
AppSearchSchema sourceSchemaA =
new AppSearchSchema.Builder("schemaA")
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("num")
+ new AppSearchSchema.LongPropertyConfig.Builder("num")
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
.build())
@@ -1332,7 +1341,7 @@
AppSearchSchema sourceSchemaB =
new AppSearchSchema.Builder("schemaB")
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("num")
+ new AppSearchSchema.LongPropertyConfig.Builder("num")
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
.build())
@@ -1367,7 +1376,7 @@
AppSearchSchema destinationSchemaB =
new AppSearchSchema.Builder("schemaB")
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("numNewProperty")
+ new AppSearchSchema.LongPropertyConfig.Builder("numNewProperty")
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
.build())
@@ -1375,7 +1384,7 @@
AppSearchSchema destinationSchemaC =
new AppSearchSchema.Builder("schemaC")
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("num")
+ new AppSearchSchema.LongPropertyConfig.Builder("num")
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
.build())
@@ -1383,7 +1392,7 @@
AppSearchSchema destinationSchemaD =
new AppSearchSchema.Builder("schemaD")
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("num")
+ new AppSearchSchema.LongPropertyConfig.Builder("num")
.setCardinality(
AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
.build())
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSessionCtsTestBase.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java
similarity index 91%
rename from tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSessionCtsTestBase.java
rename to tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java
index e180218..cfda19f 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/AppSearchSessionCtsTestBase.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import static android.app.appsearch.AppSearchResult.RESULT_INVALID_SCHEMA;
+import static android.app.appsearch.AppSearchResult.RESULT_NOT_FOUND;
import static com.android.server.appsearch.testing.AppSearchTestUtils.checkIsBatchResultSuccess;
import static com.android.server.appsearch.testing.AppSearchTestUtils.convertSearchResultsToDocuments;
@@ -234,6 +235,101 @@
}
@Test
+ public void testGetSchema_allPropertyTypes() throws Exception {
+ AppSearchSchema inSchema =
+ new AppSearchSchema.Builder("Test")
+ .addProperty(
+ new StringPropertyConfig.Builder("string")
+ .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+ .setIndexingType(
+ StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build())
+ .addProperty(
+ new AppSearchSchema.LongPropertyConfig.Builder("long")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .addProperty(
+ new AppSearchSchema.DoublePropertyConfig.Builder("double")
+ .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+ .build())
+ .addProperty(
+ new AppSearchSchema.BooleanPropertyConfig.Builder("boolean")
+ .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+ .build())
+ .addProperty(
+ new AppSearchSchema.BytesPropertyConfig.Builder("bytes")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .addProperty(
+ new AppSearchSchema.DocumentPropertyConfig.Builder(
+ "document", AppSearchEmail.SCHEMA_TYPE)
+ .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
+ .setShouldIndexNestedProperties(true)
+ .build())
+ .build();
+
+ // Add it to AppSearch and then obtain it again
+ mDb1.setSchema(
+ new SetSchemaRequest.Builder()
+ .addSchemas(inSchema, AppSearchEmail.SCHEMA)
+ .build())
+ .get();
+ GetSchemaResponse response = mDb1.getSchema().get();
+ List<AppSearchSchema> schemas = new ArrayList<>(response.getSchemas());
+ assertThat(schemas).containsExactly(inSchema, AppSearchEmail.SCHEMA);
+ AppSearchSchema outSchema;
+ if (schemas.get(0).getSchemaType().equals("Test")) {
+ outSchema = schemas.get(0);
+ } else {
+ outSchema = schemas.get(1);
+ }
+ assertThat(outSchema.getSchemaType()).isEqualTo("Test");
+ assertThat(outSchema).isNotSameInstanceAs(inSchema);
+
+ List<PropertyConfig> properties = outSchema.getProperties();
+ assertThat(properties).hasSize(6);
+
+ assertThat(properties.get(0).getName()).isEqualTo("string");
+ assertThat(properties.get(0).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_REQUIRED);
+ assertThat(((StringPropertyConfig) properties.get(0)).getIndexingType())
+ .isEqualTo(StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS);
+ assertThat(((StringPropertyConfig) properties.get(0)).getTokenizerType())
+ .isEqualTo(StringPropertyConfig.TOKENIZER_TYPE_PLAIN);
+
+ assertThat(properties.get(1).getName()).isEqualTo("long");
+ assertThat(properties.get(1).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
+ assertThat(properties.get(1)).isInstanceOf(AppSearchSchema.LongPropertyConfig.class);
+
+ assertThat(properties.get(2).getName()).isEqualTo("double");
+ assertThat(properties.get(2).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_REPEATED);
+ assertThat(properties.get(2)).isInstanceOf(AppSearchSchema.DoublePropertyConfig.class);
+
+ assertThat(properties.get(3).getName()).isEqualTo("boolean");
+ assertThat(properties.get(3).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_REQUIRED);
+ assertThat(properties.get(3)).isInstanceOf(AppSearchSchema.BooleanPropertyConfig.class);
+
+ assertThat(properties.get(4).getName()).isEqualTo("bytes");
+ assertThat(properties.get(4).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_OPTIONAL);
+ assertThat(properties.get(4)).isInstanceOf(AppSearchSchema.BytesPropertyConfig.class);
+
+ assertThat(properties.get(5).getName()).isEqualTo("document");
+ assertThat(properties.get(5).getCardinality())
+ .isEqualTo(PropertyConfig.CARDINALITY_REPEATED);
+ assertThat(((AppSearchSchema.DocumentPropertyConfig) properties.get(5)).getSchemaType())
+ .isEqualTo(AppSearchEmail.SCHEMA_TYPE);
+ assertThat(
+ ((AppSearchSchema.DocumentPropertyConfig) properties.get(5))
+ .shouldIndexNestedProperties())
+ .isEqualTo(true);
+ }
+
+ @Test
public void testGetNamespaces() throws Exception {
// Schema registration
mDb1.setSchema(new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
@@ -383,7 +479,7 @@
AppSearchSchema giftSchema =
new AppSearchSchema.Builder("Gift")
.addProperty(
- new AppSearchSchema.Int64PropertyConfig.Builder("price")
+ new AppSearchSchema.LongPropertyConfig.Builder("price")
.setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
.build())
.build();
@@ -1920,7 +2016,6 @@
@Test
public void testSnippet() throws Exception {
// Schema registration
- // TODO(tytytyww) add property for long and double.
AppSearchSchema genericSchema =
new AppSearchSchema.Builder("Generic")
.addProperty(
@@ -1975,6 +2070,57 @@
}
@Test
+ public void testCJKSnippet() throws Exception {
+ // Schema registration
+ AppSearchSchema genericSchema =
+ new AppSearchSchema.Builder("Generic")
+ .addProperty(
+ new StringPropertyConfig.Builder("subject")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(
+ StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .build())
+ .build();
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchemas(genericSchema).build()).get();
+
+ String japanese =
+ "差し出されたのが今日ランドセルでした普通の子であれば満面の笑みで俺を言うでしょうしかし私は赤いランド"
+ + "セルを見て笑うことができませんでしたどうしたのと心配そうな仕事ガラスながら渋い顔する私書いたこと言"
+ + "うんじゃないのカードとなる声を聞きたい私は目から涙をこぼしながらおじいちゃんの近くにかけおり頭をポ"
+ + "ンポンと叩きピンクが良かったんだもん";
+ // Index a document
+ GenericDocument document =
+ new GenericDocument.Builder<>("namespace", "id", "Generic")
+ .setPropertyString("subject", japanese)
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.put(new PutDocumentsRequest.Builder().addGenericDocuments(document).build()));
+
+ // Query for the document
+ SearchResultsShim searchResults =
+ mDb1.search(
+ "は",
+ new SearchSpec.Builder()
+ .addFilterSchemas("Generic")
+ .setSnippetCount(1)
+ .setSnippetCountPerProperty(1)
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .build());
+ List<SearchResult> results = searchResults.getNextPage().get();
+ assertThat(results).hasSize(1);
+
+ List<SearchResult.MatchInfo> matchInfos = results.get(0).getMatchInfos();
+ assertThat(matchInfos).isNotNull();
+ assertThat(matchInfos).hasSize(1);
+ SearchResult.MatchInfo matchInfo = matchInfos.get(0);
+ assertThat(matchInfo.getFullText()).isEqualTo(japanese);
+ assertThat(matchInfo.getExactMatchRange())
+ .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 44, /*upper=*/ 45));
+ assertThat(matchInfo.getExactMatch()).isEqualTo("は");
+ }
+
+ @Test
public void testRemove() throws Exception {
// Schema registration
mDb1.setSchema(new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
@@ -2035,6 +2181,58 @@
}
@Test
+ public void testRemove_multipleIds() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index documents
+ AppSearchEmail email1 =
+ new AppSearchEmail.Builder("namespace", "id1")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example")
+ .setBody("This is the body of the testPut email")
+ .build();
+ AppSearchEmail email2 =
+ new AppSearchEmail.Builder("namespace", "id2")
+ .setFrom("from@example.com")
+ .setTo("to1@example.com", "to2@example.com")
+ .setSubject("testPut example 2")
+ .setBody("This is the body of the testPut second email")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.put(
+ new PutDocumentsRequest.Builder()
+ .addGenericDocuments(email1, email2)
+ .build()));
+
+ // Check the presence of the documents
+ assertThat(doGet(mDb1, "namespace", "id1")).hasSize(1);
+ assertThat(doGet(mDb1, "namespace", "id2")).hasSize(1);
+
+ // Delete the document
+ checkIsBatchResultSuccess(
+ mDb1.remove(
+ new RemoveByDocumentIdRequest.Builder("namespace")
+ .addIds("id1", "id2")
+ .build()));
+
+ // Make sure it's really gone
+ AppSearchBatchResult<String, GenericDocument> getResult =
+ mDb1.getByDocumentId(
+ new GetByDocumentIdRequest.Builder("namespace")
+ .addIds("id1", "id2")
+ .build())
+ .get();
+ assertThat(getResult.isSuccess()).isFalse();
+ assertThat(getResult.getFailures().get("id1").getResultCode())
+ .isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
+ assertThat(getResult.getFailures().get("id2").getResultCode())
+ .isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
+ }
+
+ @Test
public void testRemoveByQuery() throws Exception {
// Schema registration
mDb1.setSchema(new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
@@ -2803,13 +3001,13 @@
// Email 1 has three usages and email 2 has two usages.
assertThat(results).hasSize(2);
assertThat(results.get(0).getGenericDocument()).isEqualTo(email1);
- assertThat(results.get(0).getRankingSignal()).isEqualTo(3);
assertThat(results.get(1).getGenericDocument()).isEqualTo(email2);
+ assertThat(results.get(0).getRankingSignal()).isEqualTo(3);
assertThat(results.get(1).getRankingSignal()).isEqualTo(2);
- // Query by most recent usag.
- List<GenericDocument> documents =
- convertSearchResultsToDocuments(
+ // Query by most recent usage.
+ results =
+ retrieveAllSearchResults(
mDb1.search(
"",
new SearchSpec.Builder()
@@ -2818,8 +3016,36 @@
.RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP)
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
.build()));
- // TODO(b/182958600) Check the score for usage timestamp once b/182958600 is fixed.
- assertThat(documents).containsExactly(email2, email1).inOrder();
+ assertThat(results).hasSize(2);
+ assertThat(results.get(0).getGenericDocument()).isEqualTo(email2);
+ assertThat(results.get(1).getGenericDocument()).isEqualTo(email1);
+ assertThat(results.get(0).getRankingSignal()).isEqualTo(20000);
+ assertThat(results.get(1).getRankingSignal()).isEqualTo(3000);
+ }
+
+ @Test
+ public void testReportUsage_invalidNamespace() throws Exception {
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
+ .get();
+ AppSearchEmail email1 = new AppSearchEmail.Builder("namespace", "id1").build();
+ checkIsBatchResultSuccess(
+ mDb1.put(new PutDocumentsRequest.Builder().addGenericDocuments(email1).build()));
+
+ // Use the correct namespace; it works
+ mDb1.reportUsage(new ReportUsageRequest.Builder("namespace", "id1").build()).get();
+
+ // Use an incorrect namespace; it fails
+ ExecutionException e =
+ expectThrows(
+ ExecutionException.class,
+ () ->
+ mDb1.reportUsage(
+ new ReportUsageRequest.Builder("namespace2", "id1")
+ .build())
+ .get());
+ assertThat(e).hasCauseThat().isInstanceOf(AppSearchException.class);
+ AppSearchException cause = (AppSearchException) e.getCause();
+ assertThat(cause.getResultCode()).isEqualTo(RESULT_NOT_FOUND);
}
@Test
@@ -3031,4 +3257,47 @@
assertThat(matches.get(0).getFullText()).isEqualTo("This is the body");
assertThat(matches.get(0).getExactMatch()).isEqualTo("body");
}
+
+ @Test
+ public void testCJKTQuery() throws Exception {
+ // Schema registration
+ mDb1.setSchema(new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // Index a document to instance 1.
+ AppSearchEmail inEmail1 =
+ new AppSearchEmail.Builder("namespace", "uri1").setBody("他是個男孩 is a boy").build();
+ checkIsBatchResultSuccess(
+ mDb1.put(new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
+
+ // Query for "他" (He)
+ SearchResultsShim searchResults =
+ mDb1.search(
+ "他",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .build());
+ List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inEmail1);
+
+ // Query for "男孩" (boy)
+ searchResults =
+ mDb1.search(
+ "男孩",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inEmail1);
+
+ // Query for "boy"
+ searchResults =
+ mDb1.search(
+ "boy",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .build());
+ documents = convertSearchResultsToDocuments(searchResults);
+ assertThat(documents).containsExactly(inEmail1);
+ }
}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/GenericDocumentCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java
similarity index 86%
rename from tests/appsearch/src/com/android/cts/appsearch/external/GenericDocumentCtsTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java
index c561473..649448a 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/GenericDocumentCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import static com.google.common.truth.Truth.assertThat;
@@ -39,6 +39,11 @@
.build();
@Test
+ public void testMaxIndexedProperties() {
+ assertThat(GenericDocument.getMaxIndexedProperties()).isEqualTo(16);
+ }
+
+ @Test
public void testDocumentEquals_identical() {
GenericDocument document1 =
new GenericDocument.Builder<>("namespace", "id1", "schemaType1")
@@ -166,6 +171,28 @@
.containsExactly((byte) 1, (byte) 2, (byte) 3)
.inOrder();
assertThat(document.getPropertyDocument("documentKey1")).isEqualTo(sDocumentProperties1);
+
+ assertThat(document.getProperty("longKey1")).isInstanceOf(long[].class);
+ assertThat((long[]) document.getProperty("longKey1")).asList().containsExactly(1L);
+ assertThat(document.getProperty("doubleKey1")).isInstanceOf(double[].class);
+ assertThat((double[]) document.getProperty("doubleKey1"))
+ .usingTolerance(0.05)
+ .containsExactly(1.0);
+ assertThat(document.getProperty("booleanKey1")).isInstanceOf(boolean[].class);
+ assertThat((boolean[]) document.getProperty("booleanKey1")).asList().containsExactly(true);
+ assertThat(document.getProperty("stringKey1")).isInstanceOf(String[].class);
+ assertThat((String[]) document.getProperty("stringKey1"))
+ .asList()
+ .containsExactly("test-value1");
+ assertThat(document.getProperty("byteKey1")).isInstanceOf(byte[][].class);
+ assertThat((byte[][]) document.getProperty("byteKey1"))
+ .asList()
+ .containsExactly(sByteArray1)
+ .inOrder();
+ assertThat(document.getProperty("documentKey1")).isInstanceOf(GenericDocument[].class);
+ assertThat((GenericDocument[]) document.getProperty("documentKey1"))
+ .asList()
+ .containsExactly(sDocumentProperties1);
}
@Test
@@ -213,48 +240,61 @@
@Test
public void testDocument_toString() {
- GenericDocument document =
- new GenericDocument.Builder<>(/*namespace=*/ "", "id1", "schemaType1")
- .setCreationTimestampMillis(5L)
- .setPropertyLong("longKey1", 1L, 2L, 3L)
- .setPropertyDouble("doubleKey1", 1.0, 2.0, 3.0)
- .setPropertyBoolean("booleanKey1", true, false, true)
- .setPropertyString("stringKey1", "String1", "String2", "String3")
- .setPropertyBytes("byteKey1", sByteArray1, sByteArray2)
- .setPropertyDocument(
- "documentKey1", sDocumentProperties1, sDocumentProperties2)
+ GenericDocument nestedDocValue =
+ new GenericDocument.Builder<GenericDocument.Builder<?>>(
+ "namespace", "id2", "schemaType2")
+ .setCreationTimestampMillis(1L)
+ .setScore(1)
+ .setTtlMillis(1L)
+ .setPropertyString("stringKey1", "val1", "val2")
.build();
- String exceptedString =
- "{ name: 'creationTimestampMillis' value: 5 } "
- + "{ name: 'id' value: id1 } "
- + "{ name: 'namespace' value: } "
- + "{ name: 'properties' value: "
- + "{ name: 'booleanKey1' value: [ 'true' 'false' 'true' ] } "
- + "{ name: 'byteKey1' value: "
- + "{ name: 'byteArray' value: [ '1' '2' '3' ] } "
- + "{ name: 'byteArray' value: [ '4' '5' '6' '7' ] } } "
- + "{ name: 'documentKey1' value: [ '"
- + "{ name: 'creationTimestampMillis' value: 12345 } "
- + "{ name: 'id' value: sDocumentProperties1 } "
- + "{ name: 'namespace' value: namespace } "
- + "{ name: 'properties' value: } "
- + "{ name: 'schemaType' value: sDocumentPropertiesSchemaType1 } "
- + "{ name: 'score' value: 0 } "
- + "{ name: 'ttlMillis' value: 0 } ' '"
- + "{ name: 'creationTimestampMillis' value: 6789 } "
- + "{ name: 'id' value: sDocumentProperties2 } "
- + "{ name: 'namespace' value: namespace } "
- + "{ name: 'properties' value: } "
- + "{ name: 'schemaType' value: sDocumentPropertiesSchemaType2 } "
- + "{ name: 'score' value: 0 } "
- + "{ name: 'ttlMillis' value: 0 } ' ] } "
- + "{ name: 'doubleKey1' value: [ '1.0' '2.0' '3.0' ] } "
- + "{ name: 'longKey1' value: [ '1' '2' '3' ] } "
- + "{ name: 'stringKey1' value: [ 'String1' 'String2' 'String3' ] } } "
- + "{ name: 'schemaType' value: schemaType1 } "
- + "{ name: 'score' value: 0 } "
- + "{ name: 'ttlMillis' value: 0 } ";
- assertThat(document.toString()).isEqualTo(exceptedString);
+ GenericDocument document =
+ new GenericDocument.Builder<GenericDocument.Builder<?>>(
+ "namespace", "id1", "schemaType1")
+ .setCreationTimestampMillis(1L)
+ .setScore(1)
+ .setTtlMillis(1L)
+ .setPropertyString("stringKey1", "val1", "val2")
+ .setPropertyBytes("bytesKey1", new byte[] {(byte) 1, (byte) 2})
+ .setPropertyLong("longKey1", 1L, 2L)
+ .setPropertyDouble("doubleKey1", 1.0, 2.0)
+ .setPropertyBoolean("booleanKey1", true, false)
+ .setPropertyDocument("documentKey1", nestedDocValue)
+ .build();
+
+ String documentString = document.toString();
+
+ String expectedString =
+ "{\n"
+ + " namespace: \"namespace\",\n"
+ + " id: \"id1\",\n"
+ + " score: 1,\n"
+ + " schemaType: \"schemaType1\",\n"
+ + " creationTimestampMillis: 1,\n"
+ + " timeToLiveMillis: 1,\n"
+ + " properties: {\n"
+ + " \"booleanKey1\": [true, false],\n"
+ + " \"bytesKey1\": [[1, 2]],\n"
+ + " \"documentKey1\": [\n"
+ + " {\n"
+ + " namespace: \"namespace\",\n"
+ + " id: \"id2\",\n"
+ + " score: 1,\n"
+ + " schemaType: \"schemaType2\",\n"
+ + " creationTimestampMillis: 1,\n"
+ + " timeToLiveMillis: 1,\n"
+ + " properties: {\n"
+ + " \"stringKey1\": [\"val1\", \"val2\"]\n"
+ + " }\n"
+ + " }\n"
+ + " ],\n"
+ + " \"doubleKey1\": [1.0, 2.0],\n"
+ + " \"longKey1\": [1, 2],\n"
+ + " \"stringKey1\": [\"val1\", \"val2\"]\n"
+ + " }\n"
+ + "}";
+
+ assertThat(documentString).isEqualTo(expectedString);
}
@Test
@@ -876,4 +916,53 @@
assertThat(doc.getPropertyBytes("propDocument[0].propBytes[0]"))
.isEqualTo(new byte[] {3, 4});
}
+
+ @Test
+ public void testDocumentGetPropertyNamesSingleLevel() {
+ GenericDocument document =
+ new GenericDocument.Builder<>("namespace", "id1", "schemaType1")
+ .setCreationTimestampMillis(5L)
+ .setScore(1)
+ .setTtlMillis(1L)
+ .setPropertyLong("longKey1", 1L)
+ .setPropertyDouble("doubleKey1", 1.0)
+ .setPropertyBoolean("booleanKey1", true)
+ .setPropertyString("stringKey1", "test-value1")
+ .setPropertyBytes("byteKey1", sByteArray1)
+ .build();
+ assertThat(document.getPropertyNames())
+ .containsExactly("longKey1", "doubleKey1", "booleanKey1", "stringKey1", "byteKey1");
+ }
+
+ @Test
+ public void testDocumentGetPropertyNamesMultiLevel() {
+ GenericDocument innerDoc0 =
+ new GenericDocument.Builder<>("namespace", "id2", "schema2")
+ .setPropertyString("propString", "Goodbye", "Hello")
+ .setPropertyString("propStringTwo", "Fee", "Fi")
+ .setPropertyLong("propInts", 3, 1, 4)
+ .build();
+ GenericDocument innerDoc1 =
+ new GenericDocument.Builder<>("namespace", "id3", "schema2")
+ .setPropertyString("propString", "Aloha")
+ .setPropertyLong("propInts", 7, 5, 6)
+ .setPropertyLong("propIntsTwo", 8, 6)
+ .build();
+ GenericDocument document =
+ new GenericDocument.Builder<>("namespace", "id1", "schemaType1")
+ .setCreationTimestampMillis(5L)
+ .setScore(1)
+ .setTtlMillis(1L)
+ .setPropertyString("stringKey1", "test-value1")
+ .setPropertyDocument("docKey1", innerDoc0, innerDoc1)
+ .build();
+ assertThat(document.getPropertyNames()).containsExactly("stringKey1", "docKey1");
+
+ GenericDocument[] documents = document.getPropertyDocumentArray("docKey1");
+ assertThat(documents).asList().containsExactly(innerDoc0, innerDoc1).inOrder();
+ assertThat(documents[0].getPropertyNames())
+ .containsExactly("propString", "propStringTwo", "propInts");
+ assertThat(documents[1].getPropertyNames())
+ .containsExactly("propString", "propInts", "propIntsTwo");
+ }
}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/GetByDocumentIdRequestCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/GetByDocumentIdRequestCtsTest.java
new file mode 100644
index 0000000..ba81ca9
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/GetByDocumentIdRequestCtsTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.GetByDocumentIdRequest;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class GetByDocumentIdRequestCtsTest {
+ @Test
+ public void testBuildRequest() {
+ List<String> expectedPropertyPaths1 = Arrays.asList("path1", "path2");
+ List<String> expectedPropertyPaths2 = Arrays.asList("path3", "path4");
+ GetByDocumentIdRequest getByDocumentIdRequest =
+ new GetByDocumentIdRequest.Builder("namespace")
+ .addIds("uri1", "uri2")
+ .addIds(Arrays.asList("uri3", "uri4"))
+ .addProjection("schemaType1", expectedPropertyPaths1)
+ .addProjection("schemaType2", expectedPropertyPaths2)
+ .build();
+
+ assertThat(getByDocumentIdRequest.getIds()).containsExactly("uri1", "uri2", "uri3", "uri4");
+ assertThat(getByDocumentIdRequest.getNamespace()).isEqualTo("namespace");
+ assertThat(getByDocumentIdRequest.getProjections())
+ .containsExactly(
+ "schemaType1",
+ expectedPropertyPaths1,
+ "schemaType2",
+ expectedPropertyPaths2);
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/GlobalSearchSessionCtsTestBase.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java
similarity index 99%
rename from tests/appsearch/src/com/android/cts/appsearch/external/GlobalSearchSessionCtsTestBase.java
rename to tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java
index d929a9a..281a98e 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/GlobalSearchSessionCtsTestBase.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.appsearch.cts;
+package android.app.appsearch.cts.app;
import static com.android.server.appsearch.testing.AppSearchTestUtils.checkIsBatchResultSuccess;
import static com.android.server.appsearch.testing.AppSearchTestUtils.convertSearchResultsToDocuments;
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/PackageIdentifierCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/PackageIdentifierCtsTest.java
new file mode 100644
index 0000000..f81e21e
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/PackageIdentifierCtsTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.PackageIdentifier;
+
+import org.junit.Test;
+
+public class PackageIdentifierCtsTest {
+ @Test
+ public void testGetters() {
+ PackageIdentifier packageIdentifier =
+ new PackageIdentifier("com.packageName", /*sha256Certificate=*/ new byte[] {100});
+ assertThat(packageIdentifier.getPackageName()).isEqualTo("com.packageName");
+ assertThat(packageIdentifier.getSha256Certificate()).isEqualTo(new byte[] {100});
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/RemoveByDocumentIdRequestCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/RemoveByDocumentIdRequestCtsTest.java
new file mode 100644
index 0000000..c33cac0
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/RemoveByDocumentIdRequestCtsTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.RemoveByDocumentIdRequest;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class RemoveByDocumentIdRequestCtsTest {
+ @Test
+ public void testBuildRequest() {
+ RemoveByDocumentIdRequest request =
+ new RemoveByDocumentIdRequest.Builder("namespace")
+ .addIds("uri1", "uri2")
+ .addIds(Arrays.asList("uri3"))
+ .build();
+
+ assertThat(request.getNamespace()).isEqualTo("namespace");
+ assertThat(request.getIds()).containsExactly("uri1", "uri2", "uri3").inOrder();
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/ReportSystemUsageRequestCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/ReportSystemUsageRequestCtsTest.java
new file mode 100644
index 0000000..4fbfb1d
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/ReportSystemUsageRequestCtsTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.ReportSystemUsageRequest;
+
+import org.junit.Test;
+
+public class ReportSystemUsageRequestCtsTest {
+ @Test
+ public void testGettersAndSetters() {
+ ReportSystemUsageRequest request =
+ new ReportSystemUsageRequest.Builder("package1", "database1", "namespace1", "id1")
+ .setUsageTimestampMillis(32)
+ .build();
+ assertThat(request.getPackageName()).isEqualTo("package1");
+ assertThat(request.getDatabaseName()).isEqualTo("database1");
+ assertThat(request.getNamespace()).isEqualTo("namespace1");
+ assertThat(request.getDocumentId()).isEqualTo("id1");
+ assertThat(request.getUsageTimestampMillis()).isEqualTo(32);
+ }
+
+ @Test
+ public void testUsageTimestampDefault() {
+ long startTs = System.currentTimeMillis();
+ ReportSystemUsageRequest request =
+ new ReportSystemUsageRequest.Builder("package1", "database1", "namespace1", "id1")
+ .build();
+ assertThat(request.getUsageTimestampMillis()).isAtLeast(startTs);
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/ReportUsageRequestCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/ReportUsageRequestCtsTest.java
new file mode 100644
index 0000000..189ebb0
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/ReportUsageRequestCtsTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.ReportUsageRequest;
+
+import org.junit.Test;
+
+public class ReportUsageRequestCtsTest {
+ @Test
+ public void testGettersAndSetters() {
+ ReportUsageRequest request =
+ new ReportUsageRequest.Builder("namespace1", "id1")
+ .setUsageTimestampMillis(32)
+ .build();
+ assertThat(request.getNamespace()).isEqualTo("namespace1");
+ assertThat(request.getDocumentId()).isEqualTo("id1");
+ assertThat(request.getUsageTimestampMillis()).isEqualTo(32);
+ }
+
+ @Test
+ public void testUsageTimestampDefault() {
+ long startTs = System.currentTimeMillis();
+ ReportUsageRequest request = new ReportUsageRequest.Builder("namespace1", "id1").build();
+ assertThat(request.getUsageTimestampMillis()).isAtLeast(startTs);
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/SearchResultCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/SearchResultCtsTest.java
new file mode 100644
index 0000000..588210b
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/SearchResultCtsTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.SearchResult;
+
+import com.android.server.appsearch.testing.AppSearchEmail;
+
+import org.junit.Test;
+
+public class SearchResultCtsTest {
+
+ @Test
+ public void testBuildSearchResult() {
+ SearchResult.MatchRange exactMatchRange = new SearchResult.MatchRange(3, 8);
+ SearchResult.MatchRange snippetMatchRange = new SearchResult.MatchRange(1, 10);
+ SearchResult.MatchInfo matchInfo =
+ new SearchResult.MatchInfo.Builder("body")
+ .setExactMatchRange(exactMatchRange)
+ .setSnippetRange(snippetMatchRange)
+ .build();
+
+ AppSearchEmail email =
+ new AppSearchEmail.Builder("namespace1", "id1").setBody("Hello World.").build();
+ SearchResult searchResult =
+ new SearchResult.Builder("packageName", "databaseName")
+ .setGenericDocument(email)
+ .addMatchInfo(matchInfo)
+ .setRankingSignal(2.9)
+ .build();
+
+ assertThat(searchResult.getPackageName()).isEqualTo("packageName");
+ assertThat(searchResult.getDatabaseName()).isEqualTo("databaseName");
+ assertThat(searchResult.getRankingSignal()).isEqualTo(2.9);
+ assertThat(searchResult.getGenericDocument()).isEqualTo(email);
+ assertThat(searchResult.getMatchInfos()).hasSize(1);
+ SearchResult.MatchInfo actualMatchInfo = searchResult.getMatchInfos().get(0);
+ assertThat(actualMatchInfo.getPropertyPath()).isEqualTo("body");
+ assertThat(actualMatchInfo.getExactMatchRange()).isEqualTo(exactMatchRange);
+ assertThat(actualMatchInfo.getSnippetRange()).isEqualTo(snippetMatchRange);
+ assertThat(actualMatchInfo.getExactMatch()).isEqualTo("lo Wo");
+ assertThat(actualMatchInfo.getSnippet()).isEqualTo("ello Worl");
+ assertThat(actualMatchInfo.getFullText()).isEqualTo("Hello World.");
+ }
+
+ @Test
+ public void testMatchRange() {
+ SearchResult.MatchRange matchRange = new SearchResult.MatchRange(13, 47);
+ assertThat(matchRange.getStart()).isEqualTo(13);
+ assertThat(matchRange.getEnd()).isEqualTo(47);
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/SearchSpecCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/SearchSpecCtsTest.java
new file mode 100644
index 0000000..c1a9f6c
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/SearchSpecCtsTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.app.appsearch.cts.app;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.SearchSpec;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+
+public class SearchSpecCtsTest {
+ @Test
+ public void testBuildSearchSpecWithoutTermMatch() {
+ SearchSpec searchSpec = new SearchSpec.Builder().addFilterSchemas("testSchemaType").build();
+ assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
+ }
+
+ @Test
+ public void testBuildSearchSpec() {
+ List<String> expectedPropertyPaths1 = ImmutableList.of("path1", "path2");
+ List<String> expectedPropertyPaths2 = ImmutableList.of("path3", "path4");
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .addFilterNamespaces("namespace1", "namespace2")
+ .addFilterNamespaces(ImmutableList.of("namespace3"))
+ .addFilterSchemas("schemaTypes1", "schemaTypes2")
+ .addFilterSchemas(ImmutableList.of("schemaTypes3"))
+ .addFilterPackageNames("package1", "package2")
+ .addFilterPackageNames(ImmutableList.of("package3"))
+ .setSnippetCount(5)
+ .setSnippetCountPerProperty(10)
+ .setMaxSnippetSize(15)
+ .setResultCountPerPage(42)
+ .setOrder(SearchSpec.ORDER_ASCENDING)
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
+ .setResultGrouping(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE,
+ /*limit=*/ 37)
+ .addProjection("schemaType1", expectedPropertyPaths1)
+ .addProjection("schemaType2", expectedPropertyPaths2)
+ .build();
+
+ assertThat(searchSpec.getTermMatch()).isEqualTo(SearchSpec.TERM_MATCH_PREFIX);
+ assertThat(searchSpec.getFilterNamespaces())
+ .containsExactly("namespace1", "namespace2", "namespace3")
+ .inOrder();
+ assertThat(searchSpec.getFilterSchemas())
+ .containsExactly("schemaTypes1", "schemaTypes2", "schemaTypes3")
+ .inOrder();
+ assertThat(searchSpec.getFilterPackageNames())
+ .containsExactly("package1", "package2", "package3")
+ .inOrder();
+ assertThat(searchSpec.getSnippetCount()).isEqualTo(5);
+ assertThat(searchSpec.getSnippetCountPerProperty()).isEqualTo(10);
+ assertThat(searchSpec.getMaxSnippetSize()).isEqualTo(15);
+ assertThat(searchSpec.getResultCountPerPage()).isEqualTo(42);
+ assertThat(searchSpec.getOrder()).isEqualTo(SearchSpec.ORDER_ASCENDING);
+ assertThat(searchSpec.getRankingStrategy())
+ .isEqualTo(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE);
+ assertThat(searchSpec.getResultGroupingTypeFlags())
+ .isEqualTo(
+ SearchSpec.GROUPING_TYPE_PER_NAMESPACE
+ | SearchSpec.GROUPING_TYPE_PER_PACKAGE);
+ assertThat(searchSpec.getProjections())
+ .containsExactly(
+ "schemaType1",
+ expectedPropertyPaths1,
+ "schemaType2",
+ expectedPropertyPaths2);
+ assertThat(searchSpec.getResultGroupingLimit()).isEqualTo(37);
+ }
+
+ @Test
+ public void testGetProjectionTypePropertyMasks() {
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .addProjection("TypeA", ImmutableList.of("field1", "field2.subfield2"))
+ .addProjection("TypeB", ImmutableList.of("field7"))
+ .addProjection("TypeC", ImmutableList.of())
+ .build();
+
+ Map<String, List<String>> typePropertyPathMap = searchSpec.getProjections();
+ assertThat(typePropertyPathMap.keySet()).containsExactly("TypeA", "TypeB", "TypeC");
+ assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
+ assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
+ assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/SetSchemaRequestCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/SetSchemaRequestCtsTest.java
new file mode 100644
index 0000000..bf5a44a
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/SetSchemaRequestCtsTest.java
@@ -0,0 +1,346 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.app;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+import android.app.appsearch.Migrator;
+import android.app.appsearch.PackageIdentifier;
+import android.app.appsearch.SetSchemaRequest;
+import android.util.ArrayMap;
+
+import com.android.server.appsearch.testing.AppSearchEmail;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+public class SetSchemaRequestCtsTest {
+ @Test
+ public void testBuildSetSchemaRequest() {
+ AppSearchSchema.StringPropertyConfig prop1 =
+ new AppSearchSchema.StringPropertyConfig.Builder("prop1")
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build();
+ AppSearchSchema schema1 = new AppSearchSchema.Builder("type1").addProperty(prop1).build();
+ AppSearchSchema schema2 = new AppSearchSchema.Builder("type2").addProperty(prop1).build();
+ AppSearchSchema schema3 = new AppSearchSchema.Builder("type3").addProperty(prop1).build();
+ AppSearchSchema schema4 = new AppSearchSchema.Builder("type4").addProperty(prop1).build();
+
+ PackageIdentifier packageIdentifier =
+ new PackageIdentifier("com.package.foo", new byte[] {100});
+
+ SetSchemaRequest request =
+ new SetSchemaRequest.Builder()
+ .addSchemas(schema1, schema2)
+ .addSchemas(Arrays.asList(schema3, schema4))
+ .setSchemaTypeDisplayedBySystem("type2", /*displayed=*/ false)
+ .setSchemaTypeVisibilityForPackage(
+ "type1", /*visible=*/ true, packageIdentifier)
+ .setForceOverride(true)
+ .setVersion(142857)
+ .build();
+
+ assertThat(request.getSchemas()).containsExactly(schema1, schema2, schema3, schema4);
+ assertThat(request.getSchemasNotDisplayedBySystem()).containsExactly("type2");
+
+ assertThat(request.getSchemasVisibleToPackages())
+ .containsExactly("type1", Collections.singleton(packageIdentifier));
+ assertThat(request.getVersion()).isEqualTo(142857);
+ assertThat(request.isForceOverride()).isTrue();
+ }
+
+ @Test
+ public void testSetSchemaRequestTypeChanges() {
+ AppSearchSchema.StringPropertyConfig requiredProp =
+ new AppSearchSchema.StringPropertyConfig.Builder("prop1")
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .build();
+ AppSearchSchema schema1 =
+ new AppSearchSchema.Builder("type1").addProperty(requiredProp).build();
+ AppSearchSchema schema2 =
+ new AppSearchSchema.Builder("type2").addProperty(requiredProp).build();
+ AppSearchSchema schema3 =
+ new AppSearchSchema.Builder("type3").addProperty(requiredProp).build();
+
+ Migrator expectedMigrator1 =
+ new Migrator() {
+ @Override
+ public boolean shouldMigrate(int currentVersion, int finalVersion) {
+ return true;
+ }
+
+ @NonNull
+ @Override
+ public GenericDocument onUpgrade(
+ int currentVersion,
+ int finalVersion,
+ @NonNull GenericDocument document) {
+ return document;
+ }
+
+ @NonNull
+ @Override
+ public GenericDocument onDowngrade(
+ int currentVersion,
+ int finalVersion,
+ @NonNull GenericDocument document) {
+ return document;
+ }
+ };
+ Migrator expectedMigrator2 =
+ new Migrator() {
+ @Override
+ public boolean shouldMigrate(int currentVersion, int finalVersion) {
+ return true;
+ }
+
+ @NonNull
+ @Override
+ public GenericDocument onUpgrade(
+ int currentVersion,
+ int finalVersion,
+ @NonNull GenericDocument document) {
+ return document;
+ }
+
+ @NonNull
+ @Override
+ public GenericDocument onDowngrade(
+ int currentVersion,
+ int finalVersion,
+ @NonNull GenericDocument document) {
+ return document;
+ }
+ };
+ Migrator expectedMigrator3 =
+ new Migrator() {
+ @Override
+ public boolean shouldMigrate(int currentVersion, int finalVersion) {
+ return true;
+ }
+
+ @NonNull
+ @Override
+ public GenericDocument onUpgrade(
+ int currentVersion,
+ int finalVersion,
+ @NonNull GenericDocument document) {
+ return document;
+ }
+
+ @NonNull
+ @Override
+ public GenericDocument onDowngrade(
+ int currentVersion,
+ int finalVersion,
+ @NonNull GenericDocument document) {
+ return document;
+ }
+ };
+ Map<String, Migrator> migratorMap = new ArrayMap<>();
+ migratorMap.put("type1", expectedMigrator1);
+ migratorMap.put("type2", expectedMigrator2);
+
+ SetSchemaRequest request =
+ new SetSchemaRequest.Builder()
+ .addSchemas(schema1, schema2, schema3)
+ .setForceOverride(/*forceOverride=*/ true)
+ .setMigrators(migratorMap)
+ .setMigrator("type3", expectedMigrator3)
+ .build();
+
+ assertThat(request.isForceOverride()).isTrue();
+ Map<String, Migrator> expectedMigratorMap = new ArrayMap<>();
+ expectedMigratorMap.put("type1", expectedMigrator1);
+ expectedMigratorMap.put("type2", expectedMigrator2);
+ expectedMigratorMap.put("type3", expectedMigrator3);
+ assertThat(request.getMigrators()).containsExactlyEntriesIn(expectedMigratorMap);
+ }
+
+ @Test
+ public void testInvalidSchemaReferences_fromDisplayedBySystem() {
+ IllegalArgumentException expected =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new SetSchemaRequest.Builder()
+ .setSchemaTypeDisplayedBySystem("InvalidSchema", false)
+ .build());
+ assertThat(expected).hasMessageThat().contains("referenced, but were not added");
+ }
+
+ @Test
+ public void testInvalidSchemaReferences_fromPackageVisibility() {
+ IllegalArgumentException expected =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new SetSchemaRequest.Builder()
+ .setSchemaTypeVisibilityForPackage(
+ "InvalidSchema",
+ /*visible=*/ true,
+ new PackageIdentifier(
+ "com.foo.package",
+ /*sha256Certificate=*/ new byte[] {}))
+ .build());
+ assertThat(expected).hasMessageThat().contains("referenced, but were not added");
+ }
+
+ @Test
+ public void testSetSchemaTypeDisplayedBySystem_displayed() {
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+ // By default, the schema is displayed.
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
+ assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
+
+ request =
+ new SetSchemaRequest.Builder()
+ .addSchemas(schema)
+ .setSchemaTypeDisplayedBySystem("Schema", true)
+ .build();
+ assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
+ }
+
+ @Test
+ public void testSetSchemaTypeDisplayedBySystem_notDisplayed() {
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+ SetSchemaRequest request =
+ new SetSchemaRequest.Builder()
+ .addSchemas(schema)
+ .setSchemaTypeDisplayedBySystem("Schema", false)
+ .build();
+ assertThat(request.getSchemasNotDisplayedBySystem()).containsExactly("Schema");
+ }
+
+ @Test
+ public void testSchemaTypeVisibilityForPackage_visible() {
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+ // By default, the schema is not visible.
+ SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
+ assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+
+ PackageIdentifier packageIdentifier =
+ new PackageIdentifier("com.package.foo", new byte[] {100});
+
+ request =
+ new SetSchemaRequest.Builder()
+ .addSchemas(schema)
+ .setSchemaTypeVisibilityForPackage(
+ "Schema", /*visible=*/ true, packageIdentifier)
+ .build();
+ assertThat(request.getSchemasVisibleToPackages())
+ .containsExactly("Schema", Collections.singleton(packageIdentifier));
+ }
+
+ @Test
+ public void testSchemaTypeVisibilityForPackage_notVisible() {
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+ SetSchemaRequest request =
+ new SetSchemaRequest.Builder()
+ .addSchemas(schema)
+ .setSchemaTypeVisibilityForPackage(
+ "Schema",
+ /*visible=*/ false,
+ new PackageIdentifier(
+ "com.package.foo", /*sha256Certificate=*/ new byte[] {}))
+ .build();
+ assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+ }
+
+ @Test
+ public void testSchemaTypeVisibilityForPackage_deduped() throws Exception {
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+ PackageIdentifier packageIdentifier =
+ new PackageIdentifier("com.package.foo", new byte[] {100});
+
+ SetSchemaRequest request =
+ new SetSchemaRequest.Builder()
+ .addSchemas(schema)
+ // Set it visible for "Schema"
+ .setSchemaTypeVisibilityForPackage(
+ "Schema", /*visible=*/ true, packageIdentifier)
+ // Set it visible for "Schema" again, which should be a no-op
+ .setSchemaTypeVisibilityForPackage(
+ "Schema", /*visible=*/ true, packageIdentifier)
+ .build();
+ assertThat(request.getSchemasVisibleToPackages())
+ .containsExactly("Schema", Collections.singleton(packageIdentifier));
+ }
+
+ @Test
+ public void testSchemaTypeVisibilityForPackage_removed() throws Exception {
+ AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
+
+ SetSchemaRequest request =
+ new SetSchemaRequest.Builder()
+ .addSchemas(schema)
+ // First set it as visible
+ .setSchemaTypeVisibilityForPackage(
+ "Schema",
+ /*visible=*/ true,
+ new PackageIdentifier(
+ "com.package.foo", /*sha256Certificate=*/ new byte[] {100}))
+ // Then make it not visible
+ .setSchemaTypeVisibilityForPackage(
+ "Schema",
+ /*visible=*/ false,
+ new PackageIdentifier(
+ "com.package.foo", /*sha256Certificate=*/ new byte[] {100}))
+ .build();
+
+ // Nothing should be visible.
+ assertThat(request.getSchemasVisibleToPackages()).isEmpty();
+ }
+
+ @Test
+ public void testSetVersion() {
+ IllegalArgumentException exception =
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .setVersion(0)
+ .build());
+ assertThat(exception).hasMessageThat().contains("Version must be a positive number");
+ SetSchemaRequest request =
+ new SetSchemaRequest.Builder()
+ .addSchemas(AppSearchEmail.SCHEMA)
+ .setVersion(1)
+ .build();
+ assertThat(request.getVersion()).isEqualTo(1);
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/SetSchemaResponseCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/SetSchemaResponseCtsTest.java
new file mode 100644
index 0000000..96e6b27
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/SetSchemaResponseCtsTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.SetSchemaResponse;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class SetSchemaResponseCtsTest {
+ @Test
+ public void testRebuild() {
+ SetSchemaResponse.MigrationFailure failure1 =
+ new SetSchemaResponse.MigrationFailure(
+ "namespace",
+ "failure1",
+ "schemaType",
+ AppSearchResult.newFailedResult(
+ AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage"));
+ SetSchemaResponse.MigrationFailure failure2 =
+ new SetSchemaResponse.MigrationFailure(
+ "namespace",
+ "failure2",
+ "schemaType",
+ AppSearchResult.newFailedResult(
+ AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage"));
+
+ SetSchemaResponse.Builder builder =
+ new SetSchemaResponse.Builder()
+ .addDeletedType("delete1")
+ .addIncompatibleType("incompatible1")
+ .addMigratedType("migrated1")
+ .addMigrationFailure(failure1);
+ SetSchemaResponse original = builder.build();
+ assertThat(original.getDeletedTypes()).containsExactly("delete1");
+ assertThat(original.getIncompatibleTypes()).containsExactly("incompatible1");
+ assertThat(original.getMigratedTypes()).containsExactly("migrated1");
+ assertThat(original.getMigrationFailures()).containsExactly(failure1);
+
+ SetSchemaResponse rebuild =
+ builder.addDeletedType("delete2")
+ .addIncompatibleType("incompatible2")
+ .addMigratedType("migrated2")
+ .addMigrationFailure(failure2)
+ .build();
+
+ // rebuild won't effect the original object
+ assertThat(original.getDeletedTypes()).containsExactly("delete1");
+ assertThat(original.getIncompatibleTypes()).containsExactly("incompatible1");
+ assertThat(original.getMigratedTypes()).containsExactly("migrated1");
+ assertThat(original.getMigrationFailures()).containsExactly(failure1);
+
+ assertThat(rebuild.getDeletedTypes()).containsExactly("delete1", "delete2");
+ assertThat(rebuild.getIncompatibleTypes())
+ .containsExactly("incompatible1", "incompatible2");
+ assertThat(rebuild.getMigratedTypes()).containsExactly("migrated1", "migrated2");
+ assertThat(rebuild.getMigrationFailures()).containsExactly(failure1, failure2);
+ }
+
+ @Test
+ public void testPluralAdds() {
+ SetSchemaResponse.MigrationFailure failure1 =
+ new SetSchemaResponse.MigrationFailure(
+ "namespace",
+ "failure1",
+ "schemaType",
+ AppSearchResult.newFailedResult(
+ AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage"));
+
+ SetSchemaResponse.Builder builder =
+ new SetSchemaResponse.Builder()
+ .addDeletedTypes(Arrays.asList("delete1"))
+ .addIncompatibleTypes(Arrays.asList("incompatible1"))
+ .addMigratedTypes(Arrays.asList("migrated1"))
+ .addMigrationFailures(Arrays.asList(failure1));
+ SetSchemaResponse singleEntries = builder.build();
+ assertThat(singleEntries.getDeletedTypes()).containsExactly("delete1");
+ assertThat(singleEntries.getIncompatibleTypes()).containsExactly("incompatible1");
+ assertThat(singleEntries.getMigratedTypes()).containsExactly("migrated1");
+ assertThat(singleEntries.getMigrationFailures()).containsExactly(failure1);
+
+ SetSchemaResponse.MigrationFailure failure2 =
+ new SetSchemaResponse.MigrationFailure(
+ "namespace",
+ "failure2",
+ "schemaType",
+ AppSearchResult.newFailedResult(
+ AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage"));
+ SetSchemaResponse multiEntries =
+ builder.addDeletedTypes(Arrays.asList("delete2", "deleted3", "deleted4"))
+ .addIncompatibleTypes(Arrays.asList("incompatible2"))
+ .addMigratedTypes(Arrays.asList("migrated2", "migrate3"))
+ .addMigrationFailures(Arrays.asList(failure2))
+ .build();
+
+ assertThat(multiEntries.getDeletedTypes())
+ .containsExactly("delete1", "delete2", "deleted3", "deleted4");
+ assertThat(multiEntries.getIncompatibleTypes())
+ .containsExactly("incompatible1", "incompatible2");
+ assertThat(multiEntries.getMigratedTypes())
+ .containsExactly("migrated1", "migrated2", "migrate3");
+ assertThat(multiEntries.getMigrationFailures()).containsExactly(failure1, failure2);
+ }
+
+ @Test
+ public void testMigrationFailure() {
+ AppSearchResult<Void> expectedResult =
+ AppSearchResult.newFailedResult(
+ AppSearchResult.RESULT_INTERNAL_ERROR, "This is errorMessage.");
+ SetSchemaResponse.MigrationFailure migrationFailure =
+ new SetSchemaResponse.MigrationFailure(
+ "testNamespace", "testId", "testSchemaType", expectedResult);
+ assertThat(migrationFailure.getNamespace()).isEqualTo("testNamespace");
+ assertThat(migrationFailure.getSchemaType()).isEqualTo("testSchemaType");
+ assertThat(migrationFailure.getDocumentId()).isEqualTo("testId");
+ assertThat(migrationFailure.getAppSearchResult()).isEqualTo(expectedResult);
+ assertThat(migrationFailure.toString())
+ .isEqualTo(
+ "MigrationFailure { schemaType:"
+ + " testSchemaType, namespace: testNamespace, documentId: testId, "
+ + "appSearchResult: [FAILURE(2)]: This is errorMessage.}");
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/StorageInfoCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/StorageInfoCtsTest.java
new file mode 100644
index 0000000..ac89131
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/StorageInfoCtsTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.app;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.StorageInfo;
+
+import org.junit.Test;
+
+public class StorageInfoCtsTest {
+
+ @Test
+ public void testBuildStorageInfo() {
+ StorageInfo storageInfo =
+ new StorageInfo.Builder()
+ .setAliveDocumentsCount(10)
+ .setSizeBytes(1L)
+ .setAliveNamespacesCount(10)
+ .build();
+
+ assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(10);
+ assertThat(storageInfo.getSizeBytes()).isEqualTo(1L);
+ assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(10);
+ }
+
+ @Test
+ public void testBuildStorageInfo_withDefaults() {
+ StorageInfo storageInfo = new StorageInfo.Builder().build();
+
+ assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0);
+ assertThat(storageInfo.getSizeBytes()).isEqualTo(0L);
+ assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0);
+ }
+}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/customer/CustomerDocumentTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/customer/CustomerDocumentTest.java
similarity index 98%
rename from tests/appsearch/src/com/android/cts/appsearch/external/customer/CustomerDocumentTest.java
rename to tests/appsearch/src/com/android/cts/appsearch/external/app/customer/CustomerDocumentTest.java
index 240afac..36d672c 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/customer/CustomerDocumentTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/customer/CustomerDocumentTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.app.appsearch.cts.customer;
+package android.app.appsearch.cts.app.customer;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/exceptions/AppSearchExceptionCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/exceptions/AppSearchExceptionCtsTest.java
new file mode 100644
index 0000000..470cd46
--- /dev/null
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/exceptions/AppSearchExceptionCtsTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.cts.exceptions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.exceptions.AppSearchException;
+
+import org.junit.Test;
+
+public class AppSearchExceptionCtsTest {
+ @Test
+ public void testNoMessageException() {
+ AppSearchException e = new AppSearchException(AppSearchResult.RESULT_IO_ERROR);
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_IO_ERROR);
+
+ AppSearchResult<?> result = e.toAppSearchResult();
+ assertThat(result.isSuccess()).isFalse();
+ assertThat(result.getResultCode()).isEqualTo(AppSearchResult.RESULT_IO_ERROR);
+ assertThat(result.getErrorMessage()).isNull();
+ }
+
+ @Test
+ public void testExceptionWithMessage() {
+ AppSearchException e = new AppSearchException(AppSearchResult.RESULT_NOT_FOUND, "ERROR!");
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
+
+ AppSearchResult<?> result = e.toAppSearchResult();
+ assertThat(result.isSuccess()).isFalse();
+ assertThat(result.getResultCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
+ assertThat(result.getErrorMessage()).isEqualTo("ERROR!");
+ }
+
+ @Test
+ public void testExceptionWithThrowable() {
+ IllegalArgumentException throwable = new IllegalArgumentException("You can't do that!");
+ AppSearchException e =
+ new AppSearchException(
+ AppSearchResult.RESULT_INVALID_ARGUMENT, "ERROR!", throwable);
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_INVALID_ARGUMENT);
+ assertThat(e.getCause()).isEqualTo(throwable);
+
+ AppSearchResult<?> result = e.toAppSearchResult();
+ assertThat(result.isSuccess()).isFalse();
+ assertThat(result.getResultCode()).isEqualTo(AppSearchResult.RESULT_INVALID_ARGUMENT);
+ assertThat(result.getErrorMessage()).isEqualTo("ERROR!");
+ }
+}
diff --git a/tests/autofillservice/AndroidManifest.xml b/tests/autofillservice/AndroidManifest.xml
index 58dd553..28d4ebd 100644
--- a/tests/autofillservice/AndroidManifest.xml
+++ b/tests/autofillservice/AndroidManifest.xml
@@ -149,7 +149,6 @@
<activity android:name=".activities.SimpleAfterLoginActivity"/>
<activity android:name=".activities.SimpleBeforeLoginActivity"/>
<activity android:name=".activities.NonAutofillableActivity"/>
- <activity android:name=".activities.ClientSuggestionsActivity"/>
<receiver android:name=".testcore.SelfDestructReceiver"
android:exported="true"
diff --git a/tests/autofillservice/src/android/autofillservice/cts/activities/AuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/activities/AuthenticationActivity.java
index 199e1b1..0a32981 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/activities/AuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/activities/AuthenticationActivity.java
@@ -291,8 +291,7 @@
(id) -> Helper.findNodeByResourceId(structure, id));
}
} else if (dataset != null) {
- result = dataset.asDatasetWithNodeResolver(
- (id) -> Helper.findNodeByResourceId(structure, id));
+ result = dataset.asDataset((id) -> Helper.findNodeByResourceId(structure, id));
} else {
throw new IllegalStateException("no dataset or response");
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/activities/ClientSuggestionsActivity.java b/tests/autofillservice/src/android/autofillservice/cts/activities/ClientSuggestionsActivity.java
deleted file mode 100644
index f69aa02..0000000
--- a/tests/autofillservice/src/android/autofillservice/cts/activities/ClientSuggestionsActivity.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.autofillservice.cts.activities;
-
-import android.autofillservice.cts.R;
-import android.autofillservice.cts.testcore.ClientAutofillRequestCallback;
-import android.autofillservice.cts.testcore.Helper;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.view.View;
-import android.view.autofill.AutofillId;
-
-import java.util.Map;
-import java.util.concurrent.Executor;
-
-/**
- * Activity that has the following fields:
- *
- * <ul>
- * <li>Username EditText (id: username, no input-type)
- * <li>Password EditText (id: "username", input-type textPassword)
- * <li>Clear Button
- * <li>Save Button
- * <li>Login Button
- * </ul>
- */
-public class ClientSuggestionsActivity extends LoginActivity {
- private static final String TAG = "ClientSuggestionsActivity";
- private Handler mHandler;
- ClientAutofillRequestCallback mRequestCallback;
-
- private final Map<String, AutofillId> mMap = new ArrayMap<>();
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mHandler = new Handler(Looper.myLooper());
-
- mMap.put(Helper.ID_USERNAME, findViewById(R.id.username).getAutofillId());
- mMap.put(Helper.ID_PASSWORD, findViewById(R.id.password).getAutofillId());
- mHandler = new Handler(Looper.getMainLooper());
- Executor executor = new Executor(){
- @Override
- public void execute(Runnable command) {
- mHandler.post(command);
- }
- };
- mRequestCallback = new ClientAutofillRequestCallback(mHandler, (id)-> mMap.get(id));
- getAutofillManager().setAutofillRequestCallback(executor, mRequestCallback);
- }
-
- @Override
- public void addChild(View child) {
- throw new AssertionError("Uses addChild(View, String) instead");
- }
-
- public void addChild(View child, String id) {
- Log.d(TAG, "addChild(" + child + "): id=" + child.getAutofillId());
- super.addChild(child);
-
- mMap.put(id, child.getAutofillId());
- }
-
- public ClientAutofillRequestCallback.Replier getReplier() {
- return mRequestCallback.getReplier();
- }
-}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/activities/ManualAuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/activities/ManualAuthenticationActivity.java
index e3ebd1f..58b67d7 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/activities/ManualAuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/activities/ManualAuthenticationActivity.java
@@ -58,7 +58,7 @@
result = sResponse.asFillResponse(/* contexts= */ null,
(id) -> Helper.findNodeByResourceId(structure, id));
} else if (sDataset != null) {
- result = sDataset.asDatasetWithNodeResolver(
+ result = sDataset.asDataset(
(id) -> Helper.findNodeByResourceId(structure, id));
} else {
throw new IllegalStateException("no dataset or response");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/client/ClientSuggestionsInlineTest.java b/tests/autofillservice/src/android/autofillservice/cts/client/ClientSuggestionsInlineTest.java
deleted file mode 100644
index 1419704..0000000
--- a/tests/autofillservice/src/android/autofillservice/cts/client/ClientSuggestionsInlineTest.java
+++ /dev/null
@@ -1,169 +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.autofillservice.cts.client;
-
-import static android.autofillservice.cts.testcore.CannedFillResponse.NO_RESPONSE;
-import static android.autofillservice.cts.testcore.Helper.ID_PASSWORD;
-import static android.autofillservice.cts.testcore.Helper.ID_USERNAME;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assume.assumeTrue;
-
-import android.autofillservice.cts.commontests.ClientSuggestionsCommonTestCase;
-import android.autofillservice.cts.testcore.CannedFillResponse;
-import android.autofillservice.cts.testcore.ClientAutofillRequestCallback;
-import android.autofillservice.cts.testcore.InstrumentedAutoFillService;
-import android.os.Bundle;
-
-import com.android.cts.mockime.MockImeSession;
-
-import org.junit.Test;
-
-/**
- * Tests client suggestions behaviors for the inline mode.
- */
-public class ClientSuggestionsInlineTest extends ClientSuggestionsCommonTestCase {
-
- public ClientSuggestionsInlineTest() {
- super(getInlineUiBot());
- }
-
- @Override
- protected boolean isInlineMode() {
- return true;
- }
-
- @Test
- public void testImeDisableClientSuggestions_showDropdownUi() throws Exception {
- // Set service.
- enableService();
- final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
- assumeTrue("MockIME not available", mockImeSession != null);
-
- // Disable inline suggestions for the client.
- final Bundle bundle = new Bundle();
- bundle.putBoolean("ClientSuggestions", false);
- mockImeSession.callSetInlineSuggestionsExtras(bundle);
-
- // Set expectations.
- mClientReplier.addResponse(new CannedFillResponse.CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setPresentation("The Dude", isInlineMode())
- .build());
-
- // Trigger auto-fill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- mUiBot.waitForIdleSync();
-
- // Check that no inline requests are sent to the client.
- final ClientAutofillRequestCallback.FillRequest clientRequest =
- mClientReplier.getNextFillRequest();
- assertThat(clientRequest.inlineRequest).isNull();
-
- // Check dropdown UI shown.
- getDropdownUiBot().assertDatasets("The Dude");
- }
-
- @Test
- public void testImeDisableClientSuggestions_fallbackThenShowInline() throws Exception {
- // Set service.
- enableService();
-
- final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
- assumeTrue("MockIME not available", mockImeSession != null);
-
- // Disable inline suggestions for the client.
- final Bundle bundle = new Bundle();
- bundle.putBoolean("ClientSuggestions", false);
- mockImeSession.callSetInlineSuggestionsExtras(bundle);
-
- // Set expectations.
- sReplier.addResponse(new CannedFillResponse.CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setPresentation("The Dude", isInlineMode())
- .build());
-
- mClientReplier.addResponse(NO_RESPONSE);
-
- // Trigger autofill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- mUiBot.waitForIdle();
-
- // Check that no inline requests are sent to the client.
- final ClientAutofillRequestCallback.FillRequest clientRequest =
- mClientReplier.getNextFillRequest();
- assertThat(clientRequest.inlineRequest).isNull();
-
- // Check that the inline request is sent to the service.
- final InstrumentedAutoFillService.FillRequest fillRequest = sReplier.getNextFillRequest();
- assertThat(fillRequest.inlineRequest).isNotNull();
-
- mActivity.expectAutoFill("dude", "sweet");
-
- // Select the dataset.
- mUiBot.selectDataset("The Dude");
-
- // Check the results.
- mActivity.assertAutoFilled();
- }
-
- @Test
- public void testImeDisableServiceSuggestions_fallbackThenShowDropdownUi() throws Exception {
- // Set service.
- enableService();
-
- final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
- assumeTrue("MockIME not available", mockImeSession != null);
-
- // Disable inline suggestions for the default service.
- final Bundle bundle = new Bundle();
- bundle.putBoolean("ServiceSuggestions", false);
- mockImeSession.callSetInlineSuggestionsExtras(bundle);
-
- // Set expectations.
- sReplier.addResponse(new CannedFillResponse.CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setPresentation("The Dude", isInlineMode())
- .build());
-
- mClientReplier.addResponse(NO_RESPONSE);
-
- // Trigger autofill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- mUiBot.waitForIdle();
-
- // Check that the inline request is sent to the client.
- final ClientAutofillRequestCallback.FillRequest clientRequest =
- mClientReplier.getNextFillRequest();
- assertThat(clientRequest.inlineRequest).isNotNull();
-
- // Check that no inline requests are sent to the service.
- final InstrumentedAutoFillService.FillRequest fillRequest = sReplier.getNextFillRequest();
- assertThat(fillRequest.inlineRequest).isNull();
-
- mActivity.expectAutoFill("dude", "sweet");
-
- // Select the dataset on the dropdown UI.
- getDropdownUiBot().selectDataset("The Dude");
-
- // Check the results.
- mActivity.assertAutoFilled();
- }
-}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/commontests/ClientSuggestionsCommonTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/commontests/ClientSuggestionsCommonTestCase.java
deleted file mode 100644
index e39067f..0000000
--- a/tests/autofillservice/src/android/autofillservice/cts/commontests/ClientSuggestionsCommonTestCase.java
+++ /dev/null
@@ -1,311 +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.autofillservice.cts.commontests;
-
-import static android.autofillservice.cts.testcore.CannedFillResponse.NO_RESPONSE;
-import static android.autofillservice.cts.testcore.Helper.ID_EMPTY;
-import static android.autofillservice.cts.testcore.Helper.ID_PASSWORD;
-import static android.autofillservice.cts.testcore.Helper.ID_USERNAME;
-
-import android.autofillservice.cts.R;
-import android.autofillservice.cts.activities.ClientSuggestionsActivity;
-import android.autofillservice.cts.testcore.AutofillActivityTestRule;
-import android.autofillservice.cts.testcore.CannedFillResponse;
-import android.autofillservice.cts.testcore.CannedFillResponse.CannedDataset;
-import android.autofillservice.cts.testcore.ClientAutofillRequestCallback;
-import android.autofillservice.cts.testcore.OneTimeTextWatcher;
-import android.autofillservice.cts.testcore.UiBot;
-import android.os.Bundle;
-import android.platform.test.annotations.AppModeFull;
-import android.widget.EditText;
-
-import androidx.annotation.NonNull;
-
-import org.junit.Test;
-
-/**
- * This is the test case covering most scenarios - other test cases will cover characteristics
- * specific to that test's activity (for example, custom views).
- */
-public abstract class ClientSuggestionsCommonTestCase
- extends AutoFillServiceTestCase.AutoActivityLaunch<ClientSuggestionsActivity> {
-
- private static final String TAG = "ClientSuggestions";
- protected ClientSuggestionsActivity mActivity;
- protected ClientAutofillRequestCallback.Replier mClientReplier;
-
- protected ClientSuggestionsCommonTestCase() {}
-
- protected ClientSuggestionsCommonTestCase(UiBot inlineUiBot) {
- super(inlineUiBot);
- }
-
- @Test
- public void testAutoFillOneDataset() throws Exception {
- // Set service.
- enableService();
-
- // Set expectations.
- mClientReplier.addResponse(new CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setPresentation("The Dude", isInlineMode())
- .build());
-
- // Trigger autofill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- sReplier.assertOnFillRequestNotCalled();
- mClientReplier.assertReceivedRequest();
-
- mActivity.expectAutoFill("dude", "sweet");
-
- // Select the dataset.
- mUiBot.selectDataset("The Dude");
-
- // Check the results.
- mActivity.assertAutoFilled();
- }
-
- @Test
- public void testAutoFillNoDatasets_fallbackDefaultService() throws Exception {
- // Set service.
- enableService();
-
- // Set expectations.
- sReplier.addResponse(new CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setPresentation("The Dude", isInlineMode())
- .build());
-
- mClientReplier.addResponse(NO_RESPONSE);
-
- // Trigger autofill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- mUiBot.waitForIdle();
- sReplier.getNextFillRequest();
- mClientReplier.assertReceivedRequest();
-
- mActivity.expectAutoFill("dude", "sweet");
-
- // Select the dataset.
- mUiBot.selectDataset("The Dude");
-
- // Check the results.
- mActivity.assertAutoFilled();
- }
-
- @Test
- @AppModeFull(reason = "testAutoFillNoDatasets_fallbackDefaultService() is enough")
- public void testManualRequestAfterFallbackDefaultService() throws Exception {
- // Set service.
- enableService();
-
- // Set expectations.
- sReplier.addResponse(new CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setPresentation("The Dude", isInlineMode())
- .build());
-
- mClientReplier.addResponse(NO_RESPONSE);
-
- // Trigger autofill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- mUiBot.waitForIdle();
- sReplier.getNextFillRequest();
- mClientReplier.assertReceivedRequest();
-
- // The dataset shown.
- mUiBot.assertDatasets("The Dude");
-
- // Set expectations.
- sReplier.addResponse(new CannedDataset.Builder()
- .setField(ID_USERNAME, "DUDE")
- .setField(ID_PASSWORD, "SWEET")
- .setPresentation("THE DUDE", isInlineMode())
- .build());
-
- // Trigger autofill.
- mUiBot.waitForWindowChange(() -> mActivity.forceAutofillOnUsername());
- sReplier.getNextFillRequest();
- mClientReplier.assertNoUnhandledFillRequests();
-
- mActivity.expectAutoFill("DUDE", "SWEET");
-
- // Select the dataset.
- mUiBot.selectDataset("THE DUDE");
-
- // Check the results.
- mActivity.assertAutoFilled();
- }
-
- @Test
- @AppModeFull(reason = "testAutoFillNoDatasets_fallbackDefaultService() is enough")
- public void testNewFieldAddedAfterFallbackDefaultService() throws Exception {
- // Set service.
- enableService();
-
- // Set expectations.
- sReplier.addResponse(new CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setPresentation("The Dude", isInlineMode())
- .build());
-
- mClientReplier.addResponse(NO_RESPONSE);
-
- // Trigger autofill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- mUiBot.waitForIdle();
- sReplier.getNextFillRequest();
- mClientReplier.assertReceivedRequest();
-
- // The dataset shown.
- mUiBot.assertDatasets("The Dude");
-
- // Try again, in a field that was added after the first request
- final EditText child = new EditText(mActivity);
- child.setId(R.id.empty);
- mActivity.addChild(child, ID_EMPTY);
- final OneTimeTextWatcher watcher = new OneTimeTextWatcher("child", child,
- "new view on the block");
- child.addTextChangedListener(watcher);
- sReplier.addResponse(new CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setField(ID_EMPTY, "new view on the block")
- .setPresentation("The Dude", isInlineMode())
- .build());
-
- mActivity.syncRunOnUiThread(() -> child.requestFocus());
- mUiBot.waitForIdle();
- sReplier.getNextFillRequest();
- mClientReplier.assertNoUnhandledFillRequests();
-
- mActivity.expectAutoFill("dude", "sweet");
-
- // Select the dataset.
- mUiBot.selectDataset("The Dude");
- mUiBot.waitForIdle();
-
- // Check the results.
- mActivity.assertAutoFilled();
- watcher.assertAutoFilled();
- }
-
- @Test
- public void testNoDatasetsAfterFallbackDefaultService() throws Exception {
- // Set service.
- enableService();
-
- // Set expectations.
- sReplier.addResponse(NO_RESPONSE);
- mClientReplier.addResponse(NO_RESPONSE);
-
- // Trigger autofill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- mUiBot.waitForIdle();
-
- mClientReplier.assertReceivedRequest();
- sReplier.getNextFillRequest();
-
- // Make sure UI is not shown.
- mUiBot.assertNoDatasetsEver();
- }
-
- @Test
- @AppModeFull(reason = "testAutoFillOneDataset() is enough")
- public void testAutoFillNoDatasets() throws Exception {
- // Set service.
- enableService();
-
- // Set expectations.
- setEmptyClientResponse();
-
- // Trigger autofill.
- mUiBot.selectByRelativeId(ID_USERNAME);
-
- mClientReplier.assertReceivedRequest();
-
- // Make sure UI is not shown.
- mUiBot.assertNoDatasetsEver();
- }
-
- @Test
- @AppModeFull(reason = "testAutoFillOneDataset() is enough")
- public void testNewFieldAddedAfterFirstRequest() throws Exception {
- // Set service.
- enableService();
-
- // Set expectations.
- setEmptyClientResponse();
-
- // Trigger autofill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- mClientReplier.assertReceivedRequest();
-
- // Make sure UI is not shown.
- mUiBot.assertNoDatasetsEver();
-
- // Try again, in a field that was added after the first request
- final EditText child = new EditText(mActivity);
- child.setId(R.id.empty);
- mActivity.addChild(child, ID_EMPTY);
- final OneTimeTextWatcher watcher = new OneTimeTextWatcher("child", child,
- "new view on the block");
- child.addTextChangedListener(watcher);
- mClientReplier.addResponse(new CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setField(ID_PASSWORD, "sweet")
- .setField(ID_EMPTY, "new view on the block")
- .setPresentation("The Dude", isInlineMode())
- .build());
-
- mActivity.syncRunOnUiThread(() -> child.requestFocus());
-
- mClientReplier.assertReceivedRequest();
- mActivity.expectAutoFill("dude", "sweet");
-
- // Select the dataset.
- mUiBot.selectDataset("The Dude");
-
- // Check the results.
- // Check username and password fields
- mActivity.assertAutoFilled();
- // Check the new added field
- watcher.assertAutoFilled();
- }
-
- @NonNull
- @Override
- protected AutofillActivityTestRule<ClientSuggestionsActivity> getActivityRule() {
- return new AutofillActivityTestRule<ClientSuggestionsActivity>(
- ClientSuggestionsActivity.class) {
- @Override
- protected void afterActivityLaunched() {
- mActivity = getActivity();
- mClientReplier = mActivity.getReplier();
- }
- };
- }
-
- private void setEmptyClientResponse() {
- mClientReplier.addResponse(new CannedFillResponse.Builder()
- .setExtras(new Bundle())
- .build());
- }
-}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
index f72ee57..4e3a7a5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
@@ -374,39 +374,6 @@
}
@Test
- public void testImeDisableServiceSuggestions_fallbackDropdownUi() throws Exception {
- // Set service.
- enableService();
-
- final MockImeSession mockImeSession = sMockImeSessionRule.getMockImeSession();
- assumeTrue("MockIME not available", mockImeSession != null);
-
- // Disable inline suggestions for the default service.
- final Bundle bundle = new Bundle();
- bundle.putBoolean("ServiceSuggestions", false);
- mockImeSession.callSetInlineSuggestionsExtras(bundle);
-
- final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
- .addDataset(new CannedFillResponse.CannedDataset.Builder()
- .setField(ID_USERNAME, "dude")
- .setPresentation(createPresentation("The Username"))
- .setInlinePresentation(createInlinePresentation("The Username"))
- .build());
- sReplier.addResponse(builder.build());
-
- // Trigger auto-fill.
- mUiBot.selectByRelativeId(ID_USERNAME);
- mUiBot.waitForIdleSync();
-
- // Check that no inline requests are sent to the service.
- final InstrumentedAutoFillService.FillRequest request = sReplier.getNextFillRequest();
- assertThat(request.inlineRequest).isNull();
-
- // Check dropdown UI shown.
- getDropdownUiBot().assertDatasets("The Username");
- }
-
- @Test
public void testScrollSuggestionView() throws Exception {
// Set service.
enableService();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/WebViewMultiScreenLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/WebViewMultiScreenLoginActivityTest.java
index 009a9cb..94a88c4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/WebViewMultiScreenLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/WebViewMultiScreenLoginActivityTest.java
@@ -24,6 +24,7 @@
import static android.autofillservice.cts.testcore.Helper.ID_USERNAME_LABEL;
import static android.autofillservice.cts.testcore.Helper.assertTextAndValue;
import static android.autofillservice.cts.testcore.Helper.findNodeByHtmlName;
+import static android.autofillservice.cts.testcore.Helper.getAutofillId;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_USERNAME;
@@ -102,7 +103,7 @@
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME, HTML_NAME_USERNAME)
.setSaveInfoDecorator((builder, nodeResolver) -> {
- final AutofillId usernameId = nodeResolver.apply(HTML_NAME_USERNAME);
+ final AutofillId usernameId = getAutofillId(nodeResolver, HTML_NAME_USERNAME);
final CharSequenceTransformation usernameTrans = new CharSequenceTransformation
.Builder(usernameId, MATCH_ALL, "$1").build();
builder.setCustomDescription(newCustomDescriptionWithUsernameAndPassword()
@@ -154,7 +155,7 @@
sReplier.addResponse(new CannedFillResponse.Builder()
.setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, HTML_NAME_PASSWORD)
.setSaveInfoDecorator((builder, nodeResolver) -> {
- final AutofillId passwordId = nodeResolver.apply(HTML_NAME_PASSWORD);
+ final AutofillId passwordId = getAutofillId(nodeResolver, HTML_NAME_PASSWORD);
final CharSequenceTransformation passwordTrans = new CharSequenceTransformation
.Builder(passwordId, MATCH_ALL, "$1").build();
builder.setCustomDescription(newCustomDescriptionWithUsernameAndPassword()
@@ -218,7 +219,7 @@
.setIgnoreFields(HTML_NAME_USERNAME)
.setSaveInfoFlags(SaveInfo.FLAG_DELAY_SAVE)
.setSaveInfoDecorator((builder, nodeResolver) -> {
- usernameId.set(nodeResolver.apply(HTML_NAME_USERNAME));
+ usernameId.set(getAutofillId(nodeResolver, HTML_NAME_USERNAME));
})
.build());
@@ -257,7 +258,7 @@
.setRequiredSavableIds(SAVE_DATA_TYPE_USERNAME | SAVE_DATA_TYPE_PASSWORD,
HTML_NAME_PASSWORD)
.setSaveInfoDecorator((builder, nodeResolver) -> {
- final AutofillId passwordId = nodeResolver.apply(HTML_NAME_PASSWORD);
+ final AutofillId passwordId = getAutofillId(nodeResolver, HTML_NAME_PASSWORD);
final CharSequenceTransformation usernameTrans = new CharSequenceTransformation
.Builder(usernameId.get(), MATCH_ALL, "$1").build();
final CharSequenceTransformation passwordTrans = new CharSequenceTransformation
diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/CannedFillResponse.java
index 3ec1f12..99ee293 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/testcore/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/CannedFillResponse.java
@@ -171,28 +171,12 @@
*/
public FillResponse asFillResponse(@Nullable List<FillContext> contexts,
@NonNull Function<String, ViewNode> nodeResolver) {
- return asFillResponseWithAutofillId(contexts, (id)-> {
- ViewNode node = nodeResolver.apply(id);
- if (node == null) {
- throw new AssertionError("No node with resource id " + id);
- }
- return node.getAutofillId();
- });
- }
-
- /**
- * Creates a new response, replacing the dataset field ids by the real ids from the assist
- * structure.
- */
- public FillResponse asFillResponseWithAutofillId(@Nullable List<FillContext> contexts,
- @NonNull Function<String, AutofillId> autofillIdResolver) {
final FillResponse.Builder builder = new FillResponse.Builder()
.setFlags(mFillResponseFlags);
if (mDatasets != null) {
for (CannedDataset cannedDataset : mDatasets) {
- final Dataset dataset =
- cannedDataset.asDatasetWithAutofillIdResolver(autofillIdResolver);
- assertWithMessage("Cannot create dataset").that(dataset).isNotNull();
+ final Dataset dataset = cannedDataset.asDataset(nodeResolver);
+ assertWithMessage("Cannot create datase").that(dataset).isNotNull();
builder.addDataset(dataset);
}
}
@@ -205,14 +189,13 @@
saveInfoBuilder = mRequiredSavableIds == null || mRequiredSavableIds.length == 0
? new SaveInfo.Builder(mSaveType)
: new SaveInfo.Builder(mSaveType,
- getAutofillIds(autofillIdResolver, mRequiredSavableIds));
+ getAutofillIds(nodeResolver, mRequiredSavableIds));
}
saveInfoBuilder.setFlags(mSaveInfoFlags);
if (mOptionalSavableIds != null) {
- saveInfoBuilder.setOptionalIds(
- getAutofillIds(autofillIdResolver, mOptionalSavableIds));
+ saveInfoBuilder.setOptionalIds(getAutofillIds(nodeResolver, mOptionalSavableIds));
}
if (mSaveDescription != null) {
saveInfoBuilder.setDescription(mSaveDescription);
@@ -234,7 +217,7 @@
if (saveInfoBuilder != null) {
// TODO: merge decorator and visitor
if (mSaveInfoDecorator != null) {
- mSaveInfoDecorator.decorate(saveInfoBuilder, autofillIdResolver);
+ mSaveInfoDecorator.decorate(saveInfoBuilder, nodeResolver);
}
if (mSaveInfoVisitor != null) {
Log.d(TAG, "Visiting saveInfo " + saveInfoBuilder);
@@ -245,10 +228,10 @@
builder.setSaveInfo(saveInfo);
}
if (mIgnoredIds != null) {
- builder.setIgnoredIds(getAutofillIds(autofillIdResolver, mIgnoredIds));
+ builder.setIgnoredIds(getAutofillIds(nodeResolver, mIgnoredIds));
}
if (mAuthenticationIds != null) {
- builder.setAuthentication(getAutofillIds(autofillIdResolver, mAuthenticationIds),
+ builder.setAuthentication(getAutofillIds(nodeResolver, mAuthenticationIds),
mAuthentication, mPresentation, mInlinePresentation);
}
if (mDisableDuration > 0) {
@@ -263,7 +246,7 @@
builder.setFieldClassificationIds(fieldIds);
} else if (mFieldClassificationIds != null) {
builder.setFieldClassificationIds(
- getAutofillIds(autofillIdResolver, mFieldClassificationIds));
+ getAutofillIds(nodeResolver, mFieldClassificationIds));
}
if (mExtras != null) {
builder.setClientState(mExtras);
@@ -627,21 +610,7 @@
/**
* Creates a new dataset, replacing the field ids by the real ids from the assist structure.
*/
- public Dataset asDatasetWithNodeResolver(Function<String, ViewNode> nodeResolver) {
- return asDatasetWithAutofillIdResolver((id) -> {
- ViewNode node = nodeResolver.apply(id);
- if (node == null) {
- throw new AssertionError("No node with resource id " + id);
- }
- return node.getAutofillId();
- });
- }
-
- /**
- * Creates a new dataset, replacing the field ids by the real ids from the assist structure.
- */
- public Dataset asDatasetWithAutofillIdResolver(
- Function<String, AutofillId> autofillIdResolver) {
+ public Dataset asDataset(Function<String, ViewNode> nodeResolver) {
final Dataset.Builder builder = mPresentation != null
? new Dataset.Builder(mPresentation)
: new Dataset.Builder();
@@ -656,11 +625,11 @@
if (mFieldValues != null) {
for (Map.Entry<String, AutofillValue> entry : mFieldValues.entrySet()) {
final String id = entry.getKey();
-
- final AutofillId autofillId = autofillIdResolver.apply(id);
- if (autofillId == null) {
+ final ViewNode node = nodeResolver.apply(id);
+ if (node == null) {
throw new AssertionError("No node with resource id " + id);
}
+ final AutofillId autofillId = node.getAutofillId();
final AutofillValue value = entry.getValue();
final RemoteViews presentation = mFieldPresentations.get(id);
final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(id);
@@ -981,6 +950,6 @@
}
public interface SaveInfoDecorator {
- void decorate(SaveInfo.Builder builder, Function<String, AutofillId> nodeResolver);
+ void decorate(SaveInfo.Builder builder, Function<String, ViewNode> nodeResolver);
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/ClientAutofillRequestCallback.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/ClientAutofillRequestCallback.java
deleted file mode 100644
index b970785..0000000
--- a/tests/autofillservice/src/android/autofillservice/cts/testcore/ClientAutofillRequestCallback.java
+++ /dev/null
@@ -1,379 +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.autofillservice.cts.testcore;
-
-import static android.autofillservice.cts.testcore.CannedFillResponse.ResponseType.FAILURE;
-import static android.autofillservice.cts.testcore.CannedFillResponse.ResponseType.NULL;
-import static android.autofillservice.cts.testcore.CannedFillResponse.ResponseType.TIMEOUT;
-import static android.autofillservice.cts.testcore.Timeouts.CONNECTION_TIMEOUT;
-import static android.autofillservice.cts.testcore.Timeouts.FILL_TIMEOUT;
-import static android.autofillservice.cts.testcore.Timeouts.RESPONSE_DELAY_MS;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.IntentSender;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.service.autofill.AutofillService;
-import android.service.autofill.Dataset;
-import android.service.autofill.FillCallback;
-import android.service.autofill.FillContext;
-import android.service.autofill.FillResponse;
-import android.service.autofill.SaveCallback;
-import android.util.Log;
-import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillRequestCallback;
-import android.view.inputmethod.InlineSuggestionsRequest;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.compatibility.common.util.RetryableException;
-import com.android.compatibility.common.util.TestNameUtils;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-
-/**
- * Implements an {@link AutofillRequestCallback} for testing client suggestions behavior.
- */
-public class ClientAutofillRequestCallback implements AutofillRequestCallback {
- private static final String TAG = "ClientAutofillRequestCallback";
- private final Handler mHandler;
- private final @NonNull Function<String, AutofillId> mIdResolver;
- private final Replier mReplier;
-
- public ClientAutofillRequestCallback(@NonNull Handler handler,
- @NonNull Function<String, AutofillId> idResolver) {
- mHandler = handler;
- mIdResolver = idResolver;
- mReplier = new Replier(mIdResolver);
- }
-
- @Override
- public void onFillRequest(InlineSuggestionsRequest inlineSuggestionsRequest,
- CancellationSignal cancellationSignal, FillCallback callback) {
-
- if (!TestNameUtils.isRunningTest()) {
- Log.e(TAG, "onFillRequest(client) called after tests finished");
- return;
- }
-
- mHandler.post(
- () -> mReplier.onFillRequest(
- cancellationSignal, callback, inlineSuggestionsRequest));
- }
-
- public Replier getReplier() {
- return mReplier;
- }
-
- /**
- * POJO representation of the contents of a
- * {@link ClientAutofillRequestCallback#onFillRequest(InlineSuggestionsRequest,
- * CancellationSignal, FillCallback)} that can be asserted at the end of a test case.
- */
- public static final class FillRequest {
-
- public final CancellationSignal cancellationSignal;
- public final FillCallback callback;
- public final InlineSuggestionsRequest inlineRequest;
-
- private FillRequest(CancellationSignal cancellationSignal, FillCallback callback,
- InlineSuggestionsRequest inlineRequest) {
- this.cancellationSignal = cancellationSignal;
- this.callback = callback;
- this.inlineRequest = inlineRequest;
- }
-
- @Override
- public String toString() {
- return "FillRequest[has inlineRequest=" + (inlineRequest != null) + "]";
- }
- }
-
- /**
- * Object used to answer a
- * {@link AutofillRequestCallback#onFillRequest(InlineSuggestionsRequest, CancellationSignal,
- * FillCallback)}
- * on behalf of a unit test method.
- */
- public static final class Replier {
- // TODO: refactor with InstrumentedAutoFillService$Replier
-
- private final BlockingQueue<CannedFillResponse> mResponses = new LinkedBlockingQueue<>();
- private final BlockingQueue<FillRequest> mFillRequests =
- new LinkedBlockingQueue<>();
-
- private List<Throwable> mExceptions;
- private IntentSender mOnSaveIntentSender;
- private String mAcceptedPackageName;
-
- private Handler mHandler;
-
- private boolean mReportUnhandledFillRequest = true;
- private boolean mReportUnhandledSaveRequest = true;
- private final @NonNull Function<String, AutofillId> mIdResolver;
-
- private Replier(@NonNull Function<String, AutofillId> idResolver) {
- mIdResolver = idResolver;
- }
-
- public void acceptRequestsFromPackage(String packageName) {
- mAcceptedPackageName = packageName;
- }
-
- /**
- * Gets the exceptions thrown asynchronously, if any.
- */
- @Nullable
- public List<Throwable> getExceptions() {
- return mExceptions;
- }
-
- private void addException(@Nullable Throwable e) {
- if (e == null) return;
-
- if (mExceptions == null) {
- mExceptions = new ArrayList<>();
- }
- mExceptions.add(e);
- }
-
- /**
- * Sets the expectation for the next {@code onFillRequest} as {@link FillResponse} with
- * just one {@link Dataset}.
- */
- public Replier addResponse(
- CannedFillResponse.CannedDataset dataset) {
- return addResponse(new CannedFillResponse.Builder()
- .addDataset(dataset)
- .build());
- }
-
- /**
- * Sets the expectation for the next {@code onFillRequest}.
- */
- public Replier addResponse(CannedFillResponse response) {
- if (response == null) {
- throw new IllegalArgumentException("Cannot be null - use NO_RESPONSE instead");
- }
- mResponses.add(response);
- return this;
- }
-
- /**
- * Sets the {@link IntentSender} that is passed to
- * {@link SaveCallback#onSuccess(IntentSender)}.
- */
- public Replier setOnSave(IntentSender intentSender) {
- mOnSaveIntentSender = intentSender;
- return this;
- }
-
- /**
- * Gets the next fill request, in the order received.
- */
- public FillRequest getNextFillRequest() {
- FillRequest request;
- try {
- request = mFillRequests.poll(FILL_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new IllegalStateException("Interrupted", e);
- }
- if (request == null) {
- throw new RetryableException(FILL_TIMEOUT, "onFillRequest() not called");
- }
- return request;
- }
-
- /**
- * Assets the client had received fill request.
- */
- public void assertReceivedRequest() {
- getNextFillRequest();
- }
-
- /**
- * Asserts that {@link AutofillRequestCallback#onFillRequest(InlineSuggestionsRequest,
- * CancellationSignal, FillCallback)} was not called.
- *
- * <p>Should only be called in cases where it's not expected to be called, as it will
- * sleep for a few ms.
- */
- public void assertOnFillRequestNotCalled() {
- SystemClock.sleep(FILL_TIMEOUT.getMaxValue());
- assertThat(mFillRequests).isEmpty();
- }
-
- /**
- * Asserts all {@link AutofillService#onFillRequest(
- * android.service.autofill.FillRequest, CancellationSignal, FillCallback) fill requests}
- * received by the service were properly {@link #getNextFillRequest() handled} by the test
- * case.
- */
- public void assertNoUnhandledFillRequests() {
- if (mFillRequests.isEmpty()) return; // Good job, test case!
-
- if (!mReportUnhandledFillRequest) {
- // Just log, so it's not thrown again on @After if already thrown on main body
- Log.d(TAG, "assertNoUnhandledFillRequests(): already reported, "
- + "but logging just in case: " + mFillRequests);
- return;
- }
-
- mReportUnhandledFillRequest = false;
- throw new AssertionError(mFillRequests.size()
- + " unhandled fill requests: " + mFillRequests);
- }
-
- /**
- * Gets the current number of unhandled requests.
- */
- public int getNumberUnhandledFillRequests() {
- return mFillRequests.size();
- }
-
- public void setHandler(Handler handler) {
- mHandler = handler;
- }
-
- /**
- * Resets its internal state.
- */
- public void reset() {
- mResponses.clear();
- mFillRequests.clear();
- mExceptions = null;
- mOnSaveIntentSender = null;
- mAcceptedPackageName = null;
- mReportUnhandledFillRequest = true;
- mReportUnhandledSaveRequest = true;
- }
-
- public void onFillRequest(CancellationSignal cancellationSignal, FillCallback callback,
- InlineSuggestionsRequest inlineSuggestionsRequest) {
- try {
- CannedFillResponse response = null;
- try {
- response = mResponses.poll(CONNECTION_TIMEOUT.ms(), TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- Log.w(TAG, "Interrupted getting CannedResponse: " + e);
- Thread.currentThread().interrupt();
- addException(e);
- return;
- }
- if (response == null) {
- Log.d(TAG, "response is null");
- return;
- }
- if (response.getResponseType() == NULL) {
- Log.d(TAG, "onFillRequest(): replying with null");
- callback.onSuccess(null);
- return;
- }
-
- if (response.getResponseType() == TIMEOUT) {
- Log.d(TAG, "onFillRequest(): not replying at all");
- return;
- }
-
- if (response.getResponseType() == FAILURE) {
- Log.d(TAG, "onFillRequest(): replying with failure");
- callback.onFailure("D'OH!");
- return;
- }
-
- if (response.getResponseType() == CannedFillResponse.ResponseType.NO_MORE) {
- Log.w(TAG, "onFillRequest(): replying with null when not expecting more");
- addException(new IllegalStateException("got unexpected request"));
- callback.onSuccess(null);
- return;
- }
-
- final String failureMessage = response.getFailureMessage();
- if (failureMessage != null) {
- Log.v(TAG, "onFillRequest(): failureMessage = " + failureMessage);
- callback.onFailure(failureMessage);
- return;
- }
-
- final FillResponse fillResponse;
- fillResponse = response.asFillResponseWithAutofillId(null, mIdResolver);
-
- if (response.getResponseType() == CannedFillResponse.ResponseType.DELAY) {
- mHandler.postDelayed(() -> {
- Log.v(TAG,
- "onFillRequest(): fillResponse = " + fillResponse);
- callback.onSuccess(fillResponse);
- // Add a fill request to let test case know response was sent.
- Helper.offer(
- mFillRequests,
- new FillRequest(cancellationSignal, callback,
- inlineSuggestionsRequest),
- CONNECTION_TIMEOUT.ms());
- }, RESPONSE_DELAY_MS);
- } else {
- Log.v(TAG, "onFillRequest(): fillResponse = " + fillResponse);
- callback.onSuccess(fillResponse);
- }
- } catch (Throwable t) {
- Log.d(TAG, "onFillRequest(): catch a Throwable: " + t);
- addException(t);
- } finally {
- Helper.offer(
- mFillRequests,
- new FillRequest(cancellationSignal, callback,
- inlineSuggestionsRequest),
- CONNECTION_TIMEOUT.ms());
- }
- }
-
- private void onSaveRequest(List<FillContext> contexts, Bundle data, SaveCallback callback,
- List<String> datasetIds) {
- Log.d(TAG, "onSaveRequest(): sender=" + mOnSaveIntentSender);
-
- try {
- if (mOnSaveIntentSender != null) {
- callback.onSuccess(mOnSaveIntentSender);
- } else {
- callback.onSuccess();
- }
- } finally {
- //TODO
- }
- }
-
- private void dump(PrintWriter pw) {
- pw.print("mResponses: "); pw.println(mResponses);
- pw.print("mFillRequests: "); pw.println(mFillRequests);
- pw.print("mExceptions: "); pw.println(mExceptions);
- pw.print("mOnSaveIntentSender: "); pw.println(mOnSaveIntentSender);
- pw.print("mAcceptedPackageName: "); pw.println(mAcceptedPackageName);
- pw.print("mAcceptedPackageName: "); pw.println(mAcceptedPackageName);
- pw.print("mReportUnhandledFillRequest: "); pw.println(mReportUnhandledSaveRequest);
- }
- }
-}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
index 46a613f..0794739 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
@@ -801,19 +801,39 @@
* Creates an array of {@link AutofillId} mapped from the {@code structure} nodes with the given
* {@code resourceIds}.
*/
- public static AutofillId[] getAutofillIds(Function<String, AutofillId> autofillIdResolver,
+ public static AutofillId[] getAutofillIds(Function<String, ViewNode> nodeResolver,
String[] resourceIds) {
if (resourceIds == null) return null;
final AutofillId[] requiredIds = new AutofillId[resourceIds.length];
for (int i = 0; i < resourceIds.length; i++) {
final String resourceId = resourceIds[i];
- requiredIds[i] = autofillIdResolver.apply(resourceId);
+ final ViewNode node = nodeResolver.apply(resourceId);
+ if (node == null) {
+ throw new AssertionError("No node with resourceId " + resourceId);
+ }
+ requiredIds[i] = node.getAutofillId();
+
}
return requiredIds;
}
/**
+ * Get an {@link AutofillId} mapped from the {@code structure} node with the given
+ * {@code resourceId}.
+ */
+ public static AutofillId getAutofillId(Function<String, ViewNode> nodeResolver,
+ String resourceId) {
+ if (resourceId == null) return null;
+
+ final ViewNode node = nodeResolver.apply(resourceId);
+ if (node == null) {
+ throw new AssertionError("No node with resourceId " + resourceId);
+ }
+ return node.getAutofillId();
+ }
+
+ /**
* Prevents the screen to rotate by itself
*/
public static void disableAutoRotation(UiBot uiBot) throws Exception {
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraExtensionSessionTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraExtensionSessionTest.java
index 55bcd28..88bda03 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraExtensionSessionTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraExtensionSessionTest.java
@@ -26,6 +26,10 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.compatibility.common.util.Stat;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.ex.camera2.blocking.BlockingExtensionSessionCallback;
import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
@@ -47,6 +51,8 @@
import android.media.ExifInterface;
import android.media.Image;
import android.media.ImageReader;
+import android.os.SystemClock;
+import android.util.Range;
import android.util.Size;
import static android.hardware.camera2.cts.CameraTestUtils.*;
@@ -56,6 +62,7 @@
import android.view.Surface;
import android.view.TextureView;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import org.junit.Rule;
@@ -79,6 +86,8 @@
private SurfaceTexture mSurfaceTexture = null;
private Camera2AndroidTestRule mTestRule = null;
+ private DeviceReportLog mReportLog;
+
@Override
public void setUp() throws Exception {
super.setUp();
@@ -466,7 +475,8 @@
}
// Test case for multi-frame only capture on all supported extensions and expected state
- // callbacks. Verify still frame output.
+ // callbacks. Verify still frame output, measure the average capture latency and if possible
+ // ensure that the value is within the reported range.
@Test
public void testMultiFrameCapture() throws Exception {
final int IMAGE_COUNT = 10;
@@ -510,6 +520,12 @@
new ExtensionSessionConfiguration(extension, outputConfigs,
new HandlerExecutor(mTestRule.getHandler()),
sessionListener);
+ String streamName = "test_extension_capture";
+ mReportLog = new DeviceReportLog(REPORT_LOG_NAME, streamName);
+ mReportLog.addValue("camera_id", id, ResultType.NEUTRAL, ResultUnit.NONE);
+ mReportLog.addValue("extension_id", extension, ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ double[] captureTimes = new double[IMAGE_COUNT];
try {
mTestRule.openDevice(id);
@@ -534,11 +550,13 @@
jpegOrientation);
}
CaptureRequest request = captureBuilder.build();
+ long startTimeMs = SystemClock.elapsedRealtime();
int sequenceId = extensionSession.capture(request,
new HandlerExecutor(mTestRule.getHandler()), captureCallback);
Image img =
imageListener.getImage(MULTI_FRAME_CAPTURE_IMAGE_TIMEOUT_MS);
+ captureTimes[i] = SystemClock.elapsedRealtime() - startTimeMs;
if (captureFormat == ImageFormat.JPEG) {
verifyJpegOrientation(img, maxSize, jpegOrientation);
} else {
@@ -557,12 +575,32 @@
.onCaptureSequenceCompleted(extensionSession, sequenceId);
}
+ long avgCaptureLatency = (long) Stat.getAverage(captureTimes);
+ String resultFormat = "avg_latency size: " + maxSize.toString() +
+ " image format: " + captureFormat;
+ mReportLog.addValue(resultFormat, avgCaptureLatency,
+ ResultType.LOWER_BETTER, ResultUnit.MS);
+
verify(captureCallback, times(0))
.onCaptureSequenceAborted(any(CameraExtensionSession.class),
anyInt());
verify(captureCallback, times(0))
.onCaptureFailed(any(CameraExtensionSession.class),
any(CaptureRequest.class));
+ Range<Long> latencyRange =
+ extensionChars.getEstimatedCaptureLatencyRangeMillis(extension,
+ maxSize, captureFormat);
+ if (latencyRange != null) {
+ String msg = String.format("Camera [%s]: The measured average "
+ + "capture latency of %d ms. for extension type %d "
+ + "with image format: %d and size: %dx%d must be "
+ + "within the advertised range of [%d, %d] ms.",
+ id, avgCaptureLatency, extension, captureFormat,
+ maxSize.getWidth(), maxSize.getHeight(),
+ latencyRange.getLower(), latencyRange.getUpper());
+ assertTrue(msg, latencyRange.contains(avgCaptureLatency));
+ }
+
extensionSession.close();
@@ -572,6 +610,7 @@
} finally {
mTestRule.closeDevice(id);
extensionImageReader.close();
+ mReportLog.submit(InstrumentationRegistry.getInstrumentation());
}
}
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index a6b6bd1..f7953b0 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -25,6 +25,7 @@
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
@@ -2645,6 +2646,17 @@
jpegSize.getWidth() >= FULLHD.getWidth() &&
jpegSize.getHeight() >= FULLHD.getHeight());
}
+
+ // H-1-9
+ CameraExtensionCharacteristics extensionChars =
+ mCameraManager.getCameraExtensionCharacteristics(cameraId);
+ List<Integer> supportedExtensions = extensionChars.getSupportedExtensions();
+ mCollector.expectTrue(
+ "Primary rear/front camera must support the HDR camera2 extension",
+ supportedExtensions.contains(CameraExtensionCharacteristics.EXTENSION_HDR));
+ mCollector.expectTrue(
+ "Primary rear/front camera must support the NIGHT camera2 extension",
+ supportedExtensions.contains(CameraExtensionCharacteristics.EXTENSION_NIGHT));
}
mCollector.expectTrue("There must be a primary rear camera for S performance class.",
hasPrimaryRear);
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index dc34087..b060dbc 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -61,7 +61,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int WAIT_FOR_FRAMES_TIMEOUT_MS = 3000;
- private static final int WAIT_FOR_PICTURE_TIMEOUT_MS = 5000;
+ private static final int WAIT_FOR_PICTURE_TIMEOUT_MS = 8000;
private static final int FRAMES_TO_WAIT_FOR_CAPTURE = 100;
@Presubmit
@@ -167,16 +167,20 @@
}
private Object mPictureSignal = new Object();
+ private boolean mGotPicture = false;
private byte[] mPictureData = null;
public byte[] waitForPicture() {
+ Log.i(TAG, "waitForPicture called");
synchronized(mPictureSignal) {
boolean waited = false;
while (!waited) {
try {
mPictureSignal.wait(WAIT_FOR_PICTURE_TIMEOUT_MS);
waited = true;
+ Log.i(TAG, "waitForPicture returned with mGotPicture = " + mGotPicture);
} catch (InterruptedException e) {
+ Log.e(TAG, "waitForPicture gets interrupted exception!");
}
}
return mPictureData;
@@ -184,8 +188,10 @@
}
public void onPictureTaken(byte[] data, Camera camera) {
+ Log.i(TAG, "onPictureTaken called");
synchronized(mPictureSignal) {
mPictureData = data;
+ mGotPicture = true;
mPictureSignal.notifyAll();
}
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
index 48f6d32..9009f9f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -16,6 +16,8 @@
package android.hardware.camera2.cts;
+import static android.hardware.camera2.cts.CameraTestUtils.REPORT_LOG_NAME;
+
import static com.android.ex.camera2.blocking.BlockingSessionCallback.SESSION_CLOSED;
import static org.junit.Assert.assertNotNull;
@@ -78,7 +80,6 @@
@RunWith(JUnit4.class)
public class PerformanceTest {
private static final String TAG = "PerformanceTest";
- private static final String REPORT_LOG_NAME = "CtsCameraTestCases";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int NUM_TEST_LOOPS = 10;
private static final int NUM_MAX_IMAGES = 4;
@@ -321,6 +322,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 +378,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 +457,7 @@
ResultUnit.MS);
avgResultTimes[counter] = Stat.getAverage(getResultTimes);
+ avgCaptureTimes[counter] = Stat.getAverage(captureTimes);
}
finally {
CameraTestUtils.closeImageReaders(readers);
@@ -470,10 +474,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 713d132..a479b3b 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;
@@ -192,6 +194,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/multiprocess/camera/cts/CameraEvictionTest.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
index 14dbd92..316afd7 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -18,21 +18,27 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.cts.CameraTestUtils.HandlerExecutor;
import android.hardware.cts.CameraCtsActivity;
import android.os.Handler;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
+import android.Manifest;
+
+import androidx.test.InstrumentationRegistry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
import static org.mockito.Mockito.*;
@@ -49,12 +55,15 @@
private static final int EVICTION_TIMEOUT = 1000; // Remote camera eviction timeout (ms).
private static final int WAIT_TIME = 2000; // Time to wait for process to launch (ms).
private static final int UI_TIMEOUT = 10000; // Time to wait for UI event before timeout (ms).
+ // CACHED_APP_MAX_ADJ - FG oom score
+ private static final int CACHED_APP_VS_FG_OOM_DELTA = 999;
ErrorLoggingService.ErrorServiceConnection mErrorServiceConnection;
private ActivityManager mActivityManager;
private Context mContext;
private Camera mCamera;
private CameraDevice mCameraDevice;
+ private UiAutomation mUiAutomation;
private final Object mLock = new Object();
private boolean mCompleted = false;
private int mProcessPid = -1;
@@ -122,9 +131,10 @@
mCompleted = false;
getActivity();
- mContext = getInstrumentation().getTargetContext();
+ mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ mContext = InstrumentationRegistry.getTargetContext();
System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ mActivityManager = mContext.getSystemService(ActivityManager.class);
mErrorServiceConnection = new ErrorLoggingService.ErrorServiceConnection(mContext);
mErrorServiceConnection.start();
}
@@ -159,11 +169,22 @@
testAPI1ActivityEviction(Camera1Activity.class, "camera1ActivityProcess");
}
+ public void testBasicCamera2ActivityEviction() throws Throwable {
+ testBasicCamera2ActivityEvictionInternal(/*lowerPriority*/ false);
+ }
+
+ public void testBasicCamera2ActivityEvictionOomScoreOffset() throws Throwable {
+ testBasicCamera2ActivityEvictionInternal(/*lowerPriority*/ true);
+ }
/**
* Test basic eviction scenarios for the Camera2 API.
*/
- public void testBasicCamera2ActivityEviction() throws Throwable {
- CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ private void testBasicCamera2ActivityEvictionInternal(boolean lowerPriority) throws Throwable {
+ UiAutomation uiAutomation = null;
+ if (lowerPriority && mUiAutomation != null) {
+ mUiAutomation.adoptShellPermissionIdentity();
+ }
+ CameraManager manager = mContext.getSystemService(CameraManager.class);
assertNotNull(manager);
String[] cameraIds = manager.getCameraIdListNoLazy();
@@ -236,8 +257,17 @@
verify(spyStateCb, times(1)).onOpened(any(CameraDevice.class));
// Verify that we can no longer open the camera, as it is held by a higher priority process
- try {
- manager.openCamera(chosenCamera, spyStateCb, cameraHandler);
+ try {
+ if (!lowerPriority) {
+ manager.openCamera(chosenCamera, spyStateCb, cameraHandler);
+ } else {
+ // Go to top again, try getting hold of camera with priority lowered, we should get
+ // an exception
+ Executor cameraExecutor = new HandlerExecutor(cameraHandler);
+ forceCtsActivityToTop();
+ manager.openCamera(chosenCamera, CACHED_APP_VS_FG_OOM_DELTA, cameraExecutor,
+ spyStateCb);
+ }
fail("Didn't receive exception when trying to open camera held by higher priority " +
"process.");
} catch(CameraAccessException e) {
@@ -260,14 +290,59 @@
android.os.Process.killProcess(mProcessPid);
mProcessPid = -1;
forceCtsActivityToTop();
+ if (lowerPriority && mUiAutomation != null) {
+ mUiAutomation.dropShellPermissionIdentity();
+ }
}
/**
+ * Tests that a client without SYSTEM_CAMERA permissions receives a security exception when
+ * trying to modify the oom score for camera framework.
+ */
+ public void testCamera2OomScoreOffsetPermissions() throws Throwable {
+ CameraManager manager = mContext.getSystemService(CameraManager.class);
+ assertNotNull(manager);
+ String[] cameraIds = manager.getCameraIdListNoLazy();
+
+ if (cameraIds.length == 0) {
+ Log.i(TAG, "Skipping testBasicCamera2OomScoreOffsetPermissions, no cameras present.");
+ return;
+ }
+
+ assertTrue(mContext.getMainLooper() != null);
+ for (String cameraId : cameraIds) {
+ // Setup camera manager
+ Handler cameraHandler = new Handler(mContext.getMainLooper());
+ final CameraManager.AvailabilityCallback mockAvailCb =
+ mock(CameraManager.AvailabilityCallback.class);
+
+ final CameraDevice.StateCallback spyStateCb = spy(new StateCallbackImpl());
+ manager.registerAvailabilityCallback(mockAvailCb, cameraHandler);
+
+ Thread.sleep(WAIT_TIME);
+
+ verify(mockAvailCb, times(1)).onCameraAvailable(cameraId);
+ verify(mockAvailCb, never()).onCameraUnavailable(cameraId);
+
+ try {
+ // Go to top again, try getting hold of camera with priority lowered, we should get
+ // an exception
+ Executor cameraExecutor = new HandlerExecutor(cameraHandler);
+ manager.openCamera(cameraId, CACHED_APP_VS_FG_OOM_DELTA, cameraExecutor,
+ spyStateCb);
+ fail("Didn't receive security exception when trying to open camera with modifed" +
+ "oom score without SYSTEM_CAMERA permissions");
+ } catch(SecurityException e) {
+ // fine
+ }
+ }
+ }
+ /**
* Test camera availability access callback.
*/
public void testCamera2AccessCallback() throws Throwable {
int PERMISSION_CALLBACK_TIMEOUT_MS = 2000;
- CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ CameraManager manager = mContext.getSystemService(CameraManager.class);
assertNotNull(manager);
String[] cameraIds = manager.getCameraIdListNoLazy();
@@ -303,7 +378,7 @@
*/
public void testCamera2NativeAccessCallback() throws Throwable {
int PERMISSION_CALLBACK_TIMEOUT_MS = 2000;
- CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ CameraManager manager = mContext.getSystemService(CameraManager.class);
assertNotNull(manager);
String[] cameraIds = manager.getCameraIdListNoLazy();
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 7a91b1c..831641a 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -128,6 +128,7 @@
public static final int MAX_READER_IMAGES = 5;
public static final String OFFLINE_CAMERA_ID = "offline_camera_id";
+ public static final String REPORT_LOG_NAME = "CtsCameraTestCases";
private static final int EXIF_DATETIME_LENGTH = 19;
private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60;
@@ -3507,11 +3508,13 @@
return zoomRatios;
}
+ 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 Build.VERSION.MEDIA_PERFORMANCE_CLASS == Build.VERSION_CODES.S;
+ return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S;
}
/**
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AppKilledTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AppKilledTest.java
index cc753e6..9b71320 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AppKilledTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/AppKilledTest.java
@@ -17,29 +17,64 @@
package android.contentcaptureservice.cts;
import static android.contentcaptureservice.cts.Assertions.assertNoEvents;
-import static android.contentcaptureservice.cts.OutOfProcessActivity.killOutOfProcessActivity;
-import static android.contentcaptureservice.cts.OutOfProcessActivity.startAndWaitOutOfProcessActivity;
+import static android.contentcaptureservice.cts.Helper.sContext;
+import android.app.PendingIntent;
+import android.content.Intent;
import android.contentcaptureservice.cts.CtsContentCaptureService.Session;
+import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
+import org.junit.After;
import org.junit.Test;
@AppModeFull(reason = "BlankWithTitleActivityTest is enough")
public class AppKilledTest extends AbstractContentCaptureIntegrationActivityLessTest {
+ BlockingBroadcastReceiver mReceiver;
+
@Test
public void testDoIt() throws Exception {
final CtsContentCaptureService service = enableService();
- startAndWaitOutOfProcessActivity();
+ startOutOfProcessActivity();
+ // We should send command to kill process before using BlockingBroadcastReceiver to wait
+ // start signal. Othereise, it may cause the cc starts to send events.
+ OutOfProcessActivity.killOutOfProcessActivity();
- killOutOfProcessActivity();
+ // wait Activity started
+ mReceiver.awaitForBroadcast();
final Session session = service.getOnlyFinishedSession();
Log.v(mTag, "session id: " + session.id);
assertNoEvents(session, OutOfProcessActivity.COMPONENT_NAME);
}
+
+ @After
+ public void cleanup() throws Exception {
+ if (mReceiver != null) {
+ mReceiver.unregisterQuietly();
+ }
+ }
+
+ private void startOutOfProcessActivity() {
+ final String actionActivityStart =
+ "ACTION_ACTIVITY_ON_START_" + SystemClock.uptimeMillis();
+ mReceiver = new BlockingBroadcastReceiver(sContext,
+ actionActivityStart);
+ mReceiver.register();
+ final PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(
+ sContext,
+ 0,
+ new Intent(actionActivityStart),
+ PendingIntent.FLAG_IMMUTABLE);
+ OutOfProcessActivity.launchOutOfProcessActivity(pendingIntent,
+ OutOfProcessActivity.ACTION_KILL_APP_TEST,
+ OutOfProcessActivity.EXTRA_ON_START_BROADCAST);
+ }
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
index 6ed8553..8f5899c 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/Helper.java
@@ -57,6 +57,8 @@
public static final String MY_SECOND_PACKAGE = "android.contentcaptureservice.cts2";
public static final String OTHER_PACKAGE = "NOT.android.contentcaptureservice.cts";
+ public static final String EXTRA_VERIFY_RESULT = "verify_result";
+
public static final Set<String> NO_PACKAGES = null;
public static final Set<ComponentName> NO_ACTIVITIES = null;
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/OutOfProcessActivity.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/OutOfProcessActivity.java
index 859eed2..4b8c02b 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/OutOfProcessActivity.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/OutOfProcessActivity.java
@@ -15,65 +15,65 @@
*/
package android.contentcaptureservice.cts;
-import static android.contentcaptureservice.cts.Helper.eventually;
+import static android.contentcaptureservice.cts.Helper.EXTRA_VERIFY_RESULT;
import static android.contentcaptureservice.cts.Helper.sContext;
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import android.app.Activity;
+import android.app.PendingIntent;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.contentcapture.ContentCaptureManager;
-import androidx.annotation.NonNull;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.nio.file.StandardOpenOption;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
/**
* Activity that is running out of the CTS test process.
*
- * <p>As such, it uses files to keep track of lifecycle events.
+ * <p>As such, it uses {@link BlockingBroadcastReceiver} to notify lifecycle events.
*/
public class OutOfProcessActivity extends Activity {
private static final String TAG = OutOfProcessActivity.class.getSimpleName();
- private static final String ACTION_EXTRA = "action";
+ static final String EXTRA_ACTION = "action";
+ static final String EXTRA_FINISH_BROADCAST = "finishBroadcast";
+ static final String EXTRA_ON_START_BROADCAST = "onStartBroadcast";
public static final ComponentName COMPONENT_NAME = new ComponentName(Helper.MY_PACKAGE,
OutOfProcessActivity.class.getName());
public static final int ACTION_NONE = 0;
public static final int ACTION_CHECK_MANAGER_AND_FINISH = 1;
+ public static final int ACTION_KILL_APP_TEST = 2;
public static final String NO_MANAGER = "NoMgr";
public static final String HAS_MANAGER = "Mgr:";
+ private Intent mIntent;
+
@Override
protected void onStart() {
Log.i(TAG, "onStart()");
super.onStart();
- final int action = getIntent().getIntExtra(ACTION_EXTRA, ACTION_NONE);
- final File marker = getStartedMarker(this);
-
- try {
- if (!marker.createNewFile()) {
- Log.e(TAG, "cannot write started file");
- }
- } catch (IOException e) {
- Log.e(TAG, "cannot write started file: " + e);
- }
- if (action == ACTION_CHECK_MANAGER_AND_FINISH) {
- final ContentCaptureManager ccm = getSystemService(ContentCaptureManager.class);
- write(marker, ccm == null ? NO_MANAGER : HAS_MANAGER + ccm);
- Log.v(TAG, "So Long, and Thanks for All the Fish!");
+ mIntent = getIntent();
+ int startAction = mIntent.getIntExtra(EXTRA_ACTION, ACTION_NONE);
+ if (startAction == ACTION_CHECK_MANAGER_AND_FINISH) {
finish();
+ } else if (startAction == ACTION_KILL_APP_TEST) {
+ final PendingIntent pendingIntent =
+ mIntent.getParcelableExtra(EXTRA_ON_START_BROADCAST);
+ if (pendingIntent != null) {
+ try {
+ pendingIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Pending intent " + pendingIntent + " canceled");
+ }
+ }
+ } else {
+ Log.e(TAG, "Unexpected start action.");
}
}
@@ -82,90 +82,39 @@
Log.i(TAG, "onStop()");
super.onStop();
- try {
- if (!getStoppedMarker(this).createNewFile()) {
- Log.e(TAG, "could not write stopped marker");
- } else {
- Log.v(TAG, "wrote stopped marker");
+ notifyContentCaptureStatus();
+ }
+
+ private void notifyContentCaptureStatus() {
+ final int action = mIntent.getIntExtra(EXTRA_ACTION, ACTION_NONE);
+ final PendingIntent pendingIntent = mIntent.getParcelableExtra(EXTRA_FINISH_BROADCAST);
+ if (action == ACTION_CHECK_MANAGER_AND_FINISH) {
+ final ContentCaptureManager ccm = getSystemService(ContentCaptureManager.class);
+ final String checkResult = ccm == null ? NO_MANAGER : HAS_MANAGER + ccm;
+ if (pendingIntent != null) {
+ try {
+ final Intent result = new Intent();
+ result.putExtra(EXTRA_VERIFY_RESULT, checkResult);
+ pendingIntent.send(this, 0, result);
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Pending intent " + pendingIntent + " canceled");
+ }
+ Log.v(TAG, "So Long, and Thanks for All the Fish!");
}
- } catch (IOException e) {
- Log.e(TAG, "could write stopped marker: " + e);
}
}
- /**
- * Gets the file that signals that the activity has entered {@link Activity#onStop()}.
- *
- * @param context Context of the app
- * @return The marker file that is written onStop()
- */
- @NonNull public static File getStoppedMarker(@NonNull Context context) {
- return new File(context.getFilesDir(), "stopped");
- }
-
- /**
- * Gets the file that signals that the activity has entered {@link Activity#onStart()}.
- *
- * @param context Context of the app
- * @return The marker file that is written onStart()
- */
- @NonNull public static File getStartedMarker(@NonNull Context context) {
- return new File(context.getFilesDir(), "started");
- }
-
- public static void startAndWaitOutOfProcessActivity() throws Exception {
- final Intent startIntent = new Intent(sContext,
- OutOfProcessActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getStartedMarker(sContext).delete();
+ static void launchOutOfProcessActivity(PendingIntent pendingIntent, int startAction,
+ String extraPendingIntent) {
+ final Intent startIntent = new Intent(sContext, OutOfProcessActivity.class)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startIntent.putExtra(extraPendingIntent, pendingIntent);
+ startIntent.putExtra(EXTRA_ACTION, startAction);
sContext.startActivity(startIntent);
- eventually("getStartedMarker()", () -> {
- return getStartedMarker(sContext).exists();
- });
- getStartedMarker(sContext).delete();
}
- public static void startActivity(@NonNull Context context, int action) throws Exception {
- final Intent startIntent = new Intent(context,
- OutOfProcessActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (action != ACTION_NONE) {
- startIntent.putExtra(ACTION_EXTRA, action);
- }
- getStartedMarker(context).delete();
- context.startActivity(startIntent);
- }
-
- public static File waitActivityStopped(@NonNull Context context) throws Exception {
- return eventually("getStoppedMarker()", () -> {
- final File file = getStoppedMarker(context);
- return file.exists() ? file : null;
- });
- }
-
- public static File waitActivityStarted(@NonNull Context context) throws Exception {
- return eventually("getStartedMarker()", () -> {
- final File file = getStartedMarker(context);
- return file.exists() ? file : null;
- });
- }
-
- public static void killOutOfProcessActivity() throws Exception {
- // Waiting for activity to stop (stop marker appears)
- eventually("getStoppedMarker()", () -> {
- return getStoppedMarker(sContext).exists();
- });
-
- // Kill it!
+ static void killOutOfProcessActivity() {
runShellCommand("am broadcast --receiver-foreground "
+ "-n android.contentcaptureservice.cts/.SelfDestructReceiver");
}
-
- private void write(@NonNull File file, @NonNull String string) {
- Log.d(TAG, "writing '" + string + "' to " + file);
- try {
- Files.write(Paths.get(file.getAbsolutePath()), string.getBytes(),
- StandardOpenOption.TRUNCATE_EXISTING);
- } catch (IOException e) {
- Log.e(TAG, "error writing to " + file, e);
- }
- }
}
diff --git a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/WhitelistTest.java b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/WhitelistTest.java
index 13d6037..58f1809 100644
--- a/tests/contentcaptureservice/src/android/contentcaptureservice/cts/WhitelistTest.java
+++ b/tests/contentcaptureservice/src/android/contentcaptureservice/cts/WhitelistTest.java
@@ -16,22 +16,20 @@
package android.contentcaptureservice.cts;
+import static android.contentcaptureservice.cts.Helper.EXTRA_VERIFY_RESULT;
import static android.contentcaptureservice.cts.Helper.MY_PACKAGE;
-import static android.contentcaptureservice.cts.Helper.MY_SECOND_PACKAGE;
import static android.contentcaptureservice.cts.Helper.NO_ACTIVITIES;
import static android.contentcaptureservice.cts.Helper.NO_PACKAGES;
-import static android.contentcaptureservice.cts.Helper.read;
import static android.contentcaptureservice.cts.Helper.sContext;
import static android.contentcaptureservice.cts.Helper.toSet;
-import static android.contentcaptureservice.cts.OutOfProcessActivity.ACTION_CHECK_MANAGER_AND_FINISH;
-
-import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import static com.google.common.truth.Truth.assertThat;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
+import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.service.contentcapture.ContentCaptureService;
import android.support.test.uiautomator.UiDevice;
@@ -40,10 +38,11 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ServiceTestRule;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
import org.junit.Ignore;
import org.junit.Test;
-import java.io.File;
import java.util.Set;
/**
@@ -105,7 +104,6 @@
@Test
public void testRinseAndRepeat() throws Exception {
-
// Right package
final CtsContentCaptureService service = enableService(toSet(MY_PACKAGE), NO_ACTIVITIES);
assertWhitelisted(service);
@@ -133,16 +131,34 @@
try {
assertThat(service.isContentCaptureManagerAvailable()).isEqualTo(isAvailable);
} finally {
- killService();
+ OutOfProcessActivity.killOutOfProcessActivity();
}
}
private void launchActivityAndAssert(@NonNull CtsContentCaptureService service,
- boolean expectHasManager) throws Exception {
- OutOfProcessActivity.startActivity(sContext, ACTION_CHECK_MANAGER_AND_FINISH);
- final File startedMarker = OutOfProcessActivity.waitActivityStarted(sContext);
+ boolean expectHasManager) {
+ final String actionActivityFinish =
+ "ACTION_ACTIVITY_FINISH_" + SystemClock.uptimeMillis();
+ final BlockingBroadcastReceiver receiver =
+ new BlockingBroadcastReceiver(sContext, actionActivityFinish);
+ receiver.register();
+
+ final PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(
+ sContext,
+ 0,
+ new Intent(actionActivityFinish),
+ PendingIntent.FLAG_MUTABLE);
+ OutOfProcessActivity.launchOutOfProcessActivity(pendingIntent,
+ OutOfProcessActivity.ACTION_CHECK_MANAGER_AND_FINISH,
+ OutOfProcessActivity.EXTRA_FINISH_BROADCAST);
+
try {
- final String result = read(startedMarker);
+ // Wait received result
+ final Intent intent = receiver.awaitForBroadcast();
+ assertThat(intent).isNotNull();
+
+ String result = intent.getStringExtra(EXTRA_VERIFY_RESULT);
if (expectHasManager) {
assertThat(result).startsWith(OutOfProcessActivity.HAS_MANAGER);
} else {
@@ -153,6 +169,8 @@
// CaptureOptions state in the application context
service.setIgnoreOrphanSessionEvents(true); //
OutOfProcessActivity.killOutOfProcessActivity();
+ // unregister receiver
+ receiver.unregisterQuietly();
}
}
@@ -162,12 +180,7 @@
"android.contentcaptureservice.cts",
"android.contentcaptureservice.cts.OutOfProcessDataSharingService"
));
- IBinder service = mServiceRule.bindService(outsideService);
+ IBinder service = mServiceRule.bindService(outsideService);
return IOutOfProcessDataSharingService.Stub.asInterface(service);
}
-
- private void killService() {
- runShellCommand("am broadcast --receiver-foreground "
- + "-n android.contentcaptureservice.cts/.SelfDestructReceiver");
- }
}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java
index 9704d98..64c1783 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CredentialManagementAppTest.java
@@ -26,7 +26,6 @@
import static org.testng.Assert.assertThrows;
import android.app.AppOpsManager;
-import android.app.UiAutomation;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.net.Uri;
@@ -67,7 +66,6 @@
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.TimeUnit;
@RunWith(BedsteadJUnit4.class)
public class CredentialManagementAppTest {
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileAppsTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileAppsTest.java
index 9c7c8b7..da55b9e 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileAppsTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileAppsTest.java
@@ -40,7 +40,6 @@
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.OptionalBoolean;
import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
import com.android.bedstead.harrier.annotations.Postsubmit;
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java
index f01c382..b7f0ec2 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java
@@ -46,10 +46,10 @@
import com.android.bedstead.harrier.DeviceState;
import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile;
import com.android.bedstead.harrier.annotations.EnsureHasPermission;
-import com.android.bedstead.harrier.annotations.Postsubmit;
-import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeatures;
-import com.android.bedstead.harrier.annotations.RequireFeatures;
+import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeature;
+import com.android.bedstead.harrier.annotations.RequireFeature;
import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
+import com.android.bedstead.harrier.annotations.Postsubmit;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.permissions.PermissionContext;
@@ -111,10 +111,8 @@
@RequireRunOnPrimaryUser
@EnsureHasNoWorkProfile
- @RequireFeatures({
- PackageManager.FEATURE_DEVICE_ADMIN,
- PackageManager.FEATURE_MANAGED_USERS
- })
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@Test
@Postsubmit(reason="b/181207615 flaky")
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@@ -135,10 +133,8 @@
@RequireRunOnPrimaryUser
@EnsureHasNoWorkProfile
- @RequireFeatures({
- PackageManager.FEATURE_DEVICE_ADMIN,
- PackageManager.FEATURE_MANAGED_USERS
- })
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@Test
@Postsubmit(reason="b/181207615 flaky")
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@@ -159,10 +155,8 @@
@RequireRunOnPrimaryUser
@EnsureHasNoWorkProfile
- @RequireFeatures({
- PackageManager.FEATURE_DEVICE_ADMIN,
- PackageManager.FEATURE_MANAGED_USERS
- })
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@Test
@Postsubmit(reason="b/181207615 flaky")
@EnsureHasPermission({MANAGE_PROFILE_AND_DEVICE_OWNERS, INTERACT_ACROSS_USERS_FULL})
@@ -185,10 +179,8 @@
@RequireRunOnPrimaryUser
@EnsureHasNoWorkProfile
- @RequireFeatures({
- PackageManager.FEATURE_DEVICE_ADMIN,
- PackageManager.FEATURE_MANAGED_USERS
- })
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@Test
@Postsubmit(reason="b/181207615 flaky")
@EnsureHasPermission({MANAGE_PROFILE_AND_DEVICE_OWNERS, INTERACT_ACROSS_USERS})
@@ -210,10 +202,8 @@
@RequireRunOnPrimaryUser
@EnsureHasNoWorkProfile
- @RequireFeatures({
- PackageManager.FEATURE_DEVICE_ADMIN,
- PackageManager.FEATURE_MANAGED_USERS
- })
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@Test
@Postsubmit(reason="new test")
@Ignore
@@ -238,10 +228,8 @@
@RequireRunOnPrimaryUser
@EnsureHasNoWorkProfile
- @RequireFeatures({
- PackageManager.FEATURE_DEVICE_ADMIN,
- PackageManager.FEATURE_MANAGED_USERS
- })
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@Test
@Postsubmit(reason="new test")
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@@ -266,10 +254,8 @@
@RequireRunOnPrimaryUser
@EnsureHasNoWorkProfile
- @RequireFeatures({
- PackageManager.FEATURE_DEVICE_ADMIN,
- PackageManager.FEATURE_MANAGED_USERS
- })
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@Test
@Ignore
@Postsubmit(reason="new test")
@@ -295,10 +281,8 @@
@RequireRunOnPrimaryUser
@EnsureHasNoWorkProfile
- @RequireFeatures({
- PackageManager.FEATURE_DEVICE_ADMIN,
- PackageManager.FEATURE_MANAGED_USERS
- })
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@Test
@Postsubmit(reason="new test")
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@@ -324,10 +308,8 @@
@RequireRunOnPrimaryUser
@EnsureHasNoWorkProfile
- @RequireFeatures({
- PackageManager.FEATURE_DEVICE_ADMIN,
- PackageManager.FEATURE_MANAGED_USERS
- })
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@Test
@Postsubmit(reason="new test")
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@@ -440,7 +422,7 @@
}
@RequireRunOnPrimaryUser
- @RequireFeatures(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
@Test
@EnsureHasPermission({MANAGE_PROFILE_AND_DEVICE_OWNERS})
public void newlyProvisionedFullyManagedDevice_setsDeviceOwner() throws Exception {
@@ -459,7 +441,7 @@
}
@RequireRunOnPrimaryUser
- @RequireFeatures(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
@Test
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
public void newlyProvisionedFullyManagedDevice_doesNotThrowException() throws Exception {
@@ -476,7 +458,7 @@
}
@RequireRunOnPrimaryUser
- @RequireFeatures(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
@Test
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
public void newlyProvisionedFullyManagedDevice_canControlSensorPermissionGrantsByDefault()
@@ -496,7 +478,7 @@
}
@RequireRunOnPrimaryUser
- @RequireFeatures(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
@Test
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
public void newlyProvisionedFullyManagedDevice_canOptOutOfControllingSensorPermissionGrants()
@@ -518,7 +500,7 @@
}
@RequireRunOnPrimaryUser
- @RequireFeatures(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
@Test
@Postsubmit(reason="new test")
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@@ -542,8 +524,8 @@
}
}
- @RequireFeatures(PackageManager.FEATURE_DEVICE_ADMIN)
- @RequireDoesNotHaveFeatures(PackageManager.FEATURE_AUTOMOTIVE)
+
+ @RequireDoesNotHaveFeature(PackageManager.FEATURE_AUTOMOTIVE)
@EnsureHasPermission(MANAGE_DEVICE_ADMINS)
@Test
public void getPolicyExemptAppsCanOnlyBeDefinedOnAutomotiveBuilds() throws Exception {
@@ -566,14 +548,14 @@
try (PermissionContext p = sTestApis.permissions().withPermission(WRITE_SECURE_SETTINGS)) {
Settings.Secure.putInt(sContext.getContentResolver(), USER_SETUP_COMPLETE_KEY, 0);
}
- sDevicePolicyManager.forceUpdateUserSetupComplete();
+ sDevicePolicyManager.forceUpdateUserSetupComplete(sContext.getUserId());
}
private void setUserSetupCompletedFlag() {
try (PermissionContext p = sTestApis.permissions().withPermission(WRITE_SECURE_SETTINGS)) {
Settings.Secure.putInt(sContext.getContentResolver(), USER_SETUP_COMPLETE_KEY, 1);
}
- sDevicePolicyManager.forceUpdateUserSetupComplete();
+ sDevicePolicyManager.forceUpdateUserSetupComplete(sContext.getUserId());
}
private Set<String> findSystemApps() {
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java
new file mode 100644
index 0000000..f834edc
--- /dev/null
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.devicepolicy.cts;
+
+import static com.android.bedstead.nene.permissions.Permissions.MANAGE_DEVICE_ADMINS;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeFalse;
+import static org.testng.Assert.assertThrows;
+
+import android.app.admin.DevicePolicyManager;
+
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.EnsureHasPermission;
+import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest;
+import com.android.bedstead.harrier.annotations.enterprise.NegativePolicyTest;
+import com.android.bedstead.harrier.annotations.enterprise.PositivePolicyTest;
+import com.android.bedstead.harrier.annotations.Postsubmit;
+import com.android.bedstead.harrier.policies.LockTaskPackages;
+import com.android.bedstead.nene.TestApis;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Set;
+
+@RunWith(BedsteadJUnit4.class)
+public class LockTaskTest {
+
+ private static final String PACKAGE_NAME = "com.android.package.test";
+
+ @ClassRule @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
+ private static final TestApis sTestApis = new TestApis();
+
+ private static final DevicePolicyManager mDevicePolicyManager =
+ sTestApis.context().instrumentedContext().getSystemService(DevicePolicyManager.class);
+
+ @Test
+ @Postsubmit(reason = "New test")
+ // TODO(scottjonathan): This omits the metrics test
+ @PositivePolicyTest(policy = LockTaskPackages.class)
+ public void setLockTaskPackages_lockTaskPackagesIsSet() {
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{PACKAGE_NAME});
+
+ try {
+ assertThat(sDeviceState.dpc().devicePolicyManager().getLockTaskPackages()).asList()
+ .containsExactly(PACKAGE_NAME);
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ @Test
+ @Postsubmit(reason = "New test")
+ // TODO(scottjonathan): This omits the metrics test
+ @PositivePolicyTest(policy = LockTaskPackages.class)
+ public void setLockTaskPackages_empty_lockTaskPackagesIsSet() {
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{});
+
+ try {
+ assertThat(sDeviceState.dpc().devicePolicyManager().getLockTaskPackages()).asList()
+ .isEmpty();
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ @Test
+ @Postsubmit(reason = "New test")
+ // TODO(scottjonathan): This omits the metrics test
+ @PositivePolicyTest(policy = LockTaskPackages.class)
+ @EnsureHasPermission(MANAGE_DEVICE_ADMINS) // Used for getPolicyExemptApps
+ public void setLockTaskPackages_includesPolicyExemptApp_lockTaskPackagesIsSet() {
+ Set<String> policyExemptApps = mDevicePolicyManager.getPolicyExemptApps();
+ assumeFalse("OEM does not define any policy-exempt apps",
+ policyExemptApps.isEmpty());
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+ String policyExemptApp = policyExemptApps.iterator().next();
+
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{policyExemptApp});
+
+ try {
+ assertThat(sDeviceState.dpc().devicePolicyManager().getLockTaskPackages()).asList()
+ .containsExactly(policyExemptApp);
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ @Test
+ @Postsubmit(reason = "New test")
+ @CannotSetPolicyTest(policy = LockTaskPackages.class)
+ public void setLockTaskPackages_policyIsNotAllowedToBeSet_throwsException() {
+ assertThrows(SecurityException.class,
+ () -> sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{}));
+ }
+
+ @Test
+ @Postsubmit(reason = "New test")
+ // TODO(scottjonathan): This omits the metrics test
+ @PositivePolicyTest(policy = LockTaskPackages.class)
+ public void isLockTaskPermitted_lockTaskPackageIsSet_returnsTrue() {
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{PACKAGE_NAME});
+
+ try {
+ assertThat(mDevicePolicyManager.isLockTaskPermitted(PACKAGE_NAME)).isTrue();
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ @Test
+ @Postsubmit(reason = "New test")
+ @NegativePolicyTest(policy = LockTaskPackages.class)
+ // TODO(scottjonathan): Confirm expected behaviour here
+ public void isLockTaskPermitted_lockTaskPackageIsSet_policyDoesntApply_returnsFalse() {
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{PACKAGE_NAME});
+
+ try {
+ assertThat(mDevicePolicyManager.isLockTaskPermitted(PACKAGE_NAME)).isFalse();
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ @Test
+ @Postsubmit(reason = "New test")
+ // TODO(scottjonathan): This omits the metrics test
+ @PositivePolicyTest(policy = LockTaskPackages.class)
+ public void isLockTaskPermitted_lockTaskPackageIsNotSet_returnsFalse() {
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{});
+
+ try {
+ assertThat(mDevicePolicyManager.isLockTaskPermitted(PACKAGE_NAME)).isFalse();
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ @Test
+ @Postsubmit(reason = "New test")
+ @PositivePolicyTest(policy = LockTaskPackages.class)
+ @EnsureHasPermission(MANAGE_DEVICE_ADMINS) // Used for getPolicyExemptApps
+ public void isLockTaskPermitted_includesPolicyExemptApps() {
+ Set<String> policyExemptApps = mDevicePolicyManager.getPolicyExemptApps();
+ // TODO(b/188035301): Add a unit test which ensures this actually gets tested
+ assumeFalse("OEM does not define any policy-exempt apps",
+ policyExemptApps.isEmpty());
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+
+ try {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{});
+
+ for (String app : policyExemptApps) {
+ assertWithMessage("isLockTaskPermitted(%s)", app)
+ .that(mDevicePolicyManager.isLockTaskPermitted(app)).isTrue();
+ }
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/NegativeCallAuthorizationTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/NegativeCallAuthorizationTest.java
index a759dcd..7b3ff7e 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/NegativeCallAuthorizationTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/NegativeCallAuthorizationTest.java
@@ -27,7 +27,7 @@
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.RequireFeatures;
+import com.android.bedstead.harrier.annotations.RequireFeature;
import org.junit.ClassRule;
import org.junit.Rule;
@@ -53,13 +53,13 @@
public static final DeviceState sDeviceState = new DeviceState();
@Test
- @RequireFeatures(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
public void testHasKeyPair_failIfNotOwner() {
assertThrows(SecurityException.class, () -> sDpm.hasKeyPair(ALIAS));
}
@Test
- @RequireFeatures(PackageManager.FEATURE_DEVICE_ADMIN)
+ @RequireFeature(PackageManager.FEATURE_DEVICE_ADMIN)
public void testGetKeyPairGrants_failIfNotOwner() {
assertThrows(SecurityException.class, () -> sDpm.getKeyPairGrants(ALIAS));
}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/StartProfilesTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/StartProfilesTest.java
index 6f137284..dcc8b4d 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/StartProfilesTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/StartProfilesTest.java
@@ -37,13 +37,14 @@
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.EnsureDoesNotHavePermission;
import com.android.bedstead.harrier.annotations.EnsureHasPermission;
import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
import com.android.bedstead.harrier.annotations.EnsureHasTvProfile;
import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
-import com.android.bedstead.harrier.annotations.Postsubmit;
-import com.android.bedstead.harrier.annotations.RequireFeatures;
+import com.android.bedstead.harrier.annotations.RequireFeature;
import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
+import com.android.bedstead.harrier.annotations.Postsubmit;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.users.UserReference;
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
@@ -57,11 +58,6 @@
@RunWith(BedsteadJUnit4.class)
public final class StartProfilesTest {
-
- // We set this to 30 seconds because if the total test time goes over 66 seconds then it causes
- // infrastructure problems
- private static final long PROFILE_ACCESSIBLE_BROADCAST_TIMEOUT = 30 * 1000;
-
private static final Context sContext = ApplicationProvider.getApplicationContext();
private static final UserManager sUserManager = sContext.getSystemService(UserManager.class);
private static final ActivityManager sActivityManager =
@@ -79,7 +75,7 @@
}
@Test
- @RequireFeatures(PackageManager.FEATURE_MANAGED_USERS)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
@EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
@@ -90,7 +86,7 @@
}
@Test
- @RequireFeatures(PackageManager.FEATURE_MANAGED_USERS)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
@EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
@@ -107,80 +103,59 @@
}
@Test
- @RequireFeatures(PackageManager.FEATURE_MANAGED_USERS)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
@Postsubmit(reason="b/181207615 flaky")
@EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
public void stopProfile_returnsTrue() {
- sDeviceState.workProfile().start();
-
- try {
- assertThat(sActivityManager.stopProfile(
- sDeviceState.workProfile().userHandle())).isTrue();
- } finally {
- // TODO(b/171565394): Remove once teardown is done for us
- sDeviceState.workProfile().start();
- }
+ assertThat(sActivityManager.stopProfile(sDeviceState.workProfile().userHandle())).isTrue();
}
@Test
- @RequireFeatures(PackageManager.FEATURE_MANAGED_USERS)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
@Postsubmit(reason="b/181207615 flaky")
@EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
public void stopProfile_profileIsStopped() {
- sDeviceState.workProfile().start();
BlockingBroadcastReceiver broadcastReceiver = sDeviceState.registerBroadcastReceiver(
Intent.ACTION_PROFILE_INACCESSIBLE, userIsEqual(sDeviceState.workProfile()));
- try {
- sActivityManager.stopProfile(sDeviceState.workProfile().userHandle());
- broadcastReceiver.awaitForBroadcastOrFail();
+ sActivityManager.stopProfile(sDeviceState.workProfile().userHandle());
+ broadcastReceiver.awaitForBroadcastOrFail();
- assertThat(
- sUserManager.isUserRunning(sDeviceState.workProfile().userHandle())).isFalse();
- } finally {
- // TODO(b/171565394): Remove once teardown is done for us
- sDeviceState.workProfile().start();
- }
+ assertThat(
+ sUserManager.isUserRunning(sDeviceState.workProfile().userHandle())).isFalse();
}
@Test
- @RequireFeatures(PackageManager.FEATURE_MANAGED_USERS)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
@Postsubmit(reason="b/181207615 flaky")
@EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
public void startUser_immediatelyAfterStopped_profileIsStarted() {
- sDeviceState.workProfile().start();
- BlockingBroadcastReceiver broadcastReceiver = sDeviceState.registerBroadcastReceiver(
- Intent.ACTION_PROFILE_INACCESSIBLE, userIsEqual(sDeviceState.workProfile()));
-
- sActivityManager.stopProfile(sDeviceState.workProfile().userHandle());
- broadcastReceiver.awaitForBroadcast();
-
- try {
- // start profile as soon as ACTION_PROFILE_INACCESSIBLE is received
- // verify that ACTION_PROFILE_ACCESSIBLE is received if profile is re-started
- broadcastReceiver = sDeviceState.registerBroadcastReceiver(
- Intent.ACTION_PROFILE_ACCESSIBLE, userIsEqual(sDeviceState.workProfile()));
- sActivityManager.startProfile(sDeviceState.workProfile().userHandle());
- Intent broadcast = broadcastReceiver.awaitForBroadcast();
-
- assertWithMessage("Expected to receive ACTION_PROFILE_ACCESSIBLE broadcast").that(
- broadcast).isNotNull();
- assertThat(
- sUserManager.isUserRunning(sDeviceState.workProfile().userHandle())).isTrue();
- } finally {
- // TODO(b/171565394): Remove once teardown is done for us
- sDeviceState.workProfile().start();
+ try (BlockingBroadcastReceiver broadcastReceiver = sDeviceState.registerBroadcastReceiver(
+ Intent.ACTION_PROFILE_INACCESSIBLE, userIsEqual(sDeviceState.workProfile()))) {
+ sActivityManager.stopProfile(sDeviceState.workProfile().userHandle());
}
+
+ // start profile as soon as ACTION_PROFILE_INACCESSIBLE is received
+ // verify that ACTION_PROFILE_ACCESSIBLE is received if profile is re-started
+ BlockingBroadcastReceiver broadcastReceiver = sDeviceState.registerBroadcastReceiver(
+ Intent.ACTION_PROFILE_ACCESSIBLE, userIsEqual(sDeviceState.workProfile()));
+ sActivityManager.startProfile(sDeviceState.workProfile().userHandle());
+ Intent broadcast = broadcastReceiver.awaitForBroadcast();
+
+ assertWithMessage("Expected to receive ACTION_PROFILE_ACCESSIBLE broadcast").that(
+ broadcast).isNotNull();
+ assertThat(
+ sUserManager.isUserRunning(sDeviceState.workProfile().userHandle())).isTrue();
}
@Test
- @RequireFeatures(PackageManager.FEATURE_MANAGED_USERS)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
@Postsubmit(reason="b/181207615 flaky")
@@ -190,21 +165,16 @@
// stop and restart profile without waiting for ACTION_PROFILE_INACCESSIBLE broadcast
sActivityManager.stopProfile(sDeviceState.workProfile().userHandle());
- try {
- sActivityManager.startProfile(sDeviceState.workProfile().userHandle());
+ sActivityManager.startProfile(sDeviceState.workProfile().userHandle());
- assertThat(sUserManager.isUserRunning(
- sDeviceState.workProfile().userHandle())).isTrue();
- } finally {
- // TODO(b/171565394): Remove once teardown is done for us
- sDeviceState.workProfile().start();
- }
+ assertThat(sUserManager.isUserRunning(sDeviceState.workProfile().userHandle())).isTrue();
}
@Test
- @RequireFeatures(PackageManager.FEATURE_MANAGED_USERS)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
+ @EnsureDoesNotHavePermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
@Postsubmit(reason="b/181207615 flaky")
public void startProfile_withoutPermission_throwsException() {
assertThrows(SecurityException.class,
@@ -212,17 +182,13 @@
}
@Test
- @RequireFeatures(PackageManager.FEATURE_MANAGED_USERS)
+ @RequireFeature(PackageManager.FEATURE_MANAGED_USERS)
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
+ @EnsureDoesNotHavePermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
public void stopProfile_withoutPermission_throwsException() {
- try {
- assertThrows(SecurityException.class,
- () -> sActivityManager.stopProfile(sDeviceState.workProfile().userHandle()));
- } finally {
- // TODO(b/171565394): Remove once teardown is done for us
- sDeviceState.workProfile().start();
- }
+ assertThrows(SecurityException.class,
+ () -> sActivityManager.stopProfile(sDeviceState.workProfile().userHandle()));
}
@Test
@@ -240,13 +206,8 @@
@EnsureHasSecondaryUser
@EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
public void stopProfile_stoppingFullUser_throwsException() {
- try {
- assertThrows(IllegalArgumentException.class,
- () -> sActivityManager.stopProfile(sDeviceState.secondaryUser().userHandle()));
- } finally {
- // TODO(b/171565394): Remove once teardown is done for us
- sDeviceState.secondaryUser().start();
- }
+ assertThrows(IllegalArgumentException.class,
+ () -> sActivityManager.stopProfile(sDeviceState.secondaryUser().userHandle()));
}
@Test
@@ -256,19 +217,14 @@
public void startProfile_tvProfile_profileIsStarted() {
sDeviceState.tvProfile().stop();
- try {
- BlockingBroadcastReceiver broadcastReceiver = sDeviceState.registerBroadcastReceiver(
- Intent.ACTION_PROFILE_ACCESSIBLE, userIsEqual(sDeviceState.tvProfile()));
+ try (BlockingBroadcastReceiver broadcastReceiver = sDeviceState.registerBroadcastReceiver(
+ Intent.ACTION_PROFILE_ACCESSIBLE, userIsEqual(sDeviceState.tvProfile()))) {
assertThat(
sActivityManager.startProfile(sDeviceState.tvProfile().userHandle())).isTrue();
- broadcastReceiver.awaitForBroadcast();
-
- assertThat(sUserManager.isUserRunning(sDeviceState.tvProfile().userHandle())).isTrue();
- } finally {
- // TODO(b/171565394): Remove once teardown is done for us
- sDeviceState.tvProfile().start();
}
+
+ assertThat(sUserManager.isUserRunning(sDeviceState.tvProfile().userHandle())).isTrue();
}
@Test
@@ -276,19 +232,13 @@
@EnsureHasTvProfile
@EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, CREATE_USERS})
public void stopProfile_tvProfile_profileIsStopped() {
- sDeviceState.tvProfile().start();
+ BlockingBroadcastReceiver broadcastReceiver = sDeviceState.registerBroadcastReceiver(
+ Intent.ACTION_PROFILE_INACCESSIBLE, userIsEqual(sDeviceState.tvProfile()));
- try {
- BlockingBroadcastReceiver broadcastReceiver = sDeviceState.registerBroadcastReceiver(
- Intent.ACTION_PROFILE_INACCESSIBLE, userIsEqual(sDeviceState.tvProfile()));
+ assertThat(
+ sActivityManager.stopProfile(sDeviceState.tvProfile().userHandle())).isTrue();
+ broadcastReceiver.awaitForBroadcast();
- assertThat(
- sActivityManager.stopProfile(sDeviceState.tvProfile().userHandle())).isTrue();
- broadcastReceiver.awaitForBroadcast();
-
- assertThat(sUserManager.isUserRunning(sDeviceState.tvProfile().userHandle())).isFalse();
- } finally {
- sDeviceState.tvProfile().start();
- }
+ assertThat(sUserManager.isUserRunning(sDeviceState.tvProfile().userHandle())).isFalse();
}
}
\ No newline at end of file
diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSecurityTests.java b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSecurityTests.java
index b3616d7..4f7a97fa 100644
--- a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSecurityTests.java
+++ b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSecurityTests.java
@@ -34,8 +34,6 @@
import android.server.wm.TestJournalProvider.TestJournalContainer;
import android.util.Log;
-import androidx.annotation.NonNull;
-
import org.junit.Test;
import java.util.List;
@@ -48,6 +46,203 @@
private static final String TAG = "BiometricTests/Security";
/**
+ * A strong biometric should be able to perform auth with any requested strength, since it is
+ * the highest biometric class. For example,
+ * +-------------------+--------------------+----------+
+ * | Original Strength | Requested Strength | Result |
+ * +-------------------+--------------------+----------+
+ * | BIOMETRIC_STRONG | BIOMETRIC_STRONG | Accepted |
+ * +-------------------+--------------------+----------+
+ * | BIOMETRIC_STRONG | BIOMETRIC_WEAK | Accepted |
+ * +-------------------+--------------------+----------+
+ * Note that since BiometricPrompt does not support Convenience biometrics, currently we don't
+ * have a way to test cases where the requested strength is BIOMETRIC_CONVENIENCE.
+ */
+ @Test
+ public void testBiometricStrength_StrongSensor() throws Exception {
+ final List<Integer> sensors = getSensorsOfTargetStrength(SensorProperties.STRENGTH_STRONG);
+ assumeTrue("testBiometricStrength_StrongSensor: numSensors=" + sensors.size(),
+ sensors.size() > 0);
+
+ // Tuple of originalStrength and requestedStrength
+ final int[][] testCases = {
+ // Request Strong auth
+ {Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_STRONG},
+
+ // Request Weak auth
+ {Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_WEAK}
+ };
+
+ for (Integer sensorId : sensors) {
+ for (int i = 0; i < testCases.length; i++) {
+ testBiometricStrength_forSensor_authAllowed(sensorId,
+ testCases[i][0] /* originalStrength */,
+ testCases[i][1] /* requestedStrength */);
+ }
+ }
+ }
+
+ /**
+ * A weak biometric may or may not be able to perform auth, depending on the requested strength.
+ * For example,
+ * +-------------------+--------------------+----------+
+ * | Original Strength | Requested Strength | Result |
+ * +-------------------+--------------------+----------+
+ * | BIOMETRIC_WEAK | BIOMETRIC_STRONG | Error |
+ * +-------------------+--------------------+----------+
+ * | BIOMETRIC_WEAK | BIOMETRIC_WEAK | Accepted |
+ * +-------------------+--------------------+----------+
+ * Note that since BiometricPrompt does not support Convenience biometrics, currently we don't
+ * have a way to test cases where the requested strength is BIOMETRIC_CONVENIENCE.
+ */
+ @Test
+ public void testBiometricStrength_WeakSensor() throws Exception {
+ final List<Integer> sensors = getSensorsOfTargetStrength(SensorProperties.STRENGTH_WEAK);
+ assumeTrue("testBiometricStrength_WeakSensor: numSensors: " + sensors.size(),
+ sensors.size() > 0);
+
+ for (Integer sensorId : sensors) {
+ testBiometricStrength_forSensor_authDisallowed(sensorId,
+ Authenticators.BIOMETRIC_WEAK /* originalStrength */,
+ Authenticators.BIOMETRIC_STRONG /* requestedStrength */,
+ sensors.size() > 1 /* hasMultiSensors */);
+
+ testBiometricStrength_forSensor_authAllowed(sensorId,
+ Authenticators.BIOMETRIC_WEAK /* originalStrength */,
+ Authenticators.BIOMETRIC_WEAK /* requestedStrength */);
+ }
+ }
+
+ /**
+ * A convenience biometric should not be able to perform auth with the following requested
+ * strength, due to insufficient strength.
+ * +-----------------------+--------------------+--------+
+ * | Original Strength | Requested Strength | Result |
+ * +-----------------------+--------------------+--------+
+ * | BIOMETRIC_CONVENIENCE | BIOMETRIC_STRONG | Error |
+ * +-----------------------+--------------------+--------+
+ * | BIOMETRIC_CONVENIENCE | BIOMETRIC_WEAK | Error |
+ * +-----------------------+--------------------+--------+
+ * Note that since BiometricPrompt does not support Convenience biometrics, currently we don't
+ * have a way to test cases where the requested strength is BIOMETRIC_CONVENIENCE.
+ */
+ @Test
+ public void testBiometricStrength_ConvenienceSensor() throws Exception {
+ final List<Integer> sensors =
+ getSensorsOfTargetStrength(SensorProperties.STRENGTH_CONVENIENCE);
+ assumeTrue("testBiometricStrength_ConvenienceSensor: numSensors=" + sensors.size(),
+ sensors.size() > 0);
+
+ // Tuple of originalStrength and requestedStrength
+ final int[][] testCases = {
+ // Request Strong auth
+ {Authenticators.BIOMETRIC_CONVENIENCE, Authenticators.BIOMETRIC_STRONG},
+
+ // Request Weak auth
+ {Authenticators.BIOMETRIC_CONVENIENCE, Authenticators.BIOMETRIC_WEAK}
+ };
+
+ for (Integer sensorId : sensors) {
+ for (int i = 0; i < testCases.length; i++) {
+ testBiometricStrength_forSensor_authDisallowed(sensorId,
+ testCases[i][0] /* originalStrength */,
+ testCases[i][1] /* requestedStrength */,
+ sensors.size() > 1 /* hasMultiSensors */);
+ }
+ }
+ }
+
+ private void testBiometricStrength_forSensor_authAllowed(int sensorId, int originalStrength,
+ int requestedStrength) throws Exception {
+ Log.d(TAG, "testBiometricStrength_forSensor_authAllowed: "
+ + ", sensorId=" + sensorId
+ + ", originalStrength=" + originalStrength
+ + ", requestedStrength=" + requestedStrength);
+
+ final ComponentName componentName = getComponentName(requestedStrength);
+
+ // Reset to the original strength in case it's ever changed before the test
+ updateStrength(sensorId, originalStrength);
+
+ try (BiometricTestSession session = mBiometricManager.createTestSession(sensorId);
+ ActivitySession activitySession = new ActivitySession(this, componentName)) {
+ final int userId = 0;
+ waitForAllUnenrolled();
+ enrollForSensor(session, sensorId);
+ final TestJournal journal =
+ TestJournalContainer.get(activitySession.getComponentName());
+
+ // No error code should be returned for the requested strength
+ int errCode = mBiometricManager.canAuthenticate(requestedStrength);
+ assertEquals("Device should allow auth with the requested biometric",
+ BiometricManager.BIOMETRIC_SUCCESS, errCode);
+
+ // Launch test activity
+ launchActivityAndWaitForResumed(activitySession);
+
+ BiometricCallbackHelper.State callbackState = getCallbackState(journal);
+ assertNotNull(callbackState);
+
+ BiometricServiceState state = getCurrentState();
+ assertTrue(state.toString(), state.mSensorStates.sensorStates.get(sensorId).isBusy());
+
+ // Auth should work
+ successfullyAuthenticate(session, userId);
+ mInstrumentation.waitForIdleSync();
+ callbackState = getCallbackState(journal);
+ assertNotNull(callbackState);
+ assertEquals(callbackState.toString(), 0, callbackState.mNumAuthRejected);
+ assertEquals(callbackState.toString(), 1, callbackState.mNumAuthAccepted);
+ assertEquals(callbackState.toString(), 0, callbackState.mAcquiredReceived.size());
+ assertEquals(callbackState.toString(), 0, callbackState.mErrorsReceived.size());
+ }
+ }
+
+ private void testBiometricStrength_forSensor_authDisallowed(int sensorId, int originalStrength,
+ int requestedStrength, boolean hasMultiSensors) throws Exception {
+ Log.d(TAG, "testBiometricStrength_forSensor_authDisallowed: "
+ + ", sensorId=" + sensorId
+ + ", originalStrength=" + originalStrength
+ + ", requestedStrength=" + requestedStrength
+ + ", hasMultiSensors=" + hasMultiSensors);
+
+ final ComponentName componentName = getComponentName(requestedStrength);
+
+ // Reset to the original strength in case it's ever changed before the test
+ updateStrength(sensorId, originalStrength);
+
+ try (BiometricTestSession session = mBiometricManager.createTestSession(sensorId);
+ ActivitySession activitySession = new ActivitySession(this, componentName)) {
+ final int userId = 0;
+ waitForAllUnenrolled();
+ enrollForSensor(session, sensorId);
+ final TestJournal journal =
+ TestJournalContainer.get(activitySession.getComponentName());
+
+ // Error code should be returned for the requested strength due to insufficient strength
+ int errCode = mBiometricManager.canAuthenticate(requestedStrength);
+ checkErrCode("Device shouldn't allow auth with biometrics that have insufficient"
+ + " strength. errCode: " + errCode,
+ errCode, BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
+ hasMultiSensors);
+
+ // Launch test activity
+ launchActivityAndWaitForResumed(activitySession);
+
+ // Auth shouldn't work and error code should be returned
+ mInstrumentation.waitForIdleSync();
+ BiometricCallbackHelper.State callbackState = getCallbackState(journal);
+ assertNotNull(callbackState);
+ assertEquals(callbackState.toString(), 0, callbackState.mNumAuthRejected);
+ assertEquals(callbackState.toString(), 0, callbackState.mNumAuthAccepted);
+ assertEquals(callbackState.toString(), 0, callbackState.mAcquiredReceived.size());
+ assertEquals(callbackState.toString(), 1, callbackState.mErrorsReceived.size());
+ checkErrCode(callbackState.toString(), (int) callbackState.mErrorsReceived.get(0),
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE, hasMultiSensors);
+ }
+ }
+
+ /**
* The strength of a Strong biometric may need to be downgraded to a weaker one if the biometric
* requires a security update. After downgrading, the biometric may or may not be able to
* perform auth with the requested strength. For example,
@@ -139,40 +334,18 @@
private void testBiometricStrengthDowngraded_forSensor(int sensorId, int originalStrength,
int targetStrength, int requestedStrength, boolean hasMultiSensors) throws Exception {
- assertTrue("requestedStrength: " + requestedStrength,
- requestedStrength == Authenticators.BIOMETRIC_STRONG ||
- requestedStrength == Authenticators.BIOMETRIC_WEAK);
-
- final ComponentName componentName;
- if (requestedStrength == Authenticators.BIOMETRIC_STRONG) {
- componentName = CLASS_3_BIOMETRIC_ACTIVITY;
- } else {
- componentName = CLASS_2_BIOMETRIC_ACTIVITY;
- }
-
- // Reset to the original strength in case it's ever changed before the test
- updateStrength(sensorId, originalStrength);
-
- // Test downgrading the biometric strength to the target strength
- testBiometricStrengthDowngraded_forSensor_afterDowngrading(componentName, sensorId,
- originalStrength, targetStrength, requestedStrength, hasMultiSensors);
-
- // Test undo downgrading (ie, reset to the original strength)
- testBiometricStrengthDowngraded_forSensor_afterUndoDowngrading(componentName, sensorId,
- originalStrength, targetStrength, requestedStrength);
- }
-
- private void testBiometricStrengthDowngraded_forSensor_afterDowngrading(
- @NonNull ComponentName componentName, int sensorId, int originalStrength,
- int targetStrength, int requestedStrength, boolean hasMultiSensors) throws Exception {
- Log.d(TAG, "testBiometricStrengthDowngraded_forSensor_afterDowngrading: "
- + "componentName=" + componentName
+ Log.d(TAG, "testBiometricStrengthDowngraded_forSensor: "
+ ", sensorId=" + sensorId
+ ", originalStrength=" + originalStrength
+ ", targetStrength=" + targetStrength
+ ", requestedStrength=" + requestedStrength
+ ", hasMultiSensors=" + hasMultiSensors);
+ final ComponentName componentName = getComponentName(requestedStrength);
+
+ // Reset to the original strength in case it's ever changed before the test
+ updateStrength(sensorId, originalStrength);
+
try (BiometricTestSession session = mBiometricManager.createTestSession(sensorId);
ActivitySession activitySession = new ActivitySession(this, componentName)) {
final int userId = 0;
@@ -239,53 +412,9 @@
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, hasMultiSensors);
}
}
- }
- private void testBiometricStrengthDowngraded_forSensor_afterUndoDowngrading(
- @NonNull ComponentName componentName, int sensorId, int originalStrength,
- int targetStrength, int requestedStrength) throws Exception {
- Log.d(TAG, "testBiometricStrengthDowngraded_forSensor_afterUndoDowngrading: "
- + "componentName=" + componentName
- + ", sensorId=" + sensorId
- + ", originalStrength=" + originalStrength
- + ", targetStrength=" + targetStrength
- + ", requestedStrength=" + requestedStrength);
-
- try (BiometricTestSession session = mBiometricManager.createTestSession(sensorId);
- ActivitySession activitySession = new ActivitySession(this, componentName)) {
- final int userId = 0;
- waitForAllUnenrolled();
- enrollForSensor(session, sensorId);
- final TestJournal journal =
- TestJournalContainer.get(activitySession.getComponentName());
-
- // Reset to the original strength
- updateStrength(sensorId, originalStrength);
-
- // No error code should be returned for the requested strength
- int errCode = mBiometricManager.canAuthenticate(requestedStrength);
- assertEquals("Device should allow auth with the requested biometric",
- BiometricManager.BIOMETRIC_SUCCESS, errCode);
-
- // Launch test activity
- launchActivityAndWaitForResumed(activitySession);
-
- BiometricCallbackHelper.State callbackState = getCallbackState(journal);
- assertNotNull(callbackState);
-
- BiometricServiceState state = getCurrentState();
- assertTrue(state.toString(), state.mSensorStates.sensorStates.get(sensorId).isBusy());
-
- // Auth should work
- successfullyAuthenticate(session, userId);
- mInstrumentation.waitForIdleSync();
- callbackState = getCallbackState(journal);
- assertNotNull(callbackState);
- assertEquals(callbackState.toString(), 0, callbackState.mNumAuthRejected);
- assertEquals(callbackState.toString(), 1, callbackState.mNumAuthAccepted);
- assertEquals(callbackState.toString(), 0, callbackState.mAcquiredReceived.size());
- assertEquals(callbackState.toString(), 0, callbackState.mErrorsReceived.size());
- }
+ // Cleanup: reset to the original strength
+ updateStrength(sensorId, originalStrength);
}
/**
@@ -381,16 +510,7 @@
+ ", requestedStrength=" + requestedStrength
+ ", hasMultiSensors=" + hasMultiSensors);
- assertTrue("requestedStrength: " + requestedStrength,
- requestedStrength == Authenticators.BIOMETRIC_STRONG ||
- requestedStrength == Authenticators.BIOMETRIC_WEAK);
-
- final ComponentName componentName;
- if (requestedStrength == Authenticators.BIOMETRIC_STRONG) {
- componentName = CLASS_3_BIOMETRIC_ACTIVITY;
- } else {
- componentName = CLASS_2_BIOMETRIC_ACTIVITY;
- }
+ final ComponentName componentName = getComponentName(requestedStrength);
// Reset to the original strength in case it's ever changed before the test
updateStrength(sensorId, originalStrength);
@@ -448,4 +568,16 @@
}
}
+ private ComponentName getComponentName(int requestedStrength) {
+ assertTrue("requestedStrength: " + requestedStrength,
+ requestedStrength == Authenticators.BIOMETRIC_STRONG ||
+ requestedStrength == Authenticators.BIOMETRIC_WEAK);
+
+ if (requestedStrength == Authenticators.BIOMETRIC_STRONG) {
+ return CLASS_3_BIOMETRIC_ACTIVITY;
+ } else {
+ return CLASS_2_BIOMETRIC_ACTIVITY;
+ }
+ }
+
}
diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricTestBase.java b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricTestBase.java
index 63463bd..b24206e 100644
--- a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricTestBase.java
+++ b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricTestBase.java
@@ -76,7 +76,7 @@
abstract class BiometricTestBase extends ActivityManagerTestBase {
private static final String TAG = "BiometricTestBase";
- private static final String DUMPSYS_BIOMETRIC = "dumpsys biometric --proto";
+ private static final String DUMPSYS_BIOMETRIC = Utils.DUMPSYS_BIOMETRIC;
private static final String FLAG_CLEAR_SCHEDULER_LOG = " --clear-scheduler-buffer";
private static final String DOWNGRADE_BIOMETRIC_STRENGTH =
"device_config put biometrics biometric_strengths ";
@@ -112,19 +112,10 @@
super.launchActivity(componentName);
}
- /**
- * Retrieves the current states of all biometric sensor services (e.g. FingerprintService,
- * FaceService, etc).
- *
- * Note that the states are retrieved from BiometricService, instead of individual services.
- * This is because 1) BiometricService is the source of truth for all public API-facing things,
- * and 2) This to include other information, such as UI states, etc as well.
- */
+ /** @see Utils#getBiometricServiceCurrentState() */
@NonNull
protected BiometricServiceState getCurrentState() throws Exception {
- final byte[] dump = Utils.executeShellCommand(DUMPSYS_BIOMETRIC);
- final BiometricServiceStateProto proto = BiometricServiceStateProto.parseFrom(dump);
- return BiometricServiceState.parseFrom(proto);
+ return Utils.getBiometricServiceCurrentState();
}
@NonNull
diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/SensorStates.java b/tests/framework/base/biometrics/src/android/server/biometrics/SensorStates.java
index 329e704..c8cf166 100644
--- a/tests/framework/base/biometrics/src/android/server/biometrics/SensorStates.java
+++ b/tests/framework/base/biometrics/src/android/server/biometrics/SensorStates.java
@@ -71,21 +71,24 @@
public static class SensorState {
private final SchedulerState mSchedulerState;
private final int mModality;
+ private final int[] mModalityFlags;
private final int mCurrentStrength;
@NonNull private final Map<Integer, UserState> mUserStates;
private final boolean mResetLockoutRequiresHardwareAuthToken;
private final boolean mResetLockoutRequiresChallenge;
- public SensorState(@NonNull SchedulerState schedulerState, int modality,
+ public SensorState(@NonNull SchedulerState schedulerState,
+ int modality, int[] modalityFlags,
int currentStrength, @NonNull Map<Integer, UserState> userStates,
boolean resetLockoutRequiresHardwareAuthToken,
boolean resetLockoutRequiresChallenge) {
- this.mSchedulerState = schedulerState;
- this.mModality = modality;
- this.mCurrentStrength = currentStrength;
- this.mUserStates = userStates;
- this.mResetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
- this.mResetLockoutRequiresChallenge = resetLockoutRequiresChallenge;
+ mSchedulerState = schedulerState;
+ mModality = modality;
+ mModalityFlags = modalityFlags;
+ mCurrentStrength = currentStrength;
+ mUserStates = userStates;
+ mResetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
+ mResetLockoutRequiresChallenge = resetLockoutRequiresChallenge;
}
public SchedulerState getSchedulerState() {
@@ -100,6 +103,16 @@
return mModality;
}
+ public boolean hasModalityFlag(int flag) {
+ for (int f : mModalityFlags) {
+ if (f == flag) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public int getCurrentStrength() {
return mCurrentStrength;
}
@@ -138,7 +151,8 @@
final SchedulerState schedulerState =
SchedulerState.parseFrom(sensorStateProto.scheduler);
final SensorState sensorState = new SensorState(schedulerState,
- sensorStateProto.modality, sensorStateProto.currentStrength, userStates,
+ sensorStateProto.modality, sensorStateProto.modalityFlags,
+ sensorStateProto.currentStrength, userStates,
sensorStateProto.resetLockoutRequiresHardwareAuthToken,
sensorStateProto.resetLockoutRequiresChallenge);
sensorStates.put(sensorStateProto.sensorId, sensorState);
@@ -186,6 +200,16 @@
return false;
}
+ public boolean containsModalityFlag(int flag) {
+ for (SensorState state : sensorStates.values()) {
+ if (state.hasModalityFlag(flag)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private SensorStates(@NonNull Map<Integer, SensorState> sensorStates) {
this.sensorStates = sensorStates;
}
diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/Utils.java b/tests/framework/base/biometrics/src/android/server/biometrics/Utils.java
index 53b2220..c90061e 100644
--- a/tests/framework/base/biometrics/src/android/server/biometrics/Utils.java
+++ b/tests/framework/base/biometrics/src/android/server/biometrics/Utils.java
@@ -31,6 +31,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.server.biometrics.nano.BiometricServiceStateProto;
+
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
@@ -48,6 +50,9 @@
private static final String TAG = "BiometricTestUtils";
private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
+ /** adb command for dumping the biometric proto */
+ public static final String DUMPSYS_BIOMETRIC = "dumpsys biometric --proto";
+
/**
* Retrieves the current SensorStates.
*/
@@ -100,6 +105,21 @@
}
/**
+ * Retrieves the current states of all biometric sensor services (e.g. FingerprintService,
+ * FaceService, etc).
+ *
+ * Note that the states are retrieved from BiometricService, instead of individual services.
+ * This is because 1) BiometricService is the source of truth for all public API-facing things,
+ * and 2) This to include other information, such as UI states, etc as well.
+ */
+ @NonNull
+ public static BiometricServiceState getBiometricServiceCurrentState() throws Exception {
+ final byte[] dump = Utils.executeShellCommand(DUMPSYS_BIOMETRIC);
+ final BiometricServiceStateProto proto = BiometricServiceStateProto.parseFrom(dump);
+ return BiometricServiceState.parseFrom(proto);
+ }
+
+ /**
* Runs a shell command, similar to running "adb shell ..." from the command line.
* @param cmd A command, without the preceding "adb shell" portion. For example,
* passing in "dumpsys fingerprint" would be the equivalent of running
diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/fingerprint/FingerprintServiceTest.java b/tests/framework/base/biometrics/src/android/server/biometrics/fingerprint/FingerprintServiceTest.java
index 4fa5031..43725f2 100644
--- a/tests/framework/base/biometrics/src/android/server/biometrics/fingerprint/FingerprintServiceTest.java
+++ b/tests/framework/base/biometrics/src/android/server/biometrics/fingerprint/FingerprintServiceTest.java
@@ -34,6 +34,7 @@
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
+import android.server.biometrics.BiometricServiceState;
import android.server.biometrics.SensorStates;
import android.server.biometrics.Utils;
import android.server.wm.ActivityManagerTestBase;
@@ -47,6 +48,7 @@
import androidx.annotation.Nullable;
import com.android.server.biometrics.nano.SensorServiceStateProto;
+import com.android.server.biometrics.nano.SensorStateProto;
import org.junit.After;
import org.junit.Before;
@@ -272,16 +274,22 @@
assertEquals(0, callbackState.mErrorsReceived.size());
// Send an acquire message
- testSessions.get(0).notifyAcquired(userId, FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL);
- mInstrumentation.waitForIdleSync();
- callbackState = getCallbackState(journal);
- assertNotNull(callbackState);
- assertEquals(1, callbackState.mNumAuthRejected);
- assertEquals(0, callbackState.mNumAuthAccepted);
- assertEquals(1, callbackState.mAcquiredReceived.size());
- assertEquals(FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL,
- (int) callbackState.mAcquiredReceived.get(0));
- assertEquals(0, callbackState.mErrorsReceived.size());
+ // skip this check on devices with UDFPS because they prompt to try again
+ // and do not dispatch an acquired event via BiometricPrompt
+ final boolean verifyPartial = !hasUdfps();
+ if (verifyPartial) {
+ testSessions.get(0).notifyAcquired(userId,
+ FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL);
+ mInstrumentation.waitForIdleSync();
+ callbackState = getCallbackState(journal);
+ assertNotNull(callbackState);
+ assertEquals(1, callbackState.mNumAuthRejected);
+ assertEquals(0, callbackState.mNumAuthAccepted);
+ assertEquals(1, callbackState.mAcquiredReceived.size());
+ assertEquals(FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL,
+ (int) callbackState.mAcquiredReceived.get(0));
+ assertEquals(0, callbackState.mErrorsReceived.size());
+ }
// Send an error
testSessions.get(0).notifyError(userId,
@@ -291,9 +299,13 @@
assertNotNull(callbackState);
assertEquals(1, callbackState.mNumAuthRejected);
assertEquals(0, callbackState.mNumAuthAccepted);
- assertEquals(1, callbackState.mAcquiredReceived.size());
- assertEquals(FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL,
- (int) callbackState.mAcquiredReceived.get(0));
+ if (verifyPartial) {
+ assertEquals(1, callbackState.mAcquiredReceived.size());
+ assertEquals(FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL,
+ (int) callbackState.mAcquiredReceived.get(0));
+ } else {
+ assertEquals(0, callbackState.mAcquiredReceived.size());
+ }
assertEquals(1, callbackState.mErrorsReceived.size());
assertEquals(FingerprintManager.FINGERPRINT_ERROR_CANCELED,
(int) callbackState.mErrorsReceived.get(0));
@@ -306,4 +318,9 @@
session.close();
}
}
+
+ private boolean hasUdfps() throws Exception {
+ final BiometricServiceState state = Utils.getBiometricServiceCurrentState();
+ return state.mSensorStates.containsModalityFlag(SensorStateProto.FINGERPRINT_UDFPS);
+ }
}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 4da473b..257f867 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -106,14 +106,16 @@
<activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$SecondTranslucentActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
- <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$CallbackTrackingActivity"/>
+ <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$CallbackTrackingActivity"
+ android:configChanges="keyboard|keyboardHidden|navigation"/>
<activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$SecondCallbackTrackingActivity"/>
<activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$TranslucentCallbackTrackingActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
- <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$ShowWhenLockedCallbackTrackingActivity"/>
+ <activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$ShowWhenLockedCallbackTrackingActivity"
+ android:configChanges="keyboard|keyboardHidden|navigation" />
<activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$SecondProcessCallbackTrackingActivity"
android:process=":SecondProcess"
@@ -175,7 +177,7 @@
<activity android:name="android.server.wm.MultiDisplayClientTests$ClientTestActivity"/>
<activity android:name="android.server.wm.MultiDisplayClientTests$NoRelaunchActivity"
android:resizeableActivity="true"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"/>
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen|keyboard|keyboardHidden|navigation"/>
<activity android:name="android.server.wm.HideOverlayWindowsTest$SystemWindowActivity"
android:process=":swa"
@@ -187,7 +189,8 @@
android:process=":saoa"
android:exported="true"/>
- <activity android:name="android.server.wm.KeyguardLockedTests$ShowImeAfterLockscreenActivity"/>
+ <activity android:name="android.server.wm.KeyguardLockedTests$ShowImeAfterLockscreenActivity"
+ android:configChanges="keyboard|keyboardHidden|navigation"/>
<activity android:name="android.server.wm.KeyguardLockedTests$ShowWhenLockedImeActivity"/>
@@ -236,10 +239,6 @@
android:finishOnTaskLaunch="true"
android:exported="true"/>
- <activity android:name="android.server.wm.ActivityViewTest$ActivityViewTestActivity"
- android:configChanges="keyboardHidden"
- android:exported="true"/>
-
<provider android:name="android.server.wm.TestJournalProvider"
android:authorities="android.server.wm.testjournalprovider"
android:exported="true"/>
@@ -338,7 +337,7 @@
<activity android:name="android.server.wm.KeyEventActivity"
android:exported="true"
- android:configChanges="orientation|screenLayout"
+ android:configChanges="orientation|screenLayout|keyboard|keyboardHidden|navigation"
android:showWhenLocked="true"/>
<activity android:name="android.server.wm.WindowInsetsPolicyTest$TestActivity"
android:turnScreenOn="true"
@@ -440,6 +439,16 @@
</intent-filter>
</activity>
+ <activity android:name="android.view.cts.surfacevalidator.ASurfaceControlTestActivity"
+ android:screenOrientation="locked"
+ android:theme="@style/WhiteBackgroundTheme"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="android.server.wm.WindowInputTests$TestActivity" />
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
diff --git a/tests/framework/base/windowmanager/OWNERS b/tests/framework/base/windowmanager/OWNERS
index aa788b8..1f50516 100644
--- a/tests/framework/base/windowmanager/OWNERS
+++ b/tests/framework/base/windowmanager/OWNERS
@@ -15,8 +15,6 @@
# 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
diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml
index 1ed70d3..381cd84d 100755
--- a/tests/framework/base/windowmanager/app/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml
@@ -385,6 +385,7 @@
android:exported="true"/>
<activity android:name=".ShowWhenLockedAttrRemoveAttrActivity"
+ android:configChanges="keyboard|keyboardHidden|navigation"
android:showWhenLocked="true"
android:exported="true"/>
@@ -412,7 +413,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/app/src/android/server/wm/app/LaunchEnterPipActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LaunchEnterPipActivity.java
index 6a0acb8..3b4b364 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/LaunchEnterPipActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LaunchEnterPipActivity.java
@@ -16,6 +16,8 @@
package android.server.wm.app;
+import static android.server.wm.app.Components.PipActivity.EXTRA_FINISH_SELF_ON_RESUME;
+
import android.app.Activity;
import android.os.Bundle;
@@ -25,4 +27,14 @@
super.onCreate(bundle);
PipActivity.launchEnterPipActivity(this);
}
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Finish self if requested
+ if (getIntent().hasExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
+ finish();
+ }
+ }
}
diff --git a/tests/framework/base/windowmanager/app30/Android.bp b/tests/framework/base/windowmanager/app30/Android.bp
index 55e101f..fd535c6 100644
--- a/tests/framework/base/windowmanager/app30/Android.bp
+++ b/tests/framework/base/windowmanager/app30/Android.bp
@@ -29,5 +29,6 @@
test_suites: [
"cts",
"general-tests",
+ "sts",
],
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
deleted file mode 100644
index aaaf7e8..0000000
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityViewTest.java
+++ /dev/null
@@ -1,220 +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.server.wm;
-
-import static android.server.wm.WindowManagerState.STATE_RESUMED;
-import static android.server.wm.WindowManagerState.STATE_STOPPED;
-import static android.server.wm.app.Components.TEST_ACTIVITY;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.Activity;
-import android.app.ActivityView;
-import android.app.Instrumentation;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.platform.test.annotations.Presubmit;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Build/Install/Run:
- * atest CtsWindowManagerDeviceTestCases:ActivityViewTest
- */
-@Presubmit
-@android.server.wm.annotation.Group3
-public class ActivityViewTest extends ActivityManagerTestBase {
- private static final long IME_EVENT_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
-
- private Instrumentation mInstrumentation;
- private ActivityView mActivityView;
-
- @Rule
- public final ActivityTestRule<ActivityViewTestActivity> mActivityRule =
- new ActivityTestRule<>(ActivityViewTestActivity.class, true /* initialTouchMode */,
- false /* launchActivity */);
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
- assumeTrue(supportsMultiDisplay());
- mInstrumentation = getInstrumentation();
- SystemUtil.runWithShellPermissionIdentity(() -> {
- ActivityViewTestActivity activity = mActivityRule.launchActivity(null);
- mActivityView = activity.getActivityView();
- });
- separateTestJournal();
- }
-
- @After
- public void tearDown() throws Throwable {
- if (mActivityView != null) {
- // Detach ActivityView before releasing to avoid accessing removed display.
- mActivityRule.runOnUiThread(
- () -> ((ViewGroup) mActivityView.getParent()).removeView(mActivityView));
- SystemUtil.runWithShellPermissionIdentity(() -> mActivityView.release());
- }
- }
-
- @Test
- public void testStartActivity() {
- launchActivityInActivityView(TEST_ACTIVITY);
- assertSingleLaunch(TEST_ACTIVITY);
- }
-
- @UiThreadTest
- @Test
- public void testResizeActivityView() {
- final int width = 500;
- final int height = 500;
-
- launchActivityInActivityView(TEST_ACTIVITY);
- assertSingleLaunch(TEST_ACTIVITY);
-
- mActivityView.layout(0, 0, width, height);
-
- boolean boundsMatched = checkDisplaySize(TEST_ACTIVITY, width, height);
- assertTrue("displayWidth and displayHeight must equal " + width + "x" + height,
- boundsMatched);
- }
-
- /** @return {@code true} if the display size for the activity matches the given size. */
- private boolean checkDisplaySize(ComponentName activity, int requestedWidth,
- int requestedHeight) {
- // Display size for the activity may not get updated right away. Retry in case.
- return Condition.waitFor("display size=" + requestedWidth + "x" + requestedHeight, () -> {
- final WindowManagerState wmState = mWmState;
- wmState.computeState();
-
- final int displayId = mWmState.getDisplayByActivity(activity);
- final WindowManagerState.DisplayContent display = wmState.getDisplay(displayId);
- int avDisplayWidth = 0;
- int avDisplayHeight = 0;
- if (display != null) {
- Rect bounds = display.mFullConfiguration.windowConfiguration.getAppBounds();
- if (bounds != null) {
- avDisplayWidth = bounds.width();
- avDisplayHeight = bounds.height();
- }
- }
- return avDisplayWidth == requestedWidth && avDisplayHeight == requestedHeight;
- });
- }
-
- @Test
- public void testAppStoppedWithVisibilityGone() {
- launchActivityInActivityView(TEST_ACTIVITY);
- assertSingleLaunch(TEST_ACTIVITY);
-
- separateTestJournal();
- mInstrumentation.runOnMainSync(() -> mActivityView.setVisibility(View.GONE));
- mInstrumentation.waitForIdleSync();
- mWmState.waitForActivityState(TEST_ACTIVITY, STATE_STOPPED);
-
- assertLifecycleCounts(TEST_ACTIVITY, 0, 0, 0, 1, 1, 0, CountSpec.DONT_CARE);
- }
-
- @Test
- public void testAppStoppedWithVisibilityInvisible() {
- launchActivityInActivityView(TEST_ACTIVITY);
- assertSingleLaunch(TEST_ACTIVITY);
-
- separateTestJournal();
- mInstrumentation.runOnMainSync(() -> mActivityView.setVisibility(View.INVISIBLE));
- mInstrumentation.waitForIdleSync();
- mWmState.waitForActivityState(TEST_ACTIVITY, STATE_STOPPED);
-
- assertLifecycleCounts(TEST_ACTIVITY, 0, 0, 0, 1, 1, 0, CountSpec.DONT_CARE);
- }
-
- @Test
- public void testAppStopAndStartWithVisibilityChange() {
- launchActivityInActivityView(TEST_ACTIVITY);
- assertSingleLaunch(TEST_ACTIVITY);
-
- separateTestJournal();
- mInstrumentation.runOnMainSync(() -> mActivityView.setVisibility(View.INVISIBLE));
- mInstrumentation.waitForIdleSync();
- mWmState.waitForActivityState(TEST_ACTIVITY, STATE_STOPPED);
-
- assertLifecycleCounts(TEST_ACTIVITY, 0, 0, 0, 1, 1, 0, CountSpec.DONT_CARE);
-
- separateTestJournal();
- mInstrumentation.runOnMainSync(() -> mActivityView.setVisibility(View.VISIBLE));
- mInstrumentation.waitForIdleSync();
- mWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
-
- assertLifecycleCounts(TEST_ACTIVITY, 0, 1, 1, 0, 0, 0, CountSpec.DONT_CARE);
- }
-
- private void launchActivityInActivityView(ComponentName activity) {
- Intent intent = new Intent();
- intent.setComponent(activity);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
- SystemUtil.runWithShellPermissionIdentity(() -> mActivityView.startActivity(intent));
- mWmState.waitForValidState(activity);
- }
-
- private Rect getActivityViewBoundsOnScreen() {
- final int[] location = new int[2];
- mInstrumentation.runOnMainSync(() -> {
- mActivityView.getLocationOnScreen(location);
- });
- return new Rect(location[0], location[1], location[0] + mActivityView.getWidth(),
- location[1] + mActivityView.getHeight());
- }
-
- // Test activity
- public static class ActivityViewTestActivity extends Activity {
- private ActivityView mActivityView;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mActivityView = new ActivityView(this);
- setContentView(mActivityView);
-
- ViewGroup.LayoutParams layoutParams = mActivityView.getLayoutParams();
- layoutParams.width = MATCH_PARENT;
- layoutParams.height = MATCH_PARENT;
- mActivityView.requestLayout();
- }
-
- ActivityView getActivityView() {
- return mActivityView;
- }
- }
-}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
index 85a628e..b03de6b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityVisibilityTests.java
@@ -121,27 +121,24 @@
* translucent activity is on the top most.
*/
@Test
- public void testVisibilityBehindTranslucentActivity() {
- // launch first activity
+ public void testVisibilityBehindTranslucentActivity_sameTask() {
launchActivity(TEST_ACTIVITY);
mWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
- // launch second activity
- launchActivity(BROADCAST_RECEIVER_ACTIVITY);
- mWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
+ launchAndFinishActivityBehindTranslucentActivity(true /* inSameTask */);
- // launch translucent activity
- launchActivity(TRANSLUCENT_TEST_ACTIVITY);
- mWmState.waitForActivityState(TRANSLUCENT_TEST_ACTIVITY, STATE_RESUMED);
+ mWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY));
+ mWmState.assertVisibility(TEST_ACTIVITY, true);
+ }
- // finish the second activity
- mBroadcastActionTrigger.finishBroadcastReceiverActivity();
- mWmState.computeState(BROADCAST_RECEIVER_ACTIVITY);
- mWmState.waitForActivityRemoved(BROADCAST_RECEIVER_ACTIVITY);
- mWmState.computeState(
- new WaitForValidActivityState(TEST_ACTIVITY),
- new WaitForValidActivityState(TRANSLUCENT_TEST_ACTIVITY));
+ @Test
+ public void testVisibilityBehindTranslucentActivity_diffTask() {
+ launchActivity(TEST_ACTIVITY);
+ mWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
+ launchAndFinishActivityBehindTranslucentActivity(false /* inSameTask */);
+
+ mWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY));
mWmState.assertVisibility(TEST_ACTIVITY, true);
}
@@ -150,31 +147,53 @@
* translucent activity is on the top most.
*/
@Test
- public void testHomeVisibilityBehindTranslucentActivity() {
+ public void testHomeVisibilityBehindTranslucentActivity_sameTask() {
if (!hasHomeScreen()) {
return;
}
launchHomeActivity();
- // launch first activity
- launchActivity(BROADCAST_RECEIVER_ACTIVITY);
- mWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
-
- // launch translucent activity
- launchActivity(TRANSLUCENT_TEST_ACTIVITY);
- mWmState.waitForActivityState(TRANSLUCENT_TEST_ACTIVITY, STATE_RESUMED);
- mWmState.assertVisibility(TRANSLUCENT_TEST_ACTIVITY, true);
-
- // Finish the first activity
- mBroadcastActionTrigger.finishBroadcastReceiverActivity();
- mWmState.computeState(BROADCAST_RECEIVER_ACTIVITY);
- mWmState.waitForActivityRemoved(BROADCAST_RECEIVER_ACTIVITY);
+ launchAndFinishActivityBehindTranslucentActivity(true /* inSameTask */);
mWmState.waitForHomeActivityVisible();
mWmState.assertHomeActivityVisible(true);
}
@Test
+ public void testHomeVisibilityBehindTranslucentActivity_diffTask() {
+ if (!hasHomeScreen()) {
+ return;
+ }
+ launchHomeActivity();
+
+ launchAndFinishActivityBehindTranslucentActivity(false /* inSameTask */);
+
+ mWmState.waitForHomeActivityVisible();
+ mWmState.assertHomeActivityVisible(true);
+ }
+
+ private void launchAndFinishActivityBehindTranslucentActivity(boolean inSameTask) {
+ // Launch first activity
+ launchActivity(BROADCAST_RECEIVER_ACTIVITY);
+ mWmState.waitForActivityState(BROADCAST_RECEIVER_ACTIVITY, STATE_RESUMED);
+
+ // Launch translucent activity
+ if (inSameTask) {
+ launchActivity(TRANSLUCENT_TEST_ACTIVITY);
+ } else {
+ launchActivityInNewTask(TRANSLUCENT_TEST_ACTIVITY);
+ }
+ mWmState.waitForActivityState(TRANSLUCENT_TEST_ACTIVITY, STATE_RESUMED);
+ mWmState.assertVisibility(TRANSLUCENT_TEST_ACTIVITY, true);
+
+ // Finish first activity
+ mBroadcastActionTrigger.finishBroadcastReceiverActivity();
+ mWmState.computeState(BROADCAST_RECEIVER_ACTIVITY);
+ mWmState.waitForActivityRemoved(BROADCAST_RECEIVER_ACTIVITY);
+ mWmState.computeState(new WaitForValidActivityState(TRANSLUCENT_TEST_ACTIVITY));
+ }
+
+ @Test
public void testTurnScreenOnActivity() {
assumeTrue(supportsLockScreen());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index bd14a2d..8440bd1 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -280,7 +280,7 @@
// Restore to fullscreen.
separateTestJournal();
- mTaskOrganizer.dismissedSplitScreen();
+ mTaskOrganizer.dismissSplitScreen();
// Home task could be on top since it was the top-most task while in split-screen mode
// (dock task was minimized), start the activity again to ensure the activity is at
// foreground.
@@ -946,7 +946,7 @@
// Move the activity to fullscreen and check that the size was updated
separateTestJournal();
- mTaskOrganizer.dismissedSplitScreen(true /* primaryOnTop */);
+ mTaskOrganizer.dismissSplitScreen(true /* primaryOnTop */);
waitForOrFail("Activity and application configuration must match",
() -> activityAndAppSizesMatch(activitySession));
final SizeInfo fullscreenSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY);
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 d0265f1f..1cda8b0 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java
@@ -54,6 +54,8 @@
import android.provider.Settings;
import android.server.wm.settings.SettingsSession;
+import androidx.test.filters.FlakyTest;
+
import org.junit.Ignore;
import org.junit.Test;
@@ -236,6 +238,7 @@
}
@Test
+ @FlakyTest(bugId = 188207199)
public void testTranslucentAssistantActivityStackVisibility() throws Exception {
try (final AssistantSession assistantSession = new AssistantSession()) {
assistantSession.setVoiceInteractionService(ASSISTANT_VOICE_INTERACTION_SERVICE);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayAreaPolicyTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayAreaPolicyTests.java
new file mode 100644
index 0000000..04e8c74
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayAreaPolicyTests.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.server.wm;
+
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
+import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.server.wm.WindowManagerState.DisplayArea;
+import android.server.wm.WindowManagerState.DisplayContent;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Build/Install/Run:
+ * atest CtsWindowManagerDeviceTestCases:DisplayAreaPolicyTests
+ */
+@Presubmit
+public class DisplayAreaPolicyTests extends ActivityManagerTestBase {
+
+ private List<DisplayContent> mDisplays;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // Verify on all displays.
+ mWmState.computeState();
+ mDisplays = mWmState.getDisplays();
+ }
+
+ /**
+ * DisplayContent should have feature id of FEATURE_ROOT. It should not be organized.
+ */
+ @Test
+ public void testDisplayContent() {
+ for (DisplayContent displayContent : mDisplays) {
+ assertThat(displayContent.getFeatureId()).isEqualTo(FEATURE_ROOT);
+ assertThat(displayContent.isOrganized()).isFalse();
+ }
+ }
+
+ /**
+ * There must be exactly one default TaskDisplayArea per display.
+ */
+ @Test
+ public void testDefaultTaskDisplayArea() {
+ for (DisplayContent displayContent : mDisplays) {
+ final List<DisplayArea> taskDisplayAreas = displayContent.getAllTaskDisplayAreas();
+ final List<DisplayArea> defaultTda = new ArrayList<>();
+ for (DisplayArea taskDisplayArea : taskDisplayAreas) {
+ if (taskDisplayArea.getFeatureId() == FEATURE_DEFAULT_TASK_CONTAINER) {
+ defaultTda.add(taskDisplayArea);
+ }
+ }
+ assertThat(defaultTda).hasSize(1);
+ }
+ }
+
+ /**
+ * TaskDisplayArea and RootDisplayArea must have unique feature ids per display.
+ */
+ @Test
+ public void testDisplayAreaUniqueId() {
+ for (DisplayContent displayContent : mDisplays) {
+ // TaskDisplayArea and RootDisplayArea must have unique feature ids.
+ final Set<Integer> uniqueIds = new HashSet<>();
+ uniqueIds.add(displayContent.getFeatureId());
+ // Other DisplayAreas can have non-unique feature ids, but shouldn't be the same with
+ // any TaskDisplayArea or RootDisplayArea.
+ final Set<Integer> nonUniqueIds = new HashSet<>();
+ final List<DisplayArea> displayAreas = displayContent.getAllChildDisplayAreas();
+
+ for (DisplayArea displayArea : displayAreas) {
+ final int featureId = displayArea.getFeatureId();
+ if (displayArea.isRootDisplayArea() || displayArea.isTaskDisplayArea()) {
+ assertThat(uniqueIds.add(featureId)).isTrue();
+ assertThat(nonUniqueIds).doesNotContain(featureId);
+ } else {
+ nonUniqueIds.add(featureId);
+ assertThat(uniqueIds).doesNotContain(featureId);
+ }
+ }
+ }
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayHashManagerTest.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayHashManagerTest.java
index a909509..4427b18 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayHashManagerTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayHashManagerTest.java
@@ -368,8 +368,9 @@
mTestView.generateDisplayHash(mFirstHashAlgorithm, null, mExecutor,
mSyncDisplayHashResultCallback);
- // Generate a second display hash right away.
+ mSyncDisplayHashResultCallback.getDisplayHash();
mSyncDisplayHashResultCallback.reset();
+ // Generate a second display hash right away.
mTestView.generateDisplayHash(mFirstHashAlgorithm, null, mExecutor,
mSyncDisplayHashResultCallback);
int errorCode = mSyncDisplayHashResultCallback.getError();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplaySizeTest.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplaySizeTest.java
index 667ed05..84797e2 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplaySizeTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplaySizeTest.java
@@ -134,10 +134,12 @@
executeShellCommand(String.format("wm size %sx%s -d %s",
newLength, newLength, targetDisplayId));
+
synchronized (displayManager) {
if (!displayChanged[0]) {
- displayManager.wait(1000 /* milliseconds */);
+ displayManager.wait(3000 /* milliseconds */);
}
+ assertTrue("DisplayManager must receive onDisplayChanged event", displayChanged[0]);
}
final Point expectedSize = new Point(newLength, newLength);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java b/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java
index 4b62e95..0698c153 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java
@@ -43,6 +43,7 @@
import android.view.ViewGroup;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
@@ -59,6 +60,7 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
+@FlakyTest(bugId = 186608789)
public class DragDropTest extends WindowManagerTestBase {
static final String TAG = "DragDropTest";
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/FreeformWindowingModeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/FreeformWindowingModeTests.java
index 1456486..40bfcc6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/FreeformWindowingModeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/FreeformWindowingModeTests.java
@@ -83,7 +83,7 @@
@Test
public void testNonResizeableActivityHasFullDisplayBounds() throws Exception {
- createManagedSupportsNonResizableMultiWindowSession().set(0);
+ createManagedDevEnableNonResizableMultiWindowSession().set(0);
launchActivity(TEST_ACTIVITY);
mWmState.computeState(TEST_ACTIVITY);
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 4d44076..f6eb851 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -40,7 +40,9 @@
import static android.server.wm.app.Components.BROADCAST_RECEIVER_ACTIVITY;
import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
import static android.server.wm.app.Components.NON_RESIZEABLE_ACTIVITY;
+import static android.server.wm.app.Components.NO_HISTORY_ACTIVITY;
import static android.server.wm.app.Components.RESIZEABLE_ACTIVITY;
+import static android.server.wm.app.Components.SHOW_WHEN_LOCKED_ACTIVITY;
import static android.server.wm.app.Components.SINGLE_TOP_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.server.wm.app.Components.TOP_ACTIVITY;
@@ -1001,6 +1003,37 @@
mWmState.getActivityCountInTask(taskId2, null));
}
+ @Test
+ public void testLaunchNoHistoryActivityOnNewDisplay() {
+ launchActivity(NO_HISTORY_ACTIVITY);
+ waitAndAssertTopResumedActivity(NO_HISTORY_ACTIVITY, DEFAULT_DISPLAY,
+ "Activity launched on primary display and on top");
+
+ final int taskId = mWmState.getTaskByActivity(NO_HISTORY_ACTIVITY).getTaskId();
+
+ final PrimaryDisplayStateSession displayStateSession =
+ mObjectTracker.manage(new PrimaryDisplayStateSession());
+
+ // Create new virtual display.
+ final DisplayContent newDisplay = createManagedVirtualDisplaySession()
+ .setSimulateDisplay(true)
+ .createDisplay();
+
+ launchActivityOnDisplay(NO_HISTORY_ACTIVITY, newDisplay.mId);
+
+ // Check that the activity is resumed on the external display
+ waitAndAssertActivityStateOnDisplay(NO_HISTORY_ACTIVITY, STATE_RESUMED, newDisplay.mId,
+ "Activity launched on external display must be resumed");
+ final int taskId2 = mWmState.getTaskByActivity(NO_HISTORY_ACTIVITY).getTaskId();
+
+ displayStateSession.turnScreenOff();
+ launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+
+ assertNotEquals("Activity must not be in the same task.", taskId, taskId2);
+ assertEquals("Activity is the only member of its task", 1,
+ mWmState.getActivityCountInTask(taskId2, null));
+ }
+
private void assertBroughtExistingTaskToAnotherDisplay(int flags, ComponentName topActivity) {
// Start TEST_ACTIVITY on top of LAUNCHING_ACTIVITY within the same task
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY).execute();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
index 06312f0..f5e7dc9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayKeyguardTests.java
@@ -26,6 +26,8 @@
import android.server.wm.WindowManagerState.DisplayContent;
import android.util.Size;
+import androidx.test.filters.FlakyTest;
+
import org.junit.Before;
import org.junit.Test;
@@ -37,6 +39,7 @@
*/
@Presubmit
@android.server.wm.annotation.Group3
+@FlakyTest(bugId = 186608789)
public class MultiDisplayKeyguardTests extends MultiDisplayTestBase {
@Before
@@ -189,6 +192,7 @@
}
@Test
+ @FlakyTest(bugId = 185566696)
public void testUnlockScreen_decoredSystemDisplayChanged_dismissesKeyguardOnUnlock() {
final LockScreenSession lockScreenSession = createManagedLockScreenSession();
final VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiWindowTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiWindowTests.java
index 1651a0a..6843df3 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiWindowTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiWindowTests.java
@@ -38,9 +38,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
+import android.content.res.Resources;
import android.platform.test.annotations.Presubmit;
import android.server.wm.CommandSession.ActivityCallback;
import android.window.WindowContainerToken;
@@ -83,31 +85,61 @@
/** Resizeable activity should be able to enter multi-window mode.*/
@Test
public void testResizeableActivity() {
- launchActivityInPrimarySplit(TEST_ACTIVITY);
- mWmState.assertVisibility(TEST_ACTIVITY, true);
- mWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
+ assertActivitySupportedInSplitScreen(TEST_ACTIVITY);
}
/**
- * Non-resizeable activity should NOT be able to enter multi-window mode,
- * but should still be visible.
+ * Depending on the value of
+ * {@link com.android.internal.R.integer.config_supportsNonResizableMultiWindow},
+ * non-resizeable activity may or may not be able to enter multi-window mode.
+ *
+ * Based on the flag value:
+ * -1: not support non-resizable in multi window.
+ * 0: check the screen smallest width, if it is a large screen, support non-resizable in multi
+ * window. Otherwise, not support.
+ * 1: always support non-resizable in multi window.
*/
@Test
public void testNonResizeableActivity() {
- createManagedSupportsNonResizableMultiWindowSession().set(0);
-
- boolean gotAssertionError = false;
+ createManagedDevEnableNonResizableMultiWindowSession().set(0);
+ final Resources resources = mContext.getResources();
+ final int configSupportsNonResizableMultiWindow;
try {
- launchActivityInPrimarySplit(NON_RESIZEABLE_ACTIVITY);
- } catch (AssertionError e) {
- gotAssertionError = true;
+ configSupportsNonResizableMultiWindow = resources.getInteger(resources.getIdentifier(
+ "config_supportsNonResizableMultiWindow", "integer", "android"));
+ } catch (Resources.NotFoundException e) {
+ fail("Device must define config_supportsNonResizableMultiWindow");
+ return;
}
- assertTrue("Trying to put non-resizeable activity in split should throw error.",
- gotAssertionError);
- assertTrue(mWmState.containsActivityInWindowingMode(
- NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN));
- mWmState.assertVisibility(NON_RESIZEABLE_ACTIVITY, true);
- mWmState.waitForActivityState(NON_RESIZEABLE_ACTIVITY, STATE_RESUMED);
+ switch (configSupportsNonResizableMultiWindow) {
+ case -1:
+ assertActivityNotSupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
+ break;
+ case 1:
+ assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
+ break;
+ case 0:
+ final int configLargeScreenSmallestScreenWidthDp;
+ try {
+ configLargeScreenSmallestScreenWidthDp =
+ resources.getInteger(resources.getIdentifier(
+ "config_largeScreenSmallestScreenWidthDp",
+ "integer", "android"));
+ } catch (Resources.NotFoundException e) {
+ fail("Device must define config_largeScreenSmallestScreenWidthDp");
+ return;
+ }
+ final int smallestScreenWidthDp = mWmState.getHomeTask()
+ .mFullConfiguration.smallestScreenWidthDp;
+ if (smallestScreenWidthDp >= configLargeScreenSmallestScreenWidthDp) {
+ assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
+ } else {
+ assertActivityNotSupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
+ }
+ break;
+ default:
+ fail("config_supportsNonResizableMultiWindow must be -1, 0, or 1.");
+ }
}
/**
@@ -116,15 +148,10 @@
* set.
*/
@Test
- public void testSupportsNonResizeableMultiWindow_splitScreenPrimary() {
- createManagedSupportsNonResizableMultiWindowSession().set(1);
+ public void testDevEnableNonResizeableMultiWindow_splitScreenPrimary() {
+ createManagedDevEnableNonResizableMultiWindowSession().set(1);
- launchActivityInPrimarySplit(NON_RESIZEABLE_ACTIVITY);
-
- mWmState.waitForActivityState(NON_RESIZEABLE_ACTIVITY, STATE_RESUMED);
- mWmState.assertVisibility(NON_RESIZEABLE_ACTIVITY, true);
- assertTrue(mWmState.containsActivityInWindowingMode(
- NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW));
+ assertActivitySupportedInSplitScreen(NON_RESIZEABLE_ACTIVITY);
}
/**
@@ -133,51 +160,8 @@
* set.
*/
@Test
- public void testSupportsNonResizeableMultiWindow_splitScreenSecondary() {
- createManagedSupportsNonResizableMultiWindowSession().set(1);
-
- launchActivityInPrimarySplit(TEST_ACTIVITY);
-
- mWmState.waitForActivityState(TEST_ACTIVITY, STATE_RESUMED);
- mWmState.assertVisibility(TEST_ACTIVITY, true);
- assertTrue(mWmState.containsActivityInWindowingMode(
- TEST_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW));
-
- launchActivityInSecondarySplit(NON_RESIZEABLE_ACTIVITY);
-
- mWmState.waitForActivityState(NON_RESIZEABLE_ACTIVITY, STATE_RESUMED);
- mWmState.assertVisibility(NON_RESIZEABLE_ACTIVITY, true);
- assertTrue(mWmState.containsActivityInWindowingMode(
- NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW));
- }
-
- /**
- * Non-resizeable activity can enter split-screen if
- * {@link android.provider.Settings.Global#DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW} is
- * set.
- */
- @Test
- public void testSupportsNonResizeableMultiWindow_SplitScreenPrimary() {
- createManagedSupportsNonResizableMultiWindowSession().set(1);
-
- launchActivitiesInSplitScreen(
- getLaunchActivityBuilder().setTargetActivity(NON_RESIZEABLE_ACTIVITY),
- getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
-
- mWmState.waitForActivityState(NON_RESIZEABLE_ACTIVITY, STATE_RESUMED);
- mWmState.assertVisibility(NON_RESIZEABLE_ACTIVITY, true);
- assertTrue(mWmState.containsActivityInWindowingMode(
- NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW));
- }
-
- /**
- * Non-resizeable activity can enter split-screen if
- * {@link android.provider.Settings.Global#DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW} is
- * set.
- */
- @Test
- public void testSupportsNonResizeableMultiWindow_SplitScreenSecondary() {
- createManagedSupportsNonResizableMultiWindowSession().set(1);
+ public void testDevEnableNonResizeableMultiWindow_splitScreenSecondary() {
+ createManagedDevEnableNonResizableMultiWindowSession().set(1);
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY),
@@ -189,6 +173,29 @@
NON_RESIZEABLE_ACTIVITY, WINDOWING_MODE_MULTI_WINDOW));
}
+ /** Asserts that the give activity can be shown in split screen. */
+ private void assertActivitySupportedInSplitScreen(ComponentName activity) {
+ launchActivityInPrimarySplit(activity);
+ mWmState.waitForActivityState(activity, STATE_RESUMED);
+ mWmState.assertVisibility(activity, true);
+ assertTrue(mWmState.containsActivityInWindowingMode(activity, WINDOWING_MODE_MULTI_WINDOW));
+ }
+
+ /** Asserts that the give activity can NOT be shown in split screen. */
+ private void assertActivityNotSupportedInSplitScreen(ComponentName activity) {
+ boolean gotAssertionError = false;
+ try {
+ launchActivityInPrimarySplit(activity);
+ } catch (AssertionError e) {
+ gotAssertionError = true;
+ }
+ assertTrue("Trying to put non-resizeable activity in split should throw error.",
+ gotAssertionError);
+ mWmState.waitForActivityState(activity, STATE_RESUMED);
+ mWmState.assertVisibility(activity, true);
+ assertTrue(mWmState.containsActivityInWindowingMode(activity, WINDOWING_MODE_FULLSCREEN));
+ }
+
@Test
public void testLaunchToSideMultiWindowCallbacks() {
// Launch two activities in split-screen mode.
@@ -199,7 +206,7 @@
int displayWindowingMode = mWmState.getDisplay(
mWmState.getDisplayByActivity(TEST_ACTIVITY)).getWindowingMode();
separateTestJournal();
- mTaskOrganizer.dismissedSplitScreen();
+ mTaskOrganizer.dismissSplitScreen();
if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) {
// Exit split-screen mode and ensure we only get 1 multi-window mode changed callback.
final ActivityLifecycleCounts lifecycleCounts = waitForOnMultiWindowModeChanged(
@@ -238,9 +245,18 @@
launchActivity(NO_RELAUNCH_ACTIVITY);
- // Move activities back to fullscreen screen.
separateTestJournal();
- mTaskOrganizer.dismissedSplitScreen();
+
+ // Move activities back to fullscreen screen.
+ // TestTaskOrganizer sets windowing modes of tasks to unspecific when putting them to split
+ // screens so we need to explicitly set their windowing modes back to fullscreen to avoid
+ // inheriting freeform windowing mode from the display on freeform first devices.
+ int noRelaunchTaskId = mWmState.getTaskByActivity(NO_RELAUNCH_ACTIVITY).mTaskId;
+ WindowContainerToken noRelaunchTaskToken =
+ mTaskOrganizer.getTaskInfo(noRelaunchTaskId).getToken();
+ WindowContainerTransaction t = new WindowContainerTransaction()
+ .setWindowingMode(noRelaunchTaskToken, WINDOWING_MODE_FULLSCREEN);
+ mTaskOrganizer.dismissSplitScreen(t, false /* primaryOnTop */);
lifecycleCounts = waitForOnMultiWindowModeChanged(NO_RELAUNCH_ACTIVITY);
assertEquals("mMultiWindowModeChangedCount",
@@ -322,13 +338,19 @@
targetActivityLauncher.execute();
mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY);
+ final int[] excludeTaskIds = new int[] { secondaryTaskId, INVALID_TASK_ID };
+ if (taskCountMustIncrement) {
+ mWmState.waitFor("Waiting for new activity to come up.",
+ state -> state.getTaskByActivity(targetActivityName, excludeTaskIds) != null);
+ }
WindowManagerState.ActivityTask task = mWmState.getTaskByActivity(targetActivityName,
- secondaryTaskId);
- int secondaryTaskId2 = INVALID_TASK_ID;
+ excludeTaskIds);
+ final int secondaryTaskId2;
if (task != null) {
- secondaryTaskId2 = mWmState.getTaskByActivity(targetActivityName,
- secondaryTaskId).mTaskId;
+ secondaryTaskId2 = task.mTaskId;
mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId2);
+ } else {
+ secondaryTaskId2 = INVALID_TASK_ID;
}
final int taskNumberSecondLaunch = mTaskOrganizer.getSecondarySplitTaskCount();
@@ -339,6 +361,8 @@
assertEquals("Task number must not change.", taskNumberInitial,
taskNumberSecondLaunch);
}
+ mWmState.waitForFocusedActivity("Wait for launched to side activity to be in front.",
+ targetActivityName);
mWmState.assertFocusedActivity("Launched to side activity must be in front.",
targetActivityName);
@@ -347,11 +371,16 @@
// in order to launch into split screen.
targetActivityLauncher.execute();
mWmState.computeState(targetActivityName, LAUNCHING_ACTIVITY);
+
+ excludeTaskIds[1] = secondaryTaskId2;
+ if (taskCountMustIncrement) {
+ mWmState.waitFor("Waiting for the second new activity to come up.",
+ state -> state.getTaskByActivity(targetActivityName, excludeTaskIds) != null);
+ }
WindowManagerState.ActivityTask taskFinal =
- mWmState.getTaskByActivity(targetActivityName, secondaryTaskId2);
+ mWmState.getTaskByActivity(targetActivityName, excludeTaskIds);
if (taskFinal != null) {
- int secondaryTaskId3 = mWmState.getTaskByActivity(targetActivityName,
- secondaryTaskId2).mTaskId;
+ int secondaryTaskId3 = taskFinal.mTaskId;
mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId3);
}
final int taskNumberFinal = mTaskOrganizer.getSecondarySplitTaskCount();
@@ -363,6 +392,8 @@
assertEquals("Task number must not change.", taskNumberSecondLaunch,
taskNumberFinal);
}
+ mWmState.waitForFocusedActivity("Wait for launched to side activity to be in front.",
+ targetActivityName);
mWmState.assertFocusedActivity("Launched to side activity must be in front.",
targetActivityName);
}
@@ -504,15 +535,22 @@
getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY));
+ mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
+
// Launch two more activities on a different task on top of split-screen-secondary and
// only the top opaque activity should be visible.
+ // Explicitly launch them into fullscreen mode because the control windowing mode of the
+ // launch root doesn't include freeform mode. Freeform first devices launch apps in freeform
+ // mode by default, which won't trigger the launch root.
getLaunchActivityBuilder().setTargetActivity(TRANSLUCENT_TEST_ACTIVITY)
.setUseInstrumentation()
.setWaitForLaunched(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
.execute();
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)
.setUseInstrumentation()
.setWaitForLaunched(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
.execute();
mWmState.assertVisibility(TEST_ACTIVITY, true);
mWmState.waitForActivityState(TRANSLUCENT_TEST_ACTIVITY, STATE_STOPPED);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index fbc0883..881aab8 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -840,6 +840,19 @@
}
@Test
+ public void testPipFromTaskWithMultipleActivitiesAndFinishOriginalTask() {
+ // Try to enter picture-in-picture from an activity that finished itself and ensure
+ // pinned task is removed when the original task vanishes
+ launchActivity(LAUNCH_ENTER_PIP_ACTIVITY,
+ extraString(EXTRA_FINISH_SELF_ON_RESUME, "true"));
+
+ waitForEnterPip(PIP_ACTIVITY);
+ waitForPinnedStackRemoved();
+
+ assertPinnedStackDoesNotExist();
+ }
+
+ @Test
public void testPipFromTaskWithMultipleActivitiesAndRemoveOriginalTask() {
// Try to enter picture-in-picture from an activity that has more than one activity in the
// task and ensure pinned task is removed when the original task vanishes
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java
index 487d996..dad6ddb 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java
@@ -50,7 +50,8 @@
import static android.view.WindowInsets.Type.systemBars;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.lessThan;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -67,6 +68,7 @@
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager;
@@ -114,8 +116,9 @@
final Bitmap image = takeScreenshot();
final WindowMetrics windowMetrics = mWm.getMaximumWindowMetrics();
final Rect stableBounds = new Rect(windowMetrics.getBounds());
- stableBounds.inset(windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(
- systemBars() & ~captionBar()));
+ Insets insets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(
+ systemBars() & ~captionBar());
+ stableBounds.inset(insets);
WindowManagerState.WindowState startingWindow = mWmState.findFirstWindowWithType(
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
@@ -127,7 +130,12 @@
appBounds = new Rect(startingWindow.getFrame());
}
- appBounds.intersect(stableBounds);
+ Rect topInsetsBounds = new Rect(insets.left, 0, appBounds.right - insets.right, insets.top);
+ Rect bottomInsetsBounds = new Rect(insets.left, appBounds.bottom - insets.bottom,
+ appBounds.right - insets.right, appBounds.bottom);
+ assertFalse("Top insets bounds rect is empty", topInsetsBounds.isEmpty());
+ assertFalse("Bottom insets bounds rect is empty", bottomInsetsBounds.isEmpty());
+
if (appBounds.isEmpty()) {
fail("Couldn't find splash screen bounds. Impossible to assert the colors");
}
@@ -138,40 +146,51 @@
int px = WindowManagerState.dpToPx(CENTER_ICON_SIZE,
mContext.getResources().getConfiguration().densityDpi);
Rect ignoreRect = new Rect(0, 0, px, px);
- ignoreRect.offsetTo(
- appBounds.centerX() - ignoreRect.width() / 2,
+ ignoreRect.offsetTo(appBounds.centerX() - ignoreRect.width() / 2,
appBounds.centerY() - ignoreRect.height() / 2);
- assertColors(image, appBounds, primaryColor, 0.50f, secondaryColor, 0.02f, ignoreRect);
+ appBounds.intersect(stableBounds);
+ assertColors(image, appBounds, primaryColor, 0.99f, secondaryColor, 0.02f, ignoreRect);
+ assertColors(image, topInsetsBounds, primaryColor, 0.80f, secondaryColor, 0.10f, null);
+ assertColors(image, bottomInsetsBounds, primaryColor, 0.80f, secondaryColor, 0.10f, null);
}
- private void assertColors(Bitmap img, Rect bounds, int primaryColor,
- float expectedPrimaryRatio, int secondaryColor, float acceptableWrongRatio,
- Rect ignoreRect) {
+ private void assertColors(Bitmap img, Rect bounds, int primaryColor, float expectedPrimaryRatio,
+ int secondaryColor, float acceptableWrongRatio, Rect ignoreRect) {
int primaryPixels = 0;
int secondaryPixels = 0;
int wrongPixels = 0;
+
+ assertThat(bounds.top, greaterThanOrEqualTo(0));
+ assertThat(bounds.left, greaterThanOrEqualTo(0));
+ assertThat(bounds.right, lessThanOrEqualTo(img.getWidth()));
+ assertThat(bounds.bottom, lessThanOrEqualTo(img.getHeight()));
+
for (int x = bounds.left; x < bounds.right; x++) {
for (int y = bounds.top; y < bounds.bottom; y++) {
if (ignoreRect != null && y >= ignoreRect.top && y < ignoreRect.bottom
&& x >= ignoreRect.left && x < ignoreRect.right) {
+ img.setPixel(x, y, Color.YELLOW);
continue;
}
- assertThat(x, lessThan(img.getWidth()));
- assertThat(y, lessThan(img.getHeight()));
final int color = img.getPixel(x, y);
if (primaryColor == color) {
primaryPixels++;
} else if (secondaryColor == color) {
secondaryPixels++;
} else {
+ img.setPixel(x, y, Color.MAGENTA);
wrongPixels++;
}
}
}
- final int totalPixels = bounds.width() * bounds.height();
+ int totalPixels = bounds.width() * bounds.height();
+ if (ignoreRect != null) {
+ totalPixels -= ignoreRect.width() * ignoreRect.height();
+ }
+
final float primaryRatio = (float) primaryPixels / totalPixels;
if (primaryRatio < expectedPrimaryRatio) {
fail("Less than " + (expectedPrimaryRatio * 100.0f)
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
index da18c69..145b700 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java
@@ -17,7 +17,9 @@
package android.server.wm;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.server.wm.WindowManagerState.STATE_INITIALIZING;
import static android.server.wm.WindowManagerState.STATE_STOPPED;
+import static android.server.wm.app.Components.ALT_LAUNCHING_ACTIVITY;
import static android.server.wm.app.Components.LAUNCHING_ACTIVITY;
import static android.server.wm.app.Components.NO_RELAUNCH_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
@@ -37,6 +39,7 @@
import static org.junit.Assert.assertNotEquals;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
@@ -45,9 +48,14 @@
import android.server.wm.CommandSession.ActivitySession;
import android.server.wm.intent.Activities;
+import androidx.test.filters.FlakyTest;
+
import org.junit.Test;
import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Build/Install/Run:
@@ -82,6 +90,7 @@
* from an {@link Activity} {@link android.content.Context}.
*/
@Test
+ @FlakyTest(bugId = 188207199)
public void testStartActivityContexts() {
// Note by default LaunchActivityBuilder will use LAUNCHING_ACTIVITY to launch the target.
@@ -194,6 +203,7 @@
* activity because the caller C in different uid cannot launch a non-exported activity.
*/
@Test
+ @FlakyTest(bugId = 188207199)
public void testStartActivityByNavigateUpToFromDiffUid() {
final Intent intent1 = new Intent(mContext, Activities.RegularActivity.class);
final String regularActivityName = Activities.RegularActivity.class.getName();
@@ -271,6 +281,54 @@
}
/**
+ * Test the activity launched with ActivityOptions#setTaskOverlay should remain on top of the
+ * task after start another activity.
+ */
+ @Test
+ public void testStartActivitiesTaskOverlayStayOnTop() {
+ final Intent baseIntent = new Intent(mContext, Activities.RegularActivity.class);
+ final String regularActivityName = Activities.RegularActivity.class.getName();
+ final TestActivitySession<Activities.RegularActivity> activitySession =
+ createManagedTestActivitySession();
+ activitySession.launchTestActivityOnDisplaySync(regularActivityName, baseIntent,
+ DEFAULT_DISPLAY);
+ mWmState.computeState(baseIntent.getComponent());
+ final int taskId = mWmState.getTaskByActivity(baseIntent.getComponent()).getTaskId();
+ final Activity baseActivity = activitySession.getActivity();
+
+ final ActivityOptions overlayOptions = ActivityOptions.makeBasic();
+ overlayOptions.setTaskOverlay(true, true);
+ overlayOptions.setLaunchTaskId(taskId);
+ final Intent taskOverlay = new Intent().setComponent(SECOND_ACTIVITY);
+ runWithShellPermission(() ->
+ baseActivity.startActivity(taskOverlay, overlayOptions.toBundle()));
+
+ waitAndAssertResumedActivity(taskOverlay.getComponent(),
+ "taskOverlay activity on top");
+ final Intent behindOverlay = new Intent().setComponent(TEST_ACTIVITY);
+ baseActivity.startActivity(behindOverlay);
+
+ waitAndAssertActivityState(TEST_ACTIVITY, STATE_INITIALIZING,
+ "Activity behind taskOverlay should not resumed");
+ // check order: SecondActivity(top) -> TestActivity -> RegularActivity(base)
+ final List<String> activitiesOrder = mWmState.getTaskByActivity(baseIntent.getComponent())
+ .mActivities
+ .stream()
+ .map(WindowManagerState.Activity::getName)
+ .collect(Collectors.toList());
+
+ final List<String> expectedOrder = Stream.of(
+ SECOND_ACTIVITY,
+ TEST_ACTIVITY,
+ baseIntent.getComponent())
+ .map(c -> c.flattenToShortString())
+ .collect(Collectors.toList());
+ assertEquals(activitiesOrder, expectedOrder);
+ mWmState.assertResumedActivity("TaskOverlay activity should be remained on top and "
+ + "resumed", taskOverlay.getComponent());
+ }
+
+ /**
* Invokes {@link android.app.Activity#startActivities} from {@link #TEST_ACTIVITY} and returns
* the task id of each started activity (the index 0 will be the caller {@link #TEST_ACTIVITY}).
*/
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java
index f93ffe3..6d62599 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlTest.java
@@ -15,79 +15,96 @@
*/
package android.server.wm;
-import static junit.framework.Assert.assertEquals;
+import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static android.server.wm.UiDeviceUtils.pressHomeButton;
-import static android.server.wm.UiDeviceUtils.pressUnlockButton;
-import static android.server.wm.UiDeviceUtils.pressWakeupButton;
-import static android.server.wm.WindowManagerState.getLogicalDisplaySize;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.support.test.uiautomator.UiObjectNotFoundException;
import android.view.Surface;
import android.view.SurfaceControl;
-import android.view.cts.surfacevalidator.CapturedActivity;
-import android.view.cts.surfacevalidator.PixelChecker;
+import android.view.SurfaceHolder;
import android.view.cts.surfacevalidator.PixelColor;
-import android.view.cts.surfacevalidator.RectChecker;
-import android.view.cts.surfacevalidator.SurfaceControlTestCase;
+import android.view.cts.surfacevalidator.ASurfaceControlTestActivity;
+import android.view.cts.surfacevalidator.ASurfaceControlTestActivity.PixelChecker;
-import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
+import androidx.annotation.NonNull;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
-@LargeTest
@Presubmit
public class SurfaceControlTest {
- private static final int DEFAULT_LAYOUT_WIDTH = 640;
- private static final int DEFAULT_LAYOUT_HEIGHT = 480;
- private static final int DEFAULT_BUFFER_WIDTH = 640;
- private static final int DEFAULT_BUFFER_HEIGHT = 480;
private static final int DEFAULT_SURFACE_SIZE = 100;
@Rule
- public ActivityTestRule<CapturedActivity> mActivityRule =
- new ActivityTestRule<>(CapturedActivity.class);
+ public ActivityScenarioRule<ASurfaceControlTestActivity> mActivityRule =
+ createFullscreenActivityScenarioRule(ASurfaceControlTestActivity.class);
@Rule
public TestName mName = new TestName();
- private CapturedActivity mActivity;
+ private ASurfaceControlTestActivity mActivity;
- private void verifyTest(SurfaceControlTestCase.ParentSurfaceConsumer psc,
+ private abstract class SurfaceHolderCallback implements
+ SurfaceHolder.Callback {
+
+ public abstract void addChildren(SurfaceControl parent);
+
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ addChildren(mActivity.getSurfaceView().getSurfaceControl());
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {}
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {}
+ }
+
+ private class RectChecker extends ASurfaceControlTestActivity.PixelChecker {
+ private final Rect mBoundsToCheck;
+ RectChecker(Rect boundsToCheck, int expectedColor) {
+ super(expectedColor);
+ mBoundsToCheck = boundsToCheck;
+ }
+ public boolean checkPixels(int matchingPixelCount, int width, int height) {
+ int expectedPixelCountMin = mBoundsToCheck.width() * mBoundsToCheck.height() - 100;
+ int expectedPixelCountMax = mBoundsToCheck.width() * mBoundsToCheck.height();
+ return matchingPixelCount > expectedPixelCountMin &&
+ matchingPixelCount <= expectedPixelCountMax;
+ }
+
+ @Override
+ public Rect getBoundsToCheck(Bitmap bitmap) {
+ return mBoundsToCheck;
+ }
+ }
+
+ private abstract class MultiRectChecker extends RectChecker {
+ MultiRectChecker(Rect boundsToCheck, int expectedColor) {
+ super(boundsToCheck, expectedColor);
+ }
+
+ public abstract PixelColor getExpectedColor(int x, int y);
+ }
+
+ private void verifyTest(SurfaceHolder.Callback callback,
PixelChecker pixelChecker) throws Throwable {
- mActivity.verifyTest(new SurfaceControlTestCase(psc, null,
- pixelChecker, DEFAULT_LAYOUT_WIDTH, DEFAULT_LAYOUT_HEIGHT,
- DEFAULT_BUFFER_WIDTH, DEFAULT_BUFFER_HEIGHT),
- mName);
+ mActivity.verifyTest(callback, pixelChecker, 0 /* delayInMs */);
}
@Before
public void setup() {
- pressWakeupButton();
- pressUnlockButton();
-
- mActivity = mActivityRule.getActivity();
- mActivity.dismissPermissionDialog();
- mActivity.setLogicalDisplaySize(getLogicalDisplaySize());
- }
-
- /**
- * Want to be especially sure we don't leave up the permission dialog, so try and dismiss
- * after test.
- */
- @After
- public void tearDown() throws UiObjectNotFoundException {
- mActivity.dismissPermissionDialog();
- mActivity.restoreSettings();
+ mActivityRule.getScenario().onActivity(activity -> mActivity = activity);
}
@Test
@@ -137,6 +154,15 @@
private SurfaceControl buildDefaultRedSurface(SurfaceControl parent) {
return buildDefaultSurface(parent, Color.RED);
}
+ private SurfaceControl buildSmallRedSurface(SurfaceControl parent) {
+ SurfaceControl surfaceControl = new SurfaceControl.Builder()
+ .setBufferSize(DEFAULT_SURFACE_SIZE / 2, DEFAULT_SURFACE_SIZE / 2)
+ .setName("CTS surface")
+ .setParent(parent)
+ .build();
+ fillWithColor(surfaceControl, Color.RED);
+ return surfaceControl;
+ }
/**
* Verify that showing a 100x100 surface filled with RED produces roughly 10,000 red pixels.
@@ -144,7 +170,7 @@
@Test
public void testShow() throws Throwable {
verifyTest(
- new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ new SurfaceHolderCallback() {
@Override
public void addChildren(SurfaceControl parent) {
final SurfaceControl sc = buildDefaultRedSurface(parent);
@@ -163,7 +189,7 @@
@Test
public void testHide() throws Throwable {
verifyTest(
- new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ new SurfaceHolderCallback () {
@Override
public void addChildren(SurfaceControl parent) {
final SurfaceControl sc = buildDefaultRedSurface(parent);
@@ -173,7 +199,7 @@
sc.release();
}
},
- new RectChecker(new Rect(0, 0, 100, 100), PixelColor.WHITE));
+ new RectChecker(new Rect(0, 0, 100, 100), PixelColor.BLACK));
}
/**
@@ -183,14 +209,14 @@
public void testReparentOff() throws Throwable {
final SurfaceControl sc = buildDefaultRedSurface(null);
verifyTest(
- new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ new SurfaceHolderCallback () {
@Override
public void addChildren(SurfaceControl parent) {
new SurfaceControl.Transaction().reparent(sc, parent).apply();
new SurfaceControl.Transaction().reparent(sc, null).apply();
}
},
- new RectChecker(new Rect(0, 0, 100, 100), PixelColor.WHITE));
+ new RectChecker(new Rect(0, 0, 100, 100), PixelColor.BLACK));
// Since the SurfaceControl is parented off-screen, if we release our reference
// it may completely die. If this occurs while the render thread is still rendering
// the RED background we could trigger a crash. For this test defer destroying the
@@ -206,7 +232,7 @@
@Test
public void testReparentOn() throws Throwable {
verifyTest(
- new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ new SurfaceHolderCallback () {
@Override
public void addChildren(SurfaceControl parent) {
final SurfaceControl sc = buildDefaultRedSurface(null);
@@ -227,7 +253,7 @@
@Test
public void testSetLayer() throws Throwable {
verifyTest(
- new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ new SurfaceHolderCallback () {
@Override
public void addChildren(SurfaceControl parent) {
final SurfaceControl sc = buildDefaultRedSurface(parent);
@@ -251,22 +277,30 @@
@Test
public void testSetGeometry_dstBoundsOffScreen() throws Throwable {
verifyTest(
- new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ new SurfaceHolderCallback () {
@Override
public void addChildren(SurfaceControl parent) {
final SurfaceControl sc = buildDefaultRedSurface(parent);
new SurfaceControl.Transaction().setVisibility(sc, true)
.setGeometry(sc, null, new Rect(-50, -50, 50, 50), Surface.ROTATION_0)
.apply();
-
sc.release();
}
},
// The rect should be offset by -50 pixels
- new RectChecker(
- new RectChecker.Target(new Rect(0, 0, 50, 50), PixelColor.RED),
- new RectChecker.Target(new Rect(50, 50, 150, 150), PixelColor.WHITE)));
+ new MultiRectChecker(new Rect(0, 0, 100, 100), PixelColor.RED) {
+ final PixelColor red = new PixelColor(PixelColor.RED);
+ final PixelColor black = new PixelColor(PixelColor.BLACK);
+ @Override
+ public PixelColor getExpectedColor(int x, int y) {
+ if (x < 50 && y < 50) {
+ return red;
+ } else {
+ return black;
+ }
+ }
+ });
}
/**
@@ -275,7 +309,7 @@
@Test
public void testSetGeometry_dstBoundsOnScreen() throws Throwable {
verifyTest(
- new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ new SurfaceHolderCallback () {
@Override
public void addChildren(SurfaceControl parent) {
final SurfaceControl sc = buildDefaultRedSurface(parent);
@@ -288,8 +322,18 @@
},
// The rect should be offset by 50 pixels
- new RectChecker(
- new RectChecker.Target(new Rect(50, 50, 150, 150), PixelColor.RED)));
+ new MultiRectChecker(new Rect(0, 0, 100, 100), PixelColor.RED) {
+ final PixelColor red = new PixelColor(PixelColor.RED);
+ final PixelColor black = new PixelColor(PixelColor.BLACK);
+ @Override
+ public PixelColor getExpectedColor(int x, int y) {
+ if (x >= 50 && y >= 50) {
+ return red;
+ } else {
+ return black;
+ }
+ }
+ });
}
/**
@@ -298,21 +342,19 @@
@Test
public void testSetGeometry_dstBoundsScaled() throws Throwable {
verifyTest(
- new SurfaceControlTestCase.ParentSurfaceConsumer () {
+ new SurfaceHolderCallback () {
@Override
public void addChildren(SurfaceControl parent) {
- final SurfaceControl sc = buildDefaultRedSurface(parent);
+ final SurfaceControl sc = buildSmallRedSurface(parent);
new SurfaceControl.Transaction().setVisibility(sc, true)
- .setGeometry(sc, new Rect(0, 0, DEFAULT_SURFACE_SIZE, DEFAULT_SURFACE_SIZE),
- new Rect(0, 0, DEFAULT_SURFACE_SIZE * 2, DEFAULT_SURFACE_SIZE*2),
+ .setGeometry(sc, new Rect(0, 0, DEFAULT_SURFACE_SIZE / 2, DEFAULT_SURFACE_SIZE / 2),
+ new Rect(0, 0, DEFAULT_SURFACE_SIZE , DEFAULT_SURFACE_SIZE),
Surface.ROTATION_0)
.apply();
-
sc.release();
}
},
- new RectChecker(
- new RectChecker.Target(new Rect(0, 0, 200, 200), PixelColor.RED)));
+ new RectChecker(new Rect(0, 0, 100, 100), PixelColor.RED));
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java
index e5a8504..642c82a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTests.java
@@ -158,12 +158,14 @@
final float expectedFontScale = fontScaleSession.get() + 0.3f;
fontScaleSession.set(expectedFontScale);
+ // Wait for TestComponentCallbacks#mConfiguration to be assigned.
+ callbacks.waitForConfigChanged();
+
// We don't rely on latch to verify the result because we may receive two configuration
// changes. One may from that WindowContext attaches to a DisplayArea although it is before
// ComponentCallback registration), the other is from font the scale change, which is what
// we want to verify.
- waitForOrFail("Font scale must be " + expectedFontScale + ","
- + " but was " + callbacks.mConfiguration.fontScale, () ->
+ waitForOrFail("font scale to match " + expectedFontScale, () ->
expectedFontScale == callbacks.mConfiguration.fontScale);
windowContext.unregisterComponentCallbacks(callbacks);
@@ -280,6 +282,14 @@
private Configuration mConfiguration;
private CountDownLatch mLatch = new CountDownLatch(1);
+ private void waitForConfigChanged() {
+ try {
+ assertThat(mLatch.await(4, TimeUnit.SECONDS)).isTrue();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
mConfiguration = newConfig;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
index 004e0dd..4a0920e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
@@ -61,6 +61,7 @@
import android.view.WindowManager;
import android.view.WindowMetrics;
+import androidx.test.filters.FlakyTest;
import androidx.test.rule.ActivityTestRule;
import com.android.compatibility.common.util.CtsTouchUtils;
@@ -113,6 +114,7 @@
}
@Test
+ @FlakyTest(bugId = 188207199)
public void testMoveWindowAndTap() throws Throwable {
final WindowManager wm = mActivity.getWindowManager();
final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
index df3c952..3389409 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
@@ -19,7 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.graphics.Insets.NONE;
import static android.view.WindowInsets.Type.ime;
-import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -76,7 +76,7 @@
@Test
public void testAnimationCallbacks_overlapping_opposite() {
initActivity(false /* useFloating */);
- assumeTrue(hasWindowInsets(mRootView, navigationBars()));
+ assumeTrue(hasWindowInsets(mRootView, statusBars()));
WindowInsets before = mActivity.mLastWindowInsets;
@@ -89,7 +89,7 @@
mActivity.mView.setWindowInsetsAnimationCallback(callback);
getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().hide(navigationBars()));
+ () -> mRootView.getWindowInsetsController().hide(statusBars()));
getInstrumentation().runOnMainSync(
() -> mRootView.getWindowInsetsController().show(ime()));
@@ -104,15 +104,15 @@
InOrder inOrderBar = inOrder(callback, mActivity.mListener);
InOrder inOrderIme = inOrder(callback, mActivity.mListener);
- inOrderBar.verify(callback).onPrepare(eq(callback.navBarAnim));
+ inOrderBar.verify(callback).onPrepare(eq(callback.statusBarAnim));
inOrderIme.verify(mActivity.mListener).onApplyWindowInsets(any(), argThat(
- argument -> NONE.equals(argument.getInsets(navigationBars()))
+ argument -> NONE.equals(argument.getInsets(statusBars()))
&& NONE.equals(argument.getInsets(ime()))));
- inOrderBar.verify(callback).onStart(eq(callback.navBarAnim), argThat(
+ inOrderBar.verify(callback).onStart(eq(callback.statusBarAnim), argThat(
argument -> argument.getLowerBound().equals(NONE)
- && argument.getUpperBound().equals(before.getInsets(navigationBars()))));
+ && argument.getUpperBound().equals(before.getInsets(statusBars()))));
inOrderIme.verify(callback).onPrepare(eq(callback.imeAnim));
inOrderIme.verify(mActivity.mListener).onApplyWindowInsets(
@@ -122,17 +122,17 @@
argument -> argument.getLowerBound().equals(NONE)
&& !argument.getUpperBound().equals(NONE)));
- inOrderBar.verify(callback).onEnd(eq(callback.navBarAnim));
+ inOrderBar.verify(callback).onEnd(eq(callback.statusBarAnim));
inOrderIme.verify(callback).onEnd(eq(callback.imeAnim));
- assertAnimationSteps(callback.navAnimSteps, false /* showAnimation */);
+ assertAnimationSteps(callback.statusAnimSteps, false /* showAnimation */);
assertAnimationSteps(callback.imeAnimSteps, true /* showAnimation */, ime());
- assertEquals(before.getInsets(navigationBars()),
- callback.navAnimSteps.get(0).insets.getInsets(navigationBars()));
- assertEquals(after.getInsets(navigationBars()),
- callback.navAnimSteps.get(callback.navAnimSteps.size() - 1).insets
- .getInsets(navigationBars()));
+ assertEquals(before.getInsets(statusBars()),
+ callback.statusAnimSteps.get(0).insets.getInsets(statusBars()));
+ assertEquals(after.getInsets(statusBars()),
+ callback.statusAnimSteps.get(callback.statusAnimSteps.size() - 1).insets
+ .getInsets(statusBars()));
assertEquals(before.getInsets(ime()),
callback.imeAnimSteps.get(0).insets.getInsets(ime()));
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 c6997f8..02ee61d 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
@@ -64,6 +64,7 @@
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsController;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -209,17 +210,38 @@
expectEvent(stream, editorMatcher("onStartInput", activity.mEditTextMarker), TIMEOUT);
final View rootView = activity.getWindow().getDecorView();
- getInstrumentation().runOnMainSync(() -> {
- rootView.getWindowInsetsController().show(ime());
- });
+ getInstrumentation().runOnMainSync(() -> rootView.getWindowInsetsController().show(ime()));
PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(ime()));
- getInstrumentation().runOnMainSync(() -> {
- rootView.getWindowInsetsController().hide(ime());
- });
+ getInstrumentation().runOnMainSync(() -> rootView.getWindowInsetsController().hide(ime()));
PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(ime()));
}
@Test
+ public void testImeForceShowingNavigationBar() throws Exception {
+ final Instrumentation instrumentation = getInstrumentation();
+ assumeThat(MockImeSession.getUnavailabilityReason(instrumentation.getContext()),
+ nullValue());
+ final MockImeSession imeSession = MockImeHelper.createManagedMockImeSession(this);
+ final ImeEventStream stream = imeSession.openEventStream();
+ final TestActivity activity = startActivity(TestActivity.class);
+ expectEvent(stream, editorMatcher("onStartInput", activity.mEditTextMarker), TIMEOUT);
+
+ final View rootView = activity.getWindow().getDecorView();
+ assumeTrue(rootView.getRootWindowInsets().isVisible(navigationBars()));
+ getInstrumentation().runOnMainSync(
+ () -> rootView.getWindowInsetsController().hide(navigationBars()));
+ PollingCheck.waitFor(TIMEOUT,
+ () -> !rootView.getRootWindowInsets().isVisible(navigationBars()));
+ getInstrumentation().runOnMainSync(() -> rootView.getWindowInsetsController().show(ime()));
+ PollingCheck.waitFor(TIMEOUT,
+ () -> rootView.getRootWindowInsets().isVisible(ime() | navigationBars()));
+ getInstrumentation().runOnMainSync(() -> rootView.getWindowInsetsController().hide(ime()));
+ PollingCheck.waitFor(TIMEOUT,
+ () -> !rootView.getRootWindowInsets().isVisible(ime())
+ && !rootView.getRootWindowInsets().isVisible(navigationBars()));
+ }
+
+ @Test
public void testSetSystemBarsBehavior_default() throws InterruptedException {
final TestActivity activity = startActivity(TestActivity.class);
final View rootView = activity.getWindow().getDecorView();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsActivityTests.java
index 8e7e6d8..22a5229 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowMetricsActivityTests.java
@@ -41,6 +41,8 @@
import android.view.WindowManager;
import android.view.WindowMetrics;
+import androidx.test.filters.FlakyTest;
+
import org.junit.Test;
/**
@@ -77,6 +79,7 @@
}
@Test
+ @FlakyTest(bugId = 188207199)
public void testMetricsMatchesActivityBoundsOnNonresizableActivity() {
final RotationSession rotationSession = createManagedRotationSession();
final MinAspectRatioActivity activity =
@@ -188,6 +191,7 @@
}
@Test
+ @FlakyTest(bugId = 188207199)
public void testMetricsMatchesActivityBoundsOnNonresizableSplitActivity() {
assumeTrue(supportsSplitScreenMultiWindow());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowUntrustedTouchTest.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowUntrustedTouchTest.java
index 1283769..2f7b5a5 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowUntrustedTouchTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowUntrustedTouchTest.java
@@ -67,6 +67,7 @@
import androidx.annotation.AnimRes;
import androidx.annotation.Nullable;
+import androidx.test.filters.FlakyTest;
import androidx.test.rule.ActivityTestRule;
import com.android.compatibility.common.util.AppOpsUtils;
@@ -520,6 +521,7 @@
}
@Test
+ @FlakyTest(bugId = 186608789)
public void testWhenTwoActivityWindowsFromDifferentAppsTogetherBelowThreshold_blocksTouch()
throws Throwable {
addActivityOverlay(APP_A, /* opacity */ .7f);
@@ -762,6 +764,7 @@
}
@Test
+ @FlakyTest(bugId = 188207199)
public void testWhenOneCustomToastWindow_blocksTouch() throws Throwable {
addToastOverlay(APP_A, /* custom */ true);
@@ -780,6 +783,7 @@
}
@Test
+ @FlakyTest(bugId = 188207199)
public void testWhenOneCustomToastWindowAndOneSelfSawWindow_blocksTouch()
throws Throwable {
addSawOverlay(APP_SELF, WINDOW_1, .9f);
@@ -791,6 +795,7 @@
}
@Test
+ @FlakyTest(bugId = 188207199)
public void testWhenOneCustomToastWindowAndOneSawWindowBelowThreshold_blocksTouch()
throws Throwable {
addSawOverlay(APP_A, WINDOW_1, .5f);
@@ -802,6 +807,7 @@
}
@Test
+ @FlakyTest(bugId = 188207199)
public void testWhenOneCustomToastWindowAndOneSawWindowBelowThresholdFromDifferentApp_blocksTouch()
throws Throwable {
addSawOverlay(APP_A, WINDOW_1, .5f);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
index a9264ee..24e6300 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleTests.java
@@ -60,6 +60,7 @@
import android.content.pm.ActivityInfo;
import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import com.android.compatibility.common.util.AmUtils;
@@ -947,6 +948,7 @@
}
@Test
+ @FlakyTest(bugId = 186608789)
public void testFinishBelowDialogActivity() throws Exception {
verifyFinishAtStage(ResultActivity.class, EXTRA_FINISH_IN_ON_PAUSE, "onPause",
TranslucentCallbackTrackingActivity.class);
@@ -971,6 +973,7 @@
}
@Test
+ @FlakyTest(bugId = 186608789)
public void testFinishBelowTranslucentActivityAfterDelay() throws Exception {
final Activity bottomActivity = launchActivityAndWait(CallbackTrackingActivity.class);
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 94a678c..d5a51e9 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
@@ -55,6 +55,7 @@
import android.server.wm.lifecycle.LifecycleLog.ActivityCallback;
import android.util.Pair;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.Before;
@@ -174,6 +175,7 @@
}
@Test
+ @FlakyTest(bugId = 186608789)
public void testTopPositionSwitchToTranslucentActivityOnTop() throws Exception {
final Activity activity = launchActivityAndWait(CallbackTrackingActivity.class);
@@ -417,6 +419,7 @@
}
@Test
+ @FlakyTest(bugId = 186608789)
public void testTopPositionNewIntentForPaused() throws Exception {
// Launch single top activity
final Activity singleTopActivity = launchActivityAndWait(SingleTopActivity.class);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
index f5d1693..e60ae4f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
@@ -24,10 +24,12 @@
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.server.wm.ComponentNameUtils.getActivityName;
+import static android.server.wm.UiDeviceUtils.pressWakeupButton;
import static android.server.wm.WindowManagerState.STATE_DESTROYED;
import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.app.Components.ALIAS_TEST_ACTIVITY;
import static android.server.wm.app.Components.NO_HISTORY_ACTIVITY;
+import static android.server.wm.app.Components.SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -183,6 +185,84 @@
}
/**
+ * This test case tests the behavior that a fullscreen activity was started on top of the
+ * no-history activity within different tasks during sleeping. The no-history activity must be
+ * finished.
+ */
+ @Test
+ public void testNoHistoryActivityWithDifferentTask() {
+ assumeTrue(supportsLockScreen());
+
+ final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ // Launch a no-history activity
+ getLaunchActivityBuilder().setTargetActivity(NO_HISTORY_ACTIVITY)
+ .setIntentExtra(extra -> extra.putBoolean(
+ Components.NoHistoryActivity.EXTRA_SHOW_WHEN_LOCKED, true))
+ .setWaitForLaunched(false)
+ .setUseInstrumentation()
+ .execute();
+
+ // Wait for the activity resumed.
+ waitAndAssertActivityState(NO_HISTORY_ACTIVITY, STATE_RESUMED,
+ "Activity must be resumed");
+ final int taskId = mWmState.getTaskByActivity(NO_HISTORY_ACTIVITY).getTaskId();
+ lockScreenSession.sleepDevice();
+
+ // Launch a single instance activity
+ getLaunchActivityBuilder().setTargetActivity(SINGLE_INSTANCE_ACTIVITY)
+ .setIntentExtra(extra -> extra.putBoolean(
+ SingleInstanceActivity.EXTRA_SHOW_WHEN_LOCKED, true))
+ .setWaitForLaunched(false)
+ .setUseInstrumentation()
+ .execute();
+
+ // Make sure the activity is finished.
+ final String waitFinishMsg = "Instance of no-history activity must not exist";
+ assertTrue(waitFinishMsg, mWmState.waitForWithAmState(
+ amState -> 0 == amState.getActivityCountInTask(taskId, NO_HISTORY_ACTIVITY),
+ waitFinishMsg));
+
+ // Turn the screen on after the test is completed to prevent keyDispatchingTimedOut during
+ // the lockScreenSession close.
+ pressWakeupButton();
+ }
+
+ /**
+ * This test case tests the behavior that a translucent activity was started on top of the
+ * no-history activity during sleeping. The no-history activity must not be finished.
+ */
+ @Test
+ public void testNoHistoryActivityWithTranslucentActivity() {
+ assumeTrue(supportsLockScreen());
+
+ final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ // Launch a no-history activity
+ getLaunchActivityBuilder().setTargetActivity(NO_HISTORY_ACTIVITY)
+ .setIntentExtra(extra -> extra.putBoolean(
+ Components.NoHistoryActivity.EXTRA_SHOW_WHEN_LOCKED, true))
+ .setWaitForLaunched(false)
+ .setUseInstrumentation()
+ .execute();
+
+ // Wait for the activity resumed.
+ waitAndAssertActivityState(NO_HISTORY_ACTIVITY, STATE_RESUMED,
+ "Activity must be resumed");
+
+ final int taskId = mWmState.getTaskByActivity(NO_HISTORY_ACTIVITY).getTaskId();
+ lockScreenSession.sleepDevice();
+ launchActivityNoWait(SHOW_WHEN_LOCKED_TRANSLUCENT_ACTIVITY);
+
+ final String waitFinishMsg = "Instance of no-history activity must exist";
+ assertTrue(waitFinishMsg, mWmState.waitForWithAmState(
+ amState -> 1 == amState.getActivityCountInTask(taskId, NO_HISTORY_ACTIVITY),
+ waitFinishMsg));
+
+ // Turn the screen on after the test is completed to prevent keyDispatchingTimedOut during
+ // the lockScreenSession close.
+ pressWakeupButton();
+ }
+
+ /**
* This test case tests "single top" activity behavior.
* - A first launched standard activity and a second launched single top
* activity are in same task.
@@ -662,6 +742,14 @@
// Test activity
public static class SingleInstanceActivity extends Activity {
+ public static final String EXTRA_SHOW_WHEN_LOCKED = "showWhenLocked";
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ if (getIntent().getBooleanExtra(EXTRA_SHOW_WHEN_LOCKED, false)) {
+ setShowWhenLocked(true);
+ }
+ }
}
// Test activity
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
index 2359e9c..ed92d35 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityTests.java
@@ -36,6 +36,7 @@
import android.app.Activity;
import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.Test;
@@ -120,6 +121,7 @@
* for root of task. This version verifies lifecycle when top activity is translucent
*/
@Test
+ @FlakyTest(bugId = 186608789)
public void testFinishTask_FromRoot_TranslucentOnTop() throws Exception {
final Class<? extends Activity> rootActivityClass = CallbackTrackingActivity.class;
final Activity rootActivity = launchActivityAndWait(rootActivityClass);
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 4e2aa67..acc36e5 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
@@ -118,6 +118,7 @@
import android.app.ActivityTaskManager;
import android.app.Instrumentation;
import android.app.KeyguardManager;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -156,6 +157,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
import com.android.compatibility.common.util.AppOpsUtils;
import com.android.compatibility.common.util.SystemUtil;
@@ -917,7 +919,7 @@
protected void dismissSplitScreen(boolean primaryOnTop) {
if (mTaskOrganizer != null) {
- mTaskOrganizer.dismissedSplitScreen(primaryOnTop);
+ mTaskOrganizer.dismissSplitScreen(primaryOnTop);
}
}
@@ -1243,9 +1245,9 @@
}
/** @see ObjectTracker#manage(AutoCloseable) */
- protected SupportsNonResizableMultiWindowSession
- createManagedSupportsNonResizableMultiWindowSession() {
- return mObjectTracker.manage(new SupportsNonResizableMultiWindowSession());
+ protected DevEnableNonResizableMultiWindowSession
+ createManagedDevEnableNonResizableMultiWindowSession() {
+ return mObjectTracker.manage(new DevEnableNonResizableMultiWindowSession());
}
/** @see ObjectTracker#manage(AutoCloseable) */
@@ -1550,8 +1552,8 @@
}
}
- protected class SupportsNonResizableMultiWindowSession extends SettingsSession<Integer> {
- SupportsNonResizableMultiWindowSession() {
+ protected class DevEnableNonResizableMultiWindowSession extends SettingsSession<Integer> {
+ DevEnableNonResizableMultiWindowSession() {
super(Settings.Global.getUriFor(
Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW),
(cr, name) -> Settings.Global.getInt(cr, name, 0 /* def */),
@@ -2166,6 +2168,13 @@
return new LaunchActivityBuilder(mWmState);
}
+ public static <T extends Activity>
+ ActivityScenarioRule<T> createFullscreenActivityScenarioRule(Class<T> clazz) {
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+ return new ActivityScenarioRule<>(clazz, options.toBundle());
+ }
+
protected static class LaunchActivityBuilder implements LaunchProxy {
private final WindowManagerStateHelper mAmWmState;
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/TestTaskOrganizer.java b/tests/framework/base/windowmanager/util/src/android/server/wm/TestTaskOrganizer.java
index 6105008..3292480 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/TestTaskOrganizer.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/TestTaskOrganizer.java
@@ -169,7 +169,7 @@
mRegistered = false;
NestedShellPermission.run(() -> {
- dismissedSplitScreen();
+ dismissSplitScreen();
deleteRootTask(mRootPrimary.getToken());
mRootPrimary = null;
@@ -240,18 +240,18 @@
});
}
- void dismissedSplitScreen() {
- dismissedSplitScreen(false /* primaryOnTop */);
+ void dismissSplitScreen() {
+ dismissSplitScreen(false /* primaryOnTop */);
}
- void dismissedSplitScreen(boolean primaryOnTop) {
+ void dismissSplitScreen(boolean primaryOnTop) {
+ dismissSplitScreen(new WindowContainerTransaction(), primaryOnTop);
+ }
+
+ void dismissSplitScreen(WindowContainerTransaction t, boolean primaryOnTop) {
synchronized (this) {
NestedShellPermission.run(() -> {
- final WindowContainerTransaction t = new WindowContainerTransaction()
- .setLaunchRoot(
- mRootPrimary.getToken(),
- null,
- null)
+ t.setLaunchRoot(mRootPrimary.getToken(), null, null)
.setLaunchRoot(
mRootSecondary.getToken(),
null,
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index eca4e75..e8a4280 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
@@ -27,6 +27,7 @@
import static android.server.wm.ProtoExtractors.extract;
import static android.server.wm.StateLogger.log;
import static android.server.wm.StateLogger.logE;
+import static android.server.wm.TestTaskOrganizer.INVALID_TASK_ID;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -246,7 +247,7 @@
return TYPE_NAVIGATION_BAR == navState.getType();
}
-/**
+ /**
* For a given WindowContainer, traverse down the hierarchy and add all children of type
* {@code T} to {@code outChildren}.
*/
@@ -824,29 +825,30 @@
}
public ActivityTask getTaskByActivity(ComponentName activityName) {
- return getTaskByActivity(activityName, WINDOWING_MODE_UNDEFINED, -1);
+ return getTaskByActivity(
+ activityName, WINDOWING_MODE_UNDEFINED, new int[]{ INVALID_TASK_ID });
}
- public ActivityTask getTaskByActivity(ComponentName activityName, int excludeTaskId) {
- return getTaskByActivity(activityName, WINDOWING_MODE_UNDEFINED, excludeTaskId);
+ public ActivityTask getTaskByActivity(ComponentName activityName, int[] excludeTaskIds) {
+ return getTaskByActivity(activityName, WINDOWING_MODE_UNDEFINED, excludeTaskIds);
}
private ActivityTask getTaskByActivity(ComponentName activityName, int windowingMode,
- int excludeTaskId) {
- Activity activity = getActivity(activityName, windowingMode, excludeTaskId);
+ int[] excludeTaskIds) {
+ Activity activity = getActivity(activityName, windowingMode, excludeTaskIds);
return activity == null ? null : activity.task;
}
public Activity getActivity(ComponentName activityName) {
- return getActivity(activityName, WINDOWING_MODE_UNDEFINED, -1);
+ return getActivity(activityName, WINDOWING_MODE_UNDEFINED, new int[]{ INVALID_TASK_ID });
}
private Activity getActivity(ComponentName activityName, int windowingMode,
- int excludeTaskId) {
+ int[] excludeTaskIds) {
for (ActivityTask stack : mRootTasks) {
if (windowingMode == WINDOWING_MODE_UNDEFINED
|| windowingMode == stack.getWindowingMode()) {
- Activity activity = stack.getActivity(activityName, excludeTaskId);
+ Activity activity = stack.getActivity(activityName, excludeTaskIds);
if (activity != null) return activity;
}
}
@@ -1091,7 +1093,7 @@
return mFocusedDisplayId;
}
- public static class DisplayContent extends ActivityContainer {
+ public static class DisplayContent extends DisplayArea {
public int mId;
ArrayList<ActivityTask> mRootTasks = new ArrayList<>();
int mFocusedRootTaskId;
@@ -1116,7 +1118,7 @@
private int mLastOrientation;
DisplayContent(DisplayContentProto proto) {
- super(proto.rootDisplayArea.windowContainer);
+ super(proto.rootDisplayArea);
mId = proto.id;
mFocusedRootTaskId = proto.focusedRootTaskId;
mSingleTaskInstance = proto.singleTaskInstance;
@@ -1202,11 +1204,16 @@
return false;
}
- @Nullable
- DisplayArea getTaskDisplayArea(ComponentName activityName) {
- List<DisplayArea> taskDisplayAreas = new ArrayList<>();
+ List<DisplayArea> getAllTaskDisplayAreas() {
+ final List<DisplayArea> taskDisplayAreas = new ArrayList<>();
collectDescendantsOfTypeIf(DisplayArea.class, DisplayArea::isTaskDisplayArea, this,
taskDisplayAreas);
+ return taskDisplayAreas;
+ }
+
+ @Nullable
+ DisplayArea getTaskDisplayArea(ComponentName activityName) {
+ final List<DisplayArea> taskDisplayAreas = getAllTaskDisplayAreas();
List<DisplayArea> result = taskDisplayAreas.stream().filter(
tda -> tda.containsActivity(activityName))
.collect(Collectors.toList());
@@ -1217,6 +1224,12 @@
return result.stream().findFirst().orElse(null);
}
+ List<DisplayArea> getAllChildDisplayAreas() {
+ final List<DisplayArea> displayAreas = new ArrayList<>();
+ collectDescendantsOfType(DisplayArea.class,this, displayAreas);
+ return displayAreas;
+ }
+
@Nullable
DisplayArea getDisplayArea(String windowName) {
List<DisplayArea> displayAreas = new ArrayList<>();
@@ -1404,11 +1417,19 @@
return getActivity((activity) -> activity.name.equals(fullName));
}
- public Activity getActivity(ComponentName activityName, int excludeTaskId) {
+ public Activity getActivity(ComponentName activityName, int[] excludeTaskIds) {
final String fullName = getActivityName(activityName);
- return getActivity((activity) ->
- activity.task.mTaskId != excludeTaskId
- && activity.name.equals(fullName));
+ return getActivity((activity) -> {
+ if (!activity.name.equals(fullName)) {
+ return false;
+ }
+ for (int excludeTaskId : excludeTaskIds) {
+ if (activity.task.mTaskId == excludeTaskId) {
+ return false;
+ }
+ }
+ return true;
+ });
}
boolean containsActivity(ComponentName activityName) {
@@ -1554,12 +1575,18 @@
}
public static class DisplayArea extends WindowContainer {
private final boolean mIsTaskDisplayArea;
+ private final boolean mIsRootDisplayArea;
+ private final int mFeatureId;
+ private final boolean mIsOrganized;
private ArrayList<Activity> mActivities;
private final ArrayList<WindowState> mWindows = new ArrayList<>();
DisplayArea(DisplayAreaProto proto) {
super(proto.windowContainer);
mIsTaskDisplayArea = proto.isTaskDisplayArea;
+ mIsRootDisplayArea = proto.isRootDisplayArea;
+ mFeatureId = proto.featureId;
+ mIsOrganized = proto.isOrganized;
if (mIsTaskDisplayArea) {
mActivities = new ArrayList<>();
collectDescendantsOfType(Activity.class, this, mActivities);
@@ -1571,6 +1598,18 @@
return mIsTaskDisplayArea;
}
+ boolean isRootDisplayArea() {
+ return mIsRootDisplayArea;
+ }
+
+ int getFeatureId() {
+ return mFeatureId;
+ }
+
+ boolean isOrganized() {
+ return mIsOrganized;
+ }
+
boolean containsActivity(ComponentName activityName) {
if (!mIsTaskDisplayArea) {
return false;
@@ -1614,31 +1653,31 @@
static WindowContainer getWindowContainer(WindowContainerChildProto proto,
WindowContainer parent) {
if (proto.displayContent != null) {
- return new DisplayContent(proto.displayContent);
+ return new DisplayContent(proto.displayContent);
}
if (proto.displayArea != null) {
- return new DisplayArea(proto.displayArea);
+ return new DisplayArea(proto.displayArea);
}
if (proto.task != null) {
- return new ActivityTask(proto.task);
+ return new ActivityTask(proto.task);
}
if (proto.activity != null) {
- return new Activity(proto.activity, parent);
+ return new Activity(proto.activity, parent);
}
if (proto.windowToken != null) {
- return new WindowToken(proto.windowToken);
+ return new WindowToken(proto.windowToken);
}
if (proto.window != null) {
- return new WindowState(proto.window);
+ return new WindowState(proto.window);
}
if (proto.windowContainer != null) {
- return new GenericWindowContainer(proto.windowContainer);
+ return new GenericWindowContainer(proto.windowContainer);
}
return null;
}
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
index cd351ae..c89370b 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java
@@ -318,6 +318,12 @@
}, message);
}
+ public void waitForFocusedActivity(final String msg, final ComponentName activityName) {
+ final String activityComponentName = getActivityName(activityName);
+ waitFor(msg, wmState -> Objects.equals(activityComponentName, wmState.getFocusedActivity())
+ && Objects.equals(activityComponentName, wmState.getFocusedApp()));
+ }
+
/** A variant of waitFor with different parameter order for better Kotlin interop. */
public boolean waitFor(String message, Predicate<WindowManagerState> waitCondition) {
return waitFor(waitCondition, message);
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index 2754de4..c2c0ad6 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -855,17 +855,8 @@
stylesBuilder.addStyle(InlineSuggestionUi.newStyleBuilder().build());
Bundle styles = stylesBuilder.build();
- final boolean supportedClientInlineSuggestions;
- final boolean supportedServiceInlineSuggestions;
if (mInlineSuggestionsExtras != null) {
styles.putAll(mInlineSuggestionsExtras);
- supportedClientInlineSuggestions =
- mInlineSuggestionsExtras.getBoolean("ClientSuggestions", true);
- supportedServiceInlineSuggestions =
- mInlineSuggestionsExtras.getBoolean("ServiceSuggestions", true);
- } else {
- supportedClientInlineSuggestions = true;
- supportedServiceInlineSuggestions = true;
}
return getTracer().onCreateInlineSuggestionsRequest(() -> {
@@ -881,8 +872,6 @@
final InlineSuggestionsRequest.Builder builder =
new InlineSuggestionsRequest.Builder(presentationSpecs)
.setInlineTooltipPresentationSpec(tooltipSpec)
- .setClientSupported(supportedClientInlineSuggestions)
- .setServiceSupported(supportedServiceInlineSuggestions)
.setMaxSuggestionCount(6);
if (mInlineSuggestionsExtras != null) {
builder.setExtras(mInlineSuggestionsExtras.deepCopy());
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
index 116e8a9..36516a2 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
@@ -659,6 +659,54 @@
}
}
+ @Test
+ public void testRequestFocusOnWindowFocusChanged() throws Exception {
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ try (MockImeSession imeSession = MockImeSession.create(
+ instrumentation.getContext(),
+ instrumentation.getUiAutomation(),
+ new ImeSettings.Builder())) {
+ final ImeEventStream stream = imeSession.openEventStream();
+ final String marker = getTestMarker();
+ final AtomicReference<EditText> editTextRef = new AtomicReference<>();
+
+ // Launch test activity
+ TestActivity.startSync(activity -> {
+ final LinearLayout layout = new LinearLayout(activity);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+ final EditText editText = new EditText(activity);
+ editText.setPrivateImeOptions(marker);
+ editText.setHint("editText");
+
+ // Request focus when onWindowFocusChanged
+ final ViewTreeObserver observer = editText.getViewTreeObserver();
+ observer.addOnWindowFocusChangeListener(
+ new ViewTreeObserver.OnWindowFocusChangeListener() {
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ editText.requestFocus();
+ }
+ });
+ editTextRef.set(editText);
+ layout.addView(editText);
+ return layout;
+ });
+
+ // Emulate tap event
+ final EditText editText = editTextRef.get();
+ CtsTouchUtils.emulateTapOnViewCenter(instrumentation, null, editText);
+
+ // "onStartInput" and "showSoftInput" gets called for the EditText.
+ expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+ expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
+
+ // No "hideSoftInput" happened
+ notExpectEvent(stream, event -> "hideSoftInput".equals(event.getEventName()),
+ NOT_EXPECT_TIMEOUT);
+ }
+ }
+
private static class ServiceSession implements ServiceConnection, AutoCloseable {
private final Context mContext;
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
index 49983b8..8f5ff6f 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -16,6 +16,8 @@
package android.view.inputmethod.cts;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -40,6 +42,7 @@
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
+import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -58,7 +61,12 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class InputMethodInfoTest {
+ private static final String MOCK_IME_ID = "com.android.cts.mockime/.MockIme";
+ private static final String HIDDEN_FROM_PICKER_IME_ID =
+ "com.android.cts.hiddenfrompickerime/.HiddenFromPickerIme";
+
private Context mContext;
+ private InputMethodManager mImManager;
private InputMethodInfo mInputMethodInfo;
private String mPackageName;
@@ -81,6 +89,7 @@
@Before
public void setup() {
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mImManager = mContext.getSystemService(InputMethodManager.class);
mPackageName = mContext.getPackageName();
mClassName = InputMethodSettingsActivityStub.class.getName();
mLabel = "test";
@@ -236,8 +245,7 @@
return;
}
- final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
- final List<InputMethodInfo> imis = imm.getInputMethodList();
+ final List<InputMethodInfo> imis = mImManager.getInputMethodList();
boolean hasEncryptionAwareInputMethod = false;
for (final InputMethodInfo imi : imis) {
final ServiceInfo serviceInfo = imi.getServiceInfo();
@@ -272,4 +280,27 @@
return "";
}
}
+
+ @Test
+ public void testShowInInputMethodPicker() {
+ final List<InputMethodInfo> imis = mImManager.getInputMethodList();
+
+ final InputMethodInfo shown = getImi(imis, MOCK_IME_ID);
+ assertThat(shown).isNotNull();
+ assertThat(shown.shouldShowInInputMethodPicker()).isTrue();
+
+ final InputMethodInfo hidden = getImi(imis, HIDDEN_FROM_PICKER_IME_ID);
+ assertThat(hidden).isNotNull();
+ assertThat(hidden.shouldShowInInputMethodPicker()).isFalse();
+ }
+
+ @Nullable
+ private static InputMethodInfo getImi(List<InputMethodInfo> imis, String id) {
+ for (final InputMethodInfo imi : imis) {
+ if (id.equals(imi.getId())) {
+ return imi;
+ }
+ }
+ return null;
+ }
}
diff --git a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
index 776aff1..ee25c6a 100644
--- a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
+++ b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
@@ -985,93 +985,111 @@
SoftAssert softAssert, long timeInNs) {
SatellitePvt satellitePvt = measurement.getSatellitePvt();
assertNotNull("SatellitePvt cannot be null when HAS_SATELLITE_PVT is true.", satellitePvt);
- softAssert.assertTrue("x_meters : "
- + "Satellite position X in WGS84 ECEF (meters)",
- timeInNs,
- "-43000000 <= X <= 43000000",
- String.valueOf(satellitePvt.getPositionEcef().getXMeters()),
- satellitePvt.getPositionEcef().getXMeters() >= -43000000 &&
- satellitePvt.getPositionEcef().getXMeters() <= 43000000);
- softAssert.assertTrue("y_meters : "
- + "Satellite position Y in WGS84 ECEF (meters)",
- timeInNs,
- "-43000000 <= X <= 43000000",
- String.valueOf(satellitePvt.getPositionEcef().getYMeters()),
- satellitePvt.getPositionEcef().getYMeters() >= -43000000 &&
- satellitePvt.getPositionEcef().getYMeters() <= 43000000);
- softAssert.assertTrue("z_meters : "
- + "Satellite position Z in WGS84 ECEF (meters)",
- timeInNs,
- "-43000000 <= X <= 43000000",
- String.valueOf(satellitePvt.getPositionEcef().getZMeters()),
- satellitePvt.getPositionEcef().getZMeters() >= -43000000 &&
- satellitePvt.getPositionEcef().getZMeters() <= 43000000);
- softAssert.assertTrue("ure_meters : "
- + "The Signal in Space User Range Error (URE) (meters)",
- timeInNs,
- "X > 0",
- String.valueOf(satellitePvt.getPositionEcef().getUreMeters()),
- satellitePvt.getPositionEcef().getUreMeters() > 0);
- softAssert.assertTrue("x_mps : "
- + "Satellite velocity X in WGS84 ECEF (meters per second)",
- timeInNs,
- "-4000 <= X <= 4000",
- String.valueOf(satellitePvt.getVelocityEcef().getXMetersPerSecond()),
- satellitePvt.getVelocityEcef().getXMetersPerSecond() >= -4000 &&
- satellitePvt.getVelocityEcef().getXMetersPerSecond() <= 4000);
- softAssert.assertTrue("y_mps : "
- + "Satellite velocity Y in WGS84 ECEF (meters per second)",
- timeInNs,
- "-4000 <= X <= 4000",
- String.valueOf(satellitePvt.getVelocityEcef().getYMetersPerSecond()),
- satellitePvt.getVelocityEcef().getYMetersPerSecond() >= -4000 &&
- satellitePvt.getVelocityEcef().getYMetersPerSecond() <= 4000);
- softAssert.assertTrue("z_mps : "
- + "Satellite velocity Z in WGS84 ECEF (meters per second)",
- timeInNs,
- "-4000 <= X <= 4000",
- String.valueOf(satellitePvt.getVelocityEcef().getZMetersPerSecond()),
- satellitePvt.getVelocityEcef().getZMetersPerSecond() >= -4000 &&
- satellitePvt.getVelocityEcef().getZMetersPerSecond() <= 4000);
- softAssert.assertTrue("ure_rate_mps : "
- + "The Signal in Space User Range Error Rate (URE Rate) (meters per second)",
- timeInNs,
- "X > 0",
- String.valueOf(satellitePvt.getVelocityEcef().getUreRateMetersPerSecond()),
- satellitePvt.getVelocityEcef().getUreRateMetersPerSecond() > 0);
- softAssert.assertTrue("hardware_code_bias_meters : "
- + "The satellite hardware code bias of the reported code type "
- + "w.r.t ionosphere-free measurement in meters.",
- timeInNs,
- "-17.869 < X < 17.729",
- String.valueOf(satellitePvt.getClockInfo().getHardwareCodeBiasMeters()),
- satellitePvt.getClockInfo().getHardwareCodeBiasMeters() > -17.869 &&
- satellitePvt.getClockInfo().getHardwareCodeBiasMeters() < 17.729);
- softAssert.assertTrue("time_correction_meters : "
- + "The satellite time correction for ionospheric-free signal measurement (meters)",
- timeInNs,
- "-3e6 < X < 3e6",
- String.valueOf(satellitePvt.getClockInfo().getTimeCorrectionMeters()),
- satellitePvt.getClockInfo().getTimeCorrectionMeters() > -3e6 &&
- satellitePvt.getClockInfo().getTimeCorrectionMeters() < 3e6);
- softAssert.assertTrue("clock_drift_mps : "
- + "The satellite clock drift (meters per second)",
- timeInNs,
- "-1.117 < X < 1.117",
- String.valueOf(satellitePvt.getClockInfo().getClockDriftMetersPerSecond()),
- satellitePvt.getClockInfo().getClockDriftMetersPerSecond() > -1.117 &&
- satellitePvt.getClockInfo().getClockDriftMetersPerSecond() < 1.117);
- softAssert.assertTrue("iono_delay_meters : "
- + "The ionospheric delay in meters",
- timeInNs,
- "0 < X < 100",
- String.valueOf(satellitePvt.getIonoDelayMeters()),
- satellitePvt.getIonoDelayMeters() > 0 && satellitePvt.getIonoDelayMeters() < 100);
- softAssert.assertTrue("tropo_delay_meters : "
- + "The tropospheric delay in meters",
- timeInNs,
- "0 < X < 100",
- String.valueOf(satellitePvt.getTropoDelayMeters()),
- satellitePvt.getTropoDelayMeters() > 0 && satellitePvt.getTropoDelayMeters() < 100);
+
+ if (satellitePvt.hasPositionVelocityClockInfo()){
+ assertNotNull("PositionEcef cannot be null when "
+ + "HAS_POSITION_VELOCITY_CLOCK_INFO is true.", satellitePvt.getPositionEcef());
+ assertNotNull("VelocityEcef cannot be null when "
+ + "HAS_POSITION_VELOCITY_CLOCK_INFO is true.", satellitePvt.getVelocityEcef());
+ assertNotNull("ClockInfo cannot be null when "
+ + "HAS_POSITION_VELOCITY_CLOCK_INFO is true.", satellitePvt.getClockInfo());
+ softAssert.assertTrue("x_meters : "
+ + "Satellite position X in WGS84 ECEF (meters)",
+ timeInNs,
+ "-43000000 <= X <= 43000000",
+ String.valueOf(satellitePvt.getPositionEcef().getXMeters()),
+ satellitePvt.getPositionEcef().getXMeters() >= -43000000 &&
+ satellitePvt.getPositionEcef().getXMeters() <= 43000000);
+ softAssert.assertTrue("y_meters : "
+ + "Satellite position Y in WGS84 ECEF (meters)",
+ timeInNs,
+ "-43000000 <= X <= 43000000",
+ String.valueOf(satellitePvt.getPositionEcef().getYMeters()),
+ satellitePvt.getPositionEcef().getYMeters() >= -43000000 &&
+ satellitePvt.getPositionEcef().getYMeters() <= 43000000);
+ softAssert.assertTrue("z_meters : "
+ + "Satellite position Z in WGS84 ECEF (meters)",
+ timeInNs,
+ "-43000000 <= X <= 43000000",
+ String.valueOf(satellitePvt.getPositionEcef().getZMeters()),
+ satellitePvt.getPositionEcef().getZMeters() >= -43000000 &&
+ satellitePvt.getPositionEcef().getZMeters() <= 43000000);
+ softAssert.assertTrue("ure_meters : "
+ + "The Signal in Space User Range Error (URE) (meters)",
+ timeInNs,
+ "X > 0",
+ String.valueOf(satellitePvt.getPositionEcef().getUreMeters()),
+ satellitePvt.getPositionEcef().getUreMeters() > 0);
+ softAssert.assertTrue("x_mps : "
+ + "Satellite velocity X in WGS84 ECEF (meters per second)",
+ timeInNs,
+ "-4000 <= X <= 4000",
+ String.valueOf(satellitePvt.getVelocityEcef().getXMetersPerSecond()),
+ satellitePvt.getVelocityEcef().getXMetersPerSecond() >= -4000 &&
+ satellitePvt.getVelocityEcef().getXMetersPerSecond() <= 4000);
+ softAssert.assertTrue("y_mps : "
+ + "Satellite velocity Y in WGS84 ECEF (meters per second)",
+ timeInNs,
+ "-4000 <= X <= 4000",
+ String.valueOf(satellitePvt.getVelocityEcef().getYMetersPerSecond()),
+ satellitePvt.getVelocityEcef().getYMetersPerSecond() >= -4000 &&
+ satellitePvt.getVelocityEcef().getYMetersPerSecond() <= 4000);
+ softAssert.assertTrue("z_mps : "
+ + "Satellite velocity Z in WGS84 ECEF (meters per second)",
+ timeInNs,
+ "-4000 <= X <= 4000",
+ String.valueOf(satellitePvt.getVelocityEcef().getZMetersPerSecond()),
+ satellitePvt.getVelocityEcef().getZMetersPerSecond() >= -4000 &&
+ satellitePvt.getVelocityEcef().getZMetersPerSecond() <= 4000);
+ softAssert.assertTrue("ure_rate_mps : "
+ + "The Signal in Space User Range Error Rate (URE Rate) (meters per second)",
+ timeInNs,
+ "X > 0",
+ String.valueOf(satellitePvt.getVelocityEcef().getUreRateMetersPerSecond()),
+ satellitePvt.getVelocityEcef().getUreRateMetersPerSecond() > 0);
+ softAssert.assertTrue("hardware_code_bias_meters : "
+ + "The satellite hardware code bias of the reported code type "
+ + "w.r.t ionosphere-free measurement in meters.",
+ timeInNs,
+ "-17.869 < X < 17.729",
+ String.valueOf(satellitePvt.getClockInfo().getHardwareCodeBiasMeters()),
+ satellitePvt.getClockInfo().getHardwareCodeBiasMeters() > -17.869 &&
+ satellitePvt.getClockInfo().getHardwareCodeBiasMeters() < 17.729);
+ softAssert.assertTrue("time_correction_meters : "
+ + "The satellite time correction for ionospheric-free signal measurement "
+ + "(meters)",
+ timeInNs,
+ "-3e6 < X < 3e6",
+ String.valueOf(satellitePvt.getClockInfo().getTimeCorrectionMeters()),
+ satellitePvt.getClockInfo().getTimeCorrectionMeters() > -3e6 &&
+ satellitePvt.getClockInfo().getTimeCorrectionMeters() < 3e6);
+ softAssert.assertTrue("clock_drift_mps : "
+ + "The satellite clock drift (meters per second)",
+ timeInNs,
+ "-1.117 < X < 1.117",
+ String.valueOf(satellitePvt.getClockInfo().getClockDriftMetersPerSecond()),
+ satellitePvt.getClockInfo().getClockDriftMetersPerSecond() > -1.117 &&
+ satellitePvt.getClockInfo().getClockDriftMetersPerSecond() < 1.117);
+ }
+
+ if (satellitePvt.hasIono()){
+ softAssert.assertTrue("iono_delay_meters : "
+ + "The ionospheric delay in meters",
+ timeInNs,
+ "0 < X < 100",
+ String.valueOf(satellitePvt.getIonoDelayMeters()),
+ satellitePvt.getIonoDelayMeters() > 0 &&
+ satellitePvt.getIonoDelayMeters() < 100);
+ }
+
+ if (satellitePvt.hasTropo()){
+ softAssert.assertTrue("tropo_delay_meters : "
+ + "The tropospheric delay in meters",
+ timeInNs,
+ "0 < X < 100",
+ String.valueOf(satellitePvt.getTropoDelayMeters()),
+ satellitePvt.getTropoDelayMeters() > 0 &&
+ satellitePvt.getTropoDelayMeters() < 100);
+ }
}
}
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/SatellitePvtTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/SatellitePvtTest.java
index 7495d5b..2831db6 100644
--- a/tests/location/location_privileged/src/android/location/cts/privileged/SatellitePvtTest.java
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/SatellitePvtTest.java
@@ -45,9 +45,9 @@
*/
@Test
public void testWriteToParcel() {
- SatellitePvt satellitePvt1 = createTestSatellitePvt(POSITION_ECEF_M, VELOCITY_ECEF_MPS,
- CLOCK_INFO, /*ionoDelayMeters=*/ 12.0,
- /*tropoDelayMeters=*/ 13.0);
+ SatellitePvt satellitePvt1 = createTestSatellitePvt(/*flags=*/ 7, POSITION_ECEF_M,
+ VELOCITY_ECEF_MPS, CLOCK_INFO,
+ /*ionoDelayMeters=*/ 12.0, /*tropoDelayMeters=*/ 13.0);
Parcel parcel = Parcel.obtain();
satellitePvt1.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
@@ -57,6 +57,7 @@
}
private static void verifyTestValues(SatellitePvt satPvt1, SatellitePvt satPvt2) {
+ assertEquals(satPvt1.getFlags(), satPvt2.getFlags(), DELTA);
assertEquals(satPvt1.getPositionEcef().getXMeters(),
satPvt2.getPositionEcef().getXMeters(), DELTA);
assertEquals(satPvt1.getPositionEcef().getYMeters(),
@@ -84,9 +85,10 @@
}
private static SatellitePvt createTestSatellitePvt (
- PositionEcef positionEcef, VelocityEcef velocityEcef, ClockInfo clockInfo,
+ int flags, PositionEcef positionEcef, VelocityEcef velocityEcef, ClockInfo clockInfo,
double ionoDelayMeters, double tropoDelayMeters) {
return new SatellitePvt.Builder()
+ .setFlags(flags)
.setPositionEcef(positionEcef)
.setVelocityEcef(velocityEcef)
.setClockInfo(clockInfo)
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index f12f204..dfd79297 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -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/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/quickaccesswallet/AndroidManifest.xml b/tests/quickaccesswallet/AndroidManifest.xml
index e7a1df5..d8e38d1 100755
--- a/tests/quickaccesswallet/AndroidManifest.xml
+++ b/tests/quickaccesswallet/AndroidManifest.xml
@@ -25,7 +25,7 @@
<!-- Required to test QuickAccessWalletClient feature availability -->
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
- <application>
+ <application android:testOnly="true">
<uses-library android:name="android.test.runner"/>
<activity android:name="android.quickaccesswallet.QuickAccessWalletActivity"
android:exported="true">
diff --git a/tests/quickaccesswallet/AndroidTest.xml b/tests/quickaccesswallet/AndroidTest.xml
index c605ec0..3f7a774 100644
--- a/tests/quickaccesswallet/AndroidTest.xml
+++ b/tests/quickaccesswallet/AndroidTest.xml
@@ -20,6 +20,7 @@
<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="install-arg" value="-t" />
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsQuickAccessWalletTestCases.apk" />
</target_preparer>
diff --git a/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java b/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java
index c523e1f..bbadb72 100755
--- a/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java
+++ b/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java
@@ -65,6 +65,8 @@
@RunWith(AndroidJUnit4.class)
public class QuickAccessWalletClientTest {
+ private static final String SETTING_KEY = "lockscreen_show_wallet";
+
private static final GetWalletCardsRequest GET_WALLET_CARDS_REQUEST =
new GetWalletCardsRequest(700, 440, 64, 5);
@@ -116,17 +118,14 @@
Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT);
try {
- SettingsUtils.syncSet(mContext, Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT,
- SETTING_ENABLED);
+ SettingsUtils.syncSet(mContext, SETTING_KEY, SETTING_ENABLED);
assertThat(client.isWalletFeatureAvailableWhenDeviceLocked()).isTrue();
- SettingsUtils.syncSet(mContext, Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT,
- SETTING_DISABLED);
+ SettingsUtils.syncSet(mContext, SETTING_KEY, SETTING_DISABLED);
assertThat(client.isWalletFeatureAvailableWhenDeviceLocked()).isFalse();
} finally {
// return setting to original value
- SettingsUtils.syncSet(mContext, Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT,
- showCardsAndPasses);
+ SettingsUtils.syncSet(mContext, SETTING_KEY, showCardsAndPasses);
}
}
diff --git a/tests/rotationresolverservice/src/android/rotationresolverservice/cts/CtsRotationResolverServiceDeviceTest.java b/tests/rotationresolverservice/src/android/rotationresolverservice/cts/CtsRotationResolverServiceDeviceTest.java
index 20f778e..7576ae8 100644
--- a/tests/rotationresolverservice/src/android/rotationresolverservice/cts/CtsRotationResolverServiceDeviceTest.java
+++ b/tests/rotationresolverservice/src/android/rotationresolverservice/cts/CtsRotationResolverServiceDeviceTest.java
@@ -61,6 +61,7 @@
private static final String FAKE_PACKAGE_NAME = "package_name";
private static final boolean FAKE_SHOULD_USE_CAMERA = true;
private static final long FAKE_TIME_OUT = 1000L;
+ private static final long TEMPORARY_SERVICE_DURATION = 5000L;
private final boolean isTestable =
!TextUtils.isEmpty(getRotationResolverServiceComponent());
@@ -168,11 +169,11 @@
}
private void setTestableRotationResolverService(String service) {
- runShellCommand("cmd resolver set-testing-package " + service);
+ runShellCommand("cmd resolver set-temporary-service %s %s", service, TEMPORARY_SERVICE_DURATION);
}
private void clearTestableRotationResolverService() {
- runShellCommand("cmd resolver clear-testing-package");
+ runShellCommand("cmd resolver set-temporary-service");
}
}
diff --git a/tests/searchui/src/android/searchuiservice/cts/SearchTargetEventTest.java b/tests/searchui/src/android/searchuiservice/cts/SearchTargetEventTest.java
index b04db17..dbb5ef5 100644
--- a/tests/searchui/src/android/searchuiservice/cts/SearchTargetEventTest.java
+++ b/tests/searchui/src/android/searchuiservice/cts/SearchTargetEventTest.java
@@ -32,17 +32,21 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
+
@RunWith(JUnit4.class)
public class SearchTargetEventTest {
private final String ID = "id";
private final String LOCATION = "location";
private final Builder mBuilder = new Builder(ID, ACTION_TAP).setLaunchLocation(LOCATION);
+ private final Builder mBuilderList = new Builder(new ArrayList(), ACTION_TAP);
@Test
public void testBuilder() {
SearchTargetEvent event = mBuilder
.setLaunchLocation(LOCATION).setFlags(FLAG_IME_SHOWN).build();
assertEverything(event);
+ mBuilderList.build();
}
@Test
diff --git a/tests/searchui/src/android/searchuiservice/cts/SearchTargetTest.java b/tests/searchui/src/android/searchuiservice/cts/SearchTargetTest.java
index ea9a293..f4b66e9 100644
--- a/tests/searchui/src/android/searchuiservice/cts/SearchTargetTest.java
+++ b/tests/searchui/src/android/searchuiservice/cts/SearchTargetTest.java
@@ -87,6 +87,8 @@
assertThat(target.getScore()).isEqualTo(SCORE);
assertThat(target.getParentId()).isEqualTo(PARENTID);
assertThat(target.getSearchAction()).isEqualTo(SEARCH_ACTION);
+ assertThat(target.getPackageName()).isEqualTo(PACKAGE_NAME);
+ assertThat(target.getExtras().size()).isEqualTo(EXTRAS.size());
}
@Test
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 012a3a6..5f12ea7 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
@@ -51,6 +51,8 @@
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.AppModeInstant;
import android.provider.Settings;
+import android.server.wm.WindowManagerState;
+import android.server.wm.WindowManagerStateHelper;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.Until;
@@ -124,6 +126,8 @@
"android.app.usage.cts.test2.FinishingTaskRootActivity";
private static final String TEST_APP2_CLASS_PIP =
"android.app.usage.cts.test2.PipActivity";
+ private static final ComponentName TEST_APP2_PIP_COMPONENT = new ComponentName(TEST_APP2_PKG,
+ TEST_APP2_CLASS_PIP);
private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
private static final long MINUTE = TimeUnit.MINUTES.toMillis(1);
@@ -147,6 +151,7 @@
private int mOtherUser;
private Context mOtherUserContext;
private UsageStatsManager mOtherUsageStats;
+ private WindowManagerStateHelper mWMStateHelper;
@Before
public void setUp() throws Exception {
@@ -158,6 +163,8 @@
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
mTargetPackage = mContext.getPackageName();
+ mWMStateHelper = new WindowManagerStateHelper();
+
assumeTrue("App Standby not enabled on device", AppStandbyUtils.isAppStandbyEnabled());
setAppOpsMode("allow");
mCachedUsageSourceSetting = getSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE);
@@ -174,6 +181,7 @@
setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, mCachedEnableRestrictedBucketSetting);
// Force stop test package to avoid any running test code from carrying over to the next run
SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG));
+ SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP2_PKG));
mUiDevice.pressHome();
// Destroy the other user if created
if (mOtherUser != 0) {
@@ -986,7 +994,7 @@
private ArrayList<Event> waitForEventCount(int[] whichEvents, long startTime, int count,
String packageName) {
final ArrayList<Event> events = new ArrayList<>();
- final long endTime = SystemClock.uptimeMillis() + 2000;
+ final long endTime = SystemClock.uptimeMillis() + TIMEOUT;
do {
events.clear();
getEvents(whichEvents, startTime, events, packageName);
@@ -1530,6 +1538,9 @@
@AppModeFull(reason = "No usage events access in instant apps")
@Test
public void testPipActivity() throws Exception {
+ assumeTrue("Test cannot run without Picture in Picture support",
+ mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_PICTURE_IN_PICTURE));
mUiDevice.wakeUp();
dismissKeyguard(); // also want to start out with the keyguard dismissed.
mUiDevice.pressHome();
@@ -1543,6 +1554,13 @@
launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS);
SystemClock.sleep(500);
+ mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT,
+ WindowManagerState.STATE_PAUSED);
+
+ mWMStateHelper.assertActivityDisplayed(TEST_APP2_PIP_COMPONENT);
+ mWMStateHelper.assertNotFocusedActivity("Pip activity should not be in focus",
+ TEST_APP2_PIP_COMPONENT);
+
final long endTime = System.currentTimeMillis();
final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
@@ -1582,12 +1600,13 @@
@AppModeFull(reason = "No usage events access in instant apps")
@Test
public void testPipActivity_StopToPause() throws Exception {
+ assumeTrue("Test cannot run without Picture in Picture support",
+ mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_PICTURE_IN_PICTURE));
mUiDevice.wakeUp();
dismissKeyguard(); // also want to start out with the keyguard dismissed.
mUiDevice.pressHome();
- final long startTime = System.currentTimeMillis();
-
launchTestActivity(TEST_APP2_PKG, TEST_APP2_CLASS_PIP);
SystemClock.sleep(500);
@@ -1595,9 +1614,17 @@
launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS);
SystemClock.sleep(500);
+ mWMStateHelper.assertActivityDisplayed(TEST_APP2_PIP_COMPONENT);
+ mWMStateHelper.assertNotFocusedActivity("Pip activity should not be in focus",
+ TEST_APP2_PIP_COMPONENT);
+
// Sleeping the device should cause the Pip activity to stop.
final long sleepTime = System.currentTimeMillis();
sleepDevice();
+ mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT,
+ WindowManagerState.STATE_STOPPED);
+
+ // Pip activity stop should show up in UsageStats.
final ArrayList<Event> stoppedEvent = waitForEventCount(STOPPED_EVENT, sleepTime, 1,
TEST_APP2_PKG);
assertEquals(Event.ACTIVITY_STOPPED, stoppedEvent.get(0).getEventType());
@@ -1606,52 +1633,24 @@
final long wakeTime = System.currentTimeMillis();
mUiDevice.wakeUp();
dismissKeyguard();
- final ArrayList<Event> pausedEvent = waitForEventCount(PAUSED_EVENT, wakeTime, 1,
- TEST_APP2_PKG);
- assertEquals(Event.ACTIVITY_PAUSED, pausedEvent.get(0).getEventType());
+ mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT,
+ WindowManagerState.STATE_PAUSED);
+
+ mWMStateHelper.assertActivityDisplayed(TEST_APP2_PIP_COMPONENT);
+ mWMStateHelper.assertNotFocusedActivity("Pip activity should not be in focus",
+ TEST_APP2_PIP_COMPONENT);
// Sleeping the device should cause the Pip activity to stop again.
final long secondSleepTime = System.currentTimeMillis();
sleepDevice();
+ mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT,
+ WindowManagerState.STATE_STOPPED);
+
+ // Pip activity stop should show up in UsageStats again.
final ArrayList<Event> secondStoppedEvent = waitForEventCount(STOPPED_EVENT,
secondSleepTime, 1,
TEST_APP2_PKG);
assertEquals(Event.ACTIVITY_STOPPED, secondStoppedEvent.get(0).getEventType());
-
- final long endTime = System.currentTimeMillis();
- final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
-
- int resumes = 0;
- int pauses = 0;
- int stops = 0;
-
- while (events.hasNextEvent()) {
- final UsageEvents.Event event = new UsageEvents.Event();
- assertTrue(events.getNextEvent(event));
-
- if(TEST_APP2_PKG.equals(event.getPackageName())) {
- switch (event.mEventType) {
- case Event.ACTIVITY_RESUMED:
- assertNotNull("ACTIVITY_RESUMED event Task Root should not be null",
- event.getTaskRootPackageName());
- resumes++;
- break;
- case Event.ACTIVITY_PAUSED:
- assertNotNull("ACTIVITY_PAUSED event Task Root should not be null",
- event.getTaskRootPackageName());
- pauses++;
- break;
- case Event.ACTIVITY_STOPPED:
- assertNotNull("ACTIVITY_STOPPED event Task Root should not be null",
- event.getTaskRootPackageName());
- stops++;
- break;
- }
- }
- }
- assertEquals("Unexpected number of activity resumes", 1, resumes);
- assertEquals("Unexpected number of activity pauses", 2, pauses);
- assertEquals("Unexpected number of activity stops", 2, stops);
}
@AppModeFull(reason = "No usage events access in instant apps")
diff --git a/tests/tests/appop/AndroidTest.xml b/tests/tests/appop/AndroidTest.xml
index dc40080..f61a15d 100644
--- a/tests/tests/appop/AndroidTest.xml
+++ b/tests/tests/appop/AndroidTest.xml
@@ -40,7 +40,6 @@
<option name="push-file" key="CtsAppToBlame1.apk" value="/data/local/tmp/cts/appops/CtsAppToBlame1.apk" />
<option name="push-file" key="CtsAppToBlame2.apk" value="/data/local/tmp/cts/appops/CtsAppToBlame2.apk" />
<option name="push-file" key="CtsAppToCollect.apk" value="/data/local/tmp/cts/appops/CtsAppToCollect.apk" />
- <option name="push-file" key="AppForDiscreteTest.apk" value="/data/local/tmp/cts/appops/AppForDiscreteTest.apk" />
<option name="push-file" key="AppWithDuplicateAttribution.apk" value="/data/local/tmp/cts/appops/AppWithDuplicateAttribution.apk" />
<option name="push-file" key="AppWithAttributionInheritingFromExisting.apk" value="/data/local/tmp/cts/appops/AppWithAttributionInheritingFromExisting.apk" />
<option name="push-file" key="AppWithAttributionInheritingFromSameAsOther.apk" value="/data/local/tmp/cts/appops/AppWithAttributionInheritingFromSameAsOther.apk" />
diff --git a/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml b/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml
index df614d8..056b1f3 100644
--- a/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml
+++ b/tests/tests/appop/AppThatUsesAppOps/AndroidManifest.xml
@@ -20,11 +20,12 @@
package="android.app.appops.cts.appthatusesappops">
<attribution android:tag="testAttribution" android:label="@string/dummyLabel" />
- <application>
- <service android:name=".AppOpsUserService" android:exported="true" />
- <activity android:name=".AutoClosingActivity"
- android:exported="true"
- android:permission="android.permission.ACCESS_FINE_LOCATION" />
+ <application
+ android:attributionsAreUserVisible="true">
+ <service android:name=".AppOpsUserService" android:exported="true" />
+ <activity android:name=".AutoClosingActivity"
+ android:exported="true"
+ android:permission="android.permission.ACCESS_FINE_LOCATION" />
</application>
</manifest>
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
index 22008c9..17a72a5 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
@@ -23,7 +23,6 @@
import android.app.AppOpsManager.MODE_ALLOWED
import android.app.AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY
import android.app.AppOpsManager.OPSTR_BLUETOOTH_SCAN
-import android.app.AppOpsManager.OPSTR_BLUETOOTH_CONNECT
import android.app.AppOpsManager.OPSTR_ACTIVITY_RECOGNITION
import android.app.AppOpsManager.OPSTR_CAMERA
import android.app.AppOpsManager.OPSTR_COARSE_LOCATION
@@ -56,6 +55,7 @@
import android.content.pm.PackageManager.FEATURE_BLUETOOTH
import android.content.pm.PackageManager.FEATURE_BLUETOOTH_LE
import android.content.pm.PackageManager.FEATURE_TELEPHONY
+import android.content.pm.PackageManager.GET_ATTRIBUTIONS
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
@@ -484,6 +484,9 @@
val wasEnabled = enableBTAdapter(btAdapter, testContext)
assumeTrue("Need to be able enable BT", wasEnabled)
+
+ clearCollectedNotedOps()
+
try {
btAdapter.startDiscovery()
try {
@@ -983,6 +986,13 @@
}
}
+ @Test
+ fun checkAttributionsAreUserVisible() {
+ val pi = context.packageManager.getPackageInfo(
+ TEST_SERVICE_PKG, GET_ATTRIBUTIONS)
+ assertThat(pi.applicationInfo.areAttributionsUserVisible())
+ }
+
@After
fun removeNotedAppOpsCollector() {
appOpsManager.setOnOpNotedCallback(null, null)
@@ -1033,12 +1043,12 @@
// Blame the caller
val callerSyncOp = SyncNotedAppOp(MODE_ALLOWED, strOpToOp(OPSTR_ACTIVITY_RECOGNITION),
null, TEST_SERVICE_PKG)
- AppOpsManager.collectNotedOpSync(callerSyncOp);
+ AppOpsManager.collectNotedOpSync(callerSyncOp)
// Blame ourselves
val selfSyncOp = SyncNotedAppOp(MODE_ALLOWED, strOpToOp(OPSTR_ACTIVITY_RECOGNITION),
null, context.packageName)
- AppOpsManager.collectNotedOpSync(selfSyncOp);
+ AppOpsManager.collectNotedOpSync(selfSyncOp)
// Accesses should be dispatched when the sync IPC returns.
assertThat(noted).isEmpty()
diff --git a/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt b/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt
index d8fc252..390b917 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AttributionTest.kt
@@ -20,7 +20,6 @@
import android.app.AppOpsManager.OPSTR_READ_CONTACTS
import android.app.AppOpsManager.OPSTR_WIFI_SCAN
import android.app.AppOpsManager.OP_FLAGS_ALL
-import android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE
import android.content.Intent
import android.content.ComponentName
import android.platform.test.annotations.AppModeFull
@@ -151,11 +150,11 @@
}
}
- @Test(expected = SecurityException::class)
- fun cannotUseUndeclaredAttributionTag() {
- withEnabledCompatChange(SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE, APP_PKG) {
- noteForAttribution("invalid attribution tag")
- }
+ @Test
+ fun canUseUndeclaredAttributionTagButTreatedAsNull() {
+ noteForAttribution("invalid attribution tag")
+ val opEntry = getOpEntry(appUid, APP_PKG, OPSTR_WIFI_SCAN)!!
+ assertThat(opEntry.attributedOpEntries["invalid attribution tag"]).isNull()
}
@Test
diff --git a/tests/tests/appop/src/android/app/appops/cts/DiscreteAppopsTest.kt b/tests/tests/appop/src/android/app/appops/cts/DiscreteAppopsTest.kt
index 67337d8..cb6baf0 100644
--- a/tests/tests/appop/src/android/app/appops/cts/DiscreteAppopsTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/DiscreteAppopsTest.kt
@@ -74,7 +74,8 @@
private val instrumentation get() = InstrumentationRegistry.getInstrumentation()
private val context get() = instrumentation.context
- private val uid = context.packageManager.getPackageUid(PACKAGE_NAME, 0)
+ private val uid by lazy { context.packageManager.getPackageUid(PACKAGE_NAME, 0) }
+
private val uiAutomation get() = instrumentation.uiAutomation
private lateinit var foregroundControlService: IAppOpsForegroundControlService
private lateinit var serviceConnection: ServiceConnection
@@ -118,6 +119,8 @@
"$KEY_BG_STATE_SETTLE_TIME=10")
}
+ enableDiscreteRegistryDebugMode()
+
val serviceIntent = Intent().setComponent(ComponentName(PACKAGE_NAME,
"$PACKAGE_NAME.AppOpsForegroundControlService"))
@@ -984,17 +987,22 @@
}
}
- private fun setQuantization(timeQuantMillis: Long) {
+ private fun enableDiscreteRegistryDebugMode() {
runWithShellPermissionIdentity {
appOpsManager.setHistoryParameters(HISTORICAL_MODE_ENABLED_PASSIVE,
SNAPSHOT_INTERVAL_MILLIS, INTERVAL_COMPRESSION_MULTIPLIER)
- DeviceConfig.setProperty(NAMESPACE_PRIVACY, PROPERTY_QUANTIZATION,
- timeQuantMillis.toString(), false)
appOpsManager.setHistoryParameters(HISTORICAL_MODE_ENABLED_ACTIVE,
SNAPSHOT_INTERVAL_MILLIS, INTERVAL_COMPRESSION_MULTIPLIER)
}
}
+ private fun setQuantization(timeQuantMillis: Long) {
+ runWithShellPermissionIdentity {
+ DeviceConfig.setProperty(NAMESPACE_PRIVACY, PROPERTY_QUANTIZATION,
+ timeQuantMillis.toString(), false)
+ }
+ }
+
private fun noteOp(
op: String,
uid: Int,
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java
index 46fa24c..94e17f3 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothDeviceTest.java
@@ -22,7 +22,7 @@
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
-import android.content.Context;
+import android.bluetooth.BluetoothStatusCodes;
import android.content.pm.PackageManager;
import android.test.AndroidTestCase;
@@ -72,7 +72,7 @@
// This should throw a SecurityException because there is no CDM association
try {
device.setAlias(testDeviceAlias);
- fail("BluetoothDevice alias was able to be set without a CDM association without having"
+ fail("BluetoothDevice alias was able to be set without a CDM association or "
+ "BLUETOOTH_PRIVILEGED permission");
} catch (SecurityException ex) {
assertNull(device.getAlias());
@@ -83,6 +83,8 @@
String output = runShellCommand("dumpsys companiondevice");
assertTrue("Package name missing from output", output.contains(packageName));
assertTrue("Device address missing from output", output.contains(deviceAddress));
+
+ // Takes time to update the CDM cache, so sleep to ensure the association is cached
try {
Thread.sleep(1000);
} catch (Exception e) {
@@ -93,7 +95,7 @@
* Device properties don't exist for non-existent BluetoothDevice, so calling setAlias with
* permissions should return false
*/
- assertFalse(device.setAlias(testDeviceAlias));
+ assertEquals(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED, device.setAlias(testDeviceAlias));
runShellCommand(String.format(
"cmd companiondevice disassociate %d %s %s", userId, packageName, deviceAddress));
}
diff --git a/tests/tests/car/src/android/car/cts/CarPackageManagerTest.java b/tests/tests/car/src/android/car/cts/CarPackageManagerTest.java
index 752d927..eab27ce 100644
--- a/tests/tests/car/src/android/car/cts/CarPackageManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarPackageManagerTest.java
@@ -16,8 +16,8 @@
package android.car.cts;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.fail;
import android.app.PendingIntent;
@@ -54,50 +54,65 @@
@Test
public void testActivityDistractionOptimized() throws Exception {
- assertFalse(mCarPm.isActivityDistractionOptimized("com.basic.package", "DummyActivity"));
- // Real system activity is not allowed as well.
- assertFalse(mCarPm.isActivityDistractionOptimized("com.android.phone", "CallActivity"));
+ assertThat(mCarPm.isActivityDistractionOptimized("com.basic.package", "DummyActivity"))
+ .isFalse();
- try {
- mCarPm.isActivityDistractionOptimized("com.android.settings", null);
- fail();
- } catch (IllegalArgumentException expected) {
- // Expected.
- }
- try {
- mCarPm.isActivityDistractionOptimized(null, "Any");
- fail();
- } catch (IllegalArgumentException expected) {
- // Expected.
- }
- try {
- mCarPm.isActivityDistractionOptimized(null, null);
- fail();
- } catch (IllegalArgumentException expected) {
- // Expected.
- }
+ try {
+ mCarPm.isActivityDistractionOptimized("com.android.settings", null);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ // Expected.
+ }
+ try {
+ mCarPm.isActivityDistractionOptimized(null, "Any");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ // Expected.
+ }
+ try {
+ mCarPm.isActivityDistractionOptimized(null, null);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ // Expected.
+ }
}
@Test
public void testDistractionOptimizedActivityIsAllowed() {
- // This test relies on test activity in installed apk, and AndroidManifest declaration.
+ assertThat(mCarPm.isActivityDistractionOptimized("com.android.car",
+ "com.android.car.DistractionOptimizedActivityForTesting")).isTrue();
if (Build.TYPE.equalsIgnoreCase("user")) {
- // Skip this test on user build, which checks the install source for DO activity list.
- return;
+ // Should be false as not from trusted source.
+ assertThat(mCarPm.isActivityDistractionOptimized("android.car.cts",
+ "android.car.cts.drivingstate.DistractionOptimizedActivity")).isFalse();
+ } else {
+ assertThat(mCarPm.isActivityDistractionOptimized("android.car.cts",
+ "android.car.cts.drivingstate.DistractionOptimizedActivity")).isTrue();
}
- assertTrue(mCarPm.isActivityDistractionOptimized("android.car.cts",
- "android.car.cts.drivingstate.DistractionOptimizedActivity"));
}
@Test
public void testNonDistractionOptimizedActivityNotAllowed() {
- // This test relies on test activity in installed apk, and AndroidManifest declaration.
- if (Build.TYPE.equalsIgnoreCase("user")) {
- // Skip this test on user build, which checks the install source for DO activity list.
- return;
- }
- assertFalse(mCarPm.isActivityDistractionOptimized("android.car.cts",
- "android.car.cts.drivingstate.NonDistractionOptimizedActivity"));
+ // Not distraction optimized, but from system app
+ assertThat(mCarPm.isActivityDistractionOptimized("com.android.car",
+ "com.android.car.NonDistractionOptimizedActivityForTesting")).isFalse();
+ // Not distraction optimized, also not from trusted source
+ assertThat(mCarPm.isActivityDistractionOptimized("android.car.cts",
+ "android.car.cts.drivingstate.NonDistractionOptimizedActivity")).isFalse();
+ }
+
+ @Test
+ public void testPendingIntentToDistractionOptimizedActivityIsAllowed() {
+ assertThat(mCarPm.isPendingIntentDistractionOptimized(
+ createIntent("com.android.car", ".DistractionOptimizedActivityForTesting")))
+ .isTrue();
+ }
+
+ @Test
+ public void testPendingIntentToNonDistractionOptimizedActivityNotAllowed() {
+ assertThat(mCarPm.isPendingIntentDistractionOptimized(
+ createIntent("com.android.car", ".NonDistractionOptimizedActivityForTesting")))
+ .isFalse();
}
private PendingIntent createIntent(String packageName, String relativeClassName) {
@@ -106,27 +121,4 @@
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
-
- @Test
- public void testPendingIntentToDistractionOptimizedActivityIsAllowed() {
- // This test relies on test activity in installed apk, and AndroidManifest declaration.
- if (Build.TYPE.equalsIgnoreCase("user")) {
- // Skip this test on user build, which checks the install source for DO activity list.
- return;
- }
- assertTrue(mCarPm.isPendingIntentDistractionOptimized(
- createIntent("android.car.cts", ".drivingstate.DistractionOptimizedActivity")));
- }
-
- @Test
- public void testPendingIntentToNonDistractionOptimizedActivityNotAllowed() {
- // This test relies on test activity in installed apk, and AndroidManifest declaration.
- if (Build.TYPE.equalsIgnoreCase("user")) {
- // Skip this test on user build, which checks the install source for DO activity list.
- return;
- }
- assertFalse(mCarPm.isPendingIntentDistractionOptimized(
- createIntent("android.car.cts", ".drivingstate.NonDistractionOptimizedActivity")));
- }
-
}
diff --git a/tests/tests/car/src/android/car/cts/drivingstate/DistractionOptimizedActivity.java b/tests/tests/car/src/android/car/cts/drivingstate/DistractionOptimizedActivity.java
deleted file mode 100644
index 00440b6..0000000
--- a/tests/tests/car/src/android/car/cts/drivingstate/DistractionOptimizedActivity.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package android.car.cts.drivingstate;
-
-import android.app.Activity;
-
-public class DistractionOptimizedActivity extends Activity {
-}
\ No newline at end of file
diff --git a/tests/tests/car/src/android/car/cts/drivingstate/NonDistractionOptimizedActivity.java b/tests/tests/car/src/android/car/cts/drivingstate/NonDistractionOptimizedActivity.java
deleted file mode 100644
index 0c75a3a..0000000
--- a/tests/tests/car/src/android/car/cts/drivingstate/NonDistractionOptimizedActivity.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package android.car.cts.drivingstate;
-
-import android.app.Activity;
-
-public class NonDistractionOptimizedActivity extends Activity {
-}
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index d221460..f3251c0 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -24,6 +24,12 @@
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- Disable package verifier prevents test flakiness. -->
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="restore-settings" value="true" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/cts/content" />
<option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
@@ -92,7 +98,6 @@
<option name="push-file" key="HelloWorld5.apk" value="/data/local/tmp/cts/content/malformed.apk" />
<option name="push-file" key="malformed.apk.idsig" value="/data/local/tmp/cts/content/malformed.apk.idsig" />
<option name="push-file" key="test-cert.x509.pem" value="/data/local/tmp/cts/content/test-cert.x509.pem" />
-
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/tests/content/HelloWorldApp/res/values/res_hardening.xml b/tests/tests/content/HelloWorldApp/res/values/res_hardening.xml
new file mode 100644
index 0000000..70cb280
--- /dev/null
+++ b/tests/tests/content/HelloWorldApp/res/values/res_hardening.xml
@@ -0,0 +1,58 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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="inc_string">true</string>
+ <string name="inc_string_end">true</string>
+
+ <bool name="inc_bool">true</bool>
+ <bool name="inc_bool_end">true</bool>
+
+ <integer name="inc_integer">1</integer>
+ <integer name="inc_integer_end">1</integer>
+
+ <string-array name="inc_string_array">
+ <item>@string/inc_string</item>
+ </string-array>
+ <string-array name="inc_string_array_end" />
+
+ <attr name="inc_string_attr" />
+ <attr name="inc_bool_attr" />
+ <attr name="inc_integer_attr" />
+
+ <style name="IncStyle">
+ <item name="inc_string_attr">@string/inc_string</item>
+ <item name="inc_bool_attr">@bool/inc_bool</item>
+ <item name="inc_integer_attr">@integer/inc_integer</item>
+ </style>
+
+ <public type="string" name="inc_string" id ="0x7f021000"/>
+ <public type="string" name="inc_string_end" id ="0x7f022000"/>
+
+ <public type="bool" name="inc_bool" id ="0x7f031000"/>
+ <public type="bool" name="inc_bool_end" id ="0x7f032000"/>
+
+ <public type="integer" name="inc_integer" id ="0x7f041000"/>
+ <public type="integer" name="inc_integer_end" id ="0x7f042000"/>
+
+ <public type="array" name="inc_string_array" id ="0x7f051000"/>
+ <public type="array" name="inc_string_array_end" id ="0x7f052000"/>
+
+ <public type="style" name="IncStyle" id ="0x7f061000"/>
+
+ <public type="attr" name="inc_string_attr" id ="0x7f011000"/>
+ <public type="attr" name="inc_bool_attr" id ="0x7f011001"/>
+ <public type="attr" name="inc_integer_attr" id ="0x7f011002"/>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/content/HelloWorldApp/res/xml/test_xml.xml b/tests/tests/content/HelloWorldApp/res/xml/test_xml.xml
new file mode 100644
index 0000000..3f225ee
--- /dev/null
+++ b/tests/tests/content/HelloWorldApp/res/xml/test_xml.xml
@@ -0,0 +1,18 @@
+<?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.
+ -->
+
+<Test>true</Test>
\ No newline at end of file
diff --git a/tests/tests/content/HelloWorldApp/res/xml/test_xml_attrs.xml b/tests/tests/content/HelloWorldApp/res/xml/test_xml_attrs.xml
new file mode 100644
index 0000000..28ce9bd
--- /dev/null
+++ b/tests/tests/content/HelloWorldApp/res/xml/test_xml_attrs.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<Test xmlns:app="http://schemas.android.com/apk/res-auto"
+ app:inc_string_attr="@string/inc_string"
+ app:inc_bool_attr="@bool/inc_bool"
+ app:inc_integer_attr="@integer/inc_integer" />
\ No newline at end of file
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 5aebc24..08d075f 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
@@ -135,12 +135,12 @@
cleanup();
}
- private void checkIncrementalDeliveryFeature() throws Exception {
+ static void checkIncrementalDeliveryFeature() {
Assume.assumeTrue(getPackageManager().hasSystemFeature(
PackageManager.FEATURE_INCREMENTAL_DELIVERY));
}
- private void checkIncrementalDeliveryV2Feature() throws Exception {
+ private static void checkIncrementalDeliveryV2Feature() throws Exception {
checkIncrementalDeliveryFeature();
Assume.assumeTrue(getPackageManager().hasSystemFeature(
PackageManager.FEATURE_INCREMENTAL_DELIVERY, 2));
@@ -992,7 +992,7 @@
atraceDumpDelayMs));
}
- private boolean isAppInstalled(String packageName) throws IOException {
+ static boolean isAppInstalled(String packageName) throws IOException {
final String commandResult = executeShellCommand("pm list packages");
final int prefixLength = "package:".length();
return Arrays.stream(commandResult.split("\\r?\\n"))
@@ -1092,7 +1092,7 @@
return readTime;
}
- private String uninstallPackageSilently(String packageName) throws IOException {
+ static String uninstallPackageSilently(String packageName) throws IOException {
return executeShellCommand("pm uninstall " + packageName);
}
@@ -1100,7 +1100,7 @@
boolean await() throws Exception;
}
- private static String executeShellCommand(String command) throws IOException {
+ static String executeShellCommand(String command) throws IOException {
try (InputStream inputStream = executeShellCommandStream(command)) {
return readFullStream(inputStream);
}
@@ -1143,18 +1143,18 @@
return new ParcelFileDescriptor.AutoCloseInputStream(stdout);
}
- private static String readFullStream(InputStream inputStream, long expected)
+ static String readFullStream(InputStream inputStream, long expected)
throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream();
writeFullStream(inputStream, result, expected);
return result.toString("UTF-8");
}
- private static String readFullStream(InputStream inputStream) throws IOException {
+ static String readFullStream(InputStream inputStream) throws IOException {
return readFullStream(inputStream, -1);
}
- private static void writeFullStream(InputStream inputStream, OutputStream outputStream,
+ static void writeFullStream(InputStream inputStream, OutputStream outputStream,
long expected)
throws IOException {
final byte[] buffer = new byte[1024];
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
index f25006f..f3b5a8a 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
@@ -225,7 +225,15 @@
public void testAppUpdate() throws Exception {
installPackage(TEST_HW5);
assertTrue(isAppInstalled(TEST_APP_PACKAGE));
- installPackage(TEST_HW7);
+ updatePackage(TEST_APP_PACKAGE, TEST_HW7);
+ assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+ }
+
+ @Test
+ public void testAppUpdateSameApk() throws Exception {
+ installPackage(TEST_HW5);
+ assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+ updatePackage(TEST_APP_PACKAGE, TEST_HW5);
assertTrue(isAppInstalled(TEST_APP_PACKAGE));
}
@@ -233,7 +241,15 @@
public void testAppUpdateStdIn() throws Exception {
installPackageStdIn(TEST_HW5);
assertTrue(isAppInstalled(TEST_APP_PACKAGE));
- installPackageStdIn(TEST_HW7);
+ updatePackageStdIn(TEST_APP_PACKAGE, TEST_HW7);
+ assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+ }
+
+ @Test
+ public void testAppUpdateStdInSameApk() throws Exception {
+ installPackageStdIn(TEST_HW5);
+ assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+ updatePackageStdIn(TEST_APP_PACKAGE, TEST_HW5);
assertTrue(isAppInstalled(TEST_APP_PACKAGE));
}
@@ -630,12 +646,24 @@
"pm " + mInstall + " -t -g " + file.getPath()));
}
+ private void updatePackage(String packageName, String baseName) throws IOException {
+ File file = new File(createApkPath(baseName));
+ assertEquals("Success\n", executeShellCommand(
+ "pm " + mInstall + " -t -p " + packageName + " -g " + file.getPath()));
+ }
+
private void installPackageStdIn(String baseName) throws IOException {
File file = new File(createApkPath(baseName));
assertEquals("Success\n",
executeShellCommand("pm " + mInstall + " -t -g -S " + file.length(), file));
}
+ private void updatePackageStdIn(String packageName, String baseName) throws IOException {
+ File file = new File(createApkPath(baseName));
+ assertEquals("Success\n", executeShellCommand(
+ "pm " + mInstall + " -t -p " + packageName + " -g -S " + file.length(), file));
+ }
+
private void installSplits(String[] baseNames) throws IOException {
if (mStreaming) {
installSplitsBatch(baseNames);
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 2379d10..598e882 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -160,6 +160,8 @@
+ "iRKAay19k5VFlSaM7QW9uhvlrLQqsTW01ofFzxNDbp2QfIFHZR6rebKzKBz6byQFM0DYQnYMwFWXjWkMPN"
+ "dqkRLykoFLyBup53G68k2n8w";
private static final String SHELL_PACKAGE_NAME = "com.android.shell";
+ private static final String HELLO_WORLD_PACKAGE_NAME = "com.example.helloworld";
+ private static final String HELLO_WORLD_APK = SAMPLE_APK_BASE + "HelloWorld5.apk";
@Before
public void setup() throws Exception {
@@ -171,6 +173,7 @@
public void tearDown() throws Exception {
uninstallPackage(EMPTY_APP_PACKAGE_NAME);
uninstallPackage(EMPTY_APP_MAX_PACKAGE_NAME);
+ uninstallPackage(HELLO_WORLD_PACKAGE_NAME);
}
@Test
@@ -1310,6 +1313,16 @@
.isEqualTo(ApplicationInfo.FLAG_INSTALLED);
}
+ @Test
+ public void testGetApplicationInfo_icon_MatchesUseRoundIcon() throws Exception {
+ installPackage(HELLO_WORLD_APK);
+ final boolean useRoundIcon = mContext.getResources().getBoolean(
+ mContext.getResources().getIdentifier("config_useRoundIcon", "bool", "android"));
+ final ApplicationInfo info = mPackageManager.getApplicationInfo(HELLO_WORLD_PACKAGE_NAME,
+ 0 /*flags*/);
+ assertThat(info.icon).isEqualTo((useRoundIcon ? info.roundIconRes : info.iconRes));
+ }
+
private boolean isUpdatingApexSupported() {
return SystemProperties.getBoolean("ro.apex.updatable", false);
}
diff --git a/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java b/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java
new file mode 100644
index 0000000..8280877
--- /dev/null
+++ b/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.cts;
+
+import static android.content.pm.cts.PackageManagerShellCommandIncrementalTest.checkIncrementalDeliveryFeature;
+import static android.content.pm.cts.PackageManagerShellCommandIncrementalTest.isAppInstalled;
+import static android.content.pm.cts.PackageManagerShellCommandIncrementalTest.uninstallPackageSilently;
+import static android.content.pm.cts.PackageManagerShellCommandIncrementalTest.writeFullStream;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNot.not;
+import static org.junit.Assert.assertTrue;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.cts.util.XmlUtils;
+import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.MatcherUtils;
+import com.android.incfs.install.IBlockFilter;
+import com.android.incfs.install.IncrementalInstallSession;
+import com.android.incfs.install.PendingBlock;
+
+import org.hamcrest.Matcher;
+import org.hamcrest.MatcherAssert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Paths;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(AndroidJUnit4.class)
+@AppModeFull
+@LargeTest
+public class ResourcesHardeningTest {
+ private static final String TEST_APK_PATH = "/data/local/tmp/cts/content/";
+ private static final String[] TEST_APKS = {"HelloWorld5.apk", "HelloWorld5_mdpi-v4.apk"};
+ private static final String TEST_APP_PACKAGE = "com.example.helloworld";
+
+ private static final String RES_IDENTIFIER = TEST_APP_PACKAGE + ":string/inc_string";
+ private static final String RES_XML_PATH = "res/xml/test_xml.xml";
+ private static final String RES_XML_LARGE_PATH = "res/xml/test_xml_attrs.xml";
+ private static final String RES_DRAWABLE_PATH = "res/drawable-mdpi-v4/background.jpg";
+ private static final int RES_STRING = 0x7f021000;
+ private static final int RES_ARRAY = 0x7f051000;
+ private static final int RES_STYLE = 0x7f061000;
+ private static final int[] RES_STYLEABLE = {0x7f011000, 0x7f011001, 0x7f011002};
+
+ @Before
+ public void onBefore() throws Exception {
+ checkIncrementalDeliveryFeature();
+ }
+
+ @Test
+ public void checkGetIdentifier() throws Exception {
+ testReadSuccessAndFailure(
+ (res, filter) -> {
+ filter.stopSendingBlocks();
+ return res.getIdentifier(RES_IDENTIFIER, "", "");
+ },
+ not(equalTo(0)), equalTo(0));
+ }
+
+ @Test
+ public void checkGetResourceName() throws Exception {
+ testReadSuccessAndFailureException(
+ (res, filter) -> {
+ filter.stopSendingBlocks();
+ return res.getResourceName(RES_STRING);
+ },
+ equalTo(RES_IDENTIFIER), instanceOf(Resources.NotFoundException.class));
+ }
+
+ @Test
+ public void checkGetString() throws Exception {
+ testReadSuccessAndFailureException(
+ (res, filter) -> {
+ filter.stopSendingBlocks();
+ return res.getString(RES_STRING);
+ },
+ equalTo("true"), instanceOf(Resources.NotFoundException.class));
+ }
+
+ @Test
+ public void checkGetStringArray() throws Exception {
+ testReadSuccessAndFailureException(
+ (res, filter) -> {
+ filter.stopSendingBlocks();
+ return res.getStringArray(RES_ARRAY);
+ },
+ equalTo(new String[]{"true"}), instanceOf(Resources.NotFoundException.class));
+ }
+
+ @Test
+ public void checkOpenXmlResourceParser() throws Exception {
+ testReadSuccessAndFailureException(
+ (res, filter) -> {
+ filter.stopSendingBlocks();
+ final AssetManager assets = res.getAssets();
+ try (XmlResourceParser p = assets.openXmlResourceParser(RES_XML_PATH)) {
+ XmlUtils.beginDocument(p, "Test");
+ return p.nextText();
+ }
+ },
+ equalTo("true"), instanceOf(FileNotFoundException.class));
+ }
+
+ @Test
+ public void checkApplyStyle() throws Exception {
+ testReadSuccessAndFailure(
+ (res, filter) -> {
+ filter.stopSendingBlocks();
+ final Resources.Theme theme = res.newTheme();
+ theme.applyStyle(RES_STYLE, true);
+ final TypedArray values = theme.obtainStyledAttributes(RES_STYLEABLE);
+ return new String[]{
+ values.getString(0),
+ values.getString(1),
+ values.getString(2),
+ };
+ },
+ equalTo(new String[]{"true", "true", "1"}),
+ equalTo(new String[]{null, null, null}));
+ }
+
+ @Test
+ public void checkXmlAttributes() throws Exception {
+ testReadSuccessAndFailure(
+ (res, filter) -> {
+ final AssetManager assets = res.getAssets();
+ try (XmlResourceParser p = assets.openXmlResourceParser(RES_XML_LARGE_PATH)) {
+ XmlUtils.beginDocument(p, "Test");
+ filter.stopSendingBlocks();
+ final TypedArray values = res.obtainAttributes(p, RES_STYLEABLE);
+ return new String[]{
+ values.getString(0),
+ values.getString(1),
+ values.getString(2),
+ };
+ }
+ },
+ equalTo(new String[]{"true", "true", "1"}),
+ equalTo(new String[]{null, null, null}));
+ }
+
+ @Test
+ public void checkOpen() throws Exception {
+ testReadSuccessAndFailureException(
+ (res, filter) -> {
+ final AssetManager assets = res.getAssets();
+ try (InputStream is = assets.openNonAsset(RES_DRAWABLE_PATH)) {
+ filter.stopSendingBlocks();
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ writeFullStream(is, result, AssetFileDescriptor.UNKNOWN_LENGTH);
+ return true;
+ }
+ }, equalTo(true), instanceOf(IOException.class));
+ }
+
+ @Test
+ public void checkOpenFd() throws Exception {
+ testReadSuccessAndFailureException(
+ (res, filter) -> {
+ final AssetManager assets = res.getAssets();
+ try (AssetFileDescriptor fd = assets.openNonAssetFd(RES_DRAWABLE_PATH)) {
+ filter.stopSendingBlocks();
+ final ByteArrayOutputStream result = new ByteArrayOutputStream();
+ writeFullStream(fd.createInputStream(), result,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ return true;
+ }
+ }, equalTo(true), instanceOf(IOException.class));
+ }
+
+ private interface ThrowingFunction<R> {
+ R apply(Resources res, BlockFilterController filter) throws Exception;
+ }
+
+ /**
+ * Runs the test twice to test resource resolution when all necessary blocks are available and
+ * when some necessary blocks are missing due to incremental installation.
+ *
+ * During the test of the failure path {@link TestBlockFilter#stopSendingBlocks()} will be
+ * invoked when {@link BlockFilterController#stopSendingBlocks()} is invoked; preventing any
+ * blocks that have not been served from being served during the test of the failure path.
+ *
+ * @param getValue executes resource resolution and returns a T object
+ * @param checkSuccess the matcher that represents the value when all pages are available
+ * @param checkFailure the matcher that represents the value when all pages are missing
+ * @param <T> the expected return type of the resource resolution
+ */
+ private <T> void testReadSuccessAndFailure(ThrowingFunction<T> getValue,
+ Matcher<? super T> checkSuccess, Matcher<? super T> checkFailure) throws Exception {
+ try (ShellInstallSession session = startInstallSession()) {
+ final T value = getValue.apply(session.getPackageResources(),
+ BlockFilterController.noop());
+ MatcherAssert.assertThat(value, checkSuccess);
+ }
+ try (ShellInstallSession session = startInstallSession()) {
+ final T value = getValue.apply(session.getPackageResources(),
+ BlockFilterController.allowDisable(session));
+ MatcherAssert.assertThat(value, checkFailure);
+ }
+ }
+
+ /**
+ * Variant of {@link #testReadSuccessAndFailure(ThrowingFunction, Matcher, Matcher)} that
+ * expects an exception to be thrown during the failure path.
+ */
+ private <T> void testReadSuccessAndFailureException(ThrowingFunction<T> getValue,
+ Matcher<? super T> checkSuccess, Matcher<Throwable> checkFailure)
+ throws Exception {
+ try (ShellInstallSession session = startInstallSession()) {
+ final T value = getValue.apply(session.getPackageResources(),
+ BlockFilterController.noop());
+ MatcherAssert.assertThat(value, checkSuccess);
+ }
+ try (ShellInstallSession session = startInstallSession()) {
+ MatcherUtils.assertThrows(checkFailure,
+ () -> getValue.apply(session.getPackageResources(),
+ BlockFilterController.allowDisable(session)));
+ }
+ }
+
+ private static ShellInstallSession startInstallSession() throws IOException,
+ InterruptedException {
+ return startInstallSession(TEST_APKS, TEST_APP_PACKAGE);
+ }
+
+ private static ShellInstallSession startInstallSession(String[] apks, String packageName)
+ throws IOException, InterruptedException {
+ final String v4SignatureSuffix = ".idsig";
+ final TestBlockFilter filter = new TestBlockFilter();
+ final IncrementalInstallSession.Builder builder = new IncrementalInstallSession.Builder()
+ .addExtraArgs("-t", "-i", getContext().getPackageName())
+ .setLogger(new IncrementalDeviceConnection.Logger())
+ .setBlockFilter(filter);
+ for (final String apk : apks) {
+ final String path = TEST_APK_PATH + apk;
+ builder.addApk(Paths.get(path), Paths.get(path + v4SignatureSuffix));
+ }
+
+ final ShellInstallSession session = new ShellInstallSession(
+ builder.build(), filter, packageName);
+ session.session.start(Executors.newSingleThreadExecutor(),
+ IncrementalDeviceConnection.Factory.reliable());
+ session.session.waitForInstallCompleted(10, TimeUnit.SECONDS);
+ assertTrue(isAppInstalled(packageName));
+ return session;
+ }
+
+ private static class BlockFilterController {
+ private final ShellInstallSession mSession;
+ BlockFilterController(ShellInstallSession session) {
+ mSession = session;
+ }
+ public static BlockFilterController allowDisable(ShellInstallSession session) {
+ return new BlockFilterController(session);
+ }
+ public static BlockFilterController noop() {
+ return new BlockFilterController(null);
+ }
+ public void stopSendingBlocks() {
+ if (mSession != null) {
+ mSession.stopSendingBlocks();
+ }
+ }
+ }
+
+ /**
+ * A wrapper for {@link IncrementalInstallSession} that uninstalls the installed package when
+ * testing is finished.
+ */
+ private static class ShellInstallSession implements AutoCloseable {
+ public final IncrementalInstallSession session;
+ private final TestBlockFilter mFilter;
+ private final String mPackageName;
+ private ShellInstallSession(IncrementalInstallSession session,
+ TestBlockFilter filter, String packageName) {
+ this.session = session;
+ this.mFilter = filter;
+ this.mPackageName = packageName;
+ getUiAutomation().adoptShellPermissionIdentity();
+ }
+
+ public void stopSendingBlocks() {
+ mFilter.stopSendingBlocks();
+ }
+
+ public Resources getPackageResources() throws PackageManager.NameNotFoundException {
+ return getContext().createPackageContext(mPackageName, 0).getResources();
+ }
+
+ @Override
+ public void close() throws IOException {
+ session.close();
+ getUiAutomation().dropShellPermissionIdentity();
+ uninstallPackageSilently(mPackageName);
+ }
+ }
+
+ private static class TestBlockFilter implements IBlockFilter {
+ private final AtomicBoolean mDisabled = new AtomicBoolean();
+ @Override
+ public boolean shouldServeBlock(PendingBlock block) {
+ return !mDisabled.get();
+ }
+ public void stopSendingBlocks() {
+ mDisabled.set(true);
+ }
+ }
+
+ private static Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getContext();
+ }
+
+ private static UiAutomation getUiAutomation() {
+ return InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ }
+}
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index 87684a5..c4ce26c 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -121,6 +121,49 @@
private Activity mScreenOnActivity;
+ private static class DisplayModeState {
+ public final int mHeight;
+ public final int mWidth;
+ public final float mRefreshRate;
+
+ DisplayModeState(Display display) {
+ mHeight = display.getMode().getPhysicalHeight();
+ mWidth = display.getMode().getPhysicalWidth();
+
+ // Starting Android S the, the platform might throttle down
+ // applications frame rate to a divisor of the refresh rate instead if changing the
+ // physical display refresh rate. Applications should use
+ // {@link android.view.Display#getRefreshRate} to know their frame rate as opposed to
+ // {@link android.view.Display.Mode#getRefreshRate} that returns the physical display
+ // refresh rate. See
+ // {@link com.android.server.display.DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE}
+ // for more details.
+ mRefreshRate = display.getRefreshRate();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DisplayModeState)) {
+ return false;
+ }
+
+ DisplayModeState other = (DisplayModeState) obj;
+ return mHeight == other.mHeight
+ && mWidth == other.mWidth
+ && mRefreshRate == other.mRefreshRate;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("{")
+ .append("width=").append(mWidth)
+ .append(", height=").append(mHeight)
+ .append(", fps=").append(mRefreshRate)
+ .append("}")
+ .toString();
+ }
+ }
+
@Rule
public ActivityTestRule<DisplayTestActivity> mDisplayTestActivity =
new ActivityTestRule<>(
@@ -582,10 +625,10 @@
final CountDownLatch changeSignal = new CountDownLatch(1);
final AtomicInteger changeCounter = new AtomicInteger(0);
- final int activeModeId = mDefaultDisplay.getMode().getModeId();
+ final DisplayModeState activeMode = new DisplayModeState(mDefaultDisplay);
DisplayListener listener = new DisplayListener() {
- private int mLastModeId = activeModeId;
+ private DisplayModeState mLastMode = activeMode;
@Override
public void onDisplayAdded(int displayId) {}
@@ -594,17 +637,18 @@
if (displayId != mDefaultDisplay.getDisplayId()) {
return;
}
- int newModeId = mDefaultDisplay.getMode().getModeId();
- if (mLastModeId == newModeId) {
+ DisplayModeState newMode = new DisplayModeState(mDefaultDisplay);
+ if (mLastMode.equals(newMode)) {
// We assume this display change is caused by an external factor so it's
// unrelated.
return;
}
- Log.i(TAG, "Switched mode from id=" + mLastModeId + " to id=" + newModeId);
+
+ Log.i(TAG, "Switched mode from=" + mLastMode + " to=" + newMode);
changeCounter.incrementAndGet();
changeSignal.countDown();
- mLastModeId = newModeId;
+ mLastMode = newMode;
}
@Override
@@ -624,7 +668,10 @@
// Wait until the display change is effective.
assertTrue(changeSignal.await(5, TimeUnit.SECONDS));
- assertEquals(mode.getModeId(), mDefaultDisplay.getMode().getModeId());
+ DisplayModeState currentMode = new DisplayModeState(mDefaultDisplay);
+ assertEquals(mode.getPhysicalHeight(), currentMode.mHeight);
+ assertEquals(mode.getPhysicalWidth(), currentMode.mWidth);
+ assertEquals(mode.getRefreshRate(), currentMode.mRefreshRate, 0.001f);
// Make sure no more display mode changes are registered.
Thread.sleep(Duration.ofSeconds(3).toMillis());
diff --git a/tests/tests/graphics/src/android/graphics/cts/ColorMatrixColorFilterTest.java b/tests/tests/graphics/src/android/graphics/cts/ColorMatrixColorFilterTest.java
index cdbbd09..0c7e676 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ColorMatrixColorFilterTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ColorMatrixColorFilterTest.java
@@ -55,6 +55,7 @@
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setColorFilter(filter);
+ paint.setAntiAlias(false);
canvas.drawPoint(0, 0, paint);
ColorUtils.verifyColor(Color.CYAN, bitmap.getPixel(0, 0));
paint.setColor(Color.GREEN);
diff --git a/tests/tests/graphics/src/android/graphics/cts/CornerPathEffectTest.java b/tests/tests/graphics/src/android/graphics/cts/CornerPathEffectTest.java
index b3dfd3a..da892b8 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CornerPathEffectTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CornerPathEffectTest.java
@@ -60,6 +60,7 @@
pathPaint.setStyle(Style.STROKE);
pathPaint.setStrokeWidth(0);
pathPaint.setPathEffect(effect);
+ pathPaint.setAntiAlias(false);
Bitmap bitmap = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
diff --git a/tests/tests/graphics/src/android/graphics/cts/DashPathEffectTest.java b/tests/tests/graphics/src/android/graphics/cts/DashPathEffectTest.java
index d1c7889..959f607 100644
--- a/tests/tests/graphics/src/android/graphics/cts/DashPathEffectTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/DashPathEffectTest.java
@@ -62,6 +62,7 @@
paint.setStrokeWidth(0);
paint.setColor(FOREGROUND);
paint.setPathEffect(effect);
+ paint.setAntiAlias(false);
Canvas canvas = new Canvas(bitmap);
canvas.drawPath(path, paint);
diff --git a/tests/tests/graphics/src/android/graphics/cts/DiscretePathEffectTest.java b/tests/tests/graphics/src/android/graphics/cts/DiscretePathEffectTest.java
index e76db8f..6cf975d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/DiscretePathEffectTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/DiscretePathEffectTest.java
@@ -56,6 +56,7 @@
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(0);
paint.setPathEffect(effect);
+ paint.setAntiAlias(false);
Path path = new Path();
path.moveTo(START_X, COORD_Y);
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index b1d99f8..b43f77e 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -371,11 +371,17 @@
public void testSetAntiAlias() {
Paint p = new Paint();
- p.setAntiAlias(true);
- assertTrue(p.isAntiAlias());
-
p.setAntiAlias(false);
assertFalse(p.isAntiAlias());
+
+ p.setAntiAlias(true);
+ assertTrue(p.isAntiAlias());
+ }
+
+ @Test
+ public void testDefaultAntiAlias() {
+ Paint p = new Paint();
+ assertTrue(p.isAntiAlias());
}
@Test
@@ -951,11 +957,17 @@
public void testSetDither() {
Paint p = new Paint();
- p.setDither(true);
- assertTrue(p.isDither());
-
p.setDither(false);
assertFalse(p.isDither());
+
+ p.setDither(true);
+ assertTrue(p.isDither());
+ }
+
+ @Test
+ public void testDefaultDither() {
+ Paint p = new Paint();
+ assertTrue(p.isDither());
}
@Test
@@ -1150,7 +1162,8 @@
p.reset();
assertEquals(Paint.FILTER_BITMAP_FLAG | Paint.DEV_KERN_TEXT_FLAG
- | Paint.EMBEDDED_BITMAP_TEXT_FLAG, p.getFlags());
+ | Paint.EMBEDDED_BITMAP_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG
+ | Paint.DITHER_FLAG, p.getFlags());
assertEquals(null, p.getColorFilter());
assertEquals(null, p.getMaskFilter());
assertEquals(null, p.getPathEffect());
diff --git a/tests/tests/graphics/src/android/graphics/cts/PictureTest.java b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
index e49e933..13ebaf6 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
@@ -141,6 +141,7 @@
// GREEN rectangle covering the entire canvas
paint.setColor(Color.GREEN);
paint.setStyle(Style.FILL);
+ paint.setAntiAlias(false);
canvas.drawRect(0, 0, TEST_WIDTH, TEST_HEIGHT, paint);
// horizontal red line starting from (0,0); overwrites first line of the rectangle
paint.setColor(Color.RED);
diff --git a/tests/tests/graphics/src/android/graphics/cts/SumPathEffectTest.java b/tests/tests/graphics/src/android/graphics/cts/SumPathEffectTest.java
index 207f74e6..e6ba859 100644
--- a/tests/tests/graphics/src/android/graphics/cts/SumPathEffectTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/SumPathEffectTest.java
@@ -60,6 +60,7 @@
paint.setPathEffect(first);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(0); // 1-pixel hairline
+ paint.setAntiAlias(false);
canvas.drawPath(path, paint);
PathEffect second = new DashPathEffect(new float[] { 10, 5 }, 5);
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java
index 8b26b5b..f7db74a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanFeaturesTest.java
@@ -20,6 +20,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.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
@@ -75,6 +76,7 @@
private FeatureInfo mVulkanHardwareLevel = null;
private FeatureInfo mVulkanHardwareVersion = null;
private FeatureInfo mVulkanHardwareCompute = null;
+ private JSONObject mVkJSON = null;
private JSONObject mVulkanDevices[];
private JSONObject mBestDevice = null;
@@ -103,7 +105,8 @@
}
}
- mVulkanDevices = getVulkanDevices();
+ mVkJSON = new JSONObject(nativeGetVkJSON());
+ mVulkanDevices = getVulkanDevices(mVkJSON);
mBestDevice = getBestDevice();
}
@CddTest(requirement = "7.1.4.2/C-1-1,C-2-1")
@@ -199,7 +202,7 @@
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME +
" (version >= " + VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION +
")",
- hasExtension(mBestDevice,
+ hasDeviceExtension(mBestDevice,
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION));
assertTrue("Devices with Vulkan 1.1 must support SYNC_FD external semaphores",
@@ -212,6 +215,17 @@
"externalFenceFeatures", 0x3 /* importable + exportable */));
}
+ @CddTest(requirement = "7.1.4.2/C-1-7")
+ @Test
+ public void testVulkanRequiredExtensions() throws JSONException {
+ assumeTrue("Skipping because Vulkan is not supported", mVulkanDevices.length > 0);
+
+ assertVulkanInstanceExtension("VK_KHR_surface", 25);
+ assertVulkanInstanceExtension("VK_KHR_android_surface", 6);
+ assertVulkanDeviceExtension("VK_KHR_swapchain", 68);
+ assertVulkanDeviceExtension("VK_KHR_incremental_present", 1);
+ }
+
@CddTest(requirement = "7.9.2/C-1-5")
@Test
public void testVulkanVersionForVrHighPerformance() {
@@ -230,7 +244,7 @@
assertTrue("Device - " + device.getJSONObject("properties").getString("deviceName")
+ " supports extension " + VK_KHR_PERFORMANCE_QUERY
+ ". It is blocked and hence should not be supported",
- !hasExtension(device, VK_KHR_PERFORMANCE_QUERY, 0));
+ !hasDeviceExtension(device, VK_KHR_PERFORMANCE_QUERY, 0));
}
}
@@ -364,9 +378,46 @@
return false;
}
- private boolean hasExtension(JSONObject device, String name, int minVersion)
+ private void assertVulkanDeviceExtension(final String name, final int minVersion)
throws JSONException {
- JSONArray extensions = device.getJSONArray("extensions");
+ assertTrue(
+ String.format(
+ "Devices with Vulkan must support device extension %s (version >= %d)",
+ name,
+ minVersion),
+ hasDeviceExtension(mBestDevice, name, minVersion));
+ }
+
+ private void assertVulkanInstanceExtension(final String name, final int minVersion)
+ throws JSONException {
+ assertTrue(
+ String.format(
+ "Devices with Vulkan must support instance extension %s (version >= %d)",
+ name,
+ minVersion),
+ hasInstanceExtension(name, minVersion));
+ }
+
+ private static boolean hasDeviceExtension(
+ final JSONObject device,
+ final String name,
+ final int minVersion) throws JSONException {
+ final JSONArray deviceExtensions = device.getJSONArray("extensions");
+ return hasExtension(deviceExtensions, name, minVersion);
+ }
+
+ private boolean hasInstanceExtension(
+ final String name,
+ final int minVersion) throws JSONException {
+ // Instance extensions are in the top-level vkjson object.
+ final JSONArray instanceExtensions = mVkJSON.getJSONArray("extensions");
+ return hasExtension(instanceExtensions, name, minVersion);
+ }
+
+ private static boolean hasExtension(
+ final JSONArray extensions,
+ final String name,
+ final int minVersion) throws JSONException {
for (int i = 0; i < extensions.length(); i++) {
JSONObject ext = extensions.getJSONObject(i);
if (ext.getString("extensionName").equals(name) &&
@@ -391,11 +442,11 @@
private static native String nativeGetVkJSON();
- private JSONObject[] getVulkanDevices() throws JSONException, UnsupportedEncodingException {
- JSONArray vkjson = (new JSONObject(nativeGetVkJSON())).getJSONArray("devices");
- JSONObject[] devices = new JSONObject[vkjson.length()];
- for (int i = 0; i < vkjson.length(); i++) {
- devices[i] = vkjson.getJSONObject(i);
+ private static JSONObject[] getVulkanDevices(final JSONObject vkJSON) throws JSONException {
+ JSONArray devicesArray = vkJSON.getJSONArray("devices");
+ JSONObject[] devices = new JSONObject[devicesArray.length()];
+ for (int i = 0; i < devicesArray.length(); i++) {
+ devices[i] = devicesArray.getJSONObject(i);
}
return devices;
}
diff --git a/tests/tests/hardware/src/android/hardware/hdmi/cts/HdmiControlManagerTest.java b/tests/tests/hardware/src/android/hardware/hdmi/cts/HdmiControlManagerTest.java
index dc75816..9c0dfae 100644
--- a/tests/tests/hardware/src/android/hardware/hdmi/cts/HdmiControlManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/hdmi/cts/HdmiControlManagerTest.java
@@ -55,6 +55,11 @@
public class HdmiControlManagerTest {
private static final int DEVICE_TYPE_SWITCH = 6;
+ private static final String STRING_DEVICE_TYPE_TV = "tv";
+ private static final String STRING_DEVICE_TYPE_PLAYBACK_DEVICE = "playback_device";
+ private static final String STRING_DEVICE_TYPE_AUDIO_SYSTEM = "audio_system";
+ private static final String STRING_DEVICE_TYPE_PURE_CEC_SWITCH = "pure_cec_switch";
+
private static final int TIMEOUT_HOTPLUG_EVENT_SEC = 3;
private static final int TIMEOUT_CONTENT_CHANGE_SEC = 13;
@@ -95,25 +100,30 @@
List<String> deviceTypes = Arrays.asList(deviceTypesValue.split(","));
- if (deviceTypes.contains("0")) {
+ if (deviceTypes.contains(String.valueOf(HdmiDeviceInfo.DEVICE_TV))
+ || deviceTypes.contains(STRING_DEVICE_TYPE_TV)) {
assertThat(mHdmiControlManager.getTvClient()).isInstanceOf(HdmiTvClient.class);
assertThat(mHdmiControlManager.getClient(HdmiDeviceInfo.DEVICE_TV)).isInstanceOf(
HdmiTvClient.class);
}
- if (deviceTypes.contains("4")) {
+ if (deviceTypes.contains(String.valueOf(HdmiDeviceInfo.DEVICE_PLAYBACK))
+ || deviceTypes.contains(STRING_DEVICE_TYPE_PLAYBACK_DEVICE)) {
assertThat(mHdmiControlManager.getPlaybackClient()).isInstanceOf(
HdmiPlaybackClient.class);
assertThat(mHdmiControlManager.getClient(HdmiDeviceInfo.DEVICE_PLAYBACK)).isInstanceOf(
HdmiPlaybackClient.class);
}
- if (deviceTypes.contains("5")) {
+ if (deviceTypes.contains(String.valueOf(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM))
+ || deviceTypes.contains(STRING_DEVICE_TYPE_AUDIO_SYSTEM)) {
assertThat(
mHdmiControlManager.getClient(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)).isNotNull();
}
boolean isSwitchDevice = SystemProperties.getBoolean(
"ro.hdmi.property_is_device_hdmi_cec_switch", false);
- if (deviceTypes.contains("6") || isSwitchDevice) {
+ if (deviceTypes.contains(String.valueOf(DEVICE_TYPE_SWITCH))
+ || deviceTypes.contains(STRING_DEVICE_TYPE_PURE_CEC_SWITCH)
+ || isSwitchDevice) {
assertThat(mHdmiControlManager.getSwitchClient()).isInstanceOf(HdmiSwitchClient.class);
assertThat(mHdmiControlManager.getClient(6)).isInstanceOf(HdmiSwitchClient.class);
}
@@ -128,11 +138,13 @@
List<String> deviceTypes = Arrays.asList(deviceTypesValue.split(","));
- if (deviceTypes.contains("0")) {
+ if (deviceTypes.contains(String.valueOf(HdmiDeviceInfo.DEVICE_TV))
+ || deviceTypes.contains(STRING_DEVICE_TYPE_TV)) {
assertThat(mHdmiControlManager.getTvClient().getDeviceType()).isEqualTo(
HdmiDeviceInfo.DEVICE_TV);
}
- if (deviceTypes.contains("4")) {
+ if (deviceTypes.contains(String.valueOf(HdmiDeviceInfo.DEVICE_PLAYBACK))
+ || deviceTypes.contains(STRING_DEVICE_TYPE_PLAYBACK_DEVICE)) {
assertThat(mHdmiControlManager.getPlaybackClient().getDeviceType()).isEqualTo(
HdmiDeviceInfo.DEVICE_PLAYBACK);
}
@@ -140,7 +152,9 @@
boolean isSwitchDevice = SystemProperties.getBoolean(
"ro.hdmi.property_is_device_hdmi_cec_switch", false);
- if (deviceTypes.contains(String.valueOf(DEVICE_TYPE_SWITCH)) || isSwitchDevice) {
+ if (deviceTypes.contains(String.valueOf(DEVICE_TYPE_SWITCH))
+ || deviceTypes.contains(STRING_DEVICE_TYPE_PURE_CEC_SWITCH)
+ || isSwitchDevice) {
assertThat(mHdmiControlManager.getSwitchClient().getDeviceType()).isEqualTo(
DEVICE_TYPE_SWITCH);
}
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 9afa958..f2f2a85 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/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
index dafc15d..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(
@@ -1112,7 +1109,6 @@
.setCertificateSubject(certSubject)
.setCertificateNotBefore(certNotBefore)
.setCertificateNotAfter(certNotAfter)
- .setUnlockedDeviceRequired(true)
.setIsStrongBoxBacked(true)
.build());
KeyPair keyPair = generator.generateKeyPair();
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/media/src/android/media/cts/AudioFocusTest.java b/tests/tests/media/src/android/media/cts/AudioFocusTest.java
index f326754..1cfa1ff 100644
--- a/tests/tests/media/src/android/media/cts/AudioFocusTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioFocusTest.java
@@ -227,6 +227,10 @@
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
+ /**
+ * Test delayed focus loss after fade out
+ * @throws Exception
+ */
@AppModeFull(reason = "Instant apps cannot access the SD card")
public void testAudioFocusRequestMediaGainLossWithPlayer() throws Exception {
if (hasAutomotiveFeature(getContext())) {
@@ -304,6 +308,134 @@
getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
}
}
+
+ /**
+ * Test there is no delayed focus loss when focus loser is playing speech
+ * @throws Exception
+ */
+ @AppModeFull(reason = "Instant apps cannot access the SD card")
+ public void testAudioFocusRequestMediaGainLossWithSpeechPlayer() throws Exception {
+ if (hasAutomotiveFeature(getContext())) {
+ Log.i(TAG, "Test testAudioFocusRequestMediaGainLossWithSpeechPlayer "
+ + "skipped: not required for Auto platform");
+ return;
+ }
+ doTwoFocusOwnerOnePlayerFocusLoss(
+ true /*playSpeech*/,
+ false /*speechFocus*/,
+ false /*pauseOnDuck*/);
+ }
+
+ /**
+ * Test there is no delayed focus loss when focus loser had requested focus with
+ * AudioAttributes with speech content type
+ * @throws Exception
+ */
+ @AppModeFull(reason = "Instant apps cannot access the SD card")
+ public void testAudioFocusRequestMediaGainLossWithSpeechFocusRequest() throws Exception {
+ if (hasAutomotiveFeature(getContext())) {
+ Log.i(TAG, "Test testAudioFocusRequestMediaGainLossWithSpeechPlayer "
+ + "skipped: not required for Auto platform");
+ return;
+ }
+ doTwoFocusOwnerOnePlayerFocusLoss(
+ false /*playSpeech*/,
+ true /*speechFocus*/,
+ false /*pauseOnDuck*/);
+ }
+
+ /**
+ * Test there is no delayed focus loss when focus loser had requested focus specifying
+ * it pauses on duck
+ * @throws Exception
+ */
+ @AppModeFull(reason = "Instant apps cannot access the SD card")
+ public void testAudioFocusRequestMediaGainLossWithPauseOnDuckFocusRequest() throws Exception {
+ if (hasAutomotiveFeature(getContext())) {
+ Log.i(TAG, "Test testAudioFocusRequestMediaGainLossWithSpeechPlayer "
+ + "skipped: not required for Auto platform");
+ return;
+ }
+ doTwoFocusOwnerOnePlayerFocusLoss(
+ false /*playSpeech*/,
+ false /*speechFocus*/,
+ true /*pauseOnDuck*/);
+ }
+
+ private void doTwoFocusOwnerOnePlayerFocusLoss(boolean playSpeech, boolean speechFocus,
+ boolean pauseOnDuck) throws Exception {
+ // for query of fade out duration and focus request/abandon test methods
+ getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ Manifest.permission.QUERY_AUDIO_STATE);
+
+ final int NB_FOCUS_OWNERS = 2;
+ final AudioFocusRequest[] focusRequests = new AudioFocusRequest[NB_FOCUS_OWNERS];
+ final FocusChangeListener[] focusListeners = new FocusChangeListener[NB_FOCUS_OWNERS];
+ // index of focus owner to be tested, has an active player
+ final int FOCUS_UNDER_TEST = 0;
+ // index of focus requester used to simulate a request coming from another client
+ // on a different UID than CTS
+ final int FOCUS_SIMULATED = 1;
+
+ final HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ final Handler handler = new Handler(handlerThread.getLooper());
+
+ final AudioAttributes focusAttributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .setContentType(playSpeech ? AudioAttributes.CONTENT_TYPE_SPEECH
+ : AudioAttributes.CONTENT_TYPE_MUSIC)
+ .build();
+ final AudioAttributes playerAttributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .setContentType(speechFocus ? AudioAttributes.CONTENT_TYPE_SPEECH
+ : AudioAttributes.CONTENT_TYPE_MUSIC)
+ .build();
+ for (int focusIndex : new int[]{ FOCUS_UNDER_TEST, FOCUS_SIMULATED }) {
+ focusListeners[focusIndex] = new FocusChangeListener();
+ focusRequests[focusIndex] = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
+ .setAudioAttributes(focusIndex == FOCUS_UNDER_TEST ? playerAttributes
+ : focusAttributes)
+ .setWillPauseWhenDucked(pauseOnDuck)
+ .setOnAudioFocusChangeListener(focusListeners[focusIndex], handler)
+ .build();
+ }
+ final AudioManager am = new AudioManager(getContext());
+
+ MediaPlayer mp = null;
+ final String simFocusClientId = "fakeClientId";
+ try {
+ // set up the test conditions: a focus owner is playing media on a MediaPlayer
+ mp = createPreparedMediaPlayer(
+ Uri.fromFile(new File(WorkDir.getMediaDirString() + "sine1khzs40dblong.mp3")),
+ playerAttributes);
+ int res = am.requestAudioFocus(focusRequests[FOCUS_UNDER_TEST]);
+ assertEquals("real focus request failed",
+ AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ mp.start();
+ Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+
+ res = am.requestAudioFocusForTest(focusRequests[FOCUS_SIMULATED],
+ simFocusClientId, Integer.MAX_VALUE /*fakeClientUid*/, Build.VERSION_CODES.S);
+ assertEquals("test focus request failed",
+ AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+
+ Thread.sleep(TEST_TIMING_TOLERANCE_MS);
+ assertEquals("Focus loss not dispatched", AudioManager.AUDIOFOCUS_LOSS,
+ focusListeners[FOCUS_UNDER_TEST].getFocusChangeAndReset());
+
+ }
+ finally {
+ handler.getLooper().quit();
+ handlerThread.quitSafely();
+ if (mp != null) {
+ mp.release();
+ }
+ am.abandonAudioFocusForTest(focusRequests[FOCUS_SIMULATED], simFocusClientId);
+ am.abandonAudioFocusRequest(focusRequests[FOCUS_UNDER_TEST]);
+ getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
//-----------------------------------
// Test utilities
diff --git a/tests/tests/media/src/android/media/cts/AudioModeListenerTest.java b/tests/tests/media/src/android/media/cts/AudioModeListenerTest.java
index 5818d5a..e222e28 100644
--- a/tests/tests/media/src/android/media/cts/AudioModeListenerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioModeListenerTest.java
@@ -18,6 +18,7 @@
import android.content.pm.PackageManager;
import android.media.AudioManager;
+import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import androidx.test.filters.SdkSuppress;
@@ -95,6 +96,7 @@
}
}
+ @AppModeFull(reason = "Instant apps don't have MODIFY_AUDIO_SETTINGS")
public void testModeListener() throws Exception {
if (!isValidPlatform("testModeListener")) return;
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 475fced..35597b1 100755
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1857,6 +1857,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
@@ -1878,7 +1989,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
@@ -1886,76 +1997,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
}
}
}
@@ -1993,7 +2042,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) {
@@ -2001,93 +2052,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
}
}
}
@@ -3246,6 +3218,52 @@
}
}
+ @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/MediaCasTest.java b/tests/tests/media/src/android/media/cts/MediaCasTest.java
index 854828b..a5ab09b 100644
--- a/tests/tests/media/src/android/media/cts/MediaCasTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCasTest.java
@@ -192,9 +192,13 @@
if (!MediaCas.isSystemIdSupported(CA_system_id)) {
fail("Enumerated " + descriptors[i] + " but is not supported.");
}
- mediaCas = new MediaCas(CA_system_id);
- if (mediaCas == null) {
- fail("Enumerated " + descriptors[i] + " but cannot instantiate MediaCas.");
+ try {
+ mediaCas = new MediaCas(CA_system_id);
+ } catch (UnsupportedCasException e) {
+ Log.d(TAG, "Enumerated " + descriptors[i]
+ + " but cannot instantiate MediaCas.");
+ throw new UnsupportedCasException(
+ descriptors[i] + " is enumerated, but cannot instantiate" );
}
try {
descrambler = new MediaDescrambler(CA_system_id);
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmTest.java b/tests/tests/media/src/android/media/cts/MediaDrmTest.java
index b0b7851..c14aa30 100644
--- a/tests/tests/media/src/android/media/cts/MediaDrmTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaDrmTest.java
@@ -79,6 +79,9 @@
try {
logMessages = drm.getLogMessages();
Assert.assertFalse("Empty logs", logMessages.isEmpty());
+ for (MediaDrm.LogMessage log : logMessages) {
+ Assert.assertFalse("Empty log: " + log.toString(), log.getMessage().isEmpty());
+ }
} catch (UnsupportedOperationException e) {
Log.w(TAG, scheme.toString() + ": no LogMessage support", e);
continue;
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/nativemedia/aaudio/jni/test_aaudio_mmap.cpp b/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
index f08e1df..59269f7 100644
--- a/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
+++ b/tests/tests/nativemedia/aaudio/jni/test_aaudio_mmap.cpp
@@ -72,3 +72,30 @@
AAudioStream_close(stream);
AAudioStreamBuilder_delete(builder);
}
+
+// An application should be able to create a low-latency MMAP output stream
+// if MMAP is supported.
+TEST(test_aaudio_mmap, testBasicMmapOutput) {
+ aaudio_result_t result = AAUDIO_OK;
+ AAudioStreamBuilder *builder = nullptr;
+ AAudioStream *stream = nullptr;
+
+ bool mmapAllowed = AAudioExtensions::getInstance().isMMapSupported();
+ // If the device does not support MMAP then don't even try.
+ if (!mmapAllowed) return;
+
+ EXPECT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&builder));
+
+ // LOW_LATENCY required for MMAP
+ AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
+
+ // If we do not specify any other parameters then we should get an MMAP stream.
+ result = AAudioStreamBuilder_openStream(builder, &stream);
+ EXPECT_EQ(AAUDIO_OK, result);
+ ASSERT_TRUE(AAudioExtensions::getInstance().isMMapUsed(stream)); // MMAP?
+
+ // These should not crash if NULL.
+ AAudioStream_close(stream);
+ AAudioStreamBuilder_delete(builder);
+}
diff --git a/tests/tests/neuralnetworks/java_test/Android.bp b/tests/tests/neuralnetworks/java_test/Android.bp
new file mode 100644
index 0000000..599f650
--- /dev/null
+++ b/tests/tests/neuralnetworks/java_test/Android.bp
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+android_test {
+ name: "CtsNNAPIJavaTestCases",
+ defaults: ["cts_defaults"],
+ compile_multilib: "both",
+ static_libs: [
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "junit",
+ ],
+ jni_libs: ["libctsneuralnetworks_jni"],
+ srcs: ["src/**/*.java"],
+ asset_dirs: ["assets"],
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts",
+ "mts-neuralnetworks",
+ ],
+ sdk_version: "test_current",
+ // Do not compress model data files.
+ aaptflags: ["-0 .bin"],
+}
diff --git a/tests/tests/neuralnetworks/java_test/AndroidManifest.xml b/tests/tests/neuralnetworks/java_test/AndroidManifest.xml
new file mode 100644
index 0000000..189c520
--- /dev/null
+++ b/tests/tests/neuralnetworks/java_test/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.neuralnetworks.cts">
+
+ <application></application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.neuralnetworks.cts"
+ android:label="CTS tests of NNAPI"/>
+</manifest>
diff --git a/tests/tests/neuralnetworks/java_test/AndroidTest.xml b/tests/tests/neuralnetworks/java_test/AndroidTest.xml
new file mode 100644
index 0000000..f881e59
--- /dev/null
+++ b/tests/tests/neuralnetworks/java_test/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?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="Configuration for CTS NNAPI Tests">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="neuralnetworks" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsNNAPIJavaTestCases.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.neuralnetworks.cts" />
+ </test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.neuralnetworks" />
+ </object>
+</configuration>
diff --git a/tests/tests/neuralnetworks/java_test/assets/model_data.bin b/tests/tests/neuralnetworks/java_test/assets/model_data.bin
new file mode 100644
index 0000000..2945dbe
--- /dev/null
+++ b/tests/tests/neuralnetworks/java_test/assets/model_data.bin
Binary files differ
diff --git a/tests/tests/neuralnetworks/java_test/jni/Android.bp b/tests/tests/neuralnetworks/java_test/jni/Android.bp
new file mode 100644
index 0000000..a4c8a45
--- /dev/null
+++ b/tests/tests/neuralnetworks/java_test/jni/Android.bp
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+cc_test_library {
+ name: "libctsneuralnetworks_jni",
+ gtest: false,
+ srcs: [
+ "android_neuralnetworks_cts_ModelAssetTest.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ header_libs: [
+ "jni_headers",
+ "libneuralnetworks_headers_ndk",
+ ],
+ static_libs: [
+ "libbase_ndk",
+ ],
+ shared_libs: [
+ "libandroid",
+ "liblog",
+ "libneuralnetworks",
+ ],
+ stl: "c++_static",
+ sdk_version: "current",
+}
diff --git a/tests/tests/neuralnetworks/java_test/jni/Utils.h b/tests/tests/neuralnetworks/java_test/jni/Utils.h
new file mode 100644
index 0000000..4f088db
--- /dev/null
+++ b/tests/tests/neuralnetworks/java_test/jni/Utils.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef CTS_TESTS_TESTS_NEURALNETWORKS_JAVA_TEST_JNI_UTILS_H
+#define CTS_TESTS_TESTS_NEURALNETWORKS_JAVA_TEST_JNI_UTILS_H
+
+#include <android/log.h>
+#include <jni.h>
+
+#include <cstdlib>
+#include <cstring>
+
+// NOTE: All of the following macros requires a local variable `env` of type `JNIEnv*`.
+
+// Check the condition, signal a test failure and return if the condition is not met.
+#define ASSERT_OR_RETURN(condition) \
+ if ((condition)) \
+ ; \
+ else if (!::android::nn::cts::fail(env, "assert failed on (%s) at %s:%d", #condition, \
+ __FILE__, __LINE__)) \
+ ; \
+ else \
+ return
+
+#define ASSERT_OR_RETURN_FALSE(condition) ASSERT_OR_RETURN((condition)) false
+#define ASSERT_OR_RETURN_NULL(condition) ASSERT_OR_RETURN((condition)) nullptr
+#define ASSERT_OR_RETURN_ZERO(condition) ASSERT_OR_RETURN((condition)) 0
+#define ASSERT_OR_RETURN_DEFAULT(condition) \
+ ASSERT_OR_RETURN((condition)) {}
+
+// Check and return if there is already a test failure
+#define RETURN_IF_FAILED \
+ if (!::android::nn::cts::failed(env)) \
+ ; \
+ else \
+ return
+
+namespace android::nn::cts {
+
+// Check if there is already a test failure
+inline bool failed(JNIEnv* env) {
+ return env->ExceptionCheck();
+}
+
+// Throw a java exception to signal a test failure
+// Will always return true for ASSERT_OR_RETURN
+inline bool fail(JNIEnv* env, const char* format, ...) {
+ // Return if a test failure is already signaled
+ if (failed(env)) return true;
+
+ // Construct error message
+ va_list args;
+ va_start(args, format);
+ char* msg;
+ vasprintf(&msg, format, args);
+ va_end(args);
+
+ // Throw exception
+ jclass testFailureClass = env->FindClass("android/neuralnetworks/cts/TestFailure");
+ env->ThrowNew(testFailureClass, msg);
+ free(msg);
+ return true;
+}
+
+} // namespace android::nn::cts
+
+#endif // CTS_TESTS_TESTS_NEURALNETWORKS_JAVA_TEST_JNI_UTILS_H
diff --git a/tests/tests/neuralnetworks/java_test/jni/android_neuralnetworks_cts_ModelAssetTest.cpp b/tests/tests/neuralnetworks/java_test/jni/android_neuralnetworks_cts_ModelAssetTest.cpp
new file mode 100644
index 0000000..c1ee8e7
--- /dev/null
+++ b/tests/tests/neuralnetworks/java_test/jni/android_neuralnetworks_cts_ModelAssetTest.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "ModelAssetTest"
+
+#include <NeuralNetworks.h>
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/unique_fd.h>
+#include <android/asset_manager_jni.h>
+#include <android/sharedmem.h>
+#include <fcntl.h>
+#include <jni.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Utils.h"
+
+namespace {
+
+using namespace android::nn::cts;
+
+// A map from data type to element size in bytes.
+// The keys only include the primary data types that will be used to construct a model
+// in createAndCompileAddModel. The data types are selected in a way that a driver is likely to
+// support at least one of the data types.
+const std::map<int, uint32_t> kDataTypeToElementSizeMap = {
+ {ANEURALNETWORKS_TENSOR_FLOAT16, 2},
+ {ANEURALNETWORKS_TENSOR_FLOAT32, 4},
+ {ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, 1},
+ {ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED, 1},
+};
+
+bool isQuantizedType(int dataType) {
+ return dataType == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM ||
+ dataType == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED;
+}
+
+std::vector<const char*> getNnapiDeviceNames(JNIEnv* env) {
+ // Get the number of available NNAPI devices
+ uint32_t numDevices = 0;
+ ASSERT_OR_RETURN_DEFAULT(ANeuralNetworks_getDeviceCount(&numDevices) ==
+ ANEURALNETWORKS_NO_ERROR);
+
+ std::vector<const char*> deviceNames(numDevices, nullptr);
+ for (uint32_t i = 0; i < numDevices; i++) {
+ // Get device
+ ANeuralNetworksDevice* currentDevice;
+ ASSERT_OR_RETURN_DEFAULT(ANeuralNetworks_getDevice(/*devIndex=*/i, ¤tDevice) ==
+ ANEURALNETWORKS_NO_ERROR);
+
+ // Get device name
+ const char* deviceName = nullptr;
+ ASSERT_OR_RETURN_DEFAULT(ANeuralNetworksDevice_getName(currentDevice, &deviceName) ==
+ ANEURALNETWORKS_NO_ERROR);
+ deviceNames[i] = deviceName;
+ }
+ return deviceNames;
+}
+
+int64_t getDeviceFeatureLevel(JNIEnv* env, const ANeuralNetworksDevice* device) {
+ int64_t featureLevel;
+ ASSERT_OR_RETURN_ZERO(ANeuralNetworksDevice_getFeatureLevel(device, &featureLevel) ==
+ ANEURALNETWORKS_NO_ERROR);
+ return featureLevel;
+}
+
+const ANeuralNetworksDevice* findDevice(JNIEnv* env, const std::string& name) {
+ // Get the number of available NNAPI devices
+ uint32_t numDevices = 0;
+ ASSERT_OR_RETURN_NULL(ANeuralNetworks_getDeviceCount(&numDevices) == ANEURALNETWORKS_NO_ERROR);
+
+ for (uint32_t i = 0; i < numDevices; i++) {
+ // Get device
+ ANeuralNetworksDevice* currentDevice;
+ ASSERT_OR_RETURN_NULL(ANeuralNetworks_getDevice(/*devIndex=*/i, ¤tDevice) ==
+ ANEURALNETWORKS_NO_ERROR);
+
+ // Get device name
+ const char* deviceName = nullptr;
+ ASSERT_OR_RETURN_NULL(ANeuralNetworksDevice_getName(currentDevice, &deviceName) ==
+ ANEURALNETWORKS_NO_ERROR);
+
+ // Return if name matches
+ if (name == deviceName) {
+ return currentDevice;
+ }
+ }
+ fail(env, "No device found with name %s", name.c_str());
+ return nullptr;
+}
+
+// Create a NNAPI memory directly from the asset file
+ANeuralNetworksMemory* createMemoryFromAsset(JNIEnv* env, AAsset* asset) {
+ // Open the asset file as a file descriptor
+ off_t offset, length;
+ android::base::unique_fd assetFd(AAsset_openFileDescriptor(asset, &offset, &length));
+ ASSERT_OR_RETURN_NULL(assetFd.ok());
+
+ // Create NNAPI memory from the asset file
+ ANeuralNetworksMemory* memory;
+ ASSERT_OR_RETURN_NULL(ANeuralNetworksMemory_createFromFd(length, PROT_READ, assetFd.get(),
+ offset,
+ &memory) == ANEURALNETWORKS_NO_ERROR);
+ return memory;
+}
+
+// Copy the content of the asset file to an ashmem, and create a NNAPI memory from the ashmem
+ANeuralNetworksMemory* createMemoryFromAshmem(JNIEnv* env, AAsset* asset) {
+ // Create an ashmem
+ off_t length = AAsset_getLength(asset);
+ android::base::unique_fd ashmemFd(ASharedMemory_create("model_data", length));
+ ASSERT_OR_RETURN_NULL(ashmemFd.ok());
+
+ // Copy the content of the asset file to the ashmem
+ void* ashmemData =
+ mmap(nullptr, length, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd.get(), /*offset=*/0);
+ ASSERT_OR_RETURN_NULL(ashmemData != nullptr);
+ int bytesRead = AAsset_read(asset, ashmemData, length);
+ munmap(ashmemData, length);
+ ASSERT_OR_RETURN_NULL(bytesRead == length);
+
+ // Create NNAPI memory from the ashmem
+ ANeuralNetworksMemory* memory;
+ ASSERT_OR_RETURN_NULL(ANeuralNetworksMemory_createFromFd(length, PROT_READ, ashmemFd.get(),
+ /*offset=*/0,
+ &memory) == ANEURALNETWORKS_NO_ERROR);
+ return memory;
+}
+
+// Create and compile a model with a single ADD operation. We choose the ADD operation because
+// it is commonly supported on most of the devices.
+// The input dataType must be one of the keys in kDataTypeToElementSizeMap.
+// The value of the constant tensor will be loaded from the given memory with offset = 0.
+// This method returns whether the compilation is successful or not.
+bool createAndCompileAddModel(JNIEnv* env, int dataType, const ANeuralNetworksMemory* memory,
+ const ANeuralNetworksDevice* device) {
+ ASSERT_OR_RETURN_FALSE(kDataTypeToElementSizeMap.count(dataType) > 0);
+ const uint32_t tensorDims[] = {1, 256, 256, 4};
+ const uint32_t tensorSize = 1 * 256 * 256 * 4 * kDataTypeToElementSizeMap.at(dataType);
+ const uint32_t activation = 0;
+
+ // Create model
+ ANeuralNetworksModel* model = nullptr;
+ ASSERT_OR_RETURN_FALSE(ANeuralNetworksModel_create(&model) == ANEURALNETWORKS_NO_ERROR);
+ auto modelGuard =
+ android::base::make_scope_guard([model]() { ANeuralNetworksModel_free(model); });
+
+ // Operand type for tensors
+ ANeuralNetworksOperandType tensorType;
+ tensorType.type = dataType;
+ tensorType.scale = isQuantizedType(dataType) ? 1.0f : 0.0f;
+ tensorType.zeroPoint = 0;
+ tensorType.dimensionCount = 4;
+ tensorType.dimensions = tensorDims;
+
+ // Operand type for activation
+ ANeuralNetworksOperandType activationType;
+ activationType.type = ANEURALNETWORKS_INT32;
+ activationType.scale = 0.0f;
+ activationType.zeroPoint = 0;
+ activationType.dimensionCount = 0;
+ activationType.dimensions = nullptr;
+
+ // Operands
+ ASSERT_OR_RETURN_FALSE(ANeuralNetworksModel_addOperand(model, &tensorType) ==
+ ANEURALNETWORKS_NO_ERROR);
+ ASSERT_OR_RETURN_FALSE(ANeuralNetworksModel_addOperand(model, &tensorType) ==
+ ANEURALNETWORKS_NO_ERROR);
+ ASSERT_OR_RETURN_FALSE(ANeuralNetworksModel_addOperand(model, &activationType) ==
+ ANEURALNETWORKS_NO_ERROR);
+ ASSERT_OR_RETURN_FALSE(ANeuralNetworksModel_addOperand(model, &tensorType) ==
+ ANEURALNETWORKS_NO_ERROR);
+
+ // Constant values
+ ASSERT_OR_RETURN_FALSE(
+ ANeuralNetworksModel_setOperandValueFromMemory(model, /*index=*/1, memory, /*offset=*/0,
+ tensorSize) == ANEURALNETWORKS_NO_ERROR);
+ ASSERT_OR_RETURN_FALSE(ANeuralNetworksModel_setOperandValue(model, /*index=*/2, &activation,
+ sizeof(int32_t)) ==
+ ANEURALNETWORKS_NO_ERROR);
+
+ // ADD operation
+ uint32_t operation0InputIndexes[] = {0, 1, 2};
+ uint32_t operation0OutputIndexes[] = {3};
+ ASSERT_OR_RETURN_FALSE(ANeuralNetworksModel_addOperation(model, ANEURALNETWORKS_ADD, 3,
+ operation0InputIndexes, 1,
+ operation0OutputIndexes) ==
+ ANEURALNETWORKS_NO_ERROR);
+
+ // Model inputs and outputs
+ uint32_t modelInputIndexes[] = {0};
+ uint32_t modelOutputIndexes[] = {3};
+ ASSERT_OR_RETURN_FALSE(ANeuralNetworksModel_identifyInputsAndOutputs(model, 1,
+ modelInputIndexes, 1,
+ modelOutputIndexes) ==
+ ANEURALNETWORKS_NO_ERROR);
+
+ // Finish the model
+ ASSERT_OR_RETURN_FALSE(
+ ANeuralNetworksModel_relaxComputationFloat32toFloat16(model, /*allow=*/true) ==
+ ANEURALNETWORKS_NO_ERROR);
+ ASSERT_OR_RETURN_FALSE(ANeuralNetworksModel_finish(model) == ANEURALNETWORKS_NO_ERROR);
+
+ // Create compilation
+ ANeuralNetworksCompilation* compilation;
+ ASSERT_OR_RETURN_FALSE(
+ ANeuralNetworksCompilation_createForDevices(model, &device, /*numDevices=*/1,
+ &compilation) == ANEURALNETWORKS_NO_ERROR);
+ auto compilationGuard = android::base::make_scope_guard(
+ [compilation]() { ANeuralNetworksCompilation_free(compilation); });
+
+ // Compile
+ return ANeuralNetworksCompilation_finish(compilation) == ANEURALNETWORKS_NO_ERROR;
+}
+
+} // namespace
+
+extern "C" JNIEXPORT jobjectArray JNICALL
+Java_android_neuralnetworks_cts_ModelAssetTest_getNnapiDevices(JNIEnv* env, jobject /* this */) {
+ // Get device names
+ auto deviceNames = getNnapiDeviceNames(env);
+ RETURN_IF_FAILED nullptr;
+
+ // Convert to Java string array
+ jclass stringClass = env->FindClass("java/lang/String");
+ jobjectArray jDeviceNames = env->NewObjectArray(deviceNames.size(), stringClass, nullptr);
+ for (uint32_t i = 0; i < deviceNames.size(); i++) {
+ jstring jDeviceName = env->NewStringUTF(deviceNames[i]);
+ env->SetObjectArrayElement(jDeviceNames, i, jDeviceName);
+ }
+ return jDeviceNames;
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_android_neuralnetworks_cts_ModelAssetTest_nativeTestCompileFromAssetFile(JNIEnv* env,
+ jobject /* this */,
+ jobject jAssetManager,
+ jstring jDeviceName) {
+ AAssetManager* assetManager = AAssetManager_fromJava(env, jAssetManager);
+ const char* deviceNameCStr = env->GetStringUTFChars(jDeviceName, nullptr);
+ std::string deviceName = deviceNameCStr;
+ env->ReleaseStringUTFChars(jDeviceName, deviceNameCStr);
+
+ // Find the NNAPI device
+ const auto* device = findDevice(env, deviceName);
+ RETURN_IF_FAILED;
+
+ // Check device feature level. This test is added in NNAPI feature level 5, so skip if the
+ // device is of a lower feature level.
+ const int64_t featureLevel = getDeviceFeatureLevel(env, device);
+ RETURN_IF_FAILED;
+ if (featureLevel < ANEURALNETWORKS_FEATURE_LEVEL_5) return;
+
+ // Open the asset file
+ AAsset* asset = AAssetManager_open(assetManager, "model_data.bin", AASSET_MODE_BUFFER);
+ ASSERT_OR_RETURN(asset != nullptr);
+ auto assetGuard = android::base::make_scope_guard([asset]() { AAsset_close(asset); });
+
+ // Create NNAPI memory directly from asset file
+ auto* memoryFromAsset = createMemoryFromAsset(env, asset);
+ auto memoryFromAssetGuard = android::base::make_scope_guard(
+ [memoryFromAsset]() { ANeuralNetworksMemory_free(memoryFromAsset); });
+ RETURN_IF_FAILED;
+
+ // Create NNAPI memory from ashmem
+ auto* memoryFromAshmem = createMemoryFromAshmem(env, asset);
+ auto memoryFromAshmemGuard = android::base::make_scope_guard(
+ [memoryFromAshmem]() { ANeuralNetworksMemory_free(memoryFromAshmem); });
+ RETURN_IF_FAILED;
+
+ // Compile the model with both memories, we expect the compilation results are the same
+ for (const auto& [dataType, _] : kDataTypeToElementSizeMap) {
+ bool successWithAshmem = createAndCompileAddModel(env, dataType, memoryFromAshmem, device);
+ RETURN_IF_FAILED;
+ bool successWithAsset = createAndCompileAddModel(env, dataType, memoryFromAsset, device);
+ RETURN_IF_FAILED;
+ ASSERT_OR_RETURN(successWithAshmem == successWithAsset);
+ }
+}
diff --git a/tests/tests/neuralnetworks/java_test/src/android/neuralnetworks/cts/ModelAssetTest.java b/tests/tests/neuralnetworks/java_test/src/android/neuralnetworks/cts/ModelAssetTest.java
new file mode 100644
index 0000000..37a37c5
--- /dev/null
+++ b/tests/tests/neuralnetworks/java_test/src/android/neuralnetworks/cts/ModelAssetTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.neuralnetworks.cts;
+
+import android.content.res.AssetManager;
+import androidx.test.InstrumentationRegistry;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ModelAssetTest {
+ static {
+ System.loadLibrary("ctsneuralnetworks_jni");
+ }
+
+ @Parameterized.Parameter(0)
+ public String mDeviceName;
+
+ @Parameters(name = "{0}")
+ public static List<String> deviceNameList() {
+ return Arrays.asList(getNnapiDevices());
+ }
+
+ @Test
+ public void testCompileModelFromAsset() {
+ AssetManager assets = InstrumentationRegistry.getContext().getAssets();
+ nativeTestCompileFromAssetFile(assets, mDeviceName);
+ }
+
+ private static native String[] getNnapiDevices();
+
+ private static native void nativeTestCompileFromAssetFile(
+ AssetManager jAssetManager, String jDeviceName);
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/client/ClientSuggestionsTest.java b/tests/tests/neuralnetworks/java_test/src/android/neuralnetworks/cts/TestFailure.java
similarity index 69%
rename from tests/autofillservice/src/android/autofillservice/cts/client/ClientSuggestionsTest.java
rename to tests/tests/neuralnetworks/java_test/src/android/neuralnetworks/cts/TestFailure.java
index 895ec3a..27fdd1a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/client/ClientSuggestionsTest.java
+++ b/tests/tests/neuralnetworks/java_test/src/android/neuralnetworks/cts/TestFailure.java
@@ -13,13 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.autofillservice.cts.client;
-import android.autofillservice.cts.commontests.ClientSuggestionsCommonTestCase;
+package android.neuralnetworks.cts;
-/**
- * Tests client suggestions behaviors for the dropdown mode.
- */
-public class ClientSuggestionsTest extends ClientSuggestionsCommonTestCase {
-
+public class TestFailure extends Exception {
+ public TestFailure(String message) {
+ super(message);
+ }
}
diff --git a/tests/tests/notificationlegacy/notificationlegacy30/src/android/app/notification/legacy30/cts/NotificationTemplateApi30Test.kt b/tests/tests/notificationlegacy/notificationlegacy30/src/android/app/notification/legacy30/cts/NotificationTemplateApi30Test.kt
index 84a05c3..5b70b2e 100644
--- a/tests/tests/notificationlegacy/notificationlegacy30/src/android/app/notification/legacy30/cts/NotificationTemplateApi30Test.kt
+++ b/tests/tests/notificationlegacy/notificationlegacy30/src/android/app/notification/legacy30/cts/NotificationTemplateApi30Test.kt
@@ -18,7 +18,6 @@
import android.R
import android.app.Notification
import android.app.cts.NotificationTemplateTestBase
-import android.graphics.Bitmap
import android.view.View
import android.widget.ImageView
import android.widget.TextView
@@ -31,7 +30,7 @@
}
fun testWideIcon_inCollapsedState_isSquareForLegacyApps() {
- val icon = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(200, 100)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -44,7 +43,7 @@
}
fun testWideIcon_inBigBaseState_isSquareForLegacyApps() {
- val icon = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
+ val icon = createBitmap(200, 100)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -57,8 +56,8 @@
}
fun testWideIcon_inBigPicture_isSquareForLegacyApps() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
- val icon = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
+ val icon = createBitmap(200, 100)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -72,7 +71,7 @@
}
fun testWideIcon_inBigText_isSquareForLegacyApps() {
- val bitmap = Bitmap.createBitmap(200, 100, Bitmap.Config.ARGB_8888)
+ val bitmap = createBitmap(200, 100)
val views = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -86,7 +85,7 @@
}
fun testPromoteBigPicture_withoutLargeIcon() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -110,8 +109,8 @@
}
fun testPromoteBigPicture_withLargeIcon() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
- val icon = Bitmap.createBitmap(80, 65, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
+ val icon = createBitmap(80, 65)
val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
@@ -139,8 +138,8 @@
}
fun testPromoteBigPicture_withBigLargeIcon() {
- val picture = Bitmap.createBitmap(40, 30, Bitmap.Config.ARGB_8888)
- val bigIcon = Bitmap.createBitmap(80, 75, Bitmap.Config.ARGB_8888)
+ val picture = createBitmap(40, 30)
+ val bigIcon = createBitmap(80, 75)
val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
.setContentTitle("Title")
diff --git a/tests/tests/os/src/android/os/cts/AppHibernationIntegrationTest.kt b/tests/tests/os/src/android/os/cts/AppHibernationIntegrationTest.kt
index 7b0539f..72b98cf 100644
--- a/tests/tests/os/src/android/os/cts/AppHibernationIntegrationTest.kt
+++ b/tests/tests/os/src/android/os/cts/AppHibernationIntegrationTest.kt
@@ -23,6 +23,7 @@
import android.content.pm.PackageManager
import android.os.Build
import android.net.Uri
+import android.platform.test.annotations.AppModeFull
import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
import android.provider.Settings
import android.support.test.uiautomator.By
@@ -133,6 +134,7 @@
}
}
+ @AppModeFull(reason = "Uses application details settings")
@Test
fun testAppInfo_RemovePermissionsAndFreeUpSpaceToggleExists() {
withDeviceConfig(NAMESPACE_APP_HIBERNATION, "app_hibernation_enabled", "true") {
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index 226c528..35b56ce 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -138,9 +138,17 @@
// Verify
assertPermission(PERMISSION_DENIED)
- runShellCommandOrThrow("cmd statusbar expand-notifications")
+ if (hasFeatureWatch()) {
+ expandNotificationsWatch()
+ } else {
+ runShellCommandOrThrow("cmd statusbar expand-notifications")
+ }
waitFindObject(By.textContains("unused app"))
.click()
+ if (hasFeatureWatch()) {
+ // In wear os, notification has one additional button to open it
+ waitFindObject(By.text("Open")).click();
+ }
waitFindObject(By.text(supportedAppPackageName))
waitFindObject(By.text("Calendar permission removed"))
}
@@ -406,6 +414,15 @@
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
}
+ private fun expandNotificationsWatch() {
+ with(uiDevice) {
+ wakeUp()
+ // Swipe up from bottom to reveal notifications
+ val x = displayWidth / 2
+ swipe(x, displayHeight, x, 0, 1)
+ }
+ }
+
private fun assertAllowlistState(state: Boolean) {
assertThat(
waitFindObject(By.textStartsWith("Auto-revoke allowlisted: ")).text,
diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/DenyInstallationTestWatch.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/DenyInstallationTestWatch.kt
new file mode 100644
index 0000000..9cdb8b9
--- /dev/null
+++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/DenyInstallationTestWatch.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.app.Activity
+import android.os.Build
+import android.platform.test.annotations.AppModeFull
+import android.support.test.uiautomator.By
+import android.support.test.uiautomator.BySelector
+import android.support.test.uiautomator.UiDevice
+import android.support.test.uiautomator.Until
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.concurrent.TimeUnit
+
+private const val ERROR_MESSAGE_ID = "android:id/message"
+private const val OK_BUTTON_ID = "button1"
+
+@AppModeFull(reason = "Instant apps cannot create installer sessions")
+@RunWith(AndroidJUnit4::class)
+class DenyInstallationTestWatch : PackageInstallerTestBase() {
+
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val uiDevice = UiDevice.getInstance(instrumentation)
+
+ /**
+ * Check that apps cannot be installed on watches via a package-installer session
+ */
+ @Test
+ fun confirmSessionInstallationFails() {
+ assumeWatch()
+
+ val installation = startInstallationViaSession()
+ assertWearErrorDialogShown("Install blocking dialog not shown for install via session")
+
+ // Install confirm dialog returns 'canceled' after getting dismissed
+ assertEquals(Activity.RESULT_CANCELED, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
+
+ // Make sure app is not installed
+ assertNotInstalled()
+ }
+
+ /**
+ * Check that apps cannot be installed on watches via a package-installer intent
+ */
+ @Test
+ fun confirmIntentInstallationFails() {
+ assumeWatch()
+
+ val installation = startInstallationViaIntent()
+ assertWearErrorDialogShown("Install blocking dialog not shown for install via intent")
+
+ // Install confirm dialog returns 'canceled' after getting dismissed
+ assertEquals(Activity.RESULT_CANCELED, installation.get(TIMEOUT, TimeUnit.MILLISECONDS))
+
+ // Make sure app is not installed
+ assertNotInstalled()
+ }
+
+ /**
+ * Test suite will need to be updated for S, this test checks this fact.
+ */
+ @Test
+ fun confirmPlatformVersionLessThanS() {
+ assumeWatch()
+
+ assertTrue(
+ "Must revisit method for Apk blocking for watches using Android S+, see b/187944296",
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.R
+ )
+ }
+
+ /**
+ * Click the Ok button in the alert dialog blocking installation
+ */
+ private fun clickOkButton() {
+ uiDevice.wait(Until.findObject(By.res(SYSTEM_PACKAGE_NAME, OK_BUTTON_ID)), TIMEOUT)
+ .click()
+ }
+
+ private fun assertUiObject(errorMessage: String, selector: BySelector) {
+ Assert.assertNotNull(
+ "$errorMessage after $TIMEOUT ms",
+ uiDevice.wait(Until.findObject(selector), TIMEOUT)
+ )
+ }
+
+ private fun assertWearErrorDialogShown(errorMessage: String) {
+ assertUiObject(errorMessage, By.res(ERROR_MESSAGE_ID))
+ clickOkButton()
+ }
+}
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 f006c1b..6fc29db 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
@@ -41,6 +41,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
@@ -227,4 +229,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 8df0c6b..e81f1d1 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/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index d38b063..bb409db 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -43,6 +43,7 @@
<protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED_INTERNAL" />
<protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_LOADED" />
<protected-broadcast android:name="android.intent.action.PACKAGE_ENABLE_ROLLBACK" />
<protected-broadcast android:name="android.intent.action.CANCEL_ENABLE_ROLLBACK" />
<protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" />
@@ -409,6 +410,8 @@
<protected-broadcast android:name="android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED" />
<protected-broadcast android:name="android.net.conn.TETHER_STATE_CHANGED" />
<protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" />
+ <!-- This broadcast is no longer sent in S but it should stay protected to avoid third party
+ apps broadcasting this and confusing old system apps that may not have been updated. -->
<protected-broadcast android:name="android.net.conn.NETWORK_CONDITIONS_MEASURED" />
<protected-broadcast
android:name="android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED" />
@@ -590,8 +593,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" />
@@ -693,6 +694,8 @@
<protected-broadcast android:name="android.scheduling.action.REBOOT_READY" />
<protected-broadcast android:name="android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED" />
+ <protected-broadcast android:name="android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -2895,6 +2898,12 @@
android:description="@string/permdesc_runInBackground"
android:protectionLevel="normal" />
+ <!-- Allows a companion app to start a foreground service from the background.
+ {@see android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND}
+ -->
+ <permission android:name="android.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND"
+ android:protectionLevel="normal"/>
+
<!-- Allows a companion app to use data in the background.
<p>Protection level: normal
-->
@@ -3946,7 +3955,7 @@
<!-- This permission is required among systems services when accessing
tuner resource management related APIs or information.
- <p>Protection level: signature|privileged|vendorPrivileged
+ <p>Protection level: signature|privileged|vendorPrivileged
<p>This should only be used by the OEM TvInputService.
@hide -->
<permission android:name="android.permission.TUNER_RESOURCE_ACCESS"
@@ -5752,10 +5761,10 @@
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.ACCESS_TUNED_INFO"
- android:protectionLevel="signature|privileged|vendorPrivileged" />
+ android:protectionLevel="signature|privileged|vendorPrivileged" />
<!-- Allows an application to indicate via
- {@link android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(boolean)}
+ {@link android.content.pm.PackageInstaller.SessionParams#setRequireUserAction(int)}
that user action should not be required for an app update.
<p>Protection level: normal
-->
@@ -5763,6 +5772,21 @@
android:protectionLevel="normal" />
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"/>
+ <!-- Allows an application to take screenshots of layers that normally would be blacked out when
+ a screenshot is taken. Specifically, layers that have the flag
+ {@link android.view.SurfaceControl#SECURE} will be screenshot if the caller requests to
+ capture secure layers. Normally those layers will be rendered black.
+ <p>Not for use by third-party applications.
+ @hide
+ -->
+ <permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT"
+ android:protectionLevel="signature" />
+
+ <!-- @SystemApi Allows an application to query over global data in AppSearch.
+ @hide -->
+ <permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA"
+ android:protectionLevel="internal|role" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
@@ -6011,6 +6035,12 @@
android:exported="false">
</activity>
+ <activity android:name="com.android.server.notification.NASLearnMoreActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:exported="false">
+ </activity>
+
<receiver android:name="com.android.server.BootReceiver"
android:exported="true"
android:systemUserOnly="true">
diff --git a/tests/tests/permission3/Android.bp b/tests/tests/permission3/Android.bp
index fa0cea5..d60c44e 100644
--- a/tests/tests/permission3/Android.bp
+++ b/tests/tests/permission3/Android.bp
@@ -32,6 +32,7 @@
"androidx.test.rules",
"compatibility-device-util-axt",
"ctstestrunner-axt",
+ "bluetooth-test-util-lib",
],
data: [
":CtsPermissionPolicyApp25",
diff --git a/tests/tests/permission3/AndroidManifest.xml b/tests/tests/permission3/AndroidManifest.xml
index 31e7e71..2192d13 100644
--- a/tests/tests/permission3/AndroidManifest.xml
+++ b/tests/tests/permission3/AndroidManifest.xml
@@ -22,6 +22,7 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application>
diff --git a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
index 0c59dc5..a7b4709 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
@@ -127,6 +127,11 @@
return UiAutomatorUtils.waitFindObject(selector, timeoutMillis)
}
+ protected fun waitFindObjectOrNull(selector: BySelector, timeoutMillis: Long): UiObject2? {
+ waitForIdle()
+ return UiAutomatorUtils.waitFindObjectOrNull(selector, timeoutMillis)
+ }
+
protected fun click(selector: BySelector, timeoutMillis: Long = 20_000) {
waitFindObject(selector, timeoutMillis).click()
waitForIdle()
diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
index 9b229f6..07245ec 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
@@ -157,7 +157,7 @@
click(By.res("android:id/button1"))
protected fun clickPermissionReviewContinue() {
- if (isAutomotive) {
+ if (isAutomotive || isWatch) {
click(By.text(getPermissionControllerString("review_button_continue")))
} else {
click(By.res("com.android.permissioncontroller:id/continue_button"))
@@ -165,7 +165,7 @@
}
protected fun clickPermissionReviewCancel() {
- if (isAutomotive) {
+ if (isAutomotive || isWatch) {
click(By.text(getPermissionControllerString("review_button_cancel")))
} else {
click(By.res("com.android.permissioncontroller:id/cancel_button"))
@@ -296,7 +296,7 @@
}
protected fun clickAllowAlwaysInSettings() {
- if (isAutomotive || isTv) {
+ if (isAutomotive || isTv || isWatch) {
click(By.text(getPermissionControllerString("app_permission_button_allow_always")))
} else {
click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
@@ -313,7 +313,7 @@
}
protected fun clickPermissionRequestDenyButton() {
- if (isAutomotive) {
+ if (isAutomotive || isWatch) {
click(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
} else {
click(By.res(DENY_BUTTON))
@@ -322,7 +322,7 @@
protected fun clickPermissionRequestSettingsLinkAndDeny() {
clickPermissionRequestSettingsLink()
- if (isAutomotive) {
+ if (isAutomotive || isWatch) {
click(By.text(getPermissionControllerString("app_permission_button_deny")))
} else {
click(By.res("com.android.permissioncontroller:id/deny_radio_button"))
@@ -356,14 +356,23 @@
protected fun clickPermissionRequestDenyAndDontAskAgainButton() {
if (isAutomotive) {
click(By.text(getPermissionControllerString(DENY_AND_DONT_ASK_AGAIN_BUTTON_TEXT)))
+ } else if (isWatch) {
+ click(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
} else {
click(By.res(DENY_AND_DONT_ASK_AGAIN_BUTTON))
}
}
// Only used in TV and Watch form factors
- protected fun clickPermissionRequestDontAskAgainButton() =
- click(By.res("com.android.permissioncontroller:id/permission_deny_dont_ask_again_button"))
+ protected fun clickPermissionRequestDontAskAgainButton() {
+ if (isWatch) {
+ click(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)))
+ } else {
+ click(
+ By.res("com.android.permissioncontroller:id/permission_deny_dont_ask_again_button")
+ )
+ }
+ }
protected fun clickPermissionRequestNoUpgradeAndDontAskAgainButton() {
if (isAutomotive) {
@@ -426,11 +435,20 @@
click(By.text(permissionLabel))
+ // Watch does not show an alert dialog when the user turns on permission, only when they
+ // turns it off.
+ if (isWatch && waitFindObjectOrNull(By.text(permissionLabel), 1000) != null) {
+ continue
+ }
+
val wasGranted = if (isAutomotive) {
// Automotive doesn't support one time permissions, and thus
// won't show an "Ask every time" message
!waitFindObject(byTextRes(R.string.deny)).isChecked
} else {
+ if (isWatch) {
+ click(By.text("Deny"))
+ }
!(waitFindObject(byTextRes(R.string.deny)).isChecked ||
(!isLegacyApp && hasAskButton(permission) &&
waitFindObject(byTextRes(R.string.ask)).isChecked))
@@ -488,7 +506,9 @@
null
)
val confirmText = resources.getString(confirmTextRes)
- click(byTextStartsWithCaseInsensitive(confirmText))
+ if (!isWatch) {
+ click(byTextStartsWithCaseInsensitive(confirmText))
+ }
}
pressBack()
}
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt
index 6355ada..ab08b4e 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt
@@ -16,6 +16,7 @@
package android.permission3.cts
+import androidx.core.os.BuildCompat
import androidx.test.filters.FlakyTest
import org.junit.Assume
import org.junit.Before
@@ -181,7 +182,11 @@
// Request the permission and allow it
// Make sure the permission is granted
requestAppPermissionsAndAssertResult(android.Manifest.permission.CAMERA to true) {
- clickPermissionRequestAllowForegroundButton()
+ if (BuildCompat.isAtLeastS()) {
+ clickPermissionRequestAllowForegroundButton()
+ } else {
+ clickPermissionRequestAllowButton()
+ }
}
}
@@ -285,7 +290,11 @@
null to false,
android.Manifest.permission.RECORD_AUDIO to true
) {
- clickPermissionRequestAllowForegroundButton()
+ if (BuildCompat.isAtLeastS()) {
+ clickPermissionRequestAllowForegroundButton()
+ } else {
+ clickPermissionRequestAllowButton()
+ }
clickPermissionRequestAllowButton()
}
}
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30WithBluetooth.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30WithBluetooth.kt
index 1bed5e9..b88246b 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30WithBluetooth.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30WithBluetooth.kt
@@ -16,10 +16,17 @@
package android.permission3.cts
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothManager
+import android.bluetooth.cts.BTAdapterUtils
import android.content.Intent
+import android.content.pm.PackageManager
import androidx.core.os.BuildCompat
import androidx.test.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow
import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
@@ -30,6 +37,8 @@
class PermissionTest30WithBluetooth : BaseUsePermissionTest() {
val AUTHORITY = "android.permission3.cts.usepermission.AccessBluetoothOnCommand"
+ private lateinit var bluetoothAdapter: BluetoothAdapter
+ private var bluetoothAdapterWasEnabled: Boolean = false
private enum class BluetoothScanResult {
UNKNOWN, EXCEPTION, EMPTY, FILTERED, FULL
@@ -40,6 +49,25 @@
installPackage(APP_APK_PATH_30_WITH_BLUETOOTH)
}
+ @Before
+ fun enableBluetooth() {
+ assumeTrue(supportsBluetooth())
+ bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter
+ bluetoothAdapterWasEnabled = bluetoothAdapter.isEnabled()
+ assertTrue(BTAdapterUtils.enableAdapter(bluetoothAdapter, context))
+ runShellCommandOrThrow("dumpsys activity service" +
+ " com.android.bluetooth/.btservice.AdapterService set-test-mode enabled")
+ }
+
+ @After
+ fun disableBluetooth() {
+ assumeTrue(supportsBluetooth())
+ runShellCommandOrThrow("dumpsys activity service" +
+ " com.android.bluetooth/.btservice.AdapterService set-test-mode disabled")
+ if (!bluetoothAdapterWasEnabled)
+ assertTrue(BTAdapterUtils.disableAdapter(bluetoothAdapter, context))
+ }
+
@Test
fun testGivenBluetoothIsDeniedWhenScanIsAttemptedThenThenGetEmptyScanResult() {
assumeTrue(BuildCompat.isAtLeastS())
@@ -52,4 +80,7 @@
val result = resolver.call(AUTHORITY, "", null, null)
return BluetoothScanResult.values()[result!!.getInt(Intent.EXTRA_INDEX)]
}
+
+ private fun supportsBluetooth(): Boolean =
+ context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
}
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 cf52bf2..98fd59e 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -370,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/security/res/raw/cve_2020_11196.mp4 b/tests/tests/security/res/raw/cve_2020_11196.mp4
new file mode 100644
index 0000000..1f6bb40d
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2020_11196.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 6554369..8c086c4 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -1770,6 +1770,12 @@
***********************************************************/
@Test
+ @SecurityTest(minPatchLevel = "2020-11")
+ public void testStagefright_cve_2020_11196() throws Exception {
+ doStagefrightTest(R.raw.cve_2020_11196);
+ }
+
+ @Test
@SecurityTest(minPatchLevel = "2018-11")
public void testStagefright_cve_2018_9531() throws Exception {
assumeFalse(ModuleDetector.moduleIsPlayManaged(
diff --git a/tests/tests/sensorprivacy/TEST_MAPPING b/tests/tests/sensorprivacy/TEST_MAPPING
new file mode 100644
index 0000000..a6e6b34
--- /dev/null
+++ b/tests/tests/sensorprivacy/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsSensorPrivacyTestCases"
+ }
+ ]
+}
diff --git a/tests/tests/settings/src/android/settings/cts/SettingsHandlerTest.java b/tests/tests/settings/src/android/settings/cts/SettingsHandlerTest.java
new file mode 100644
index 0000000..c60faa4
--- /dev/null
+++ b/tests/tests/settings/src/android/settings/cts/SettingsHandlerTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.settings.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.storage.StorageManager;
+import android.provider.DocumentsContract;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Test to make there is only default handler for settings intent. */
+@RunWith(AndroidJUnit4.class)
+public class SettingsHandlerTest {
+
+ private static final String MEDIA_PROVIDER_AUTHORITY = "com.android.providers.media.documents";
+ private static final String RESOLVER_ACTIVITY_PACKAGE_NAME = "android";
+ private static final String RESOLVER_ACTIVITY_NAME =
+ "com.android.internal.app.ResolverActivity";
+
+ PackageManager mPackageManager =
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+
+ @Test
+ public void oneDefaultHandlerForVideoRoot() throws Exception {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ Uri uri = DocumentsContract.buildRootUri(MEDIA_PROVIDER_AUTHORITY, "videos_root");
+ intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
+
+ assertThat(hasDefaultHandlerForIntent(intent)).isTrue();
+ }
+
+ @Test
+ public void oneDefaultHandlerForImageRoot() throws Exception {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ Uri uri = DocumentsContract.buildRootUri(MEDIA_PROVIDER_AUTHORITY, "images_root");
+ intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
+
+ assertThat(hasDefaultHandlerForIntent(intent)).isTrue();
+ }
+
+ @Test
+ public void oneDefaultHandlerForAudioRoot() throws Exception {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ Uri uri = DocumentsContract.buildRootUri(MEDIA_PROVIDER_AUTHORITY, "audio_root");
+ intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
+
+ assertThat(hasDefaultHandlerForIntent(intent)).isTrue();
+ }
+
+ @Test
+ public void oneDefaultHandlerForDocumentRoot() throws Exception {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ Uri uri = DocumentsContract.buildRootUri(MEDIA_PROVIDER_AUTHORITY, "documents_root");
+ intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
+
+ assertThat(hasDefaultHandlerForIntent(intent)).isTrue();
+ }
+
+ @Test
+ public void oneDefaultHandlerForManageStorage() throws Exception {
+ Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+
+ assertThat(hasDefaultHandlerForIntent(intent)).isTrue();
+ }
+
+ private boolean hasDefaultHandlerForIntent(Intent intent) {
+ ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, /* flags= */ 0);
+ String packageName = resolveInfo.activityInfo.packageName;
+ String activityName = resolveInfo.activityInfo.name;
+ // If there are more than one handlers with no preferences set, the intent will resolve
+ // to com.android.internal.app.ResolverActivity.
+ return !packageName.equals(RESOLVER_ACTIVITY_PACKAGE_NAME) || !activityName.equals(
+ RESOLVER_ACTIVITY_NAME);
+ }
+}
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java
index f3319f3..c868b1e 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerClientApiTest.java
@@ -2192,8 +2192,6 @@
runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(
makeShortcut("s1"))));
- getManager().updateShortcutVisibility(
- mPackageContext2.getPackageName(), new byte[] {100}, true);
});
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
diff --git a/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java b/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java
index 1071975..29509d8 100644
--- a/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java
+++ b/tests/tests/syncmanager/src/android/content/syncmanager/cts/CtsSyncManagerTest.java
@@ -28,6 +28,7 @@
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import android.accounts.Account;
import android.app.usage.UsageStatsManager;
@@ -251,6 +252,36 @@
AmUtils.setStandbyBucket(APP1_PACKAGE, UsageStatsManager.STANDBY_BUCKET_RARE);
+ Bundle b = makeBundle(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true,
+ ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
+
+ ContentResolver.requestSync(ACCOUNT_1_A, APP1_AUTHORITY, b);
+
+ waitUntil("Expedited job sync didn't run in Doze", 30, () -> {
+ final Response res = mRpc.invoke(APP1_PACKAGE,
+ rb -> rb.setGetSyncInvocations(GetSyncInvocations.newBuilder()));
+ final int calls = res.getSyncInvocations().getSyncInvocationsCount();
+ Log.i(TAG, "NumSyncInvocations=" + calls);
+ return calls == 1;
+ });
+ }
+
+ @Test
+ public void testExpeditedJobSync_InDoze() throws Exception {
+ assumeTrue(isDozeFeatureEnabled());
+
+ setDozeState(false);
+ removeAllAccounts();
+
+ // Let the initial sync happen.
+ addAccountAndLetInitialSyncRun(ACCOUNT_1_A, APP1_AUTHORITY);
+
+ writeSyncConfig(2, 1, 2, 3);
+
+ clearSyncInvocations(APP1_PACKAGE);
+
+ AmUtils.setStandbyBucket(APP1_PACKAGE, UsageStatsManager.STANDBY_BUCKET_RARE);
+
setDozeState(true);
Bundle b = makeBundle(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, true,
ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
@@ -297,6 +328,11 @@
assertTrue(extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE));
}
+ private static boolean isDozeFeatureEnabled() {
+ final String output = ShellUtils.runShellCommand("cmd deviceidle enabled deep").trim();
+ return Integer.parseInt(output) != 0;
+ }
+
private void setDozeState(final boolean on) throws Exception {
ShellUtils.runShellCommand("cmd deviceidle " + (on ? "force-idle" : "unforce"));
if (!on) {
diff --git a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
index b214df2..19a7cc5 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java
@@ -511,6 +511,7 @@
verifyCarModeBound(mCarModeIncallServiceControlOne);
disableAndVerifyCarMode(mCarModeIncallServiceControlOne, Configuration.UI_MODE_TYPE_NORMAL);
+ mInCallCallbacks.getService().disconnectAllCalls();
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
index 56edd38..bd03a14 100755
--- a/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ConnectionServiceTest.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.Parcel;
import android.telecom.Call;
import android.telecom.CallScreeningService;
import android.telecom.CallScreeningService.CallResponse;
@@ -287,6 +288,25 @@
}
+ public void testCallFilteringCompletionInfoParcelable() {
+ Connection.CallFilteringCompletionInfo info = new Connection.CallFilteringCompletionInfo(
+ false /* isBlocked */,
+ false /* isInContacts */,
+ null /* callResponse */,
+ null /* callScreeningComponent */
+ );
+ Parcel p = Parcel.obtain();
+ info.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ Connection.CallFilteringCompletionInfo info2 =
+ Connection.CallFilteringCompletionInfo.CREATOR.createFromParcel(p);
+
+ assertEquals(info.isBlocked(), info2.isBlocked());
+ assertEquals(info.isInContacts(), info2.isInContacts());
+ assertEquals(info.getCallResponse(), info2.getCallResponse());
+ assertEquals(info.getCallScreeningComponent(), info2.getCallScreeningComponent());
+ }
+
public void testCallFilteringCompleteSignalNotInContacts() throws Exception {
if (!mShouldTestTelecom) {
return;
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 b548643..4767ad7 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
@@ -77,6 +77,7 @@
private static final String COMMAND_REMOVE_UCE_REQUEST_DISALLOWED_STATUS =
"uce remove-request-disallowed-status ";
private static final String COMMAND_SET_TEST_MODE_ENABLED = "src set-test-enabled ";
+ private static final String COMMAND_SET_D2D_ENABLED = "d2d set-device-support ";
private class TestCarrierServiceConnection implements ServiceConnection {
@@ -628,4 +629,9 @@
.append(COMMAND_SLOT_IDENTIFIER).append(slotId);
TelephonyUtils.executeShellCommand(mInstrumentation, cmdBuilder.toString());
}
+
+ void setDeviceToDeviceCommunicationEnabled(boolean enabled) throws Exception {
+ TelephonyUtils.executeShellCommand(mInstrumentation, COMMAND_BASE
+ + COMMAND_SET_D2D_ENABLED + (enabled ? "true" : "default"));
+ }
}
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 a22ef89..cb1fd88 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
@@ -50,6 +50,7 @@
import android.telephony.ims.RcsClientConfiguration;
import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
@@ -76,6 +77,7 @@
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -3042,6 +3044,40 @@
overrideCarrierConfig(null);
}
+ /**
+ * Verifies that the RTP header extensions are set as expected when D2D communication is
+ * available on the device and for the current carrier.
+ * @throws Exception
+ */
+ @Test
+ public void testSetRtpHeaderExtensions() throws Exception {
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+ sServiceConnector.setDeviceToDeviceCommunicationEnabled(true);
+ try {
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(
+ CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL,
+ true);
+ bundle.putBoolean(CarrierConfigManager
+ .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL, true);
+ overrideCarrierConfig(bundle);
+
+ triggerFrameworkConnectToCarrierImsService();
+
+ sServiceConnector.getCarrierService().getMmTelFeature()
+ .getOfferedRtpHeaderExtensionLatch().await(5000, TimeUnit.MILLISECONDS);
+ Set<RtpHeaderExtensionType> extensions = sServiceConnector.getCarrierService()
+ .getMmTelFeature().getOfferedRtpHeaderExtensionTypes();
+
+ assertTrue(extensions.size() > 0);
+ } finally {
+ sServiceConnector.setDeviceToDeviceCommunicationEnabled(false);
+ overrideCarrierConfig(null);
+ }
+ }
+
private void verifyIntKey(ProvisioningManager pm,
LinkedBlockingQueue<Pair<Integer, Integer>> intQueue, int key, int value)
throws Exception {
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 dfdcc6f..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
@@ -182,7 +182,8 @@
featureTags.add(FEATURE_TAG_CHAT_SESSION);
featureTags.add(FEATURE_TAG_FILE_TRANSFER);
- OptionsBuilder optionsBuilder = new OptionsBuilder(TEST_CONTACT);
+ 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);
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 187d4b8..c3e1a91 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;
@@ -74,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;
@@ -595,7 +598,8 @@
// 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, REQUEST_RESULT_FOUND, true, true);
+ verifyCapabilityResult(capability, sTestNumberUri, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_FOUND, true, true);
waitForResult(completeQueue);
errorQueue.clear();
@@ -607,7 +611,8 @@
// 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);
overrideCarrierConfig(null);
@@ -903,7 +908,7 @@
}
@Test
- public void testRequestCapabilities() throws Exception {
+ public void testRequestCapabilitiesWithPresenceMechanism() throws Exception {
if (!ImsUtils.shouldTestImsService()) {
return;
}
@@ -969,15 +974,18 @@
// 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, REQUEST_RESULT_FOUND, true, true);
+ 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, REQUEST_RESULT_FOUND, true, false);
+ 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, REQUEST_RESULT_FOUND, false, false);
+ verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+ false, false);
// Verify the onCompleted is called
waitForResult(completeQueue);
@@ -997,13 +1005,16 @@
// Verify the contacts are not found.
capability = waitForResult(capabilityQueue);
- verifyCapabilityResult(capability, contact1, REQUEST_RESULT_NOT_FOUND, false, false);
+ verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+ false, false);
capability = waitForResult(capabilityQueue);
- verifyCapabilityResult(capability, contact2, REQUEST_RESULT_NOT_FOUND, false, false);
+ verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+ false, 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);
int errorCode = waitForResult(errorQueue);
assertEquals(RcsUceAdapter.ERROR_NOT_FOUND, errorCode);
@@ -1035,14 +1046,17 @@
// 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);
@@ -1051,6 +1065,129 @@
}
@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;
@@ -1138,11 +1275,14 @@
for (RcsContactUceCapability capability : capabilityQueue) {
Uri contact = capability.getContactUri();
if (contact1.equals(contact)) {
- verifyCapabilityResult(capability, contact1, REQUEST_RESULT_FOUND, true, true);
+ verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_FOUND, true, true);
} else if (contact2.equals(contact)) {
- verifyCapabilityResult(capability, contact2, REQUEST_RESULT_FOUND, true, false);
+ verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_FOUND, true, false);
} else if (contact3.equals(contact)) {
- verifyCapabilityResult(capability, contact3, REQUEST_RESULT_FOUND, false, false);
+ verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_FOUND, false, false);
} else {
fail("The contact of the capabilities result is invalid.");
}
@@ -1158,10 +1298,11 @@
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
receiveRequestCount.incrementAndGet();
cb.onNetworkResponse(networkRespCode, networkRespReason);
- if (receiveRequestCount.get() == 1) {
- // Notify capabilities updated for the first contact
+ assertEquals(1, uris.size());
+ String uriPart = uris.iterator().next().getSchemeSpecificPart();
+ if (contact1.getSchemeSpecificPart().equalsIgnoreCase(uriPart)) {
cb.onNotifyCapabilitiesUpdate(pidfXml1);
- } else if (receiveRequestCount.get() == 2 || receiveRequestCount.get() == 3) {
+ } else {
// Notify resources terminated for the reset contacts
List<Uri> uriList = new ArrayList(uris);
List<Pair<Uri, String>> terminatedResources = new ArrayList<>();
@@ -1187,11 +1328,14 @@
for (RcsContactUceCapability capability : capabilityQueue) {
Uri contact = capability.getContactUri();
if (contact1.equals(contact)) {
- verifyCapabilityResult(capability, contact1, REQUEST_RESULT_FOUND, true, true);
+ verifyCapabilityResult(capability, contact1, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_FOUND, true, true);
} else if (contact2.equals(contact)) {
- verifyCapabilityResult(capability, contact2, REQUEST_RESULT_NOT_FOUND, true, false);
+ verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_NOT_FOUND, true, false);
} else if (contact3.equals(contact)) {
- verifyCapabilityResult(capability, contact3, REQUEST_RESULT_NOT_FOUND, false,
+ verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_NOT_FOUND, false,
false);
} else {
fail("The contact of the capabilities result is invalid.");
@@ -1328,6 +1472,142 @@
}
@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;
@@ -1480,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,
@@ -1542,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());
@@ -1691,6 +1971,178 @@
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((deactivated, expectedResult) -> {
+ String terminatedReason = (String) deactivated.arg1;
+ Long terminatedRetryAfterMillis = (Long) deactivated.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);
+ }
+
+ deactivated.recycle();
+ expectedResult.recycle();
+ errorQueue.clear();
+ errorRetryQueue.clear();
+ completeQueue.clear();
+ capabilityQueue.clear();
+ removeTestContactFromEab();
+ });
+
+ // Verify the subscribe request is sccessful when the terminated is timeout and the
+ // retryAfter is 0L
+ String terminatedReason = "timemout";
+ 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();
+
+ overrideCarrierConfig(null);
+ }
+
private void setupTestImsService(RcsUceAdapter uceAdapter, boolean presencePublishEnabled,
boolean presenceCapExchangeEnabled, boolean sipOptionsEnabled) throws Exception {
// Trigger carrier config changed
@@ -1729,12 +2181,13 @@
}
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.
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestMmTelFeature.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestMmTelFeature.java
index 13706f8..cb82d71 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/TestMmTelFeature.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/TestMmTelFeature.java
@@ -16,12 +16,15 @@
package android.telephony.ims.cts;
+import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.feature.CapabilityChangeRequest;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
public class TestMmTelFeature extends MmTelFeature {
@@ -34,6 +37,8 @@
private MmTelCapabilities mCapabilities =
new MmTelCapabilities(MmTelCapabilities.CAPABILITY_TYPE_SMS);
private TestImsSmsImpl mSmsImpl;
+ private Set<RtpHeaderExtensionType> mOfferedRtpHeaderExtensionTypes;
+ private CountDownLatch mOfferedRtpHeaderExtensionLatch = new CountDownLatch(1);
TestMmTelFeature(TestImsService.ReadyListener readyListener,
TestImsService.RemovedListener removedListener,
@@ -93,6 +98,12 @@
mRemovedListener.onRemoved();
}
+ @Override
+ public void changeOfferedRtpHeaderExtensionTypes(Set<RtpHeaderExtensionType> extensionTypes) {
+ mOfferedRtpHeaderExtensionTypes = extensionTypes;
+ mOfferedRtpHeaderExtensionLatch.countDown();
+ }
+
public void setCapabilities(MmTelCapabilities capabilities) {
mCapabilities = capabilities;
}
@@ -100,4 +111,12 @@
public MmTelCapabilities getCapabilities() {
return mCapabilities;
}
+
+ public Set<RtpHeaderExtensionType> getOfferedRtpHeaderExtensionTypes() {
+ return mOfferedRtpHeaderExtensionTypes;
+ }
+
+ public CountDownLatch getOfferedRtpHeaderExtensionLatch() {
+ return mOfferedRtpHeaderExtensionLatch;
+ }
}
diff --git a/tests/tests/text/src/android/text/cts/TextPaintTest.java b/tests/tests/text/src/android/text/cts/TextPaintTest.java
index d5a6ca1..8d3ed45 100644
--- a/tests/tests/text/src/android/text/cts/TextPaintTest.java
+++ b/tests/tests/text/src/android/text/cts/TextPaintTest.java
@@ -33,7 +33,8 @@
TextPaint textPaint;
textPaint = new TextPaint();
- assertEquals(DEFAULT_PAINT_FLAGS, textPaint.getFlags());
+ assertEquals(DEFAULT_PAINT_FLAGS | TextPaint.ANTI_ALIAS_FLAG | TextPaint.DITHER_FLAG,
+ textPaint.getFlags());
textPaint = new TextPaint(TextPaint.DITHER_FLAG);
assertEquals((TextPaint.DITHER_FLAG | DEFAULT_PAINT_FLAGS),
diff --git a/tests/tests/time/AndroidManifest.xml b/tests/tests/time/AndroidManifest.xml
index a3bd06d..aaf2c47 100644
--- a/tests/tests/time/AndroidManifest.xml
+++ b/tests/tests/time/AndroidManifest.xml
@@ -18,8 +18,9 @@
package="android.time.cts">
<!-- The permissions below would be needed if tests were not using "adopt shell permissions" to
- obtain the necessary MANAGE_TIME_AND_ZONE_DETECTION privileged permission. -->
+ obtain the necessary privileged permissions. -->
<!-- uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" /-->
+ <!-- uses-permission android:name="android.permission.SUGGEST_EXTERNAL_TIME" /-->
<!-- Required for LocationManager.setLocationEnabledForUser() -->
<!-- uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/-->
<eat-comment />
diff --git a/tests/tests/time/src/android/app/time/cts/TimeManagerTest.java b/tests/tests/time/src/android/app/time/cts/TimeManagerTest.java
index 72f74ad..1b43e58 100644
--- a/tests/tests/time/src/android/app/time/cts/TimeManagerTest.java
+++ b/tests/tests/time/src/android/app/time/cts/TimeManagerTest.java
@@ -16,12 +16,14 @@
package android.app.time.cts;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.app.UiAutomation;
import android.app.time.Capabilities;
import android.app.time.ExternalTimeSuggestion;
import android.app.time.TimeManager;
@@ -30,33 +32,64 @@
import android.app.time.TimeZoneConfiguration;
import android.content.Context;
import android.location.LocationManager;
-import android.os.Parcel;
-import android.os.Parcelable;
+import android.os.SystemClock;
import android.os.UserHandle;
-
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
+import android.provider.DeviceConfig;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.AdoptShellPermissionsRule;
+import com.google.common.io.ByteStreams;
-import java.lang.reflect.Field;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
-/** Tests for {@link TimeManager} and associated classes. */
+/** In-process tests for {@link TimeManager} and associated classes. */
public class TimeManagerTest {
/**
* This rule adopts the Shell process permissions, needed because MANAGE_TIME_AND_ZONE_DETECTION
- * is a privileged permission.
+ * and SUGGEST_EXTERNAL_TIME required by {@link TimeManager} are privileged permissions.
*/
@Rule
public final AdoptShellPermissionsRule shellPermRule = new AdoptShellPermissionsRule();
+ private boolean mWasDeviceConfigSyncDisabled;
+
+ @Before
+ public void before() throws Exception {
+ // This anticipates a future state where a generally applied target preparer may disable
+ // device_config sync for all CTS tests: only suspend syncing if it isn't already suspended,
+ // and only resume it if this test suspended it.
+ mWasDeviceConfigSyncDisabled = DeviceConfigShellCommand.isSyncDisabled();
+ if (!mWasDeviceConfigSyncDisabled) {
+ DeviceConfigShellCommand.setSyncDisabled(
+ DeviceConfigShellCommand.SYNC_DISABLED_MODE_UNTIL_REBOOT);
+ }
+ }
+
+ @After
+ public void after() throws Exception {
+ DeviceConfigShellCommand.resetToDefaults(
+ DeviceConfigShellCommand.RESET_MODE_TRUSTED_DEFAULTS, NAMESPACE_SYSTEM_TIME);
+ if (!mWasDeviceConfigSyncDisabled) {
+ // Turn syncing back on if this test disabled it.
+ DeviceConfigShellCommand.setSyncDisabled(
+ DeviceConfigShellCommand.SYNC_DISABLED_MODE_NONE);
+ }
+ }
+
/**
* Registers a {@link android.app.time.TimeManager.TimeZoneDetectorListener}, makes changes
* to the configuration and checks that the listener is called.
@@ -144,6 +177,64 @@
}
}
+ @Test
+ public void externalSuggestions() throws Exception {
+ long startCurrentTimeMillis = System.currentTimeMillis();
+ long elapsedRealtimeMillis = SystemClock.elapsedRealtime();
+
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ TimeManager timeManager = context.getSystemService(TimeManager.class);
+ assertNotNull(timeManager);
+
+ // Set the time detector to only use ORIGIN_NETWORK. The important aspect is that it isn't
+ // ORIGIN_EXTERNAL, and so suggestions from external should be ignored.
+ DeviceConfig.setProperty(NAMESPACE_SYSTEM_TIME,
+ TimeDetectorServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
+ TimeDetectorServerFlags.ORIGIN_NETWORK,
+ /*makeDefault=*/false);
+ sleepForAsyncOperation();
+
+ long suggestion1Millis =
+ Instant.ofEpochMilli(startCurrentTimeMillis).plus(1, ChronoUnit.DAYS).toEpochMilli();
+ ExternalTimeSuggestion futureTimeSuggestion1 =
+ new ExternalTimeSuggestion(elapsedRealtimeMillis, suggestion1Millis);
+ long suggestion2Millis =
+ Instant.ofEpochMilli(suggestion1Millis).plus(1, ChronoUnit.DAYS).toEpochMilli();
+ ExternalTimeSuggestion futureTimeSuggestion2 =
+ new ExternalTimeSuggestion(elapsedRealtimeMillis, suggestion2Millis);
+
+ // Suggest a change. It shouldn't be used.
+ timeManager.suggestExternalTime(futureTimeSuggestion1);
+ sleepForAsyncOperation();
+
+ // The suggestion should have been ignored so the system clock should not have advanced.
+ assertTrue(System.currentTimeMillis() < suggestion1Millis);
+
+ // Set the time detector to only use ORIGIN_EXTERNAL.
+ // The suggestion should have been stored and acted upon when the origin list changes.
+ DeviceConfig.setProperty(NAMESPACE_SYSTEM_TIME,
+ TimeDetectorServerFlags.KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE,
+ TimeDetectorServerFlags.ORIGIN_EXTERNAL,
+ /*makeDefault=*/false);
+ sleepForAsyncOperation();
+ assertTrue(System.currentTimeMillis() >= suggestion1Millis);
+
+ // Suggest a change. It should be used.
+ timeManager.suggestExternalTime(futureTimeSuggestion2);
+ sleepForAsyncOperation();
+ assertTrue(System.currentTimeMillis() >= suggestion2Millis);
+
+ // Now do our best to return the device to its original state.
+ ExternalTimeSuggestion originalTimeSuggestion =
+ new ExternalTimeSuggestion(elapsedRealtimeMillis, startCurrentTimeMillis);
+ timeManager.suggestExternalTime(
+ originalTimeSuggestion);
+ sleepForAsyncOperation();
+
+ DeviceConfigShellCommand.resetToDefaults(
+ DeviceConfigShellCommand.RESET_MODE_TRUSTED_DEFAULTS, NAMESPACE_SYSTEM_TIME);
+ }
+
/**
* Registers a {@link android.app.time.TimeManager.TimeZoneDetectorListener}, makes changes
* to the "location enabled" setting and checks that the listener is called.
@@ -195,4 +286,78 @@
}
assertEquals(expectedValue, actualValue.get());
}
+
+ /**
+ * A class for interacting with the {@link DeviceConfig} service via the command line. Some
+ * behavior it supports is not available via the Android @SystemApi.
+ * See {@link com.android.providers.settings.DeviceConfigService} for the shell command
+ * implementation details.
+ */
+ private static class DeviceConfigShellCommand {
+
+ static final String RESET_MODE_TRUSTED_DEFAULTS = "trusted_defaults";
+ static final String SYNC_DISABLED_MODE_NONE = "none";
+ static final String SYNC_DISABLED_MODE_UNTIL_REBOOT = "until_reboot";
+
+ private static final String SHELL_CMD_PREFIX = "cmd device_config ";
+
+ private DeviceConfigShellCommand() {}
+
+ static boolean isSyncDisabled() throws Exception {
+ String cmd = SHELL_CMD_PREFIX + "is_sync_disabled_for_tests";
+ String result = executeShellCommandInternal(cmd);
+ return Boolean.parseBoolean(result);
+ }
+
+ static void setSyncDisabled(String syncDisabledMode) throws Exception {
+ String cmd = String.format(
+ SHELL_CMD_PREFIX + "set_sync_disabled_for_tests %s", syncDisabledMode);
+ executeShellCommandInternal(cmd);
+ }
+
+ static void resetToDefaults(String resetMode, String namespace) throws Exception {
+ String cmd = String.format(SHELL_CMD_PREFIX + "reset %s %s", resetMode, namespace);
+ executeShellCommandInternal(cmd);
+ }
+
+ private static String executeShellCommandInternal(String cmd) throws IOException {
+ UiAutomation uiAutomation =
+ InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try (FileInputStream output = new FileInputStream(
+ uiAutomation.executeShellCommand(cmd).getFileDescriptor())) {
+ return new String(ByteStreams.toByteArray(output));
+ }
+ }
+ }
+
+ /**
+ * device_config service flags and values for controlling the time_detector service.
+ */
+ private interface TimeDetectorServerFlags {
+ /**
+ * See {@link
+ * com.android.server.timedetector.ServerFlags#KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE}
+ */
+ String KEY_TIME_DETECTOR_ORIGIN_PRIORITIES_OVERRIDE =
+ "time_detector_origin_priorities_override";
+
+ /**
+ * See {@link com.android.server.timedetector.TimeDetectorStrategy#ORIGIN_NETWORK}.
+ */
+ String ORIGIN_NETWORK = "network";
+
+ /**
+ * See {@link com.android.server.timedetector.TimeDetectorStrategy#ORIGIN_EXTERNAL}.
+ */
+ String ORIGIN_EXTERNAL = "external";
+ }
+
+ /**
+ * Sleeps for a length of time sufficient to allow async operations to complete. Many time
+ * manager APIs are or could be asynchronous and deal with time, so there are no practical
+ * alternatives.
+ */
+ private static void sleepForAsyncOperation() throws Exception{
+ Thread.sleep(5_000);
+ }
}
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 95de736..221912f 100755
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -104,8 +104,6 @@
// 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");
@@ -118,10 +116,6 @@
} finally {
getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
}
- // Make sure the grant worked
- assertSame(packageManager.checkPermission(Manifest.permission.ANSWER_PHONE_CALLS,
- context.getPackageName()), PackageManager.PERMISSION_GRANTED);
-
// Try to access APIs guarded by a platform defined signature permissions
assertThrows(SecurityException.class,
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java
index 2bc3ddb..6b0a5c2 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/EdgeEffectTests.java
@@ -23,12 +23,16 @@
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ColorFilter;
import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
+import android.graphics.drawable.Drawable;
import android.uirendering.cts.bitmapverifiers.ColorVerifier;
import android.uirendering.cts.bitmapverifiers.PerPixelBitmapVerifier;
import android.uirendering.cts.bitmapverifiers.RegionVerifier;
@@ -309,6 +313,113 @@
});
}
+ @Test
+ public void testStretchEffectUsesLayer() {
+ // Verify that any overscroll effect leverages a layer for rendering to ensure
+ // consistent graphics behavior.
+ EdgeEffect effect = new EdgeEffect(getContext());
+ OverscrollDrawable drawable = new OverscrollDrawable(effect, Color.CYAN);
+ drawable.setBounds(0, 0, TEST_WIDTH, TEST_HEIGHT);
+
+ // Verify that the black background is visible when rendering without overscroll
+ createTest()
+ .addCanvasClientWithoutUsingPicture((canvas, width, height) -> {
+ canvas.drawColor(Color.BLACK);
+ drawable.setOverscroll(false);
+ drawable.draw(canvas);
+ }, true)
+ .runWithVerifier(
+ new RegionVerifier().addVerifier(
+ new Rect(0, 0, TEST_WIDTH, TEST_HEIGHT),
+ new ColorVerifier(Color.BLACK)
+ ));
+
+ // Verify cyan color is visible when rendering with overscroll enabled
+ createTest()
+ .addCanvasClientWithoutUsingPicture((canvas, width, height) -> {
+ canvas.drawColor(Color.BLACK);
+ drawable.setOverscroll(true);
+ drawable.draw(canvas);
+ }, true)
+ .runWithVerifier(
+ new RegionVerifier().addVerifier(
+ new Rect(0, 0, TEST_WIDTH, TEST_HEIGHT),
+ new ColorVerifier(Color.CYAN)
+ ));
+ }
+
+ /**
+ * Test drawable class that renders content using {@link BlendMode#DST_OVER} which
+ * requires a compositing layer in order to draw pixels, otherwise nothing is drawn.
+ */
+ private static class OverscrollDrawable extends Drawable {
+
+ private final int mChildColor;
+
+ private final EdgeEffect mEdgeEffect;
+
+ private final RenderNode mParentNode = new RenderNode("");
+ private final RenderNode mChildNode = new RenderNode("");
+
+ private boolean mOverscroll = false;
+
+ public OverscrollDrawable(EdgeEffect edgeEffect, int color) {
+ mChildColor = color;
+ mEdgeEffect = edgeEffect;
+ }
+
+ public void setOverscroll(boolean overscroll) {
+ mOverscroll = overscroll;
+ invalidateSelf();
+ }
+
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mEdgeEffect.setSize(bounds.width(), bounds.height());
+ mParentNode.setPosition(0, 0, bounds.width(), bounds.height());
+ mChildNode.setPosition(0, 0, bounds.width(), bounds.height());
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mOverscroll) {
+ mEdgeEffect.onPullDistance(1.0f, 0.5f);
+ } else {
+ mEdgeEffect.finish();
+ }
+
+ RecordingCanvas parentCanvas = mParentNode.beginRecording();
+ {
+ RecordingCanvas childCanvas = mChildNode.beginRecording();
+ {
+ childCanvas.drawColor(mChildColor, BlendMode.DST_OVER);
+ }
+ mChildNode.endRecording();
+
+ parentCanvas.drawRenderNode(mChildNode);
+ mEdgeEffect.draw(parentCanvas);
+ }
+ mParentNode.endRecording();
+
+ canvas.drawRenderNode(mParentNode);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ // NO-OP
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ // NO-OP
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+ }
+
/**
* A held pull should not retract.
*/
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
index d8c4c33..ddf9c54 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -123,6 +123,7 @@
Paint p = new Paint();
canvas.drawColor(Color.WHITE);
p.setColor(Color.BLACK);
+ p.setAntiAlias(false);
float[] pts = {
0, 0, 80, 80, 80, 0, 0, 80, 40, 50, 60, 50
};
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
index 7c2121d..c962915 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
@@ -112,6 +112,7 @@
// from there execute a normal canvas test with that.
CanvasClient canvasClient = (canvas, width, height) -> {
Paint paint = new Paint();
+ paint.setAntiAlias(false);
modifierAccessor.modifyDrawing(canvas, paint);
if (drawOp != null) {
drawOp.modifyDrawing(paint, canvas);
diff --git a/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java
index 23990d4..20bdc0a 100644
--- a/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnGatewayConnectionConfigTest.java
@@ -18,10 +18,14 @@
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;
@@ -76,4 +80,17 @@
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/VcnTestBase.java b/tests/tests/vcn/src/android/net/vcn/cts/VcnTestBase.java
index 54aa1e9..7ed201e 100644
--- a/tests/tests/vcn/src/android/net/vcn/cts/VcnTestBase.java
+++ b/tests/tests/vcn/src/android/net/vcn/cts/VcnTestBase.java
@@ -16,6 +16,7 @@
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;
@@ -30,6 +31,24 @@
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(
@@ -43,23 +62,12 @@
final String testRemoteId = "test.server.com";
final byte[] psk = "psk".getBytes();
- final IkeSessionParams ikeParams =
- new IkeSessionParams.Builder()
- .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();
-
- return new IkeTunnelConnectionParams(ikeParams, childParams);
+ 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/AndroidTest.xml b/tests/tests/view/AndroidTest.xml
index 5750c26..b30904a 100644
--- a/tests/tests/view/AndroidTest.xml
+++ b/tests/tests/view/AndroidTest.xml
@@ -29,4 +29,8 @@
<option name="hidden-api-checks" value="false" />
<option name="isolated-storage" value="false" />
</test>
+ <!-- Collect the dumped files for debugging -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="view_.*" />
+ </metrics_collector>
</configuration>
diff --git a/tests/tests/view/src/android/view/cts/ASurfaceControlTest.java b/tests/tests/view/src/android/view/cts/ASurfaceControlTest.java
index 4baa60f..7a3ba98 100644
--- a/tests/tests/view/src/android/view/cts/ASurfaceControlTest.java
+++ b/tests/tests/view/src/android/view/cts/ASurfaceControlTest.java
@@ -742,31 +742,31 @@
}
};
verifyTest(callback,
- new PixelChecker(PixelColor.RED) { //1111
+ new PixelChecker(PixelColor.RED) { //2500
@Override
public boolean checkPixels(int pixelCount, int width, int height) {
- return pixelCount > 1000 && pixelCount < 1250;
+ return pixelCount > 2250 && pixelCount < 2750;
}
});
verifyTest(callback,
- new PixelChecker(PixelColor.BLUE) { //1111
+ new PixelChecker(PixelColor.BLUE) { //2500
@Override
public boolean checkPixels(int pixelCount, int width, int height) {
- return pixelCount > 1000 && pixelCount < 1250;
+ return pixelCount > 2250 && pixelCount < 2750;
}
});
verifyTest(callback,
- new PixelChecker(PixelColor.MAGENTA) { //1111
+ new PixelChecker(PixelColor.MAGENTA) { //2500
@Override
public boolean checkPixels(int pixelCount, int width, int height) {
- return pixelCount > 1000 && pixelCount < 1250;
+ return pixelCount > 2250 && pixelCount < 2750;
}
});
verifyTest(callback,
- new PixelChecker(PixelColor.GREEN) { //1111
+ new PixelChecker(PixelColor.GREEN) { //2500
@Override
public boolean checkPixels(int pixelCount, int width, int height) {
- return pixelCount > 1000 && pixelCount < 1250;
+ return pixelCount > 2250 && pixelCount < 2750;
}
});
}
diff --git a/tests/tests/view/src/android/view/cts/ViewRootSyncTest.java b/tests/tests/view/src/android/view/cts/AttachedSurfaceControlSyncTest.java
similarity index 95%
rename from tests/tests/view/src/android/view/cts/ViewRootSyncTest.java
rename to tests/tests/view/src/android/view/cts/AttachedSurfaceControlSyncTest.java
index b1f401a..453b07a 100644
--- a/tests/tests/view/src/android/view/cts/ViewRootSyncTest.java
+++ b/tests/tests/view/src/android/view/cts/AttachedSurfaceControlSyncTest.java
@@ -61,8 +61,8 @@
@LargeTest
@SuppressLint("RtlHardcoded")
@RequiresDevice
-public class ViewRootSyncTest {
- private static final String TAG = "ViewRootSyncTests";
+public class AttachedSurfaceControlSyncTest {
+ private static final String TAG = "AttachedSurfaceControlSyncTests";
@Rule
public ActivityTestRule<CapturedActivityWithResource> mActivityRule =
@@ -92,7 +92,7 @@
t.setGeometry(mSurfaceControl, null, new Rect(mLocation[0], mLocation[1],
mLocation[0]+100,
mLocation[1]+100), 0);
- getViewRoot().applyTransactionOnDraw(t);
+ getRootSurfaceControl().applyTransactionOnDraw(t);
return true;
};
@@ -125,7 +125,8 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- SurfaceControl.Transaction t = getViewRoot().buildReparentTransaction(mSurfaceControl);
+ SurfaceControl.Transaction t =
+ getRootSurfaceControl().buildReparentTransaction(mSurfaceControl);
t.setLayer(mSurfaceControl, -1)
.setVisibility(mSurfaceControl, true)
.apply();
diff --git a/tests/tests/view/src/android/view/cts/PixelCopyTest.java b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
index 9c86cb3..b0c01af 100644
--- a/tests/tests/view/src/android/view/cts/PixelCopyTest.java
+++ b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
@@ -20,7 +20,6 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -29,10 +28,16 @@
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorSpace;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.media.Image;
+import android.media.ImageReader;
+import android.media.ImageWriter;
import android.os.Debug;
import android.os.Debug.MemoryInfo;
import android.util.Half;
@@ -42,6 +47,7 @@
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import android.view.cts.util.BitmapDumper;
import android.view.cts.util.DisableFixedToUserRotationRule;
import androidx.test.InstrumentationRegistry;
@@ -52,9 +58,11 @@
import com.android.compatibility.common.util.SynchronousPixelCopy;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
@@ -98,6 +106,9 @@
@Rule
public SurfaceTextureRule mSurfaceRule = new SurfaceTextureRule();
+ @Rule
+ public TestName mTestName = new TestName();
+
private Instrumentation mInstrumentation;
private SynchronousPixelCopy mCopyHelper;
@@ -180,7 +191,7 @@
activity.getView().requestRender();
}
} catch (InterruptedException ex) {
- fail("Interrupted, error=" + ex.getMessage());
+ Assert.fail("Interrupted, error=" + ex.getMessage());
}
return activity;
}
@@ -706,7 +717,7 @@
assertNotLeaking(1000, meminfoStart, meminfoEnd);
} catch (InterruptedException e) {
- fail("Interrupted, error=" + e.getMessage());
+ Assert.fail("Interrupted, error=" + e.getMessage());
}
}
@@ -779,56 +790,110 @@
Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, 30);
}
+ @Test
+ public void testBufferQueueCrop() throws InterruptedException {
+ ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 1);
+ ImageWriter writer = ImageWriter.newInstance(reader.getSurface(), 1);
+ Image image = writer.dequeueInputImage();
+ Image.Plane plane = image.getPlanes()[0];
+ Bitmap bitmap = Bitmap.createBitmap(plane.getRowStride() / 4,
+ image.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ Paint paint = new Paint();
+ paint.setAntiAlias(false);
+ canvas.drawColor(Color.MAGENTA);
+ canvas.translate(20f, 70f);
+ paint.setColor(Color.RED);
+ canvas.drawRect(0f, 0f, 10f, 10f, paint);
+ paint.setColor(Color.GREEN);
+ canvas.drawRect(10f, 0f, 20f, 10f, paint);
+ paint.setColor(Color.BLUE);
+ canvas.drawRect(0f, 10f, 10f, 20f, paint);
+ paint.setColor(Color.BLACK);
+ canvas.drawRect(10f, 10f, 20f, 20f, paint);
+ bitmap.copyPixelsToBuffer(plane.getBuffer());
+ image.setCropRect(new Rect(20, 70, 40, 90));
+ writer.queueInputImage(image);
+
+ // implicit size
+ Bitmap result = Bitmap.createBitmap(20, 20, Config.ARGB_8888);
+ int status = mCopyHelper.request(reader.getSurface(), result);
+ assertEquals("Copy request failed", PixelCopy.SUCCESS, status);
+ assertBitmapQuadColor(result, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK);
+
+ // specified size
+ result = Bitmap.createBitmap(20, 20, Config.ARGB_8888);
+ status = mCopyHelper.request(reader.getSurface(), new Rect(0, 0, 20, 20), result);
+ assertEquals("Copy request failed", PixelCopy.SUCCESS, status);
+ assertBitmapQuadColor(result, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK);
+ }
+
private static int getPixelFloatPos(Bitmap bitmap, float xpos, float ypos) {
Log.d(TAG, "getPixelFloatPos(): x=" + ((int) (bitmap.getWidth() * xpos))
+ " y=" + ((int) (bitmap.getHeight() * ypos)));
return bitmap.getPixel((int) (bitmap.getWidth() * xpos), (int) (bitmap.getHeight() * ypos));
}
- public static void assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight,
- int bottomLeft, int bottomRight) {
- // Just quickly sample 4 pixels in the various regions.
- assertEquals("Top left " + Integer.toHexString(topLeft) + ", actual= "
- + Integer.toHexString(getPixelFloatPos(bitmap, .25f, .25f)),
- topLeft, getPixelFloatPos(bitmap, .25f, .25f));
- assertEquals("Top right", topRight, getPixelFloatPos(bitmap, .75f, .25f));
- assertEquals("Bottom left", bottomLeft, getPixelFloatPos(bitmap, .25f, .75f));
- assertEquals("Bottom right", bottomRight, getPixelFloatPos(bitmap, .75f, .75f));
+ private void assertBitmapQuadColor(Bitmap bitmap,
+ int topLeft, int topRight, int bottomLeft, int bottomRight) {
+ assertBitmapQuadColor(mTestName.getMethodName(), "PixelCopyTest", bitmap,
+ topLeft, topRight, bottomLeft, bottomRight);
+ }
- // and some closer to the center point, to ensure that our quadrants are even
- float below = .45f;
- float above = .55f;
- Log.d(TAG, "bitmap w=" + bitmap.getWidth() + " h=" + bitmap.getHeight());
- assertEquals("Top left II " + Integer.toHexString(topLeft) + ", actual= "
- + Integer.toHexString(getPixelFloatPos(bitmap, below, below)),
- topLeft, getPixelFloatPos(bitmap, below, below));
- assertEquals("Top right II", topRight, getPixelFloatPos(bitmap, above, below));
- assertEquals("Bottom left II", bottomLeft, getPixelFloatPos(bitmap, below, above));
- assertEquals("Bottom right II", bottomRight, getPixelFloatPos(bitmap, above, above));
+ public static void assertBitmapQuadColor(String testName, String className, Bitmap bitmap,
+ int topLeft, int topRight, int bottomLeft, int bottomRight) {
+ try {
+ // Just quickly sample 4 pixels in the various regions.
+ assertEquals("Top left " + Integer.toHexString(topLeft) + ", actual= "
+ + Integer.toHexString(getPixelFloatPos(bitmap, .25f, .25f)),
+ topLeft, getPixelFloatPos(bitmap, .25f, .25f));
+ assertEquals("Top right", topRight, getPixelFloatPos(bitmap, .75f, .25f));
+ assertEquals("Bottom left", bottomLeft, getPixelFloatPos(bitmap, .25f, .75f));
+ assertEquals("Bottom right", bottomRight, getPixelFloatPos(bitmap, .75f, .75f));
+
+ // and some closer to the center point, to ensure that our quadrants are even
+ float below = .45f;
+ float above = .55f;
+ Log.d(TAG, "bitmap w=" + bitmap.getWidth() + " h=" + bitmap.getHeight());
+ assertEquals("Top left II " + Integer.toHexString(topLeft) + ", actual= "
+ + Integer.toHexString(getPixelFloatPos(bitmap, below, below)),
+ topLeft, getPixelFloatPos(bitmap, below, below));
+ assertEquals("Top right II", topRight, getPixelFloatPos(bitmap, above, below));
+ assertEquals("Bottom left II", bottomLeft, getPixelFloatPos(bitmap, below, above));
+ assertEquals("Bottom right II", bottomRight, getPixelFloatPos(bitmap, above, above));
+ } catch (AssertionError err) {
+ BitmapDumper.dumpBitmap(bitmap, testName, className);
+ throw err;
+ }
}
private void assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight,
int bottomLeft, int bottomRight, int threshold) {
- // Just quickly sample 4 pixels in the various regions.
- assertTrue("Top left", pixelsAreSame(topLeft, getPixelFloatPos(bitmap, .25f, .25f),
- threshold));
- assertTrue("Top right", pixelsAreSame(topRight, getPixelFloatPos(bitmap, .75f, .25f),
- threshold));
- assertTrue("Bottom left", pixelsAreSame(bottomLeft, getPixelFloatPos(bitmap, .25f, .75f),
- threshold));
- assertTrue("Bottom right", pixelsAreSame(bottomRight, getPixelFloatPos(bitmap, .75f, .75f),
- threshold));
+ try {
+ // Just quickly sample 4 pixels in the various regions.
+ assertTrue("Top left", pixelsAreSame(topLeft,
+ getPixelFloatPos(bitmap, .25f, .25f), threshold));
+ assertTrue("Top right", pixelsAreSame(topRight,
+ getPixelFloatPos(bitmap, .75f, .25f), threshold));
+ assertTrue("Bottom left", pixelsAreSame(bottomLeft,
+ getPixelFloatPos(bitmap, .25f, .75f), threshold));
+ assertTrue("Bottom right", pixelsAreSame(bottomRight,
+ getPixelFloatPos(bitmap, .75f, .75f), threshold));
- float below = .45f;
- float above = .55f;
- assertTrue("Top left II", pixelsAreSame(topLeft, getPixelFloatPos(bitmap, below, below),
- threshold));
- assertTrue("Top right II", pixelsAreSame(topRight, getPixelFloatPos(bitmap, above, below),
- threshold));
- assertTrue("Bottom left II", pixelsAreSame(bottomLeft, getPixelFloatPos(bitmap, below, above),
- threshold));
- assertTrue("Bottom right II", pixelsAreSame(bottomRight, getPixelFloatPos(bitmap, above, above),
- threshold));
+ float below = .45f;
+ float above = .55f;
+ assertTrue("Top left II", pixelsAreSame(topLeft,
+ getPixelFloatPos(bitmap, below, below), threshold));
+ assertTrue("Top right II", pixelsAreSame(topRight,
+ getPixelFloatPos(bitmap, above, below), threshold));
+ assertTrue("Bottom left II", pixelsAreSame(bottomLeft,
+ getPixelFloatPos(bitmap, below, above), threshold));
+ assertTrue("Bottom right II", pixelsAreSame(bottomRight,
+ getPixelFloatPos(bitmap, above, above), threshold));
+ } catch (AssertionError err) {
+ BitmapDumper.dumpBitmap(bitmap, mTestName.getMethodName(), "PixelCopyTest");
+ throw err;
+ }
}
private void assertBitmapEdgeColor(Bitmap bitmap, int edgeColor) {
@@ -858,10 +923,15 @@
return (error < threshold);
}
+ private void fail(Bitmap bitmap, String message) {
+ BitmapDumper.dumpBitmap(bitmap, mTestName.getMethodName(), "PixelCopyTest");
+ Assert.fail(message);
+ }
+
private void assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y) {
int pixel = bitmap.getPixel(x, y);
if (!pixelsAreSame(color, pixel, 10)) {
- fail(debug + "; expected=" + Integer.toHexString(color) + ", actual="
+ fail(bitmap, debug + "; expected=" + Integer.toHexString(color) + ", actual="
+ Integer.toHexString(pixel));
}
}
@@ -869,7 +939,7 @@
private void assertBitmapNotColor(String debug, Bitmap bitmap, int color, int x, int y) {
int pixel = bitmap.getPixel(x, y);
if (pixelsAreSame(color, pixel, 10)) {
- fail(debug + "; actual=" + Integer.toHexString(pixel)
+ fail(bitmap, debug + "; actual=" + Integer.toHexString(pixel)
+ " shouldn't have matched " + Integer.toHexString(color));
}
}
diff --git a/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java b/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java
index 780b071..c89cdb8 100644
--- a/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java
+++ b/tests/tests/view/src/android/view/cts/PixelCopyViewProducerActivity.java
@@ -27,6 +27,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
+import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.WindowInsets;
@@ -44,11 +45,14 @@
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
};
+ // TODO: Lower this (or remove it entirely) by leveraging things like
+ // ViewTreeObserver#registerFrameCommitCallback (and possibly display orientation listeners?)
+ private static final int DRAW_FRAME_COUNT_BEFORE_CAPTURE = 10;
private int mCurrentOrientation = 0;
private View mContent;
private Rect mContentBounds = new Rect();
private Rect mOutsets = new Rect();
- private CountDownLatch mFence = new CountDownLatch(3);
+ private CountDownLatch mFence = new CountDownLatch(DRAW_FRAME_COUNT_BEFORE_CAPTURE);
private boolean mSupportsRotation;
@Override
@@ -58,6 +62,7 @@
// Check if the device supports both of portrait and landscape orientation screens.
mSupportsRotation = DisplayUtils.supportOrientationRequest(this);
if (mSupportsRotation) {
+ Log.d("PixelCopyTest", "Setting orientation index = " + mCurrentOrientation);
setRequestedOrientation(ORIENTATIONS[mCurrentOrientation]);
}
@@ -121,9 +126,10 @@
// Do not rotate the screen if it is not supported.
return false;
}
- mFence = new CountDownLatch(3);
+ mFence = new CountDownLatch(DRAW_FRAME_COUNT_BEFORE_CAPTURE);
runOnUiThread(() -> {
mCurrentOrientation = (mCurrentOrientation + 1) % ORIENTATIONS.length;
+ Log.d("PixelCopyTest", "Setting orientation index = " + mCurrentOrientation);
setRequestedOrientation(ORIENTATIONS[mCurrentOrientation]);
});
waitForFirstDrawCompleted(10, TimeUnit.SECONDS);
diff --git a/tests/tests/view/src/android/view/cts/TextureViewTest.java b/tests/tests/view/src/android/view/cts/TextureViewTest.java
index 8736f36..6d949c6 100644
--- a/tests/tests/view/src/android/view/cts/TextureViewTest.java
+++ b/tests/tests/view/src/android/view/cts/TextureViewTest.java
@@ -49,6 +49,7 @@
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
@@ -71,6 +72,9 @@
public ActivityTestRule<TextureViewCtsActivity> mActivityRule =
new ActivityTestRule<>(TextureViewCtsActivity.class, false, false);
+ @Rule
+ public TestName mTestName = new TestName();
+
@Test
public void testFirstFrames() throws Throwable {
final TextureViewCtsActivity activity = mActivityRule.launchActivity(null);
@@ -119,7 +123,7 @@
mActivityRule.runOnUiThread(() -> {
activity.getTextureView().getBitmap(bitmap);
});
- PixelCopyTest.assertBitmapQuadColor(bitmap,
+ assertBitmapQuadColor(bitmap,
Color.RED, Color.GREEN, Color.BLUE, Color.BLACK);
}
@@ -136,7 +140,7 @@
activity.getTextureView().getBitmap(bitmap);
});
// Verify the matrix did not rotate content of getTextureView.getBitmap().
- PixelCopyTest.assertBitmapQuadColor(bitmap,
+ assertBitmapQuadColor(bitmap,
Color.RED, Color.GREEN, Color.BLUE, Color.BLACK);
// Remove cover and calculate TextureView position on the screen.
@@ -158,7 +162,7 @@
int result = new SynchronousPixelCopy().request(window, viewPos, screenshot);
assertEquals("Copy request failed", PixelCopy.SUCCESS, result);
// Verify the matrix rotated the TextureView content drawn on the screen.
- PixelCopyTest.assertBitmapQuadColor(screenshot,
+ assertBitmapQuadColor(screenshot,
Color.BLACK, Color.BLUE, Color.GREEN, Color.RED);
}
@@ -178,7 +182,7 @@
activity.getTextureView().getBitmap(bitmap);
});
// Verify the matrix did not affect the content of getTextureView.getBitmap().
- PixelCopyTest.assertBitmapQuadColor(bitmap,
+ assertBitmapQuadColor(bitmap,
Color.RED, Color.GREEN, Color.BLUE, Color.BLACK);
// Remove cover and calculate TextureView position on the screen.
@@ -583,4 +587,10 @@
}
throw new TimeoutException();
}
+
+ private void assertBitmapQuadColor(Bitmap bitmap,
+ int topLeft, int topRight, int bottomLeft, int bottomRight) {
+ PixelCopyTest.assertBitmapQuadColor(mTestName.getMethodName(), "TextureViewTest",
+ bitmap, topLeft, topRight, bottomLeft, bottomRight);
+ }
}
diff --git a/tests/tests/view/src/android/view/cts/ViewUnbufferedTest.java b/tests/tests/view/src/android/view/cts/ViewUnbufferedTest.java
index b0ec086..2645d00 100644
--- a/tests/tests/view/src/android/view/cts/ViewUnbufferedTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewUnbufferedTest.java
@@ -129,6 +129,8 @@
mMaxReceivedCountPerFrame = 0;
PollingCheck.waitFor(mActivity::hasWindowFocus);
mView = mActivity.findViewById(R.id.test_view);
+ // Make sure all window animations are completed.
+ mAutomation.syncInputTransactions(true /* waitForAnimations */);
}
@After
diff --git a/tests/tests/view/src/android/view/cts/util/BitmapDumper.java b/tests/tests/view/src/android/view/cts/util/BitmapDumper.java
new file mode 100644
index 0000000..752e5b8
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/util/BitmapDumper.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.cts.util;
+
+import android.app.Instrumentation;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+/**
+ * A utility class that will allow the user to save bitmaps to the sdcard on the device.
+ */
+public final class BitmapDumper {
+ private static final String TAG = "BitmapDumper";
+ private static final String KEY_PREFIX = "view_";
+ private static final String TYPE_SINGULAR = "capture";
+
+ // Magic number for an in-progress status report
+ private static final int INST_STATUS_IN_PROGRESS = 2;
+ private static boolean sInitialized = false;
+ private static File sDumpDirectory;
+ private static Instrumentation sInstrumentation;
+
+ private BitmapDumper() {}
+
+ private static void initializeIfNeeded() {
+ if (sInitialized) return;
+ sInitialized = true;
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ sDumpDirectory = sInstrumentation.getContext().getExternalCacheDir();
+
+ // Cleanup old tests
+ // These are removed on uninstall anyway but just in case...
+ File[] toRemove = sDumpDirectory.listFiles();
+ if (toRemove != null && toRemove.length > 0) {
+ for (File file : toRemove) {
+ deleteContentsAndDir(file);
+ }
+ }
+ }
+
+ private static boolean deleteContentsAndDir(File dir) {
+ if (deleteContents(dir)) {
+ return dir.delete();
+ } else {
+ return false;
+ }
+ }
+
+ private static boolean deleteContents(File dir) {
+ File[] files = dir.listFiles();
+ boolean success = true;
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ success &= deleteContents(file);
+ }
+ if (!file.delete()) {
+ Log.w(TAG, "Failed to delete " + file);
+ success = false;
+ }
+ }
+ }
+ return success;
+ }
+
+ private static File getFile(String className, String testName, String type) {
+ File testDirectory = new File(sDumpDirectory, className);
+ testDirectory.mkdirs();
+ return new File(testDirectory, testName + "_" + type + ".png");
+ }
+
+ private static String bypassContentProvider(File file) {
+ // TradeFed currently insists on bouncing off of a content provider for the path
+ // we are using, but that content provider will never have permissions
+ // Since we want to avoid needing to use requestLegacyStorage & there's currently no
+ // option to tell TF to not use the content provider, just break its file
+ // detection pattern
+ // b/183140644
+ return "/." + file.getAbsolutePath();
+ }
+
+ public static void dumpBitmap(Bitmap bitmap, String testName, String className) {
+ if (bitmap == null) {
+ Log.d(TAG, "File not saved, bitmap was null for test : " + testName);
+ return;
+ }
+ initializeIfNeeded();
+ File capture = getFile(className, testName, TYPE_SINGULAR);
+ saveBitmap(bitmap, capture);
+ Log.d(TAG, testName + " saved " + capture.getAbsolutePath());
+ Bundle report = new Bundle();
+ report.putString(KEY_PREFIX + TYPE_SINGULAR, bypassContentProvider(capture));
+ sInstrumentation.sendStatus(INST_STATUS_IN_PROGRESS, report);
+ }
+
+ private static void logIfBitmapSolidColor(String fileName, Bitmap bitmap) {
+ int firstColor = bitmap.getPixel(0, 0);
+ for (int x = 0; x < bitmap.getWidth(); x++) {
+ for (int y = 0; y < bitmap.getHeight(); y++) {
+ if (bitmap.getPixel(x, y) != firstColor) {
+ return;
+ }
+ }
+ }
+
+ Log.w(TAG, String.format("%s entire bitmap color is %x", fileName, firstColor));
+ }
+
+ private static void saveBitmap(Bitmap bitmap, File file) {
+ if (bitmap == null) {
+ Log.d(TAG, "File not saved, bitmap was null");
+ return;
+ }
+
+ logIfBitmapSolidColor(file.getName(), bitmap);
+
+ try (FileOutputStream fileStream = new FileOutputStream(file)) {
+ bitmap.compress(Bitmap.CompressFormat.PNG, 0 /* ignored for PNG */, fileStream);
+ fileStream.flush();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java
index e90fe2e..ea91537 100644
--- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java
+++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/ASurfaceControlTestActivity.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -133,11 +134,16 @@
swBitmap.getHeight());
swBitmap.recycle();
- assertTrue(success);
+ assertTrue("Actual matched pixels:" + numMatchingPixels
+ + " Bitmap size:" + swBitmap.getWidth() + "x" + swBitmap.getHeight(), success);
+ }
+
+ public SurfaceView getSurfaceView() {
+ return mSurfaceView;
}
public abstract static class PixelChecker {
- private final PixelColor mPixelColor;
+ private PixelColor mPixelColor;
public PixelChecker() {
mPixelColor = new PixelColor();
@@ -149,10 +155,11 @@
int getNumMatchingPixels(Bitmap bitmap) {
int numMatchingPixels = 0;
- for (int x = 0; x < bitmap.getWidth(); x++) {
- for (int y = 0; y < bitmap.getHeight(); y++) {
+ Rect boundsToCheck = getBoundsToCheck(bitmap);
+ for (int x = boundsToCheck.left; x < boundsToCheck.right; x++) {
+ for (int y = boundsToCheck.top; y < boundsToCheck.bottom; y++) {
int color = bitmap.getPixel(x, y);
- if (matchesColor(mPixelColor, color)) {
+ if (matchesColor(getExpectedColor(x, y), color)) {
numMatchingPixels++;
}
}
@@ -177,6 +184,14 @@
}
public abstract boolean checkPixels(int matchingPixelCount, int width, int height);
+
+ public Rect getBoundsToCheck(Bitmap bitmap) {
+ return new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ }
+
+ public PixelColor getExpectedColor(int x, int y) {
+ return mPixelColor;
+ }
}
public static class SurfaceHolderCallback implements SurfaceHolder.Callback {
@@ -212,5 +227,4 @@
}
}
}
-
}
diff --git a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/AbstractRecognitionServiceTest.java b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/AbstractRecognitionServiceTest.java
index 8d7f8aa..ee6ec03 100644
--- a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/AbstractRecognitionServiceTest.java
+++ b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/AbstractRecognitionServiceTest.java
@@ -44,6 +44,7 @@
import com.android.compatibility.common.util.PollingCheck;
import com.google.common.collect.ImmutableList;
+import com.google.common.truth.TruthJUnit;
import org.junit.Before;
import org.junit.Rule;
@@ -87,6 +88,12 @@
prepareDevice();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mActivity = mActivityTestRule.getActivity();
+ if (isOnDeviceTest()) {
+ // Has to happen before mActivity#init or createOnDeviceSpeechRecognizer() will fail.
+ TruthJUnit.assume()
+ .that(SpeechRecognizer.isOnDeviceRecognitionAvailable(mActivity))
+ .isTrue();
+ }
mActivity.init(isOnDeviceTest(), customRecognizer());
}
@@ -279,8 +286,10 @@
List<CallbackMethod> callbackMethodInstructions,
List<Boolean> expectedRecognizerServiceMethodsToPropagate,
List<CallbackMethod> expectedClientCallbackMethods) {
- setCurrentRecognizer(mActivity.mRecognizer, IN_PACKAGE_RECOGNITION_SERVICE);
mUiDevice.waitForIdle();
+ SpeechRecognizer speechRecognizer = mActivity.mRecognizer;
+ assertThat(speechRecognizer).isNotNull();
+ setCurrentRecognizer(speechRecognizer, IN_PACKAGE_RECOGNITION_SERVICE);
mActivity.mCallbackMethodsInvoked.clear();
CtsRecognitionService.sInvokedRecognizerMethods.clear();
diff --git a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/OnDeviceRecognitionServiceTest.java b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/OnDeviceRecognitionServiceTest.java
index 22a10df..591c992 100644
--- a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/OnDeviceRecognitionServiceTest.java
+++ b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/OnDeviceRecognitionServiceTest.java
@@ -18,13 +18,10 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
-
import android.content.ComponentName;
import android.speech.SpeechRecognizer;
import android.util.Log;
-import androidx.annotation.NonNull;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
@@ -46,8 +43,10 @@
@After
public void tearDown() {
- mRecognizer.setTemporaryOnDeviceRecognizer(null);
- mRecognizer = null;
+ if (mRecognizer != null) {
+ mRecognizer.setTemporaryOnDeviceRecognizer(null);
+ mRecognizer = null;
+ }
getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
}
diff --git a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
index 28ce7bf..c0a850d 100644
--- a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
+++ b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/RecognitionServiceMicIndicatorTest.java
@@ -161,6 +161,7 @@
return componentName != null ? componentName.getPackageName() : "";
}
+ @Ignore("b/184963112")
@Test
public void testNonTrustedRecognitionServiceCanBlameCallingApp() throws Throwable {
// This is a workaound solution for R QPR. We treat trusted if the current voice recognizer
diff --git a/tests/tests/voiceRecognition/src/android/voicerecognition/cts/SpeechRecognizerAvailabilityTest.java b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/SpeechRecognizerAvailabilityTest.java
new file mode 100644
index 0000000..640c6b6
--- /dev/null
+++ b/tests/tests/voiceRecognition/src/android/voicerecognition/cts/SpeechRecognizerAvailabilityTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.voicerecognition.cts;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.speech.SpeechRecognizer;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.truth.TruthJUnit;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public final class SpeechRecognizerAvailabilityTest {
+
+ protected final Context mContext = InstrumentationRegistry.getTargetContext();
+ private ActivityScenario<SpeechRecognitionActivity> mScenario;
+
+ @Rule
+ public ActivityScenarioRule<SpeechRecognitionActivity> mScenarioRule =
+ new ActivityScenarioRule<>(SpeechRecognitionActivity.class);
+
+ @Before
+ public void setup() {
+ prepareDevice();
+ mScenario = mScenarioRule.getScenario();
+ }
+
+ private void prepareDevice() {
+ // Unlock screen.
+ runShellCommand("input keyevent KEYCODE_WAKEUP");
+ // Dismiss keyguard, in case it's set as "Swipe to unlock".
+ runShellCommand("wm dismiss-keyguard");
+ }
+
+ @Test
+ public void testOnDeviceRecognizer_canInstantiateWhenTrue() {
+ mScenario.onActivity(
+ activity -> {
+ TruthJUnit.assume()
+ .that(SpeechRecognizer.isOnDeviceRecognitionAvailable(activity))
+ .isTrue();
+
+ SpeechRecognizer recognizer =
+ SpeechRecognizer.createOnDeviceSpeechRecognizer(activity);
+ assertThat(recognizer).isNotNull();
+ });
+ }
+
+ @Test
+ public void testOnDeviceRecognizer_cannotInstantiateWhenFalse() {
+ mScenario.onActivity(
+ activity -> {
+ TruthJUnit.assume()
+ .that(SpeechRecognizer.isOnDeviceRecognitionAvailable(activity))
+ .isFalse();
+
+ assertThrows(
+ UnsupportedOperationException.class,
+ () -> SpeechRecognizer.createOnDeviceSpeechRecognizer(activity));
+ });
+ }
+}
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
index fa964ac..43448de 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
@@ -21,6 +21,7 @@
import android.os.PersistableBundle;
import android.os.SharedMemory;
import android.service.voice.AlwaysOnHotwordDetector;
+import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionService;
import android.system.ErrnoException;
import android.text.TextUtils;
@@ -43,7 +44,7 @@
// TODO: Check the capture session (needs to be reflectively accessed).
if (eventPayload.getTriggerAudio().length == 1024) {
- callback.onDetected(null);
+ callback.onDetected(new HotwordDetectedResult.Builder().build());
}
}
@@ -88,7 +89,7 @@
if(isSame(buffer, BasicVoiceInteractionService.FAKE_HOTWORD_AUDIO_DATA,
buffer.length)) {
Log.d(TAG, "call callback.onDetected");
- callback.onDetected(null);
+ callback.onDetected(new HotwordDetectedResult.Builder().build());
}
} catch (IOException e) {
Log.w(TAG, "Failed to read data : ", e);
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
index a605ef4..4636dbc 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.platform.test.annotations.AppModeFull;
+import android.service.voice.HotwordDetectionService;
import android.voiceinteraction.common.Utils;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -39,6 +40,12 @@
static final String TAG = "HotwordDetectionServiceBasicTest";
@Test
+ public void testHotwordDetectionService_getMaxCustomInitializationStatus()
+ throws Throwable {
+ assertThat(HotwordDetectionService.getMaxCustomInitializationStatus()).isEqualTo(2);
+ }
+
+ @Test
public void testHotwordDetectionService_validHotwordDetectionComponentName_triggerSuccess()
throws Throwable {
createAndVerifyHotwordDetectionServiceBindSuccess();
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index d5f7155..c43bf16 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -19,18 +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" />
- <option name="test-file-name" value="TestIme.apk" />
- </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!--
+ To fail-fast in case test-ime setup somehow failed. Consider increasing the
+ sleep time below if the following commands fail. See b/188094681 for detais.
+ -->
+ <option name="throw-if-cmd-fail" value="true" />
+ <!--
+ 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" />
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index acee4ad..f02f893 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -225,6 +225,7 @@
@LargeTest
@Test
public void testSetOnScrollListener() throws Throwable {
+ mListView.setOverScrollMode(View.OVER_SCROLL_NEVER);
AbsListView.OnScrollListener mockScrollListener =
mock(AbsListView.OnScrollListener.class);
@@ -528,7 +529,7 @@
mListView.setRecyclerListener(mockRecyclerListener);
List<View> views = new ArrayList<>();
- mListView.reclaimViews(views);
+ mActivityRule.runOnUiThread(() -> mListView.reclaimViews(views));
assertTrue(views.size() > 0);
diff --git a/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewTest.java b/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewTest.java
index 4a0a010..b6ce772 100644
--- a/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/HorizontalScrollViewTest.java
@@ -16,7 +16,7 @@
package android.widget.cts;
-import static android.widget.cts.util.StretchEdgeUtil.dragHoldAndRun;
+import static android.widget.cts.util.StretchEdgeUtil.dragAndHoldExecute;
import static android.widget.cts.util.StretchEdgeUtil.fling;
import static org.junit.Assert.assertEquals;
@@ -42,6 +42,7 @@
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
+import android.widget.cts.util.NoReleaseEdgeEffect;
import android.widget.cts.util.StretchEdgeUtil;
import androidx.test.InstrumentationRegistry;
@@ -62,17 +63,12 @@
import java.util.ArrayList;
-import kotlin.Unit;
-
/**
* Test {@link HorizontalScrollView}.
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
public class HorizontalScrollViewTest {
- static final long USE_STRETCH_EDGE_EFFECT_BY_DEFAULT = 171228096L;
- static final long USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED = 178807038L;
-
private static final int ITEM_WIDTH = 250;
private static final int ITEM_HEIGHT = 100;
private static final int ITEM_COUNT = 15;
@@ -812,18 +808,31 @@
// Make sure that the scroll view we care about is on screen and at the left:
showOnlyStretch();
- assertTrue(StretchEdgeUtil.dragRightStretches(mActivityRule, mScrollViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowLeft = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragStretches(
+ mActivityRule,
+ mScrollViewStretch,
+ edgeEffect,
+ 300,
+ 0
+ ));
}
- // If this test is showing as flaky, it is more likely that it is broken. I've
- // leaned toward false positive over false negative.
- @LargeTest
@Test
public void testStretchAtLeftAndCatch() throws Throwable {
// Make sure that the scroll view we care about is on screen and at the top:
showOnlyStretch();
- assertTrue(StretchEdgeUtil.dragRightTapAndHoldStretches(mActivityRule, mScrollViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowLeft = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragAndHoldKeepsStretch(
+ mActivityRule,
+ mScrollViewStretch,
+ edgeEffect,
+ 300,
+ 0
+ ));
}
@Test
@@ -836,11 +845,17 @@
mScrollViewStretch.scrollTo(210, 0);
});
- assertTrue(StretchEdgeUtil.dragLeftStretches(mActivityRule, mScrollViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowRight = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragStretches(
+ mActivityRule,
+ mScrollViewStretch,
+ edgeEffect,
+ -300,
+ 0
+ ));
}
- // If this test is showing as flaky, it is more likely that it is broken. I've
- // leaned toward false positive over false negative.
@LargeTest
@Test
public void testStretchAtRightAndCatch() throws Throwable {
@@ -852,7 +867,15 @@
mScrollViewStretch.scrollTo(210, 0);
});
- assertTrue(StretchEdgeUtil.dragLeftTapAndHoldStretches(mActivityRule, mScrollViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowRight = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragAndHoldKeepsStretch(
+ mActivityRule,
+ mScrollViewStretch,
+ edgeEffect,
+ -300,
+ 0
+ ));
}
@LargeTest
@@ -862,18 +885,22 @@
showOnlyStretch();
InterceptView interceptView = mActivity.findViewById(R.id.wrapped_stretch);
- dragHoldAndRun(
+
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowLeft = edgeEffect;
+
+ dragAndHoldExecute(
mActivityRule,
mScrollViewStretch,
- mScrollViewStretch.getWidth() / 2,
- mScrollViewStretch.getHeight() / 2,
+ edgeEffect,
300,
0,
- () -> {
- interceptView.requestDisallowInterceptCalled = false;
- return Unit.INSTANCE;
- },
- () -> Unit.INSTANCE
+ () -> interceptView.requestDisallowInterceptCalled = false,
+ null
+ );
+
+ mActivityRule.runOnUiThread(
+ () -> assertFalse(interceptView.requestDisallowInterceptCalled)
);
mActivityRule.runOnUiThread(
diff --git a/tests/tests/widget/src/android/widget/cts/ListViewTest.java b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
index 1ded00e..52f5fc4 100644
--- a/tests/tests/widget/src/android/widget/cts/ListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListViewTest.java
@@ -64,6 +64,7 @@
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.TextView;
+import android.widget.cts.util.NoReleaseEdgeEffect;
import android.widget.cts.util.StretchEdgeUtil;
import android.widget.cts.util.TestUtils;
@@ -1165,18 +1166,31 @@
// Make sure that the view we care about is on screen and at the top:
showOnlyStretch();
- assertTrue(StretchEdgeUtil.dragDownStretches(mActivityRule, mListViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mListViewStretch.mEdgeGlowTop = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragStretches(
+ mActivityRule,
+ mListViewStretch,
+ edgeEffect,
+ 0,
+ 300
+ ));
}
- // If this test is showing as flaky, it is more likely that it is broken. I've
- // leaned toward false positive over false negative.
- @LargeTest
@Test
public void testStretchTopAndCatch() throws Throwable {
// Make sure that the view we care about is on screen and at the top:
showOnlyStretch();
- assertTrue(StretchEdgeUtil.dragDownTapAndHoldStretches(mActivityRule, mListViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mListViewStretch.mEdgeGlowTop = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragAndHoldKeepsStretch(
+ mActivityRule,
+ mListViewStretch,
+ edgeEffect,
+ 0,
+ 300
+ ));
}
private void scrollToBottomOfStretch() throws Throwable {
@@ -1193,19 +1207,32 @@
showOnlyStretch();
scrollToBottomOfStretch();
- assertTrue(StretchEdgeUtil.dragUpStretches(mActivityRule, mListViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mListViewStretch.mEdgeGlowBottom = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragStretches(
+ mActivityRule,
+ mListViewStretch,
+ edgeEffect,
+ 0,
+ -300
+ ));
}
- // If this test is showing as flaky, it is more likely that it is broken. I've
- // leaned toward false positive over false negative.
- @LargeTest
@Test
public void testStretchBottomAndCatch() throws Throwable {
// Make sure that the view we care about is on screen and at the top:
showOnlyStretch();
scrollToBottomOfStretch();
- assertTrue(StretchEdgeUtil.dragUpTapAndHoldStretches(mActivityRule, mListViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mListViewStretch.mEdgeGlowBottom = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragAndHoldKeepsStretch(
+ mActivityRule,
+ mListViewStretch,
+ edgeEffect,
+ 0,
+ -300
+ ));
}
@Test
diff --git a/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java b/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java
index 63b1dd9..adb8fd5 100644
--- a/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ScrollViewTest.java
@@ -16,7 +16,7 @@
package android.widget.cts;
-import static android.widget.cts.util.StretchEdgeUtil.dragHoldAndRun;
+import static android.widget.cts.util.StretchEdgeUtil.dragAndHoldExecute;
import static android.widget.cts.util.StretchEdgeUtil.fling;
import static org.junit.Assert.assertEquals;
@@ -42,6 +42,7 @@
import android.widget.FrameLayout;
import android.widget.ScrollView;
import android.widget.TextView;
+import android.widget.cts.util.NoReleaseEdgeEffect;
import android.widget.cts.util.StretchEdgeUtil;
import android.widget.cts.util.TestUtils;
@@ -60,8 +61,6 @@
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
-import kotlin.Unit;
-
/**
* Test {@link ScrollView}.
*/
@@ -76,9 +75,6 @@
private static final int PAGE_HEIGHT_DPI = 100;
private static final int TOLERANCE = 2;
- static final long USE_STRETCH_EDGE_EFFECT_BY_DEFAULT = 171228096L;
- static final long USE_STRETCH_EDGE_EFFECT_FOR_SUPPORTED = 178807038L;
-
private int mItemWidth;
private int mItemHeight;
private int mPageWidth;
@@ -856,18 +852,31 @@
// Make sure that the scroll view we care about is on screen and at the top:
showOnlyStretch();
- assertTrue(StretchEdgeUtil.dragDownStretches(mActivityRule, mScrollViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowTop = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragStretches(
+ mActivityRule,
+ mScrollViewStretch,
+ edgeEffect,
+ 0,
+ 300
+ ));
}
- // If this test is showing as flaky, it is more likely that it is broken. I've
- // leaned toward false positive over false negative.
- @LargeTest
@Test
public void testStretchAtTopAndCatch() throws Throwable {
// Make sure that the scroll view we care about is on screen and at the top:
showOnlyStretch();
- assertTrue(StretchEdgeUtil.dragDownTapAndHoldStretches(mActivityRule, mScrollViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowTop = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragAndHoldKeepsStretch(
+ mActivityRule,
+ mScrollViewStretch,
+ edgeEffect,
+ 0,
+ 3000
+ ));
}
@LargeTest
@@ -877,18 +886,18 @@
showOnlyStretch();
InterceptView interceptView = mActivity.findViewById(R.id.wrapped_stretch);
- Unit result = dragHoldAndRun(
+
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowTop = edgeEffect;
+
+ dragAndHoldExecute(
mActivityRule,
mScrollViewStretch,
- mScrollViewStretch.getWidth() / 2,
- mScrollViewStretch.getHeight() / 2,
+ edgeEffect,
0,
300,
- () -> {
- interceptView.requestDisallowInterceptCalled = false;
- return Unit.INSTANCE;
- },
- () -> Unit.INSTANCE
+ () -> interceptView.requestDisallowInterceptCalled = false,
+ null
);
mActivityRule.runOnUiThread(
@@ -906,12 +915,17 @@
mScrollViewStretch.scrollTo(0, 210);
});
- assertTrue(StretchEdgeUtil.dragUpStretches(mActivityRule, mScrollViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowBottom = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragStretches(
+ mActivityRule,
+ mScrollViewStretch,
+ edgeEffect,
+ 0,
+ -300
+ ));
}
- // If this test is showing as flaky, it is more likely that it is broken. I've
- // leaned toward false positive over false negative.
- @LargeTest
@Test
public void testStretchAtBottomAndCatch() throws Throwable {
// Make sure that the scroll view we care about is on screen and at the top:
@@ -922,7 +936,15 @@
mScrollViewStretch.scrollTo(0, 210);
});
- assertTrue(StretchEdgeUtil.dragUpTapAndHoldStretches(mActivityRule, mScrollViewStretch));
+ NoReleaseEdgeEffect edgeEffect = new NoReleaseEdgeEffect(mActivity);
+ mScrollViewStretch.mEdgeGlowBottom = edgeEffect;
+ assertTrue(StretchEdgeUtil.dragAndHoldKeepsStretch(
+ mActivityRule,
+ mScrollViewStretch,
+ edgeEffect,
+ 0,
+ -300
+ ));
}
@Test
diff --git a/tests/tests/widget/src/android/widget/cts/util/StretchEdgeUtil.kt b/tests/tests/widget/src/android/widget/cts/util/StretchEdgeUtil.kt
index 7fa0682..b06c52a 100644
--- a/tests/tests/widget/src/android/widget/cts/util/StretchEdgeUtil.kt
+++ b/tests/tests/widget/src/android/widget/cts/util/StretchEdgeUtil.kt
@@ -17,105 +17,17 @@
package android.widget.cts.util
-import android.graphics.Bitmap
-import android.graphics.Color
-import android.graphics.Rect
+import android.app.Instrumentation
+import android.app.UiAutomation
+import android.content.Context
import android.os.SystemClock
-import android.view.PixelCopy
+import android.view.InputDevice
+import android.view.MotionEvent
import android.view.View
-import android.view.Window
-import android.view.animation.AnimationUtils
+import android.widget.EdgeEffect
import androidx.test.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
-import com.android.compatibility.common.util.CtsTouchUtils
-import com.android.compatibility.common.util.CtsTouchUtils.EventInjectionListener
-import com.android.compatibility.common.util.SynchronousPixelCopy
-import org.junit.Assert
-
-/* ---------------------------------------------------------------------------
- * This file contains utility functions for testing the overscroll stretch
- * effect. Containers are 90 x 90 pixels and contains colored rectangles
- * that are 90 x 50 pixels (or 50 x 90 pixels for horizontal containers).
- *
- * The first rectangle must be Color.BLUE and the last rectangle must be
- * Color.MAGENTA.
- * ---------------------------------------------------------------------------
- */
-
-/**
- * This sleeps until the [AnimationUtils.currentAnimationTimeMillis] changes
- * by at least `durationMillis` milliseconds. This is useful for EdgeEffect because
- * it uses that mechanism to determine the animation duration.
- *
- * @param durationMillis The time to sleep in milliseconds.
- */
-private fun sleepAnimationTime(durationMillis: Long) {
- val startTime = AnimationUtils.currentAnimationTimeMillis()
- var currentTime = startTime
- val endTime = startTime + durationMillis
- do {
- Thread.sleep(endTime - currentTime)
- currentTime = AnimationUtils.currentAnimationTimeMillis()
- } while (currentTime < endTime)
-}
-
-/**
- * Takes a screen shot at the given coordinates and returns the Bitmap.
- */
-private fun takeScreenshot(
- window: Window,
- screenPositionX: Int,
- screenPositionY: Int,
- width: Int,
- height: Int
-): Bitmap {
- val copy = SynchronousPixelCopy()
- val dest = Bitmap.createBitmap(
- width, height,
- if (window.isWideColorGamut()) Bitmap.Config.RGBA_F16 else Bitmap.Config.ARGB_8888)
- val srcRect = Rect(0, 0, width, height)
- srcRect.offset(screenPositionX, screenPositionY)
- val copyResult: Int = copy.request(window, srcRect, dest)
- Assert.assertEquals(PixelCopy.SUCCESS.toLong(), copyResult.toLong())
- return dest
-}
-
-/**
- * Drags an area of the screen and executes [onFinalMove] after sending the final drag
- * motion and [onUp] after the drag up event has been sent.
- */
-fun dragAndExecute(
- activityRule: ActivityTestRule<*>,
- screenX: Int,
- screenY: Int,
- deltaX: Int,
- deltaY: Int,
- onFinalMove: () -> Unit = {},
- onUp: () -> Unit = {}
-) {
- val instrumentation = InstrumentationRegistry.getInstrumentation()
- CtsTouchUtils.emulateDragGesture(instrumentation, activityRule,
- screenX,
- screenY,
- deltaX,
- deltaY,
- 160,
- 20,
- object : EventInjectionListener {
- private var mNumEvents = 0
- override fun onDownInjected(xOnScreen: Int, yOnScreen: Int) {}
- override fun onMoveInjected(xOnScreen: IntArray, yOnScreen: IntArray) {
- mNumEvents++
- if (mNumEvents == 20) {
- onFinalMove()
- }
- }
-
- override fun onUpInjected(xOnScreen: Int, yOnScreen: Int) {
- onUp()
- }
- })
-}
+import com.android.compatibility.common.util.WidgetTestUtils
/**
* Flings [view] from the center by ([deltaX], [deltaY]) pixels over 16 milliseconds.
@@ -135,32 +47,27 @@
val screenY = locationOnScreen[1]
val instrumentation = InstrumentationRegistry.getInstrumentation()
- CtsTouchUtils.emulateDragGesture(instrumentation, activityRule,
+ emulateDragGesture(instrumentation, activityRule,
screenX + (view.width / 2),
screenY + (view.height / 2),
deltaX,
deltaY,
16,
- 4,
- null
+ 4
)
}
/**
- * Drags inside [view] starting at coordinates ([viewX], [viewY]) relative to [view] and moving
- * ([deltaX], [deltaY]) pixels before lifting. A Bitmap is captured after the final drag event,
- * before the up event.
- * @return A Bitmap of [view] after the final drag motion event.
+ * Drags [view] from the center by [dragX], [dragY]. Returns `true` if [edgeEffect]'s distance
+ * is more than 0 after the stretch.
*/
-private fun dragAndCapture(
+fun dragStretches(
activityRule: ActivityTestRule<*>,
view: View,
- viewX: Int,
- viewY: Int,
- deltaX: Int,
- deltaY: Int
-): Bitmap {
- var bitmap: Bitmap? = null
+ edgeEffect: NoReleaseEdgeEffect,
+ dragX: Int,
+ dragY: Int
+): Boolean {
val locationOnScreen = IntArray(2)
activityRule.runOnUiThread {
view.getLocationOnScreen(locationOnScreen)
@@ -168,310 +75,230 @@
val screenX = locationOnScreen[0]
val screenY = locationOnScreen[1]
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ emulateDragGesture(instrumentation, activityRule,
+ screenX + view.width / 2,
+ screenY + view.height / 2,
+ dragX,
+ dragY,
+ 160,
+ 20
+ )
+ return edgeEffect.distance > 0
+}
- dragAndExecute(
- activityRule = activityRule,
- screenX = screenX + viewX,
- screenY = screenY + viewY,
- deltaX = deltaX,
- deltaY = deltaY,
- onFinalMove = {
- bitmap = takeScreenshot(
- activityRule.activity.window,
- screenX,
- screenY,
- view.width,
- view.height
- )
+/**
+ * Drags [view] from the center by [dragX], [dragY], then taps to hold.
+ * [edgeEffect] must be the [EdgeEffect] that is being stretched in [view].
+ * After the drag and before the tap down, [beforeDown] is called. After the
+ * tap down and before the tap up, [beforeUp] is called.
+ */
+fun dragAndHoldExecute(
+ activityRule: ActivityTestRule<*>,
+ view: View,
+ edgeEffect: NoReleaseEdgeEffect,
+ dragX: Int = 0,
+ dragY: Int = 0,
+ beforeDown: Runnable?,
+ beforeUp: Runnable?
+) {
+ val locationOnScreen = IntArray(2)
+ activityRule.runOnUiThread {
+ view.getLocationOnScreen(locationOnScreen)
+ }
+
+ val screenX = locationOnScreen[0] + view.width / 2
+ val screenY = locationOnScreen[1] + view.height / 2
+ val instrumentation = InstrumentationRegistry.getInstrumentation()
+ emulateDragGesture(instrumentation, activityRule,
+ screenX,
+ screenY,
+ dragX,
+ dragY,
+ 160,
+ 20
+ )
+ edgeEffect.pauseRelease = false
+ edgeEffect.onRelease()
+
+ beforeDown?.run()
+
+ val downTime = SystemClock.uptimeMillis()
+ injectDownEvent(
+ instrumentation.getUiAutomation(),
+ downTime,
+ screenX,
+ screenY
+ )
+
+ beforeUp?.run()
+
+ injectUpEvent(
+ instrumentation.getUiAutomation(),
+ downTime,
+ downTime + 10,
+ screenX,
+ screenY
+ )
+}
+
+/**
+ * Drag, release, then tap-and-hold and ensure that the stretch stays after the hold.
+ */
+fun dragAndHoldKeepsStretch(
+ activityRule: ActivityTestRule<*>,
+ view: View,
+ edgeEffect: NoReleaseEdgeEffect,
+ dragX: Int = 0,
+ dragY: Int = 0
+): Boolean {
+ var startDistance = 0f
+ var nextFrameDistance = 0f
+
+ dragAndHoldExecute(
+ activityRule,
+ view,
+ edgeEffect,
+ dragX,
+ dragY,
+ beforeDown = null,
+ beforeUp = Runnable {
+ activityRule.runOnUiThread {
+ startDistance = edgeEffect.distance
+ }
+ activityRule.runOnUiThread {
+ nextFrameDistance = edgeEffect.distance
+ }
}
)
- return bitmap!!
+
+ return startDistance == nextFrameDistance && startDistance > 0f
}
/**
- * Drags in [view], starting at coordinates ([viewX], [viewY]) relative to [view] and moving
- * ([deltaX], [deltaY]) pixels before lifting. Immediately after the up event, a down event
- * is sent. If it happens within 50 milliseconds of the last motion event, the Bitmap is captured
- * after 600ms more. If an animation was going to run, this allows that animation to finish before
- * capturing the Bitmap. This is attempted up to 5 times.
- *
- * @return A Bitmap of [view] after the drag, release, then tap and hold, or `null` if the
- * device did not respond quickly enough.
+ * An [EdgeEffect] that does not release with [onRelease] unless [pauseRelease] is `false`.
*/
-private fun dragHoldAndCapture(
- activityRule: ActivityTestRule<*>,
- view: View,
- viewX: Int,
- viewY: Int,
- deltaX: Int,
- deltaY: Int
-): Bitmap? {
- val locationOnScreen = IntArray(2)
- activityRule.runOnUiThread {
- view.getLocationOnScreen(locationOnScreen)
- }
+class NoReleaseEdgeEffect(context: Context) : EdgeEffect(context) {
+ var pauseRelease = true
- val screenX = locationOnScreen[0]
- val screenY = locationOnScreen[1]
-
- return dragHoldAndRun(
- activityRule,
- view,
- viewX,
- viewY,
- deltaX,
- deltaY
- ) {
- takeScreenshot(
- activityRule.activity.window,
- screenX,
- screenY,
- view.width,
- view.height
- )
- }
-}
-
-/**
- * Drags in [view], starting at coordinates ([viewX], [viewY]) relative to [view] and moving
- * ([deltaX], [deltaY]) pixels before lifting. Immediately after the up event,
- * [runBeforeTapDown] is called and then a down event is sent. If it happens within 50 milliseconds
- * of the last motion event, [runAfterTapDown] is run after 600ms more. If an animation was going
- * to run, this allows that animation to finish before [runAfterTapDown] is executed.
- * This is attempted up to 5 times.
- *
- * @return The return value from [runAfterTapDown] or `null` if the device did not respond quickly
- * enough.
- */
-fun <T> dragHoldAndRun(
- activityRule: ActivityTestRule<*>,
- view: View,
- viewX: Int,
- viewY: Int,
- deltaX: Int,
- deltaY: Int,
- runBeforeTapDown: () -> Unit = {},
- runAfterTapDown: () -> T
-): T? {
- val locationOnScreen = IntArray(2)
- activityRule.runOnUiThread {
- view.getLocationOnScreen(locationOnScreen)
- }
-
- val screenX = locationOnScreen[0]
- val screenY = locationOnScreen[1]
-
- val instrumentation = InstrumentationRegistry.getInstrumentation()
-
- // Try 5 times at most. If it fails, just return the null bitmap
- repeat(5) {
- var lastMotion = 0L
- var returnValue: T? = null
- dragAndExecute(
- activityRule = activityRule,
- screenX = screenX + viewX,
- screenY = screenY + viewY,
- deltaX = deltaX,
- deltaY = deltaY,
- onFinalMove = {
- lastMotion = AnimationUtils.currentAnimationTimeMillis()
- },
- onUp = {
- // Now press
- runBeforeTapDown()
- CtsTouchUtils.injectDownEvent(instrumentation.getUiAutomation(),
- SystemClock.uptimeMillis(), screenX + viewX,
- screenY + viewY, null)
-
- val downInjected = AnimationUtils.currentAnimationTimeMillis()
-
- // The receding time is based on the spring, but 100 ms should be soon
- // enough that the animation is within the beginning and it shouldn't have
- // receded far yet.
- if (downInjected - lastMotion < 50) {
- // Now make sure that we wait until the release should normally have finished:
- sleepAnimationTime(600)
-
- returnValue = runAfterTapDown()
- }
- }
- )
-
- CtsTouchUtils.injectUpEvent(instrumentation.getUiAutomation(),
- SystemClock.uptimeMillis(), false,
- screenX + viewX, screenY + viewY, null)
-
- if (returnValue != null) {
- return returnValue // success!
+ override fun onRelease() {
+ if (!pauseRelease) {
+ super.onRelease()
}
}
- return null // timing didn't allow for success this time, so return a null
}
/**
- * Drags down on [view] and ensures that the blue rectangle is stretched to beyond its normal
- * size.
+ * Emulates a linear drag gesture between 2 points across the screen.
+ *
+ * @param instrumentation the instrumentation used to run the test
+ * @param dragStartX Start X of the emulated drag gesture
+ * @param dragStartY Start Y of the emulated drag gesture
+ * @param dragAmountX X amount of the emulated drag gesture
+ * @param dragAmountY Y amount of the emulated drag gesture
+ * @param dragDurationMs The time in milliseconds over which the drag occurs
+ * @param moveEventCount The number of events that produce the movement
*/
-fun dragDownStretches(
- activityRule: ActivityTestRule<*>,
- view: View
-): Boolean {
- val bitmap = dragAndCapture(
- activityRule,
- view,
- 45,
- 20,
- 0,
- 300
- )
+private fun emulateDragGesture(
+ instrumentation: Instrumentation,
+ activityTestRule: ActivityTestRule<*>?,
+ dragStartX: Int,
+ dragStartY: Int,
+ dragAmountX: Int,
+ dragAmountY: Int,
+ dragDurationMs: Int,
+ moveEventCount: Int
+) {
+ // We are using the UiAutomation object to inject events so that drag works
+ // across view / window boundaries (such as for the emulated drag and drop
+ // sequences)
+ val uiAutomation = instrumentation.uiAutomation
+ val downTime = SystemClock.uptimeMillis()
+ injectDownEvent(uiAutomation, downTime, dragStartX, dragStartY)
- // The blue should stretch beyond its normal dimensions
- return bitmap.getPixel(45, 51) == Color.BLUE
+ val dragEndX = dragStartX + dragAmountX
+ val dragEndY = dragStartY + dragAmountY
+ // Inject a sequence of MOVE events that emulate the "move" part of the gesture
+ injectMoveEventsForDrag(uiAutomation, downTime, downTime, dragStartX, dragStartY,
+ dragEndX, dragEndY, moveEventCount, dragDurationMs)
+ injectUpEvent(uiAutomation, downTime, downTime + dragDurationMs, dragEndX, dragEndY)
+
+ // Wait for the system to process all events in the queue
+ if (activityTestRule != null) {
+ WidgetTestUtils.runOnMainAndDrawSync(activityTestRule,
+ activityTestRule.activity.getWindow().getDecorView(), null)
+ } else {
+ instrumentation.waitForIdleSync()
+ }
+}
+
+private fun injectMoveEventsForDrag(
+ uiAutomation: UiAutomation,
+ downTime: Long,
+ dragStartTime: Long = downTime,
+ dragStartX: Int,
+ dragStartY: Int,
+ dragEndX: Int,
+ dragEndY: Int,
+ moveEventCount: Int,
+ dragDurationMs: Int
+) {
+ val dragAmountX = dragEndX - dragStartX
+ val dragAmountY = dragEndY - dragStartY
+
+ for (i in 0 until moveEventCount) {
+ // Note that the first MOVE event is generated "away" from the coordinates
+ // of the start / DOWN event, and the last MOVE event is generated
+ // at the same coordinates as the subsequent UP event.
+ val moveX = dragStartX + (dragAmountX * i / moveEventCount)
+ val moveY = dragStartY + (dragAmountY * i / moveEventCount)
+ val eventTime = dragStartTime + (dragDurationMs * i / moveEventCount)
+ injectEvent(uiAutomation, MotionEvent.ACTION_MOVE, downTime, eventTime, moveX, moveY)
+ }
}
/**
- * Drags right on [view] and ensures that the blue rectangle is stretched to beyond its normal
- * size.
+ * Injects an [MotionEvent.ACTION_UP] event at the given coordinates.
+ *
+ * @param downTime The time of the event, usually from [SystemClock.uptimeMillis]
+ * @param xOnScreen The x screen coordinate to press on
+ * @param yOnScreen The y screen coordinate to press on
+ * sent.
*/
-fun dragRightStretches(
- activityRule: ActivityTestRule<*>,
- view: View
-): Boolean {
- val bitmap = dragAndCapture(
- activityRule,
- view,
- 20,
- 45,
- 300,
- 0
- )
-
- // The blue should stretch beyond its normal dimensions
- return bitmap.getPixel(50, 45) == Color.BLUE
-}
+private fun injectUpEvent(
+ uiAutomation: UiAutomation,
+ downTime: Long,
+ upTime: Long,
+ xOnScreen: Int,
+ yOnScreen: Int
+) = injectEvent(uiAutomation, MotionEvent.ACTION_UP, downTime, upTime, xOnScreen, yOnScreen)
/**
- * Drags up on [view] and ensures that the magenta rectangle is stretched to beyond its normal
- * size.
+ * Injects an [MotionEvent.ACTION_DOWN] event at the given coordinates.
+ *
+ * @param downTime The time of the event, usually from [SystemClock.uptimeMillis]
+ * @param xOnScreen The x screen coordinate to press on
+ * @param yOnScreen The y screen coordinate to press on
+ * sent.
*/
-fun dragUpStretches(
- activityRule: ActivityTestRule<*>,
- view: View
-): Boolean {
- val bitmap = dragAndCapture(
- activityRule,
- view,
- 45,
- 70,
- 0,
- -300
- )
+private fun injectDownEvent(
+ uiAutomation: UiAutomation,
+ downTime: Long,
+ xOnScreen: Int,
+ yOnScreen: Int
+) = injectEvent(uiAutomation, MotionEvent.ACTION_DOWN, downTime, downTime, xOnScreen, yOnScreen)
- // The magenta should stretch beyond its normal dimensions
- return bitmap.getPixel(45, 39) == Color.MAGENTA
-}
-
-/**
- * Drags left on [view] and ensures that the magenta rectangle is stretched to beyond its normal
- * size.
- */
-fun dragLeftStretches(
- activityRule: ActivityTestRule<*>,
- view: View
-): Boolean {
- val bitmap = dragAndCapture(
- activityRule,
- view,
- 70,
- 45,
- -300,
- 0
- )
-
- // The magenta should stretch beyond its normal dimensions
- return bitmap.getPixel(39, 45) == Color.MAGENTA
-}
-
-/**
- * Drags down, then taps and holds to ensure that holding stops the stretch from receding.
- * @return `true` if the hold event prevented the stretch from being released.
- */
-fun dragDownTapAndHoldStretches(
- activityRule: ActivityTestRule<*>,
- view: View
-): Boolean {
- val bitmap = dragHoldAndCapture(
- activityRule,
- view,
- 45,
- 20,
- 0,
- 300
- ) ?: return true // when timing fails to get a bitmap, don't treat it as a flake
-
- // The blue should stretch beyond its normal dimensions
- return bitmap.getPixel(45, 50) == Color.BLUE
-}
-
-/**
- * Drags right, then taps and holds to ensure that holding stops the stretch from receding.
- * @return `true` if the hold event prevented the stretch from being released.
- */
-fun dragRightTapAndHoldStretches(
- activityRule: ActivityTestRule<*>,
- view: View
-): Boolean {
- val bitmap = dragHoldAndCapture(
- activityRule,
- view,
- 20,
- 45,
- 300,
- 0
- ) ?: return true // when timing fails to get a bitmap, don't treat it as a flake
-
- // The blue should stretch beyond its normal dimensions
- return bitmap.getPixel(50, 45) == Color.BLUE
-}
-
-/**
- * Drags up, then taps and holds to ensure that holding stops the stretch from receding.
- * @return `true` if the hold event prevented the stretch from being released.
- */
-fun dragUpTapAndHoldStretches(
- activityRule: ActivityTestRule<*>,
- view: View
-): Boolean {
- val bitmap = dragHoldAndCapture(
- activityRule,
- view,
- 45,
- 70,
- 0,
- -300
- ) ?: return true // when timing fails to get a bitmap, don't treat it as a flake
-
- // The magenta should stretch beyond its normal dimensions
- return bitmap.getPixel(45, 39) == Color.MAGENTA
-}
-
-/**
- * Drags left, then taps and holds to ensure that holding stops the stretch from receding.
- * @return `true` if the hold event prevented the stretch from being released.
- */
-fun dragLeftTapAndHoldStretches(
- activityRule: ActivityTestRule<*>,
- view: View
-): Boolean {
- val bitmap = dragHoldAndCapture(
- activityRule,
- view,
- 70,
- 45,
- -300,
- 0
- ) ?: return true // when timing fails to get a bitmap, don't treat it as a flake
-
- // The magenta should stretch beyond its normal dimensions
- return bitmap.getPixel(39, 45) == Color.MAGENTA
+private fun injectEvent(
+ uiAutomation: UiAutomation,
+ action: Int,
+ downTime: Long,
+ eventTime: Long,
+ xOnScreen: Int,
+ yOnScreen: Int
+) {
+ val eventUp = MotionEvent.obtain(
+ downTime, eventTime, action, xOnScreen.toFloat(), yOnScreen.toFloat(), 1)
+ eventUp.source = InputDevice.SOURCE_TOUCHSCREEN
+ uiAutomation.injectInputEvent(eventUp, true)
+ eventUp.recycle()
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java
index 101dc7c..2647aa2 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/ConnectedNetworkScorerTest.java
@@ -39,6 +39,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@@ -370,6 +371,23 @@
}
}
+ /**
+ * Tests the {@link android.net.wifi.WifiManager#setWifiScoringEnabled(boolean)}
+ *
+ * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
+ */
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ @Test
+ public void testSetWifiScoringEnabled() throws Exception {
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ assertTrue(mWifiManager.setWifiScoringEnabled(true));
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
private static abstract class TestConnectedNetworkScorer implements
WifiManager.WifiConnectedNetworkScorer {
protected CountDownLatch mCountDownLatch;
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java b/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java
index f4307b7..c21432c 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/TestHelper.java
@@ -794,4 +794,22 @@
}
}
+ public static int getBandFromFrequency(final int freqMHz) {
+ if (freqMHz < 1000) {
+ return ScanResult.UNSPECIFIED;
+ } else if (freqMHz < 4000) { // getFrequency is in WifiInfo.FREQUENCY_UNITS = MHz
+ return ScanResult.WIFI_BAND_24_GHZ;
+ } else if (freqMHz < 5900) {
+ // 5GHz band stops at 5885MHz, 6GHz band starts at 5955. See android.net.wifi.ScanResult
+ return ScanResult.WIFI_BAND_5_GHZ;
+ } else if (freqMHz < 10_000) {
+ return ScanResult.WIFI_BAND_6_GHZ;
+ } else if (freqMHz < 71_000) {
+ // 60 GHz band stops at 70_200
+ return ScanResult.WIFI_BAND_60_GHZ;
+ } else {
+ return ScanResult.UNSPECIFIED;
+ }
+ }
+
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiBackupRestoreTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiBackupRestoreTest.java
index 1f587d0..b114d5b 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiBackupRestoreTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiBackupRestoreTest.java
@@ -384,12 +384,13 @@
}
/**
- * Asserts that the 2 lists of WifiConfigurations are equal. This compares all the elements
- * saved for backup/restore.
+ * Check that expected configrations could be found in restored configurations.
+ * As multi-type configurations would be converted to several single-type configurations,
+ * two list could not be compared directly.
*/
private void assertConfigurationsEqual(
List<WifiConfiguration> expected, List<WifiConfiguration> actual) {
- assertThat(actual.size()).isEqualTo(expected.size());
+ assertThat(actual.size() >= expected.size()).isTrue();
for (WifiConfiguration expectedConfiguration : expected) {
String expectedConfigKey = expectedConfiguration.getKey();
boolean didCompare = false;
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiBuildCompat.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiBuildCompat.java
index 0730e37..46e5a78 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiBuildCompat.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiBuildCompat.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageManager;
+import android.util.Log;
import androidx.core.os.BuildCompat;
@@ -44,7 +45,9 @@
* of the module from the provided dessert release.
*/
public class WifiBuildCompat {
- private static final String WIFI_APEX_NAME = "com.android.wifi";
+ private static final String TAG = "WifiBuildCompat";
+
+ private static final String WIFI_PACKAGE_NAME_SUFFIX = ".android.wifi";
private static final long WIFI_APEX_BASE_VERSION_CODE_FOR_S = 310000000;
@@ -52,14 +55,21 @@
PackageManager packageManager = ctx.getPackageManager();
long wifiStackVersion = 0;
try {
- ModuleInfo wifiModule = packageManager.getModuleInfo(
- WIFI_APEX_NAME, PackageManager.MODULE_APEX_NAME);
- String wifiPackageName = wifiModule.getPackageName();
+ String wifiPackageName = null;
+ for (ModuleInfo moduleInfo : packageManager.getInstalledModules(0)) {
+ if (moduleInfo.getPackageName().endsWith(WIFI_PACKAGE_NAME_SUFFIX)) {
+ wifiPackageName = moduleInfo.getPackageName();
+ break;
+ }
+ }
if (wifiPackageName != null) {
wifiStackVersion = packageManager.getPackageInfo(
wifiPackageName, PackageManager.MATCH_APEX).getLongVersionCode();
}
+ Log.v(TAG, "Wifi Module package name is " + wifiPackageName
+ + ", version is " + wifiStackVersion);
} catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Wifi Module is missing! Exception: " + e.getMessage());
}
return wifiStackVersion;
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
index ec7f740..e7d5c9a 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -916,12 +916,8 @@
assertThat(copy.getRealm()).isEqualTo(REALM);
}
- /**
- * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
- */
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
public void testIsEnterpriseConfigServerCertNotEnabled() {
- if (!hasWifi()) {
+ if (!hasWifi() || !WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) {
return;
}
WifiEnterpriseConfig baseConfig = new WifiEnterpriseConfig();
@@ -947,34 +943,22 @@
assertFalse(noValidationConfig.isEapMethodServerCertUsed());
}
- /**
- * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
- */
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
public void testIsEnterpriseConfigServerCertEnabledWithPeap() {
- if (!hasWifi()) {
+ if (!hasWifi() || !WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) {
return;
}
testIsEnterpriseConfigServerCertEnabled(Eap.PEAP);
}
- /**
- * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
- */
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
public void testIsEnterpriseConfigServerCertEnabledWithTls() {
- if (!hasWifi()) {
+ if (!hasWifi() || !WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) {
return;
}
testIsEnterpriseConfigServerCertEnabled(Eap.TLS);
}
- /**
- * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
- */
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
public void testIsEnterpriseConfigServerCertEnabledWithTTLS() {
- if (!hasWifi()) {
+ if (!hasWifi() || !WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(getContext())) {
return;
}
testIsEnterpriseConfigServerCertEnabled(Eap.TTLS);
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
index 7df9e91..f37b62c 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -25,6 +25,7 @@
import static android.net.wifi.WifiManager.COEX_RESTRICTION_WIFI_DIRECT;
import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertNotEquals;
@@ -1208,11 +1209,12 @@
return;
}
UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ WifiManager.AddNetworkResult result = null;
try {
uiAutomation.adoptShellPermissionIdentity();
WifiConfiguration newOpenNetwork = new WifiConfiguration();
newOpenNetwork.SSID = "\"" + TEST_SSID_UNQUOTED + "\"";
- WifiManager.AddNetworkResult result = mWifiManager.addNetworkPrivileged(newOpenNetwork);
+ result = mWifiManager.addNetworkPrivileged(newOpenNetwork);
assertEquals(WifiManager.AddNetworkResult.STATUS_SUCCESS, result.statusCode);
assertTrue(result.networkId >= 0);
List<WifiConfiguration> configuredNetworks = mWifiManager.getConfiguredNetworks();
@@ -1224,9 +1226,145 @@
break;
}
}
- assertTrue("addNetworkPrivileged returns success but the network is not found", found);
- mWifiManager.removeNetwork(result.networkId);
+ assertTrue("addNetworkPrivileged returns success"
+ + "but the network is not found in getConfiguredNetworks", found);
+
+ List<WifiConfiguration> privilegedConfiguredNetworks =
+ mWifiManager.getPrivilegedConfiguredNetworks();
+ found = false;
+ for (WifiConfiguration config : privilegedConfiguredNetworks) {
+ if (config.networkId == result.networkId
+ && config.SSID.equals(newOpenNetwork.SSID)) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue("addNetworkPrivileged returns success"
+ + "but the network is not found in getPrivilegedConfiguredNetworks", found);
+
+ List<WifiConfiguration> callerConfiguredNetworks =
+ mWifiManager.getCallerConfiguredNetworks();
+ found = false;
+ for (WifiConfiguration config : callerConfiguredNetworks) {
+ if (config.networkId == result.networkId
+ && config.SSID.equals(newOpenNetwork.SSID)) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue("addNetworkPrivileged returns success"
+ + "but the network is not found in getCallerConfiguredNetworks", found);
} finally {
+ if (null != result) {
+ mWifiManager.removeNetwork(result.networkId);
+ }
+ uiAutomation.dropShellPermissionIdentity();
+ }
+ }
+
+ private WifiConfiguration createConfig(
+ String ssid, int type) {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = "\"" + ssid + "\"";
+ config.setSecurityParams(type);
+ // set necessary fields for different types.
+ switch (type) {
+ case WifiConfiguration.SECURITY_TYPE_OPEN:
+ case WifiConfiguration.SECURITY_TYPE_OWE:
+ break;
+ case WifiConfiguration.SECURITY_TYPE_PSK:
+ case WifiConfiguration.SECURITY_TYPE_SAE:
+ config.preSharedKey = "\"1qaz@WSX\"";
+ break;
+ case WifiConfiguration.SECURITY_TYPE_EAP:
+ case WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE:
+ case WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE_192_BIT:
+ config.enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.SIM);
+ break;
+ }
+ return config;
+ }
+
+ private void assertConfigsAreFound(
+ List<WifiConfiguration> expectedConfigs,
+ List<WifiConfiguration> configs) {
+ for (WifiConfiguration expectedConfig: expectedConfigs) {
+ boolean found = false;
+ for (WifiConfiguration config : configs) {
+ if (config.networkId == expectedConfig.networkId
+ && config.getKey().equals(expectedConfig.getKey())) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue("the network " + expectedConfig.getKey() + " is not found", found);
+ }
+ }
+
+ /**
+ * Verify {@link WifiManager#addNetworkPrivileged(WifiConfiguration)} works
+ * with merging types properly when the calling app has permissions.
+ */
+ public void testAddNetworkPrivilegedMergingTypeSuccess() {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(mContext)) {
+ // Skip the test if wifi module version is older than S.
+ return;
+ }
+ List<WifiConfiguration> testConfigs = new ArrayList<>();
+ testConfigs.add(createConfig("test-open-owe-jdur", WifiConfiguration.SECURITY_TYPE_OPEN));
+ testConfigs.add(createConfig("test-open-owe-jdur", WifiConfiguration.SECURITY_TYPE_OWE));
+ testConfigs.add(createConfig("test-psk-sae-ijfe", WifiConfiguration.SECURITY_TYPE_PSK));
+ testConfigs.add(createConfig("test-psk-sae-ijfe", WifiConfiguration.SECURITY_TYPE_SAE));
+ testConfigs.add(createConfig("test-wpa2e-wpa3e-plki",
+ WifiConfiguration.SECURITY_TYPE_EAP));
+ testConfigs.add(createConfig("test-wpa2e-wpa3e-plki",
+ WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE));
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ try {
+ uiAutomation.adoptShellPermissionIdentity();
+ final int originalConfiguredNetworksNumber = mWifiManager.getConfiguredNetworks().size();
+ final int originalPrivilegedConfiguredNetworksNumber =
+ mWifiManager.getPrivilegedConfiguredNetworks().size();
+ final int originalCallerConfiguredNetworksNumber =
+ mWifiManager.getCallerConfiguredNetworks().size();
+ for (WifiConfiguration c: testConfigs) {
+ WifiManager.AddNetworkResult result = mWifiManager.addNetworkPrivileged(c);
+ assertEquals(WifiManager.AddNetworkResult.STATUS_SUCCESS, result.statusCode);
+ assertTrue(result.networkId >= 0);
+ c.networkId = result.networkId;
+ }
+ // open/owe, psk/sae, and wpa2e/wpa3e should be merged
+ // so they should have the same network ID.
+ assertEquals(testConfigs.get(0).networkId, testConfigs.get(1).networkId);
+ assertEquals(testConfigs.get(2).networkId, testConfigs.get(3).networkId);
+ assertEquals(testConfigs.get(4).networkId, testConfigs.get(5).networkId);
+ List<WifiConfiguration> configuredNetworks = mWifiManager.getConfiguredNetworks();
+ assertEquals(originalConfiguredNetworksNumber + testConfigs.size(),
+ configuredNetworks.size());
+ assertConfigsAreFound(testConfigs, configuredNetworks);
+
+ List<WifiConfiguration> privilegedConfiguredNetworks =
+ mWifiManager.getPrivilegedConfiguredNetworks();
+ assertEquals(originalPrivilegedConfiguredNetworksNumber + testConfigs.size(),
+ privilegedConfiguredNetworks.size());
+ assertConfigsAreFound(testConfigs, privilegedConfiguredNetworks);
+
+ List<WifiConfiguration> callerConfiguredNetworks =
+ mWifiManager.getCallerConfiguredNetworks();
+ assertEquals(originalCallerConfiguredNetworksNumber + testConfigs.size(),
+ callerConfiguredNetworks.size());
+ assertConfigsAreFound(testConfigs, callerConfiguredNetworks);
+
+ } finally {
+ for (WifiConfiguration c: testConfigs) {
+ if (c.networkId >= 0) {
+ mWifiManager.removeNetwork(c.networkId);
+ }
+ }
uiAutomation.dropShellPermissionIdentity();
}
}
@@ -1862,7 +2000,7 @@
// Register callback to get SoftApCapability
mWifiManager.registerSoftApCallback(executor, callback);
PollingCheck.check(
- "SoftAp register failed!", 2_000,
+ "SoftAp register failed!", 5_000,
() -> {
executor.runAll();
// Verify callback is run on the supplied executor and called
@@ -2252,14 +2390,20 @@
executor.runAll();
int sapChannel = ScanResult.convertFrequencyMhzToChannelIfSupported(
callback.getCurrentSoftApInfo().getFrequency());
- return WifiManager.WIFI_AP_STATE_ENABLED == callback.getCurrentState()
+ boolean isInfoCallbackSupported =
+ callback.getOnSoftapInfoChangedCalledCount() > 1;
+ if (isInfoCallbackSupported) {
+ return WifiManager.WIFI_AP_STATE_ENABLED == callback.getCurrentState()
&& testBandsAndChannels.valueAt(0) == sapChannel;
+ }
+ return WifiManager.WIFI_AP_STATE_ENABLED == callback.getCurrentState();
});
- // After Soft Ap enabled, check SoftAp info
- if (isSupportCustomizedMac) {
+ // After Soft Ap enabled, check SoftAp info if it supported
+ if (isSupportCustomizedMac && callback.getOnSoftapInfoChangedCalledCount() > 1) {
assertEquals(callback.getCurrentSoftApInfo().getBssid(), TEST_MAC);
}
- if (PropertyUtil.isVndkApiLevelNewerThan(Build.VERSION_CODES.S)) {
+ if (PropertyUtil.isVndkApiLevelNewerThan(Build.VERSION_CODES.S)
+ && callback.getOnSoftapInfoChangedCalledCount() > 1) {
assertNotEquals(callback.getCurrentSoftApInfo().getWifiStandard(),
ScanResult.WIFI_STANDARD_UNKNOWN);
}
@@ -2537,7 +2681,8 @@
newNetworkId = mWifiManager.addNetwork(newOpenNetwork);
assertNotEquals(INVALID_NETWORK_ID, newNetworkId);
- assertEquals(savedNetworks.size() + 1, mWifiManager.getConfiguredNetworks().size());
+ // Multi-type configurations might be converted to more than 1 configuration.
+ assertThat(savedNetworks.size() < mWifiManager.getConfiguredNetworks().size()).isTrue();
// Need an effectively-final holder because we need to modify inner Intent in callback.
class IntentHolder {
@@ -4145,48 +4290,20 @@
}
/**
- * Test that {@link WifiManager#setOverrideCountryCode()},
- * {@link WifiManager#clearOverrideCountryCode()} and
- * {@link WifiManager#setDefaultCountryCode()}
- * throws UnsupportedOperationException if the release is older than S.
- */
- // TODO(b/167575586): Wait for S SDK finalization before changing
- // to `maxSdkVersion = Build.VERSION_CODES.R`
- @SdkSuppress(maxSdkVersion = -1, codeName = "REL")
- public void testManageCountryCodeMethodsOnROrOlder() throws Exception {
- if (!WifiFeature.isWifiSupported(getContext())) {
- // skip the test if WiFi is not supported
- return;
- }
- try {
- mWifiManager.setOverrideCountryCode(TEST_COUNTRY_CODE);
- fail("setOverrideCountryCode() Expected to fail - UnsupportedOperationException");
- } catch (UnsupportedOperationException ex) {}
-
- try {
- mWifiManager.clearOverrideCountryCode();
- fail("clearOverrideCountryCode() Expected to fail - UnsupportedOperationException");
- } catch (UnsupportedOperationException ex) {}
-
- try {
- mWifiManager.setDefaultCountryCode(TEST_COUNTRY_CODE);
- fail("setDefaultCountryCode() Expected to fail - UnsupportedOperationException");
- } catch (UnsupportedOperationException ex) {}
- }
-
- /**
* Test that call to {@link WifiManager#setOverrideCountryCode()},
* {@link WifiManager#clearOverrideCountryCode()} and
* {@link WifiManager#setDefaultCountryCode()} need privileged permission
* and the permission is not even given to shell user.
*/
- // TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
public void testManageCountryCodeMethodsFailWithoutPermissions() throws Exception {
if (!WifiFeature.isWifiSupported(getContext())) {
// skip the test if WiFi is not supported
return;
}
+ if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(mContext)) {
+ // Skip the test if wifi module version is older than S.
+ return;
+ }
ShellIdentityUtils.invokeWithShellPermissions(() -> {
try {
mWifiManager.setOverrideCountryCode(TEST_COUNTRY_CODE);
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 e81bafa..1d7c987 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
@@ -26,8 +26,12 @@
import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
@@ -36,7 +40,9 @@
import android.os.PatternMatcher;
import android.platform.test.annotations.AppModeFull;
import android.support.test.uiautomator.UiDevice;
+import android.util.Pair;
+import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -62,6 +68,9 @@
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.List;
+import java.util.ArrayList;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
/**
* Tests the entire connection flow using {@link WifiNetworkSpecifier} embedded in a
@@ -389,6 +398,63 @@
}
/**
+ * Tests using the specifier to set a band.
+ */
+ // TODO(b/167575586): Wait for S SDK finalization to change minSdkVersion to
+ // Build.VERSION_CODES.S
+ @SdkSuppress(minSdkVersion = 31, codeName = "S")
+ @Test
+ public void testWifiBandInNetworkCallback() throws Exception {
+ // Enable all networks and wait for Internet connectivity to be restored.
+ // The callbacks in this test will match the existing network as soon as they are filed.
+ enableAllSavedNetworks(mWifiManager);
+ mTestHelper.assertWifiInternetConnectionAvailable();
+
+ final LinkedBlockingQueue<Pair<Integer, Integer>> results = new LinkedBlockingQueue<>();
+ final int[] bands = { ScanResult.WIFI_BAND_24_GHZ, ScanResult.WIFI_BAND_5_GHZ,
+ ScanResult.WIFI_BAND_6_GHZ, ScanResult.WIFI_BAND_60_GHZ };
+ final ArrayList<NetworkCallback> registeredCallbacks = new ArrayList<>();
+ for (final int band : bands) {
+ final NetworkCallback callback = new NetworkCallback() {
+ @Override public void onCapabilitiesChanged(final Network net,
+ final NetworkCapabilities caps) {
+ results.offer(new Pair(band, TestHelper.getBandFromFrequency(
+ ((WifiInfo) caps.getTransportInfo()).getFrequency())));
+ }
+ };
+
+ final WifiNetworkSpecifier specifier =
+ new WifiNetworkSpecifier.Builder().setBand(band).build();
+ assertThat(specifier.getBand()).isEqualTo(band);
+
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setNetworkSpecifier(specifier)
+ .build();
+ mConnectivityManager.registerNetworkCallback(request, callback);
+ registeredCallbacks.add(callback);
+ }
+
+ try {
+ // There should be at least one network callback about availability of the wifi network
+ // on the right band. If the device is currently connected to multiple WiFi networks,
+ // there will be several. Wait for a relatively long time for any callback, but only
+ // a short time for subsequent ones (as the last timeout will be incurred always).
+ Pair<Integer, Integer> result = results.poll(10, TimeUnit.SECONDS);
+ assertThat(result).isNotNull();
+ while (null != result) {
+ assertThat(result.first).isEqualTo(result.second);
+ result = results.poll(200, TimeUnit.MILLISECONDS);
+ }
+ } finally {
+ for (final NetworkCallback cb : registeredCallbacks) {
+ mConnectivityManager.unregisterNetworkCallback(cb);
+ }
+ }
+ }
+
+ /**
* Tests the entire connection flow using a specific SSID in the specifier and ensure that the
* device auto connects back to some saved network or suggestions in range of the device (that
* can provide internet connectivity) when the request is released.
@@ -460,6 +526,10 @@
*/
@Test
public void testBuilderForWpa3EnterpriseWithStandardApi() {
+ if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(mContext)) {
+ // Skip the test if wifi module version is older than S.
+ return;
+ }
WifiNetworkSpecifier specifier1 = new WifiNetworkSpecifier.Builder()
.setSsid(WifiInfo.sanitizeSsid(sTestNetwork.SSID))
.setWpa3EnterpriseStandardModeConfig(new WifiEnterpriseConfig())
@@ -477,6 +547,10 @@
*/
@Test
public void testBuilderForWpa3Enterprise192bit() {
+ if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(mContext)) {
+ // Skip the test if wifi module version is older than S.
+ return;
+ }
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
enterpriseConfig.setCaCertificate(CA_SUITE_B_ECDSA_CERT);
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSuggestionTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSuggestionTest.java
index 82e2a0a..3278ad1 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSuggestionTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSuggestionTest.java
@@ -852,12 +852,13 @@
/**
* Tests {@link android.net.wifi.WifiNetworkSuggestion.Builder} class.
- *
- * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
*/
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
@Test
public void testBuilderWithWpa3EnterpriseWithStandardApi() throws Exception {
+ if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(sContext)) {
+ // Skip the test if wifi module version is older than S.
+ return;
+ }
WifiEnterpriseConfig enterpriseConfig = createEnterpriseConfig();
WifiNetworkSuggestion suggestion =
createBuilderWithCommonParams()
@@ -942,12 +943,13 @@
/**
* Tests {@link android.net.wifi.WifiNetworkSuggestion.Builder} class.
- *
- * TODO(b/167575586): Wait for S SDK finalization to determine the final minSdkVersion.
*/
- @SdkSuppress(minSdkVersion = 31, codeName = "S")
@Test
public void testBuilderWithWpa3Enterprise192bitWithSuiteBEccCerts() throws Exception {
+ if (!WifiBuildCompat.isPlatformOrWifiModuleAtLeastS(sContext)) {
+ // Skip the test if wifi module version is older than S.
+ return;
+ }
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
enterpriseConfig.setCaCertificate(CA_SUITE_B_ECDSA_CERT);
diff --git a/tests/translation/AndroidTest.xml b/tests/translation/AndroidTest.xml
index f83ed7f..378599f 100644
--- a/tests/translation/AndroidTest.xml
+++ b/tests/translation/AndroidTest.xml
@@ -29,4 +29,15 @@
<option name="hidden-api-checks" value="false" />
<option name="isolated-storage" value="false" />
</test>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- Unlock screen -->
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <!-- Dismiss keyguard, in case it's set as "Swipe to unlock" -->
+ <option name="run-command" value="wm dismiss-keyguard" />
+ <!-- Collapse notifications -->
+ <option name="run-command" value="cmd statusbar collapse" />
+ <!-- dismiss all system dialogs before launch test -->
+ <option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS" />
+ </target_preparer>
</configuration>
diff --git a/tests/translation/res/layout/simple_activity.xml b/tests/translation/res/layout/simple_activity.xml
index 76a7184..4ef5d05 100644
--- a/tests/translation/res/layout/simple_activity.xml
+++ b/tests/translation/res/layout/simple_activity.xml
@@ -30,4 +30,11 @@
android:textSize="16sp"
android:text="Hello World" />
+ <TextView
+ android:id="@+id/textview_password"
+ android:password="true"
+ android:minWidth="1dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
</LinearLayout>
\ No newline at end of file
diff --git a/tests/translation/src/android/translation/cts/CtsTranslationService.java b/tests/translation/src/android/translation/cts/CtsTranslationService.java
index ce5f429..e72b4b0 100644
--- a/tests/translation/src/android/translation/cts/CtsTranslationService.java
+++ b/tests/translation/src/android/translation/cts/CtsTranslationService.java
@@ -111,12 +111,6 @@
}
@Override
- public void onCreateTranslationSession(@NonNull TranslationContext translationContext,
- int sessionId) {
- Log.v(TAG, "deprecated");
- }
-
- @Override
public void onFinishTranslationSession(int sessionId) {
Log.v(TAG, "onFinishTranslationSession");
mSessionDestroyedLatch.countDown();
@@ -125,7 +119,7 @@
@Override
public void onTranslationRequest(@NonNull TranslationRequest request, int sessionId,
@NonNull CancellationSignal cancellationSignal,
- @NonNull OnTranslationResultCallback callback) {
+ @NonNull Consumer<TranslationResponse> callback) {
Log.v(TAG, "onTranslationRequest(" + request + ")");
mHandler.post(() -> sTranslationReplier.handleOnTranslationRequest(getApplicationContext(),
@@ -274,7 +268,7 @@
private void handleOnTranslationRequest(@NonNull Context context,
@NonNull TranslationRequest request, int sessionId,
@NonNull CancellationSignal cancellationSignal,
- @NonNull OnTranslationResultCallback callback) {
+ @NonNull Consumer<TranslationResponse> callback) {
Log.d(TAG, "offering " + request);
offer(mTranslationRequests, request, TRANSLATION_TIMEOUT_MS);
try {
@@ -295,7 +289,7 @@
}
Log.v(TAG, "onTranslationRequest(): response = " + response);
- callback.onTranslationSuccess(response);
+ callback.accept(response);
} catch (Throwable t) {
addException(t);
}
diff --git a/tests/translation/src/android/translation/cts/TranslationManagerTest.java b/tests/translation/src/android/translation/cts/TranslationManagerTest.java
index 5c4f875..e9800fe 100644
--- a/tests/translation/src/android/translation/cts/TranslationManagerTest.java
+++ b/tests/translation/src/android/translation/cts/TranslationManagerTest.java
@@ -26,6 +26,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.icu.util.ULocale;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
@@ -55,6 +56,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
@@ -166,6 +168,7 @@
// unregister listeners
manager.removeOnDeviceTranslationCapabilityUpdateListener(updateListener);
+ SystemClock.sleep(1_000);
// update text to text TranslationCapability
service.updateTranslationCapability(updatedText2TextCapability);
@@ -185,80 +188,6 @@
}
@Test
- public void testSingleTranslation_deprecatedSyncCreateTranslator() throws Exception{
- enableCtsTranslationService();
-
- final TranslationManager manager = sContext.getSystemService(TranslationManager.class);
-
- sTranslationReplier.addResponse(
- new TranslationResponse.Builder(TranslationResponse.TRANSLATION_STATUS_SUCCESS)
- .setTranslationResponseValue(0, new TranslationResponseValue
- .Builder(TranslationResponseValue.STATUS_SUCCESS)
- .setText("success")
- .build())
- .build());
-
- final CountDownLatch translationLatch = new CountDownLatch(1);
- final AtomicReference<TranslationResponse> responseRef = new AtomicReference<>();
-
- final TranslationContext translationContext = new TranslationContext.Builder(
- new TranslationSpec(ULocale.ENGLISH, TranslationSpec.DATA_FORMAT_TEXT),
- new TranslationSpec(ULocale.FRENCH, TranslationSpec.DATA_FORMAT_TEXT))
- .build();
- final Translator translator = manager.createOnDeviceTranslator(translationContext);
-
- try {
- mServiceWatcher.waitOnConnected();
- } catch (InterruptedException e) {
- Log.w(TAG, "Exception waiting for onConnected");
- }
-
- assertThat(translator.isDestroyed()).isFalse();
-
- final Consumer<TranslationResponse> callback = new Consumer<TranslationResponse>() {
- @Override
- public void accept(TranslationResponse translationResponse) {
- responseRef.set(translationResponse);
- translationLatch.countDown();
- }
- };
-
- translator.translate(new TranslationRequest.Builder()
- .addTranslationRequestValue(TranslationRequestValue.forText("hello world"))
- .build(), new CancellationSignal(), (r) -> r.run(), callback);
-
- sTranslationReplier.getNextTranslationRequest();
-
- translator.destroy();
- assertThat(translator.isDestroyed()).isTrue();
- try {
- mServiceWatcher.waitOnDisconnected();
- } catch (InterruptedException e) {
- Log.w(TAG, "Exception waiting for onDisconnected");
- }
-
- // Wait for translation to finish
- translationLatch.await();
- sTranslationReplier.assertNoUnhandledTranslationRequests();
-
- final TranslationResponse response = responseRef.get();
- Log.v(TAG, "TranslationResponse=" + response);
-
- assertThat(response).isNotNull();
- assertThat(response.getTranslationStatus())
- .isEqualTo(TranslationResponse.TRANSLATION_STATUS_SUCCESS);
- assertThat(response.isFinalResponse()).isTrue();
- assertThat(response.getTranslationResponseValues().size()).isEqualTo(1);
- assertThat(response.getViewTranslationResponses().size()).isEqualTo(0);
-
- final TranslationResponseValue value = response.getTranslationResponseValues().get(0);
- assertThat(value.getStatusCode()).isEqualTo(TranslationResponseValue.STATUS_SUCCESS);
- assertThat(value.getText()).isEqualTo("success");
- assertThat(value.getTransliteration()).isNull();
- assertThat(value.getDictionaryDescription()).isNull();
- }
-
- @Test
public void testSingleTranslation() throws Exception{
enableCtsTranslationService();
@@ -273,9 +202,9 @@
.build());
final TranslationContext translationContext = new TranslationContext.Builder(
- new TranslationSpec(ULocale.ENGLISH.getLanguage(),
+ new TranslationSpec(ULocale.ENGLISH,
TranslationSpec.DATA_FORMAT_TEXT),
- new TranslationSpec(ULocale.FRENCH.getLanguage(),
+ new TranslationSpec(ULocale.FRENCH,
TranslationSpec.DATA_FORMAT_TEXT))
.build();
@@ -303,8 +232,10 @@
final CountDownLatch translationLatch = new CountDownLatch(1);
final AtomicReference<TranslationResponse> responseRef = new AtomicReference<>();
+ final ArrayList<TranslationRequestValue> values = new ArrayList<>();
+ values.add(TranslationRequestValue.forText("hello world"));
translator.translate(new TranslationRequest.Builder()
- .addTranslationRequestValue(TranslationRequestValue.forText("hello world"))
+ .setTranslationRequestValues(values)
.build(), new CancellationSignal(), (r) -> r.run(),
new Consumer<TranslationResponse>() {
@Override
@@ -342,7 +273,7 @@
assertThat(value.getStatusCode()).isEqualTo(TranslationResponseValue.STATUS_SUCCESS);
assertThat(value.getText()).isEqualTo("success");
assertThat(value.getTransliteration()).isNull();
- assertThat(value.getDictionaryDescription()).isNull();
+ assertThat(value.getExtras()).isEqualTo(Bundle.EMPTY);
}
@Test
@@ -366,7 +297,20 @@
new TranslationSpec(ULocale.ENGLISH, TranslationSpec.DATA_FORMAT_TEXT),
new TranslationSpec(ULocale.FRENCH, TranslationSpec.DATA_FORMAT_TEXT))
.build();
- final Translator translator = manager.createOnDeviceTranslator(translationContext);
+
+ final CountDownLatch createTranslatorLatch = new CountDownLatch(1);
+ final AtomicReference<Translator> translatorRef = new AtomicReference<>();
+ manager.createOnDeviceTranslator(translationContext, r -> r.run(),
+ new Consumer<Translator>() {
+ @Override
+ public void accept(Translator translator) {
+ createTranslatorLatch.countDown();
+ translatorRef.set(translator);
+ }
+ });
+
+ createTranslatorLatch.await(5_000, TimeUnit.MILLISECONDS);
+ final Translator translator = translatorRef.get();
try {
mServiceWatcher.waitOnConnected();
@@ -386,8 +330,10 @@
final CancellationSignal cancellationSignal = new CancellationSignal();
+ final ArrayList<TranslationRequestValue> values = new ArrayList<>();
+ values.add(TranslationRequestValue.forText("hello world"));
translator.translate(new TranslationRequest.Builder()
- .addTranslationRequestValue(TranslationRequestValue.forText("hello world"))
+ .setTranslationRequestValues(values)
.build(), cancellationSignal, (r) -> r.run(), callback);
// TODO: implement with cancellation signal listener
diff --git a/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java b/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
index b3ecf0d..6239716 100644
--- a/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
+++ b/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
@@ -58,6 +58,7 @@
import android.view.translation.TranslationResponseValue;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationManager;
+import android.view.translation.UiTranslationSpec;
import android.view.translation.UiTranslationStateCallback;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
@@ -184,7 +185,8 @@
TranslationSpec.DATA_FORMAT_TEXT),
new TranslationSpec(ULocale.FRENCH,
TranslationSpec.DATA_FORMAT_TEXT),
- views, contentCaptureContext.getActivityId());
+ views, contentCaptureContext.getActivityId(),
+ new UiTranslationSpec.Builder().setShouldPadContentForCompat(true).build());
// Check request
final TranslationRequest request = sTranslationReplier.getNextTranslationRequest();
@@ -251,7 +253,8 @@
TranslationSpec.DATA_FORMAT_TEXT),
new TranslationSpec(ULocale.FRENCH,
TranslationSpec.DATA_FORMAT_TEXT),
- views, contentCaptureContext.getActivityId());
+ views, contentCaptureContext.getActivityId(),
+ new UiTranslationSpec.Builder().setShouldPadContentForCompat(true).build());
SystemClock.sleep(UI_WAIT_TIMEOUT);
});
// Send broadcat to request IME to check the onStarted() result
@@ -343,7 +346,8 @@
TranslationSpec.DATA_FORMAT_TEXT),
new TranslationSpec(ULocale.FRENCH,
TranslationSpec.DATA_FORMAT_TEXT),
- views, contentCaptureContext.getActivityId());
+ views, contentCaptureContext.getActivityId(),
+ new UiTranslationSpec.Builder().setShouldPadContentForCompat(true).build());
SystemClock.sleep(UI_WAIT_TIMEOUT);
assertThat(callback.isOnStartedCalled()).isFalse();
@@ -475,11 +479,6 @@
}
@Override
- public void onStarted(String source, String target) {
- // no-op
- }
-
- @Override
public void onStarted(ULocale sourceLocale, ULocale targetLocale) {
mStartCalled = true;
mSourceLocale = sourceLocale;
diff --git a/tests/translation/src/android/translation/cts/unittests/TextViewTranslationTest.java b/tests/translation/src/android/translation/cts/unittests/TextViewTranslationTest.java
new file mode 100644
index 0000000..1c79192
--- /dev/null
+++ b/tests/translation/src/android/translation/cts/unittests/TextViewTranslationTest.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.translation.cts.unittests;
+
+import static android.view.translation.TranslationResponseValue.STATUS_SUCCESS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.text.InputType;
+import android.text.method.PasswordTransformationMethod;
+import android.translation.cts.R;
+import android.translation.cts.SimpleActivity;
+import android.util.Log;
+import android.view.translation.TranslationResponseValue;
+import android.view.translation.TranslationSpec;
+import android.view.translation.ViewTranslationRequest;
+import android.view.translation.ViewTranslationResponse;
+import android.widget.TextView;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Tests for {@link TextView} translation related APIs.
+ */
+@RunWith(AndroidJUnit4.class)
+public class TextViewTranslationTest {
+
+ private static final String TAG = "TextViewTranslationTest";
+ private static final int[] SUPPORTED_DATA_FORMAT = new int[]{TranslationSpec.DATA_FORMAT_TEXT};
+
+ @Rule
+ public ActivityScenarioRule<SimpleActivity> mActivityRule = new ActivityScenarioRule<>(
+ SimpleActivity.class);
+
+ ActivityScenario<SimpleActivity> mScenario;
+ TextView mTestTextView;
+ TestRequestConsumer mRequestConsumer;
+
+ @Before
+ public void setup() throws Exception {
+ mScenario = mActivityRule.getScenario();
+ mScenario.onActivity(activity -> {
+ mTestTextView = activity.findViewById(R.id.hello);
+ });
+ mRequestConsumer = new TestRequestConsumer();
+ }
+
+ @After
+ public void cleanup() throws Exception {
+ if (mScenario != null) {
+ mScenario.close();
+ mScenario = null;
+ }
+ mTestTextView = null;
+ }
+
+ @Test
+ public void testOnCreateViewTranslationRequest() throws Throwable {
+ mScenario.onActivity(activity -> {
+ // Normal text view
+ mTestTextView.onCreateViewTranslationRequest(SUPPORTED_DATA_FORMAT, mRequestConsumer);
+ });
+ ViewTranslationRequest request = mRequestConsumer.getRequest();
+ Log.d(TAG, "Collect request = " + request);
+
+ assertThat(request).isNotNull();
+ assertThat(request.getAutofillId()).isEqualTo(mTestTextView.getAutofillId());
+ assertThat(request.getValue(ViewTranslationRequest.ID_TEXT).getText())
+ .isEqualTo(mTestTextView.getText());
+ }
+
+ @Test
+ public void testOnCreateViewTranslationRequest_emptyText_returnsNull() throws Throwable {
+ mScenario.onActivity(activity -> {
+ // TextView with empty
+ mTestTextView.setText("");
+ mTestTextView.onCreateViewTranslationRequest(SUPPORTED_DATA_FORMAT, mRequestConsumer);
+ });
+ Log.d(TAG, "Collect request = " + mRequestConsumer.getRequest());
+
+ assertThat(mRequestConsumer.getRequest()).isNull();
+ }
+
+ @Test
+ public void testOnCreateViewTranslationRequest_hasPassword_returnsNull() throws Throwable {
+ mScenario.onActivity(activity -> {
+ // TextView with android:password true
+ mTestTextView = activity.findViewById(R.id.textview_password);
+ mTestTextView.setText("fake_password");
+ mTestTextView.onCreateViewTranslationRequest(SUPPORTED_DATA_FORMAT, mRequestConsumer);
+ });
+ Log.d(TAG, "Collect request = " + mRequestConsumer.getRequest());
+
+ assertThat(mRequestConsumer.getRequest()).isNull();
+ }
+
+ @Test
+ public void testOnCreateViewTranslationRequest_hasPasswordTransformationMethod_returnsNull()
+ throws Throwable {
+ mScenario.onActivity(activity -> {
+ // TextView with PasswordTransformationMethod
+ mTestTextView.setTransformationMethod(new PasswordTransformationMethod());
+ mTestTextView.onCreateViewTranslationRequest(SUPPORTED_DATA_FORMAT, mRequestConsumer);
+ });
+ Log.d(TAG, "Collect request = " + mRequestConsumer.getRequest());
+
+ assertThat(mRequestConsumer.getRequest()).isNull();
+ }
+
+ @Test
+ public void testOnCreateViewTranslationRequest_textSelectable_returnsNull()
+ throws Throwable {
+ mScenario.onActivity(activity -> {
+ // TextView is selectable
+ mTestTextView.setTextIsSelectable(true);
+ mTestTextView.onCreateViewTranslationRequest(SUPPORTED_DATA_FORMAT, mRequestConsumer);
+ });
+ Log.d(TAG, "Collect request = " + mRequestConsumer.getRequest());
+
+ assertThat(mRequestConsumer.getRequest()).isNull();
+ }
+
+ @Test
+ public void testOnCreateViewTranslationRequest_textEditable_returnsNull()
+ throws Throwable {
+ mScenario.onActivity(activity -> {
+ // TextView is editable
+ mTestTextView.setText("Test", TextView.BufferType.EDITABLE);
+ mTestTextView.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);
+ mTestTextView.onCreateViewTranslationRequest(SUPPORTED_DATA_FORMAT, mRequestConsumer);
+ });
+ Log.d(TAG, "Collect request = " + mRequestConsumer.getRequest());
+
+ assertThat(mRequestConsumer.getRequest()).isNull();
+ }
+
+ @Test
+ public void testonViewTranslationResponse() throws Throwable {
+ final ViewTranslationResponse expectedResponse =
+ new ViewTranslationResponse.Builder(mTestTextView.getAutofillId())
+ .setValue(ViewTranslationRequest.ID_TEXT,
+ new TranslationResponseValue.Builder(STATUS_SUCCESS)
+ .setText("HELLO WORLD").build()).build();
+ mScenario.onActivity(activity -> {
+ // ViewTranslationResponse is set in onViewTranslationResponse
+ mTestTextView.onViewTranslationResponse(expectedResponse);
+ });
+ final ViewTranslationResponse response = mTestTextView.getViewTranslationResponse();
+ Log.d(TAG, "response = " + response);
+
+ assertThat(response).isNotNull();
+ assertThat(mTestTextView.getViewTranslationResponse()).isEqualTo(expectedResponse);
+ }
+
+ private static final class TestRequestConsumer implements Consumer<ViewTranslationRequest> {
+ private ViewTranslationRequest mRequest;
+
+ @Override
+ public void accept(ViewTranslationRequest request) {
+ mRequest = request;
+ }
+
+ ViewTranslationRequest getRequest() {
+ return mRequest;
+ }
+ }
+}
diff --git a/tests/translation/src/android/translation/cts/unittests/TranslationContextTest.java b/tests/translation/src/android/translation/cts/unittests/TranslationContextTest.java
index e0fbd94..3922d18 100644
--- a/tests/translation/src/android/translation/cts/unittests/TranslationContextTest.java
+++ b/tests/translation/src/android/translation/cts/unittests/TranslationContextTest.java
@@ -50,11 +50,11 @@
public void testContext_validContext() {
final TranslationContext context =
new TranslationContext.Builder(sourceSpec, targetSpec)
- .setTranslationFlags(TranslationContext.FLAG_DICTIONARY_DESCRIPTION)
+ .setTranslationFlags(TranslationContext.FLAG_DEFINITIONS)
.build();
assertThat(context.getTranslationFlags())
- .isEqualTo(TranslationContext.FLAG_DICTIONARY_DESCRIPTION);
+ .isEqualTo(TranslationContext.FLAG_DEFINITIONS);
assertThat(context.getSourceSpec().getLocale()).isEqualTo(ULocale.ENGLISH);
assertThat(context.getSourceSpec().getDataFormat())
diff --git a/tests/translation/src/android/translation/cts/unittests/TranslationValueTest.java b/tests/translation/src/android/translation/cts/unittests/TranslationValueTest.java
index dd6217a..45512bb 100644
--- a/tests/translation/src/android/translation/cts/unittests/TranslationValueTest.java
+++ b/tests/translation/src/android/translation/cts/unittests/TranslationValueTest.java
@@ -22,6 +22,7 @@
import static org.testng.Assert.assertThrows;
+import android.os.Bundle;
import android.view.translation.TranslationRequestValue;
import android.view.translation.TranslationResponseValue;
@@ -30,6 +31,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.Arrays;
+
@RunWith(AndroidJUnit4.class)
public class TranslationValueTest {
@@ -60,14 +64,32 @@
@Test
public void testTranslationResponseValue_validDictionary() {
+ final Bundle definitions = new Bundle();
+ definitions.putStringArrayList("noun",
+ new ArrayList<>(Arrays.asList("def1", "def2")));
+ definitions.putStringArrayList("verb",
+ new ArrayList<>(Arrays.asList("def3", "def4")));
+ final Bundle extras = new Bundle();
+ extras.putBundle(TranslationResponseValue.EXTRA_DEFINITIONS, definitions);
+
final TranslationResponseValue value = new TranslationResponseValue.Builder(STATUS_SUCCESS)
- .setDictionaryDescription("definition")
+ .setExtras(extras)
.build();
assertThat(value.getStatusCode()).isEqualTo(STATUS_SUCCESS);
assertThat(value.getText()).isNull();
- assertThat(value.getDictionaryDescription()).isEqualTo("definition");
assertThat(value.getTransliteration()).isNull();
+ assertThat(value.getExtras()).isNotNull();
+
+ final Bundle e = value.getExtras();
+ assertThat(e).isNotNull();
+ final Bundle defs = e.getBundle(TranslationResponseValue.EXTRA_DEFINITIONS);
+ assertThat(defs).isNotNull();
+ assertThat(defs.keySet()).containsExactly("noun", "verb");
+ assertThat(defs.getStringArrayList("noun"))
+ .containsExactly("def1", "def2");
+ assertThat(defs.getStringArrayList("verb"))
+ .containsExactly("def3", "def4");
}
@Test
@@ -78,7 +100,7 @@
assertThat(value.getStatusCode()).isEqualTo(STATUS_SUCCESS);
assertThat(value.getText()).isNull();
- assertThat(value.getDictionaryDescription()).isNull();
+ assertThat(value.getExtras()).isEqualTo(Bundle.EMPTY);
assertThat(value.getTransliteration()).isEqualTo("pronunciation");
}
}
diff --git a/tools/cts-device-info/Android.mk b/tools/cts-device-info/Android.mk
index 2fcbd6b..20c64c2 100644
--- a/tools/cts-device-info/Android.mk
+++ b/tools/cts-device-info/Android.mk
@@ -41,6 +41,10 @@
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
+# Disable dexpreopt and <uses-library> check for test.
+LOCAL_ENFORCE_USES_LIBRARIES := false
+LOCAL_DEX_PREOPT := false
+
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts general-tests sts mts vts
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 c832859..5985926 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -59,6 +59,9 @@
<!-- 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/183234756, b/80388296, b/110260628, b/159295445, b/159294948 CtsDevicePolicyManagerTestCases -->
<option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases" />