diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-08 16:04:35 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-08 16:04:35 +0000 |
commit | 72642b73a754d41245839523c215258315d750e5 (patch) | |
tree | 8d461616163abf73c8b0cd55b31cdbc40e0c2836 | |
parent | 019ff3c01d4839dbe9d87a3866f78949aca40585 (diff) | |
parent | 3b0ffb30ee3666352c7932b283f464880cf24a45 (diff) | |
download | apex-aml_tz2_304500300.tar.gz |
Snap for 8426163 from 3b0ffb30ee3666352c7932b283f464880cf24a45 to mainline-tzdata2-releaseandroid-mainline-12.0.0_r112aml_tz2_305400500aml_tz2_305400300aml_tz2_305400100aml_tz2_304500300aml_tz2_303900110aml_tz2_303900102aml_tz2_303800002aml_tz2_303800001aml_tz2_303200001android12-mainline-tzdata2-releaseaml_tz2_305400100
Change-Id: Ia8d138ec480f72157c64e4205bf1080c4887ebd7
340 files changed, 3144 insertions, 17239 deletions
diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_not_pre_installed_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_not_pre_installed_apex.asciipb deleted file mode 100644 index f87c6b9a..00000000 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_not_pre_installed_apex.asciipb +++ /dev/null @@ -1,15 +0,0 @@ -drops { - android_build_drop { - build_id: "7552332" - target: "CtsShim" - source_file: "aosp_arm64/com.android.apex.cts.shim_not_pre_installed.apex" - } - dest_file: "shim/prebuilts//arm/com.android.apex.cts.shim_not_pre_installed.apex" - version: "" - version_group: "" - git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } -} diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v1_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v1_apex.asciipb index 03cf8a05..5bef6b27 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v1_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v1_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v1.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_additional_file_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_additional_file_apex.asciipb index c93ee730..f2032412 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_additional_file_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_additional_file_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_additional_file.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb index d5922e10..d5b1515a 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_additional_folder.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_apex.asciipb index 18e12ee1..5c1ecd8d 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb index 00f9f6cb..a3e05b58 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__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: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb index 861ffa32..d4f42de6 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_different_certificate.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb index 79de3751..30c1658b 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_different_package_name.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb index 91f5ef2e..7a4ba73a 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_no_hashtree.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_rebootless_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_rebootless_apex.asciipb deleted file mode 100644 index e8f22833..00000000 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_rebootless_apex.asciipb +++ /dev/null @@ -1,15 +0,0 @@ -drops { - android_build_drop { - build_id: "7552332" - target: "CtsShim" - source_file: "aosp_arm64/com.android.apex.cts.shim.v2_rebootless.apex" - } - dest_file: "shim/prebuilts//arm/com.android.apex.cts.shim.v2_rebootless.apex" - version: "" - version_group: "" - git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } -} diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb index e56d8552..e046e3ab 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_sdk_target_p.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb deleted file mode 100644 index ec3b1d67..00000000 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb +++ /dev/null @@ -1,15 +0,0 @@ -drops { - android_build_drop { - build_id: "7552332" - target: "CtsShim" - source_file: "aosp_arm64/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex" - } - dest_file: "shim/prebuilts//arm/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex" - version: "" - version_group: "" - git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } -} diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb index a8b55abf..6ea9a6b5 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_signed_bob.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb index 23567d8b..fe76dce4 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_signed_bob_rot.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb index 84c7328d..c90ffbbe 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb index ed4ca4b8..779f4993 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_unsigned_payload.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb index 5fb0fb28..0bf9288a 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_with_post_install_hook.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb index 38f20a00..5a1baf9b 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_with_pre_install_hook.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb index ac823c9b..316c8d12 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_without_apk_in_apex.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb index 0281ed48..3d5c0e90 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v2_wrong_sha.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_apex.asciipb index 50c821d1..e03ee6e7 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v3.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_rebootless_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_rebootless_apex.asciipb deleted file mode 100644 index f900c468..00000000 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_rebootless_apex.asciipb +++ /dev/null @@ -1,15 +0,0 @@ -drops { - android_build_drop { - build_id: "7552332" - target: "CtsShim" - source_file: "aosp_arm64/com.android.apex.cts.shim.v3_rebootless.apex" - } - dest_file: "shim/prebuilts//arm/com.android.apex.cts.shim.v3_rebootless.apex" - version: "" - version_group: "" - git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } -} diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb index d908d0e9..daecf3c3 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v3_signed_bob.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb index cb1a634b..420522bb 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__arm_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_arm64/com.android.apex.cts.shim.v3_signed_bob_rot.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_not_pre_installed_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_not_pre_installed_apex.asciipb deleted file mode 100644 index 0d09fcba..00000000 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_not_pre_installed_apex.asciipb +++ /dev/null @@ -1,15 +0,0 @@ -drops { - android_build_drop { - build_id: "7552332" - target: "CtsShim" - source_file: "aosp_x86_64/com.android.apex.cts.shim_not_pre_installed.apex" - } - dest_file: "shim/prebuilts//x86/com.android.apex.cts.shim_not_pre_installed.apex" - version: "" - version_group: "" - git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } -} diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v1_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v1_apex.asciipb index 742bca47..239132ac 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v1_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v1_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v1.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_additional_file_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_additional_file_apex.asciipb index 783107cf..f84e0c89 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_additional_file_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_additional_file_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_additional_file.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb index 5ab45ea3..635e2bbd 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_additional_folder_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_additional_folder.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_apex.asciipb index aee50ef4..cea53405 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb index 9eb21eea..2622af8f 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_apk_in_apex_sdk_target_p_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__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: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb index 670489d5..c25509a2 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_different_certificate_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_different_certificate.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb index c5d28ffe..ab8601fc 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_different_package_name_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_different_package_name.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb index 17ae6c5d..f1ccd780 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_no_hashtree_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_no_hashtree.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_rebootless_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_rebootless_apex.asciipb deleted file mode 100644 index c817a79e..00000000 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_rebootless_apex.asciipb +++ /dev/null @@ -1,15 +0,0 @@ -drops { - android_build_drop { - build_id: "7552332" - target: "CtsShim" - source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_rebootless.apex" - } - dest_file: "shim/prebuilts//x86/com.android.apex.cts.shim.v2_rebootless.apex" - version: "" - version_group: "" - git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } -} diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb index b1210498..1f24dedd 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_sdk_target_p_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_sdk_target_p.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb deleted file mode 100644 index 39e49e1f..00000000 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_sign_payload_with_different_key_apex.asciipb +++ /dev/null @@ -1,15 +0,0 @@ -drops { - android_build_drop { - build_id: "7552332" - target: "CtsShim" - source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex" - } - dest_file: "shim/prebuilts//x86/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex" - version: "" - version_group: "" - git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } -} diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb index 2056dc18..e8c48e1e 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_signed_bob.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb index 229b0f31..faf0e28b 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_rot_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_signed_bob_rot.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb index fc464db4..5f1e6642 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_signed_bob_rot_rollback_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb index cffcd608..aef234a7 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_unsigned_payload_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_unsigned_payload.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb index c2168d82..87b7c50f 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_with_post_install_hook_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_with_post_install_hook.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb index 1f2469a6..727bce0d 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_with_pre_install_hook_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_with_pre_install_hook.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb index 986a4595..23155ba5 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_without_apk_in_apex_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_without_apk_in_apex.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb index d53ab6c4..2190954d 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v2_wrong_sha_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v2_wrong_sha.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_apex.asciipb index ca8bade1..e7006ae6 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v3.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_rebootless_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_rebootless_apex.asciipb deleted file mode 100644 index e2c369f7..00000000 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_rebootless_apex.asciipb +++ /dev/null @@ -1,15 +0,0 @@ -drops { - android_build_drop { - build_id: "7552332" - target: "CtsShim" - source_file: "aosp_x86_64/com.android.apex.cts.shim.v3_rebootless.apex" - } - dest_file: "shim/prebuilts//x86/com.android.apex.cts.shim.v3_rebootless.apex" - version: "" - version_group: "" - git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } -} diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb index ea066941..bdcb62fb 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_signed_bob_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v3_signed_bob.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb index 3d41a84e..4f258589 100644 --- a/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb +++ b/.prebuilt_info/prebuilt_info_shim_prebuilts__x86_com_android_apex_cts_shim_v3_signed_bob_rot_apex.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "7552332" + build_id: "6508977" target: "CtsShim" source_file: "aosp_x86_64/com.android.apex.cts.shim.v3_signed_bob_rot.apex" } @@ -8,8 +8,5 @@ drops { version: "" version_group: "" git_project: "platform/system/apex" - git_branch: "sc-dev" - transform: TRANSFORM_NONE - transform_options { - } + git_branch: "rvc-dev" } diff --git a/Android.bp b/Android.bp deleted file mode 100644 index b198f250..00000000 --- a/Android.bp +++ /dev/null @@ -1,68 +0,0 @@ -// This introduces the module type library_linking_strategy_cc_defaults -// To use in other Android.bp files, add the following lines: -// soong_config_module_type_import { -// from: "system/apex/Android.bp", -// module_types: ["library_linking_strategy_cc_defaults"], -// } - -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -soong_config_string_variable { - name: "library_linking_strategy", - values: [ - "prefer_static", - ], -} - -soong_config_module_type { - name: "library_linking_strategy_cc_defaults", - module_type: "cc_defaults", - config_namespace: "ANDROID", - variables: ["library_linking_strategy"], - properties: [ - "shared_libs", - "static_libs", - "stl", - ], -} - -// TODO(b/178585590): delete this after testing linking strategy -soong_config_module_type { - name: "library_linking_strategy_apex_defaults", - module_type: "apex_defaults", - config_namespace: "ANDROID", - variables: ["library_linking_strategy"], - properties: [ - "manifest", - "min_sdk_version", - ], -} - -library_linking_strategy_cc_defaults { - name: "library_linking_strategy_sample_defaults", - soong_config_variables: { - library_linking_strategy: { - prefer_static: { - static_libs: [ - "libbase", - "liblog", - ], - stl: "c++_static", - }, - conditions_default: { - shared_libs: [ - "libbase", - "liblog", - ], - }, - }, - }, -} - -cc_binary { - name: "library_linking_strategy_sample_binary", - srcs: ["library_linking_strategy.cc"], - defaults: ["library_linking_strategy_sample_defaults"], -} diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index ccaf5feb..4c6fbd61 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -6,7 +6,6 @@ clang_format = true commit_msg_changeid_field = true commit_msg_test_field = true gofmt = true -pylint3 = true [Builtin Hooks Options] -clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp +clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
\ No newline at end of file diff --git a/apexd/Android.bp b/apexd/Android.bp index ba2de033..e8d221d5 100644 --- a/apexd/Android.bp +++ b/apexd/Android.bp @@ -1,46 +1,25 @@ // List of clang-tidy checks that are reported as errors. // Please keep this list ordered lexicographically. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - tidy_errors = [ - "android-*", - "bugprone-infinite-loop", - "bugprone-macro-parentheses", - "bugprone-misplaced-widening-cast", - "bugprone-move-forwarding-reference", - "bugprone-sizeof-container", - "bugprone-sizeof-expression", - "bugprone-string-constructor", - "bugprone-terminating-continue", - "bugprone-undefined-memory-manipulation", - "bugprone-undelegated-constructor", - // "bugprone-unhandled-self-assignment", // found in apex_manifest.proto - "bugprone-unused-raii", "cert-err34-c", "google-default-arguments", - // "google-explicit-constructor", // found in com_android_apex.h - "google-readability-avoid-underscore-in-googletest-name", - "google-readability-todo", + "google-explicit-constructor", "google-runtime-int", "google-runtime-member-string-references", "misc-move-const-arg", "misc-move-forwarding-reference", - // "misc-unused-parameters", // found in apexd_utils.h + "misc-unused-parameters", "misc-unused-using-decls", "misc-use-after-move", - // "modernize-pass-by-value", // found in apex_database.h + "modernize-pass-by-value", "performance-faster-string-find", "performance-for-range-copy", "performance-implicit-conversion-in-loop", "performance-inefficient-vector-operation", "performance-move-const-arg", - // "performance-move-constructor-init", // found in apexd_loop.h + "performance-move-constructor-init", "performance-noexcept-move-constructor", - "performance-unnecessary-copy-initialization", "performance-unnecessary-value-param", - // "readability-avoid-const-params-in-decls", // found in apexd.h ] cc_defaults { @@ -68,8 +47,8 @@ cc_defaults { tidy_checks: tidy_errors, tidy_checks_as_errors: tidy_errors, tidy_flags: [ - "-format-style=file", - "-header-filter=system/apex/", + "-format-style='file'", + "--header-filter='system/apex/'", ], } @@ -79,6 +58,7 @@ cc_defaults { defaults: ["libapex-deps"], shared_libs: [ "libbinder", + "libselinux", "liblog", "liblogwrap", ], @@ -89,7 +69,6 @@ cc_defaults { "libext2_uuid", "libverity_tree", "libvold_binder", - "libxml2", ], whole_static_libs: ["com.android.sysprop.apex"], } @@ -102,8 +81,6 @@ aidl_interface { "aidl/android/apex/ApexInfoList.aidl", "aidl/android/apex/ApexSessionInfo.aidl", "aidl/android/apex/ApexSessionParams.aidl", - "aidl/android/apex/CompressedApexInfo.aidl", - "aidl/android/apex/CompressedApexInfoList.aidl", "aidl/android/apex/IApexService.aidl", ], local_include_dir: "aidl", @@ -150,15 +127,14 @@ cc_library_static { srcs: [ "apex_database.cpp", "apexd.cpp", - "apexd_lifecycle.cpp", "apexd_loop.cpp", "apexd_prepostinstall.cpp", "apexd_private.cpp", + "apexd_prop.cpp", "apexd_session.cpp", "apexd_verity.cpp", ], export_include_dirs: ["."], - generated_sources: ["apex-info-list"], // Don't add shared/static libs here; add to libapexd_defaults instead. } @@ -194,9 +170,6 @@ cc_library_static { static_libs: [ "libapexd", ], - cflags: [ - "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", - ], } cc_defaults { @@ -207,7 +180,6 @@ cc_defaults { "libcutils", "libprotobuf-cpp-full", "libziparchive", - "libselinux", ], static_libs: [ "lib_apex_session_state_proto", @@ -217,7 +189,6 @@ cc_defaults { static: { whole_static_libs: ["libc++fs"], }, - cpp_std: "experimental", shared: { static_libs: ["libc++fs"], }, @@ -231,8 +202,8 @@ cc_library_static { ], srcs: [ "apex_file.cpp", - "apex_file_repository.cpp", "apex_manifest.cpp", + "apex_preinstalled_data.cpp", "apex_shim.cpp", ], host_supported: true, @@ -252,7 +223,7 @@ cc_library_static { genrule { // Generates an apex which has a different manifest outside the filesystem // image. - name: "gen_manifest_mismatch_apex", + name: "gen_bad_apexes", out: ["apex.apexd_test_manifest_mismatch.apex"], srcs: [":apex.apexd_test"], tools: ["soong_zip", "zipalign", "conv_apex_manifest"], @@ -266,38 +237,6 @@ genrule { } genrule { - // Generates an apex which has a different manifest outside the filesystem - // image. - name: "gen_manifest_mismatch_apex_no_hashtree", - out: ["apex.apexd_test_no_hashtree_manifest_mismatch.apex"], - srcs: [":apex.apexd_test_no_hashtree"], - tools: ["soong_zip", "zipalign", "conv_apex_manifest"], - cmd: "unzip -q $(in) -d $(genDir) && " + - "$(location conv_apex_manifest) setprop version 137 $(genDir)/apex_manifest.pb && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) " + - "-s apex_manifest.pb -s apex_payload.img -s apex_pubkey " + - "-o $(genDir)/unaligned.apex && " + - "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " + - "$(genDir)/apex.apexd_test_no_hashtree_manifest_mismatch.apex" -} - -genrule { - // Generates an apex with a corrupted filesystem superblock, which should cause - // Apex::Open to fail - name: "gen_corrupt_superblock_apex", - out: ["apex.apexd_test_corrupt_superblock_apex.apex"], - srcs: [":apex.apexd_test"], - tools: ["soong_zip", "zipalign"], - cmd: "unzip -q $(in) -d $(genDir) && " + - "dd if=/dev/zero of=$(genDir)/apex_payload.img conv=notrunc bs=1024 seek=1 count=1 && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) " + - "-s apex_manifest.pb -s apex_payload.img -s apex_pubkey " + - "-o $(genDir)/unaligned.apex && " + - "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " + - "$(genDir)/apex.apexd_test_corrupt_superblock_apex.apex" -} - -genrule { // Generates an apex with a corrupted filesystem image, which should cause // dm-verity verification to fail name: "gen_corrupt_apex", @@ -313,93 +252,6 @@ genrule { "$(genDir)/apex.apexd_test_corrupt_apex.apex" } -genrule { - // Extract the root digest with avbtool - name: "apex.apexd_test_digest", - out: ["apex.apexd_test_digest.txt"], - srcs: [":apex.apexd_test"], - tools: ["avbtool"], - cmd: "unzip -q $(in) -d $(genDir) apex_payload.img && " + - "$(location avbtool) print_partition_digests --image $(genDir)/apex_payload.img " + - "| cut -c 3-| tee $(out)" -} - -genrule { - // Extract the root digest with avbtool - name: "apex.apexd_test_f2fs_digest", - out: ["apex.apexd_test_f2fs_digest.txt"], - srcs: [":apex.apexd_test_f2fs"], - tools: ["avbtool"], - cmd: "unzip -q $(in) -d $(genDir) apex_payload.img && " + - "$(location avbtool) print_partition_digests --image $(genDir)/apex_payload.img " + - "| cut -c 3-| tee $(out)" -} - -genrule { - // Generates an apex which has same module name as apex.apexd_test.apex, but - // is actually signed with a different key. - name: "gen_key_mismatch_apex", - out: ["apex.apexd_test_different_key.apex"], - srcs: [":apex.apexd_test_no_inst_key"], - tools: ["soong_zip", "zipalign", "conv_apex_manifest"], - cmd: "unzip -q $(in) -d $(genDir) && " + - "$(location conv_apex_manifest) setprop name com.android.apex.test_package $(genDir)/apex_manifest.pb && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) " + - "-s apex_manifest.pb -s apex_payload.img -s apex_pubkey " + - "-o $(genDir)/unaligned.apex && " + - "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " + - "$(genDir)/apex.apexd_test_different_key.apex" -} - -genrule { - // Generates an apex which has same module name as apex.apexd_test.apex, but - // is actually signed with a different key. - name: "gen_key_mismatch_apex_v2", - out: ["apex.apexd_test_different_key_v2.apex"], - srcs: [":apex.apexd_test_no_inst_key"], - tools: ["soong_zip", "zipalign", "conv_apex_manifest"], - cmd: "unzip -q $(in) -d $(genDir) && " + - "$(location conv_apex_manifest) setprop name com.android.apex.test_package $(genDir)/apex_manifest.pb && " + - "$(location conv_apex_manifest) setprop version 2 $(genDir)/apex_manifest.pb && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) " + - "-s apex_manifest.pb -s apex_payload.img -s apex_pubkey " + - "-o $(genDir)/unaligned.apex && " + - "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " + - "$(genDir)/apex.apexd_test_different_key_v2.apex" -} - -genrule { - // Generates an apex which has a different manifest outside the filesystem - // image. - name: "gen_manifest_mismatch_rebootless_apex", - out: ["test.rebootless_apex_manifest_mismatch.apex"], - srcs: [":test.rebootless_apex_v1"], - tools: ["soong_zip", "zipalign", "conv_apex_manifest"], - cmd: "unzip -q $(in) -d $(genDir) && " + - "$(location conv_apex_manifest) setprop version 137 $(genDir)/apex_manifest.pb && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) " + - "-s apex_manifest.pb -s apex_payload.img -s apex_pubkey " + - "-o $(genDir)/unaligned.apex && " + - "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " + - "$(genDir)/test.rebootless_apex_manifest_mismatch.apex" -} - -genrule { - // Generates an apex with a corrupted filesystem image, which should cause - // dm-verity verification to fail - name: "gen_corrupt_rebootless_apex", - out: ["test.rebootless_apex_corrupted.apex"], - srcs: [":test.rebootless_apex_v1"], - tools: ["soong_zip", "zipalign"], - cmd: "unzip -q $(in) -d $(genDir) && " + - "dd if=/dev/zero of=$(genDir)/apex_payload.img conv=notrunc bs=1024 seek=16 count=1 && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) " + - "-s apex_manifest.pb -s apex_payload.img -s apex_pubkey " + - "-o $(genDir)/unaligned.apex && " + - "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " + - "$(genDir)/test.rebootless_apex_corrupted.apex" -} - cc_test { name: "ApexTestCases", defaults: [ @@ -414,32 +266,18 @@ cc_test { ], data: [ ":apex.apexd_test", - ":apex.apexd_test_f2fs", - ":apex.apexd_test_digest", - ":apex.apexd_test_f2fs_digest", ":apex.apexd_test_different_app", ":apex.apexd_test_no_hashtree", ":apex.apexd_test_no_hashtree_2", ":apex.apexd_test_no_inst_key", - ":apex.apexd_test_f2fs_no_inst_key", ":apex.apexd_test_nocode", ":apex.apexd_test_postinstall", ":apex.apexd_test_preinstall", ":apex.apexd_test_prepostinstall.fail", ":apex.apexd_test_v2", ":apex.corrupted_b146895998", - ":apex.banned_name", - ":gen_key_mismatch_apex", - ":gen_key_mismatch_apex_v2", - ":gen_key_mismatch_capex", - ":gen_manifest_mismatch_apex", - ":gen_manifest_mismatch_apex_no_hashtree", - ":gen_corrupt_superblock_apex", + ":gen_bad_apexes", ":gen_corrupt_apex", - ":gen_capex_not_decompressible", - ":gen_capex_without_apex", - ":gen_capex_with_v2_apex", - ":gen_key_mismatch_with_original_capex", ":com.android.apex.cts.shim.v1_prebuilt", ":com.android.apex.cts.shim.v2_prebuilt", ":com.android.apex.cts.shim.v2_wrong_sha_prebuilt", @@ -447,41 +285,13 @@ cc_test { ":com.android.apex.cts.shim.v2_additional_folder_prebuilt", ":com.android.apex.cts.shim.v2_with_pre_install_hook_prebuilt", ":com.android.apex.cts.shim.v2_with_post_install_hook_prebuilt", - ":com.android.apex.compressed_sharedlibs", - ":com.android.apex.compressed.v1", - ":com.android.apex.compressed.v1_different_digest", - ":com.android.apex.compressed.v1_different_digest_original", - ":com.android.apex.compressed.v1_original", - ":com.android.apex.compressed.v2", - ":com.android.apex.compressed.v2_original", - ":gen_manifest_mismatch_compressed_apex_v2", "apexd_testdata/com.android.apex.test_package.avbpubkey", - "apexd_testdata/com.android.apex.compressed.avbpubkey", - ":com.android.apex.test.sharedlibs_generated.v1.libvX_prebuilt", - ":com.android.apex.test.sharedlibs_generated.v2.libvY_prebuilt", - ":test.rebootless_apex_v1", - ":test.rebootless_apex_v2", - ":test.rebootless_apex_v2_no_hashtree", - ":gen_manifest_mismatch_rebootless_apex", - ":gen_corrupt_rebootless_apex", - ":test.rebootless_apex_provides_sharedlibs", - ":test.rebootless_apex_provides_native_libs", - ":test.rebootless_apex_requires_shared_apex_libs", - ":test.rebootless_apex_jni_libs", - ":test.rebootless_apex_add_native_lib", - ":test.rebootless_apex_remove_native_lib", - ":test.rebootless_apex_app_in_apex", - ":test.rebootless_apex_priv_app_in_apex", ], srcs: [ "apex_database_test.cpp", "apex_file_test.cpp", - "apex_file_repository_test.cpp", "apex_manifest_test.cpp", - "apexd_test.cpp", - "apexd_session_test.cpp", "apexd_verity_test.cpp", - "apexd_utils_test.cpp", "apexservice_test.cpp", ], host_supported: false, @@ -498,7 +308,6 @@ cc_test { "libfs_mgr", "libutils", ], - generated_sources: ["apex-info-list"], test_suites: ["device-tests"], test_config: "AndroidTest.xml", } @@ -521,10 +330,3 @@ cc_test { test_config: "flattened_apex_test_config.xml", } -xsd_config { - name: "apex-info-list", - srcs: ["ApexInfoList.xsd"], - package_name: "com.android.apex", - api_dir: "apex-info-list-api", - gen_writer: true, -} diff --git a/apexd/ApexInfoList.xsd b/apexd/ApexInfoList.xsd deleted file mode 100644 index 440b975f..00000000 --- a/apexd/ApexInfoList.xsd +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element name="apex-info-list"> - <xs:complexType> - <xs:sequence> - <xs:element ref="apex-info" minOccurs="1" maxOccurs="unbounded"/> - </xs:sequence> - </xs:complexType> - </xs:element> - - <xs:element name="apex-info"> - <xs:complexType> - <xs:attribute name="moduleName" type="xs:string" use="required"/> - <xs:attribute name="modulePath" type="xs:string" use="required"/> - <xs:attribute name="preinstalledModulePath" type="xs:string"/> - <xs:attribute name="versionCode" type="xs:long" use="required"/> - <xs:attribute name="versionName" type="xs:string" use="required"/> - <xs:attribute name="isFactory" type="xs:boolean" use="required"/> - <xs:attribute name="isActive" type="xs:boolean" use="required"/> - <xs:attribute name="lastUpdateMillis" type="xs:long"/> - </xs:complexType> - </xs:element> -</xs:schema> diff --git a/apexd/OWNERS b/apexd/OWNERS new file mode 100644 index 00000000..ed9813ee --- /dev/null +++ b/apexd/OWNERS @@ -0,0 +1 @@ +agampe@google.com diff --git a/apexd/aidl/android/apex/ApexSessionInfo.aidl b/apexd/aidl/android/apex/ApexSessionInfo.aidl index 65da1135..a7a6daf2 100644 --- a/apexd/aidl/android/apex/ApexSessionInfo.aidl +++ b/apexd/aidl/android/apex/ApexSessionInfo.aidl @@ -29,5 +29,4 @@ parcelable ApexSessionInfo { boolean isReverted; boolean isRevertFailed; @utf8InCpp String crashingNativeProcess; - @utf8InCpp String errorMessage; } diff --git a/apexd/aidl/android/apex/IApexService.aidl b/apexd/aidl/android/apex/IApexService.aidl index 2d77a822..3626d9ac 100644 --- a/apexd/aidl/android/apex/IApexService.aidl +++ b/apexd/aidl/android/apex/IApexService.aidl @@ -20,7 +20,6 @@ import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.ApexSessionParams; -import android.apex.CompressedApexInfoList; interface IApexService { void submitStagedSession(in ApexSessionParams params, out ApexInfoList packages); @@ -37,9 +36,9 @@ interface IApexService { /** * Copies the CE apex data directory for the given user to the backup - * location. + * location, and returns the inode of the snapshot directory. */ - void snapshotCeData(int user_id, int rollback_id, in @utf8InCpp String apex_name); + long snapshotCeData(int user_id, int rollback_id, in @utf8InCpp String apex_name); /** * Restores the snapshot of the CE apex data directory for the given user and @@ -53,11 +52,6 @@ interface IApexService { void destroyDeSnapshots(int rollback_id); /** - * Deletes credential-encrypted snapshots for the given user, for the given rollback id. - */ - void destroyCeSnapshots(int user_id, int rollback_id); - - /** * Deletes all credential-encrypted snapshots for the given user, except for * those listed in retain_rollback_ids. */ @@ -121,42 +115,4 @@ interface IApexService { * on user builds. Only root is allowed to call this method. */ void remountPackages(); - /** - * Forces apexd to recollect pre-installed data from the given |paths|. - * - * Not meant for use outside of testing. This call will not be functional - * on user builds. Only root is allowed to call this method. - */ - void recollectPreinstalledData(in @utf8InCpp List<String> paths); - /** - * Forces apexd to recollect data apex from the given |path|. - * - * Not meant for use outside of testing. This call will not be functional - * on user builds. Only root is allowed to call this method. - */ - void recollectDataApex(in @utf8InCpp String path, in@utf8InCpp String decompression_dir); - - /** - * Informs apexd that the boot has completed. - */ - void markBootCompleted(); - - /** - * Assuming the provided compressed APEX will be installed on next boot, - * calculate how much space will be required for decompression - */ - long calculateSizeForCompressedApex(in CompressedApexInfoList compressed_apex_info_list); - - /** - * Reserve space on /data partition for compressed APEX decompression. Returns error if - * reservation fails. If empty list is passed, then reserved space is deallocated. - */ - void reserveSpaceForCompressedApex(in CompressedApexInfoList compressed_apex_info_list); - - /** - * Performs a non-staged install of the given APEX. - * Note: don't confuse this to preInstall and postInstall binder calls which are only used to - * test corresponding features of APEX packages. - */ - ApexInfo installAndActivatePackage(in @utf8InCpp String packagePath); } diff --git a/apexd/apex-info-list-api/current.txt b/apexd/apex-info-list-api/current.txt deleted file mode 100644 index 8db5a9a0..00000000 --- a/apexd/apex-info-list-api/current.txt +++ /dev/null @@ -1,45 +0,0 @@ -// Signature format: 2.0 -package com.android.apex { - - public class ApexInfo { - ctor public ApexInfo(); - method public boolean getIsActive(); - method public boolean getIsFactory(); - method public long getLastUpdateMillis(); - method public String getModuleName(); - method public String getModulePath(); - method public String getPreinstalledModulePath(); - method public long getVersionCode(); - method public String getVersionName(); - method public void setIsActive(boolean); - method public void setIsFactory(boolean); - method public void setLastUpdateMillis(long); - method public void setModuleName(String); - method public void setModulePath(String); - method public void setPreinstalledModulePath(String); - method public void setVersionCode(long); - method public void setVersionName(String); - } - - public class ApexInfoList { - ctor public ApexInfoList(); - method public java.util.List<com.android.apex.ApexInfo> getApexInfo(); - } - - public class XmlParser { - ctor public XmlParser(); - method public static com.android.apex.ApexInfo readApexInfo(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static com.android.apex.ApexInfoList readApexInfoList(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - } - - public class XmlWriter implements java.io.Closeable { - ctor public XmlWriter(java.io.PrintWriter); - method public void close(); - method public static void write(com.android.apex.XmlWriter, com.android.apex.ApexInfoList) throws java.io.IOException; - method public static void write(com.android.apex.XmlWriter, com.android.apex.ApexInfo) throws java.io.IOException; - } - -} - diff --git a/apexd/apex-info-list-api/last_current.txt b/apexd/apex-info-list-api/last_current.txt deleted file mode 100644 index 8db5a9a0..00000000 --- a/apexd/apex-info-list-api/last_current.txt +++ /dev/null @@ -1,45 +0,0 @@ -// Signature format: 2.0 -package com.android.apex { - - public class ApexInfo { - ctor public ApexInfo(); - method public boolean getIsActive(); - method public boolean getIsFactory(); - method public long getLastUpdateMillis(); - method public String getModuleName(); - method public String getModulePath(); - method public String getPreinstalledModulePath(); - method public long getVersionCode(); - method public String getVersionName(); - method public void setIsActive(boolean); - method public void setIsFactory(boolean); - method public void setLastUpdateMillis(long); - method public void setModuleName(String); - method public void setModulePath(String); - method public void setPreinstalledModulePath(String); - method public void setVersionCode(long); - method public void setVersionName(String); - } - - public class ApexInfoList { - ctor public ApexInfoList(); - method public java.util.List<com.android.apex.ApexInfo> getApexInfo(); - } - - public class XmlParser { - ctor public XmlParser(); - method public static com.android.apex.ApexInfo readApexInfo(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static com.android.apex.ApexInfoList readApexInfoList(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; - } - - public class XmlWriter implements java.io.Closeable { - ctor public XmlWriter(java.io.PrintWriter); - method public void close(); - method public static void write(com.android.apex.XmlWriter, com.android.apex.ApexInfoList) throws java.io.IOException; - method public static void write(com.android.apex.XmlWriter, com.android.apex.ApexInfo) throws java.io.IOException; - } - -} - diff --git a/apexd/apex-info-list-api/last_removed.txt b/apexd/apex-info-list-api/last_removed.txt deleted file mode 100644 index e69de29b..00000000 --- a/apexd/apex-info-list-api/last_removed.txt +++ /dev/null diff --git a/apexd/apex-info-list-api/removed.txt b/apexd/apex-info-list-api/removed.txt deleted file mode 100644 index d802177e..00000000 --- a/apexd/apex-info-list-api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apexd/apex_constants.h b/apexd/apex_constants.h index 798afd5a..4e989431 100644 --- a/apexd/apex_constants.h +++ b/apexd/apex_constants.h @@ -17,7 +17,6 @@ #pragma once #include <string> -#include <unordered_set> #include <vector> namespace android { @@ -27,8 +26,6 @@ static constexpr const char* kApexDataDir = "/data/apex"; static constexpr const char* kActiveApexPackagesDataDir = "/data/apex/active"; static constexpr const char* kApexBackupDir = "/data/apex/backup"; static constexpr const char* kApexHashTreeDir = "/data/apex/hashtree"; -static constexpr const char* kApexDecompressedDir = "/data/apex/decompressed"; -static constexpr const char* kOtaReservedDir = "/data/apex/ota_reserved"; static constexpr const char* kApexPackageSystemDir = "/system/apex"; static constexpr const char* kApexPackageSystemExtDir = "/system_ext/apex"; static constexpr const char* kApexPackageVendorDir = "/vendor/apex"; @@ -42,7 +39,6 @@ static constexpr const char* kApexRoot = "/apex"; static constexpr const char* kStagedSessionsDir = "/data/app-staging"; static constexpr const char* kApexDataSubDir = "apexdata"; -static constexpr const char* kApexSharedLibsSubDir = "sharedlibs"; static constexpr const char* kApexSnapshotSubDir = "apexrollback"; static constexpr const char* kPreRestoreSuffix = "-prerestore"; @@ -51,26 +47,8 @@ static constexpr const char* kDeNDataDir = "/data/misc_de"; static constexpr const char* kCeDataDir = "/data/misc_ce"; static constexpr const char* kApexPackageSuffix = ".apex"; -static constexpr const char* kCompressedApexPackageSuffix = ".capex"; -static constexpr const char* kDecompressedApexPackageSuffix = - ".decompressed.apex"; -static constexpr const char* kOtaApexPackageSuffix = ".ota.apex"; static constexpr const char* kManifestFilenameJson = "apex_manifest.json"; static constexpr const char* kManifestFilenamePb = "apex_manifest.pb"; - -static constexpr const char* kApexInfoList = "apex-info-list.xml"; - -// These should be in-sync with system/sepolicy/private/property_contexts -static constexpr const char* kApexStatusSysprop = "apexd.status"; -static constexpr const char* kApexStatusStarting = "starting"; -static constexpr const char* kApexStatusActivated = "activated"; -static constexpr const char* kApexStatusReady = "ready"; - -// Banned APEX names -static const std::unordered_set<std::string> kBannedApexName = { - kApexSharedLibsSubDir, // To avoid conflicts with predefined - // /apex/sharedlibs directory -}; } // namespace apex } // namespace android diff --git a/apexd/apex_database.cpp b/apexd/apex_database.cpp index 7a1ee360..d027f95c 100644 --- a/apexd/apex_database.cpp +++ b/apexd/apex_database.cpp @@ -35,9 +35,6 @@ #include <utility> using android::base::ConsumeSuffix; -using android::base::EndsWith; -using android::base::ErrnoError; -using android::base::Error; using android::base::ParseInt; using android::base::ReadFileToString; using android::base::Result; @@ -79,12 +76,12 @@ class BlockDevice { fs::path DevPath() const { return kDevBlock / name; } Result<std::string> GetProperty(const std::string& property) const { - auto property_file = SysPath() / property; - std::string property_value; - if (!ReadFileToString(property_file, &property_value)) { + auto propertyFile = SysPath() / property; + std::string propertyValue; + if (!ReadFileToString(propertyFile, &propertyValue)) { return ErrnoError() << "Fail to read"; } - return Trim(property_value); + return Trim(propertyValue); } std::vector<BlockDevice> GetSlaves() const { @@ -103,17 +100,17 @@ class BlockDevice { } }; -std::pair<fs::path, fs::path> ParseMountInfo(const std::string& mount_info) { - const auto& tokens = Split(mount_info, " "); +std::pair<fs::path, fs::path> parseMountInfo(const std::string& mountInfo) { + const auto& tokens = Split(mountInfo, " "); if (tokens.size() < 2) { return std::make_pair("", ""); } return std::make_pair(tokens[0], tokens[1]); } -std::pair<std::string, int> ParseMountPoint(const std::string& mount_point) { - auto package_id = fs::path(mount_point).filename(); - auto split = Split(package_id, "@"); +std::pair<std::string, int> parseMountPoint(const std::string& mountPoint) { + auto packageId = fs::path(mountPoint).filename(); + auto split = Split(packageId, "@"); if (split.size() == 2) { int version; if (!ParseInt(split[1], &version)) { @@ -121,17 +118,14 @@ std::pair<std::string, int> ParseMountPoint(const std::string& mount_point) { } return std::make_pair(split[0], version); } - return std::make_pair(package_id, -1); + return std::make_pair(packageId, -1); } -bool IsActiveMountPoint(const std::string& mount_point) { - return (mount_point.find('@') == std::string::npos); +bool isActiveMountPoint(const std::string& mountPoint) { + return (mountPoint.find('@') == std::string::npos); } Result<void> PopulateLoopInfo(const BlockDevice& top_device, - const std::string& active_apex_dir, - const std::string& decompression_dir, - const std::string& apex_hash_tree_dir, MountedApexData* apex_data) { std::vector<BlockDevice> slaves = top_device.GetSlaves(); if (slaves.size() != 1 && slaves.size() != 2) { @@ -153,22 +147,18 @@ Result<void> PopulateLoopInfo(const BlockDevice& top_device, // Enforce following invariant: // * slaves[0] always represents a data loop device // * if size = 2 then slaves[1] represents an external hashtree loop device - auto is_data_loop_device = [&](const std::string& backing_file) { - return StartsWith(backing_file, active_apex_dir) || - StartsWith(backing_file, decompression_dir); - }; if (slaves.size() == 2) { - if (!is_data_loop_device(backing_files[0])) { + if (!StartsWith(backing_files[0], kActiveApexPackagesDataDir)) { std::swap(slaves[0], slaves[1]); std::swap(backing_files[0], backing_files[1]); } } - if (!is_data_loop_device(backing_files[0])) { + if (!StartsWith(backing_files[0], kActiveApexPackagesDataDir)) { return Error() << "Data loop device " << slaves[0].DevPath() << " has unexpected backing file " << backing_files[0]; } if (slaves.size() == 2) { - if (!StartsWith(backing_files[1], apex_hash_tree_dir)) { + if (!StartsWith(backing_files[1], kApexHashTreeDir)) { return Error() << "Hashtree loop device " << slaves[1].DevPath() << " has unexpected backing file " << backing_files[1]; } @@ -181,7 +171,7 @@ Result<void> PopulateLoopInfo(const BlockDevice& top_device, // This is not the right place to do this normalization, but proper solution // will require some refactoring first. :( -// TODO(b/158469911): introduce MountedApexDataBuilder and delegate all +// TODO(ioffe): introduce MountedApexDataBuilder and delegate all // building/normalization logic to it. void NormalizeIfDeleted(MountedApexData* apex_data) { std::string_view full_path = apex_data->full_path; @@ -198,22 +188,18 @@ void NormalizeIfDeleted(MountedApexData* apex_data) { apex_data->full_path = full_path; } -Result<MountedApexData> ResolveMountInfo( - const BlockDevice& block, const std::string& mount_point, - const std::string& active_apex_dir, const std::string& decompression_dir, - const std::string& apex_hash_tree_dir) { - bool temp_mount = EndsWith(mount_point, ".tmp"); +Result<MountedApexData> resolveMountInfo(const BlockDevice& block, + const std::string& mountPoint) { // Now, see if it is dm-verity or loop mounted switch (block.GetType()) { case LoopDevice: { - auto backing_file = block.GetProperty("loop/backing_file"); - if (!backing_file.ok()) { - return backing_file.error(); + auto backingFile = block.GetProperty("loop/backing_file"); + if (!backingFile.ok()) { + return backingFile.error(); } - auto result = MountedApexData(block.DevPath(), *backing_file, mount_point, + auto result = MountedApexData(block.DevPath(), *backingFile, mountPoint, /* device_name= */ "", - /* hashtree_loop_name= */ "", - /* is_temp_mount */ temp_mount); + /* hashtree_loop_name= */ ""); NormalizeIfDeleted(&result); return result; } @@ -223,12 +209,9 @@ Result<MountedApexData> ResolveMountInfo( return name.error(); } MountedApexData result; - result.mount_point = mount_point; + result.mount_point = mountPoint; result.device_name = *name; - result.is_temp_mount = temp_mount; - auto status = PopulateLoopInfo(block, active_apex_dir, decompression_dir, - apex_hash_tree_dir, &result); - if (!status.ok()) { + if (auto status = PopulateLoopInfo(block, &result); !status.ok()) { return status.error(); } NormalizeIfDeleted(&result); @@ -263,45 +246,40 @@ Result<MountedApexData> ResolveMountInfo( // By synchronizing the mounts info with Database on startup, // Apexd serves the correct package list even on the devices // which are not ro.apex.updatable. -void MountedApexDatabase::PopulateFromMounts( - const std::string& active_apex_dir, const std::string& decompression_dir, - const std::string& apex_hash_tree_dir) REQUIRES(!mounted_apexes_mutex_) { +void MountedApexDatabase::PopulateFromMounts() { LOG(INFO) << "Populating APEX database from mounts..."; - std::unordered_map<std::string, int> active_versions; + std::unordered_map<std::string, int> activeVersions; std::ifstream mounts("/proc/mounts"); std::string line; - std::lock_guard lock(mounted_apexes_mutex_); while (std::getline(mounts, line)) { - auto [block, mount_point] = ParseMountInfo(line); - // TODO(b/158469914): distinguish between temp and non-temp mounts - if (fs::path(mount_point).parent_path() != kApexRoot) { + auto [block, mountPoint] = parseMountInfo(line); + // TODO(jooyung): ignore tmp mount? + if (fs::path(mountPoint).parent_path() != kApexRoot) { continue; } - if (IsActiveMountPoint(mount_point)) { + if (isActiveMountPoint(mountPoint)) { continue; } - auto mount_data = - ResolveMountInfo(BlockDevice(block), mount_point, active_apex_dir, - decompression_dir, apex_hash_tree_dir); - if (!mount_data.ok()) { - LOG(WARNING) << "Can't resolve mount info " << mount_data.error(); + auto mountData = resolveMountInfo(BlockDevice(block), mountPoint); + if (!mountData.ok()) { + LOG(WARNING) << "Can't resolve mount info " << mountData.error(); continue; } - auto [package, version] = ParseMountPoint(mount_point); - AddMountedApexLocked(package, false, *mount_data); + auto [package, version] = parseMountPoint(mountPoint); + AddMountedApex(package, false, *mountData); - auto active = active_versions[package] < version; + auto active = activeVersions[package] < version; if (active) { - active_versions[package] = version; - SetLatestLocked(package, mount_data->full_path); + activeVersions[package] = version; + SetLatest(package, mountData->full_path); } - LOG(INFO) << "Found " << mount_point << " backed by" - << (mount_data->deleted ? " deleted " : " ") << "file " - << mount_data->full_path; + LOG(INFO) << "Found " << mountPoint << " backed by" + << (mountData->deleted ? " deleted " : " ") << "file " + << mountData->full_path; } LOG(INFO) << mounted_apexes_.size() << " packages restored."; diff --git a/apexd/apex_database.h b/apexd/apex_database.h index 113b88ec..1fb5bd3a 100644 --- a/apexd/apex_database.h +++ b/apexd/apex_database.h @@ -18,14 +18,10 @@ #define ANDROID_APEXD_APEX_DATABASE_H_ #include <map> -#include <mutex> -#include <optional> #include <string> #include <unordered_set> #include <android-base/logging.h> -#include <android-base/result.h> -#include <android-base/thread_annotations.h> namespace android { namespace apex { @@ -44,21 +40,17 @@ class MountedApexDatabase { std::string hashtree_loop_name; // Whenever apex file specified in full_path was deleted. bool deleted; - // Whether the mount is a temp mount or not. - bool is_temp_mount; MountedApexData() {} MountedApexData(const std::string& loop_name, const std::string& full_path, const std::string& mount_point, const std::string& device_name, - const std::string& hashtree_loop_name, - bool is_temp_mount = false) + const std::string& hashtree_loop_name) : loop_name(loop_name), full_path(full_path), mount_point(mount_point), device_name(device_name), - hashtree_loop_name(hashtree_loop_name), - is_temp_mount(is_temp_mount) {} + hashtree_loop_name(hashtree_loop_name) {} inline bool operator<(const MountedApexData& rhs) const { int compare_val = loop_name.compare(rhs.loop_name); @@ -89,10 +81,42 @@ class MountedApexDatabase { } }; + inline void CheckAtMostOneLatest() { + for (const auto& apex_set : mounted_apexes_) { + size_t count = 0; + for (const auto& pair : apex_set.second) { + if (pair.second) { + count++; + } + } + CHECK_LE(count, 1u) << apex_set.first; + } + } + + inline void CheckUniqueLoopDm() { + std::unordered_set<std::string> loop_devices; + std::unordered_set<std::string> dm_devices; + for (const auto& apex_set : mounted_apexes_) { + for (const auto& pair : apex_set.second) { + if (pair.first.loop_name != "") { + CHECK(loop_devices.insert(pair.first.loop_name).second) + << "Duplicate loop device: " << pair.first.loop_name; + } + if (pair.first.device_name != "") { + CHECK(dm_devices.insert(pair.first.device_name).second) + << "Duplicate dm device: " << pair.first.device_name; + } + if (pair.first.hashtree_loop_name != "") { + CHECK(loop_devices.insert(pair.first.hashtree_loop_name).second) + << "Duplicate loop device: " << pair.first.hashtree_loop_name; + } + } + } + } + template <typename... Args> - inline void AddMountedApexLocked(const std::string& package, bool latest, - Args&&... args) - REQUIRES(mounted_apexes_mutex_) { + inline void AddMountedApex(const std::string& package, bool latest, + Args&&... args) { auto it = mounted_apexes_.find(package); if (it == mounted_apexes_.end()) { auto insert_it = @@ -109,18 +133,8 @@ class MountedApexDatabase { CheckUniqueLoopDm(); } - template <typename... Args> - inline void AddMountedApex(const std::string& package, bool latest, - Args&&... args) REQUIRES(!mounted_apexes_mutex_) { - std::lock_guard lock(mounted_apexes_mutex_); - AddMountedApexLocked(package, latest, args...); - } - inline void RemoveMountedApex(const std::string& package, - const std::string& full_path, - bool match_temp_mounts = false) - REQUIRES(!mounted_apexes_mutex_) { - std::lock_guard lock(mounted_apexes_mutex_); + const std::string& full_path) { auto it = mounted_apexes_.find(package); if (it == mounted_apexes_.end()) { return; @@ -129,8 +143,7 @@ class MountedApexDatabase { auto& pkg_map = it->second; for (auto pkg_it = pkg_map.begin(); pkg_it != pkg_map.end(); ++pkg_it) { - if (pkg_it->first.full_path == full_path && - pkg_it->first.is_temp_mount == match_temp_mounts) { + if (pkg_it->first.full_path == full_path) { pkg_map.erase(pkg_it); return; } @@ -138,15 +151,7 @@ class MountedApexDatabase { } inline void SetLatest(const std::string& package, - const std::string& full_path) - REQUIRES(!mounted_apexes_mutex_) { - std::lock_guard lock(mounted_apexes_mutex_); - SetLatestLocked(package, full_path); - } - - inline void SetLatestLocked(const std::string& package, - const std::string& full_path) - REQUIRES(mounted_apexes_mutex_) { + const std::string& full_path) { auto it = mounted_apexes_.find(package); CHECK(it != mounted_apexes_.end()); @@ -168,108 +173,47 @@ class MountedApexDatabase { LOG(FATAL) << "Did not find " << package << " " << full_path; } + inline void UnsetLatestForall(const std::string& package) { + auto it = mounted_apexes_.find(package); + if (it == mounted_apexes_.end()) { + return; + } + for (auto& data : it->second) { + data.second = false; + } + } + template <typename T> - inline void ForallMountedApexes(const std::string& package, const T& handler, - bool match_temp_mounts = false) const - REQUIRES(!mounted_apexes_mutex_) { - std::lock_guard lock(mounted_apexes_mutex_); + inline void ForallMountedApexes(const std::string& package, + const T& handler) const { auto it = mounted_apexes_.find(package); if (it == mounted_apexes_.end()) { return; } for (auto& pair : it->second) { - if (pair.first.is_temp_mount == match_temp_mounts) { - handler(pair.first, pair.second); - } + handler(pair.first, pair.second); } } template <typename T> - inline void ForallMountedApexes(const T& handler, - bool match_temp_mounts = false) const - REQUIRES(!mounted_apexes_mutex_) { - std::lock_guard lock(mounted_apexes_mutex_); + inline void ForallMountedApexes(const T& handler) const { for (const auto& pkg : mounted_apexes_) { for (const auto& pair : pkg.second) { - if (pair.first.is_temp_mount == match_temp_mounts) { - handler(pkg.first, pair.first, pair.second); - } + handler(pkg.first, pair.first, pair.second); } } } - inline std::optional<MountedApexData> GetLatestMountedApex( - const std::string& package) REQUIRES(!mounted_apexes_mutex_) { - std::optional<MountedApexData> ret; - ForallMountedApexes(package, - [&ret](const MountedApexData& data, bool latest) { - if (latest) { - ret.emplace(data); - } - }); - return ret; - } - - void PopulateFromMounts(const std::string& active_apex_dir, - const std::string& decompression_dir, - const std::string& apex_hash_tree_dir); - - // Resets state of the database. Should only be used in testing. - inline void Reset() REQUIRES(!mounted_apexes_mutex_) { - std::lock_guard lock(mounted_apexes_mutex_); - mounted_apexes_.clear(); - } + void PopulateFromMounts(); private: // A map from package name to mounted apexes. // Note: using std::maps to // a) so we do not have to worry about iterator invalidation. // b) do not have to const_cast (over std::set) - // TODO(b/158467745): This structure (and functions) need to be guarded by - // locks. - std::map<std::string, std::map<MountedApexData, bool>> mounted_apexes_ - GUARDED_BY(mounted_apexes_mutex_); - - // To fix thread safety negative capability warning - class Mutex : public std::mutex { - public: - // for negative capabilities - const Mutex& operator!() const { return *this; } - }; - mutable Mutex mounted_apexes_mutex_; - - inline void CheckAtMostOneLatest() REQUIRES(mounted_apexes_mutex_) { - for (const auto& apex_set : mounted_apexes_) { - size_t count = 0; - for (const auto& pair : apex_set.second) { - if (pair.second) { - count++; - } - } - CHECK_LE(count, 1u) << apex_set.first; - } - } - - inline void CheckUniqueLoopDm() REQUIRES(mounted_apexes_mutex_) { - std::unordered_set<std::string> loop_devices; - std::unordered_set<std::string> dm_devices; - for (const auto& apex_set : mounted_apexes_) { - for (const auto& pair : apex_set.second) { - if (pair.first.loop_name != "") { - CHECK(loop_devices.insert(pair.first.loop_name).second) - << "Duplicate loop device: " << pair.first.loop_name; - } - if (pair.first.device_name != "") { - CHECK(dm_devices.insert(pair.first.device_name).second) - << "Duplicate dm device: " << pair.first.device_name; - } - if (pair.first.hashtree_loop_name != "") { - CHECK(loop_devices.insert(pair.first.hashtree_loop_name).second) - << "Duplicate loop device: " << pair.first.hashtree_loop_name; - } - } - } - } + // TODO: Eventually this structure (and functions) need to be guarded by + // locks. + std::map<std::string, std::map<MountedApexData, bool>> mounted_apexes_; }; } // namespace apex diff --git a/apexd/apex_database_test.cpp b/apexd/apex_database_test.cpp index 8c3a98d0..522619fc 100644 --- a/apexd/apex_database_test.cpp +++ b/apexd/apex_database_test.cpp @@ -35,7 +35,6 @@ TEST(MountedApexDataTest, LinearOrder) { constexpr const char* kDm[] = {"dm1", "dm2", "dm3"}; constexpr const char* kHashtreeLoopName[] = {"hash-loop1", "hash-loop2", "hash-loop3"}; - // NOLINTNEXTLINE(bugprone-sizeof-expression) constexpr size_t kCount = arraysize(kLoopName) * arraysize(kPath) * arraysize(kMount) * arraysize(kDm); @@ -201,37 +200,6 @@ TEST(ApexDatabaseTest, MountMultiple) { kDeviceName[3], kHashtreeLoopName[3])); } -TEST(ApexDatabaseTest, GetLatestMountedApex) { - constexpr const char* kPackage = "package"; - constexpr const char* kLoopName = "loop"; - constexpr const char* kPath = "path"; - constexpr const char* kMountPoint = "mount"; - constexpr const char* kDeviceName = "dev"; - constexpr const char* kHashtreeLoopName = "hash-loop"; - - MountedApexDatabase db; - ASSERT_EQ(CountPackages(db), 0u); - - db.AddMountedApex(kPackage, true, kLoopName, kPath, kMountPoint, kDeviceName, - kHashtreeLoopName); - - auto ret = db.GetLatestMountedApex(kPackage); - MountedApexData expected(kLoopName, kPath, kMountPoint, kDeviceName, - kHashtreeLoopName); - ASSERT_TRUE(ret.has_value()); - ASSERT_EQ(ret->loop_name, std::string(kLoopName)); - ASSERT_EQ(ret->full_path, std::string(kPath)); - ASSERT_EQ(ret->mount_point, std::string(kMountPoint)); - ASSERT_EQ(ret->device_name, std::string(kDeviceName)); - ASSERT_EQ(ret->hashtree_loop_name, std::string(kHashtreeLoopName)); -} - -TEST(ApexDatabaseTest, GetLatestMountedApexReturnsNullopt) { - MountedApexDatabase db; - auto ret = db.GetLatestMountedApex("no-such-name"); - ASSERT_FALSE(ret.has_value()); -} - #pragma clang diagnostic push // error: 'ReturnSentinel' was marked unused but was used // [-Werror,-Wused-but-marked-unused] diff --git a/apexd/apex_file.cpp b/apexd/apex_file.cpp index c043defa..f91b5d18 100644 --- a/apexd/apex_file.cpp +++ b/apexd/apex_file.cpp @@ -23,108 +23,61 @@ #include <filesystem> #include <fstream> -#include <span> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/scopeguard.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <google/protobuf/util/message_differencer.h> #include <libavb/libavb.h> -#include <ziparchive/zip_archive.h> #include "apex_constants.h" +#include "apex_preinstalled_data.h" #include "apexd_utils.h" +#include "string_log.h" -using android::base::borrowed_fd; -using android::base::ErrnoError; +using android::base::EndsWith; using android::base::Error; using android::base::ReadFullyAtOffset; -using android::base::RemoveFileIfExists; using android::base::Result; +using android::base::StartsWith; using android::base::unique_fd; -using ::apex::proto::ApexManifest; +using google::protobuf::util::MessageDifferencer; namespace android { namespace apex { namespace { constexpr const char* kImageFilename = "apex_payload.img"; -constexpr const char* kCompressedApexFilename = "original_apex"; constexpr const char* kBundledPublicKeyFilename = "apex_pubkey"; -struct FsMagic { - const char* type; - int32_t offset; - int16_t len; - const char* magic; -}; -constexpr const FsMagic kFsType[] = {{"f2fs", 1024, 4, "\x10\x20\xf5\xf2"}, - {"ext4", 1024 + 0x38, 2, "\123\357"}}; - -Result<std::string> RetrieveFsType(borrowed_fd fd, int32_t image_offset) { - for (const auto& fs : kFsType) { - char buf[fs.len]; - if (!ReadFullyAtOffset(fd, buf, fs.len, image_offset + fs.offset)) { - return ErrnoError() << "Couldn't read filesystem magic"; - } - if (memcmp(buf, fs.magic, fs.len) == 0) { - return std::string(fs.type); - } - } - return Error() << "Couldn't find filesystem magic"; -} - } // namespace Result<ApexFile> ApexFile::Open(const std::string& path) { - std::optional<int32_t> image_offset; - std::optional<size_t> image_size; + int32_t image_offset; + size_t image_size; std::string manifest_content; std::string pubkey; - std::optional<std::string> fs_type; - ZipEntry entry; - - unique_fd fd(open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC)); - if (fd < 0) { - return Error() << "Failed to open package " << path << ": " - << "I/O error"; - } ZipArchiveHandle handle; auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); }); - int ret = OpenArchiveFd(fd.get(), path.c_str(), &handle, false); + int ret = OpenArchive(path.c_str(), &handle); if (ret < 0) { return Error() << "Failed to open package " << path << ": " << ErrorCodeString(ret); } - bool is_compressed = true; - ret = FindEntry(handle, kCompressedApexFilename, &entry); + // Locate the mountable image within the zipfile and store offset and size. + ZipEntry entry; + ret = FindEntry(handle, kImageFilename, &entry); if (ret < 0) { - is_compressed = false; - } - - if (!is_compressed) { - // Locate the mountable image within the zipfile and store offset and size. - ret = FindEntry(handle, kImageFilename, &entry); - if (ret < 0) { - return Error() << "Could not find entry \"" << kImageFilename - << "\" or \"" << kCompressedApexFilename - << "\" in package " << path << ": " - << ErrorCodeString(ret); - } - image_offset = entry.offset; - image_size = entry.uncompressed_length; - - auto fs_type_result = RetrieveFsType(fd, image_offset.value()); - if (!fs_type_result.ok()) { - return Error() << "Failed to retrieve filesystem type for " << path - << ": " << fs_type_result.error(); - } - fs_type = std::move(*fs_type_result); + return Error() << "Could not find entry \"" << kImageFilename + << "\" in package " << path << ": " << ErrorCodeString(ret); } + image_offset = entry.offset; + image_size = entry.uncompressed_length; ret = FindEntry(handle, kManifestFilenamePb, &entry); if (ret < 0) { @@ -159,20 +112,8 @@ Result<ApexFile> ApexFile::Open(const std::string& path) { return manifest.error(); } - if (is_compressed && manifest->providesharedapexlibs()) { - return Error() << "Apex providing sharedlibs shouldn't be compressed"; - } - - // b/179211712 the stored path should be the realpath, otherwise the path we - // get by scanning the directory would be different from the path we get - // by reading /proc/mounts, if the apex file is on a symlink dir. - std::string realpath; - if (!android::base::Realpath(path, &realpath)) { - return ErrnoError() << "can't get realpath of " << path; - } - - return ApexFile(realpath, image_offset, image_size, std::move(*manifest), - pubkey, fs_type, is_compressed); + return ApexFile(path, image_offset, image_size, std::move(*manifest), pubkey, + isPathForBuiltinApexes(path)); } // AVB-related code. @@ -181,7 +122,7 @@ namespace { static constexpr int kVbMetaMaxSize = 64 * 1024; -std::string BytesToHex(const uint8_t* bytes, size_t bytes_len) { +std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) { std::ostringstream s; s << std::hex << std::setfill('0'); @@ -191,32 +132,28 @@ std::string BytesToHex(const uint8_t* bytes, size_t bytes_len) { return s.str(); } -std::string GetSalt(const AvbHashtreeDescriptor& desc, - const uint8_t* trailing_data) { - const uint8_t* desc_salt = trailing_data + desc.partition_name_len; +std::string getSalt(const AvbHashtreeDescriptor& desc, + const uint8_t* trailingData) { + const uint8_t* desc_salt = trailingData + desc.partition_name_len; - return BytesToHex(desc_salt, desc.salt_len); + return bytes_to_hex(desc_salt, desc.salt_len); } -std::string GetDigest(const AvbHashtreeDescriptor& desc, - const uint8_t* trailing_data) { +std::string getDigest(const AvbHashtreeDescriptor& desc, + const uint8_t* trailingData) { const uint8_t* desc_digest = - trailing_data + desc.partition_name_len + desc.salt_len; + trailingData + desc.partition_name_len + desc.salt_len; - return BytesToHex(desc_digest, desc.root_digest_len); + return bytes_to_hex(desc_digest, desc.root_digest_len); } -Result<std::unique_ptr<AvbFooter>> GetAvbFooter(const ApexFile& apex, +Result<std::unique_ptr<AvbFooter>> getAvbFooter(const ApexFile& apex, const unique_fd& fd) { std::array<uint8_t, AVB_FOOTER_SIZE> footer_data; auto footer = std::make_unique<AvbFooter>(); // The AVB footer is located in the last part of the image - if (!apex.GetImageOffset() || !apex.GetImageSize()) { - return Error() << "Cannot check avb footer without image offset and size"; - } - off_t offset = apex.GetImageSize().value() + apex.GetImageOffset().value() - - AVB_FOOTER_SIZE; + off_t offset = apex.GetImageSize() + apex.GetImageOffset() - AVB_FOOTER_SIZE; int ret = lseek(fd, offset, SEEK_SET); if (ret == -1) { return ErrnoError() << "Couldn't seek to AVB footer"; @@ -242,10 +179,8 @@ bool CompareKeys(const uint8_t* key, size_t length, memcmp(&public_key_content[0], key, length) == 0; } -// Verifies correctness of vbmeta and returns public key it was signed with. -Result<std::span<const uint8_t>> VerifyVbMetaSignature(const ApexFile& apex, - const uint8_t* data, - size_t length) { +Result<void> verifyVbMetaSignature(const ApexFile& apex, const uint8_t* data, + size_t length) { const uint8_t* pk; size_t pk_len; AvbVBMetaVerifyResult res; @@ -266,46 +201,49 @@ Result<std::span<const uint8_t>> VerifyVbMetaSignature(const ApexFile& apex, return Error() << "Error verifying " << apex.GetPath() << ": " << "unsupported version"; default: - return Error() << "Unknown vmbeta_image_verify return value : " << res; + return Errorf("Unknown vmbeta_image_verify return value"); } - return std::span<const uint8_t>(pk, pk_len); + Result<const std::string> public_key = getApexKey(apex.GetManifest().name()); + if (public_key.ok()) { + // TODO(b/115718846) + // We need to decide whether we need rollback protection, and whether + // we can use the rollback protection provided by libavb. + if (!CompareKeys(pk, pk_len, *public_key)) { + return Error() << "Error verifying " << apex.GetPath() << ": " + << "public key doesn't match the pre-installed one"; + } + } else { + return public_key.error(); + } + LOG(VERBOSE) << apex.GetPath() << ": public key matches."; + return {}; } -Result<std::unique_ptr<uint8_t[]>> VerifyVbMeta(const ApexFile& apex, +Result<std::unique_ptr<uint8_t[]>> verifyVbMeta(const ApexFile& apex, const unique_fd& fd, - const AvbFooter& footer, - const std::string& public_key) { + const AvbFooter& footer) { if (footer.vbmeta_size > kVbMetaMaxSize) { return Errorf("VbMeta size in footer exceeds kVbMetaMaxSize."); } - if (!apex.GetImageOffset()) { - return Error() << "Cannot check VbMeta size without image offset"; - } - - off_t offset = apex.GetImageOffset().value() + footer.vbmeta_offset; + off_t offset = apex.GetImageOffset() + footer.vbmeta_offset; std::unique_ptr<uint8_t[]> vbmeta_buf(new uint8_t[footer.vbmeta_size]); if (!ReadFullyAtOffset(fd, vbmeta_buf.get(), footer.vbmeta_size, offset)) { return ErrnoError() << "Couldn't read AVB meta-data"; } - Result<std::span<const uint8_t>> st = - VerifyVbMetaSignature(apex, vbmeta_buf.get(), footer.vbmeta_size); + Result<void> st = + verifyVbMetaSignature(apex, vbmeta_buf.get(), footer.vbmeta_size); if (!st.ok()) { return st.error(); } - if (!CompareKeys(st->data(), st->size(), public_key)) { - return Error() << "Error verifying " << apex.GetPath() << " : " - << "public key doesn't match the pre-installed one"; - } - return vbmeta_buf; } -Result<const AvbHashtreeDescriptor*> FindDescriptor(uint8_t* vbmeta_data, +Result<const AvbHashtreeDescriptor*> findDescriptor(uint8_t* vbmeta_data, size_t vbmeta_size) { const AvbDescriptor** descriptors; size_t num_descriptors; @@ -339,127 +277,118 @@ Result<const AvbHashtreeDescriptor*> FindDescriptor(uint8_t* vbmeta_data, return Errorf("Couldn't find any AVB hashtree descriptors."); } -Result<std::unique_ptr<AvbHashtreeDescriptor>> VerifyDescriptor( +Result<std::unique_ptr<AvbHashtreeDescriptor>> verifyDescriptor( const AvbHashtreeDescriptor* desc) { - auto verified_desc = std::make_unique<AvbHashtreeDescriptor>(); + auto verifiedDesc = std::make_unique<AvbHashtreeDescriptor>(); if (!avb_hashtree_descriptor_validate_and_byteswap(desc, - verified_desc.get())) { + verifiedDesc.get())) { return Errorf("Couldn't validate AvbDescriptor."); } - return verified_desc; + return verifiedDesc; } } // namespace -Result<ApexVerityData> ApexFile::VerifyApexVerity( - const std::string& public_key) const { - if (IsCompressed()) { - return Error() << "Cannot verify ApexVerity of compressed APEX"; - } - - ApexVerityData verity_data; +Result<ApexVerityData> ApexFile::VerifyApexVerity() const { + ApexVerityData verityData; unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC)); if (fd.get() == -1) { return ErrnoError() << "Failed to open " << GetPath(); } - Result<std::unique_ptr<AvbFooter>> footer = GetAvbFooter(*this, fd); + Result<std::unique_ptr<AvbFooter>> footer = getAvbFooter(*this, fd); if (!footer.ok()) { return footer.error(); } Result<std::unique_ptr<uint8_t[]>> vbmeta_data = - VerifyVbMeta(*this, fd, **footer, public_key); + verifyVbMeta(*this, fd, **footer); if (!vbmeta_data.ok()) { return vbmeta_data.error(); } Result<const AvbHashtreeDescriptor*> descriptor = - FindDescriptor(vbmeta_data->get(), (*footer)->vbmeta_size); + findDescriptor(vbmeta_data->get(), (*footer)->vbmeta_size); if (!descriptor.ok()) { return descriptor.error(); } - Result<std::unique_ptr<AvbHashtreeDescriptor>> verified_descriptor = - VerifyDescriptor(*descriptor); - if (!verified_descriptor.ok()) { - return verified_descriptor.error(); + Result<std::unique_ptr<AvbHashtreeDescriptor>> verifiedDescriptor = + verifyDescriptor(*descriptor); + if (!verifiedDescriptor.ok()) { + return verifiedDescriptor.error(); } - verity_data.desc = std::move(*verified_descriptor); + verityData.desc = std::move(*verifiedDescriptor); // This area is now safe to access, because we just verified it - const uint8_t* trailing_data = + const uint8_t* trailingData = (const uint8_t*)*descriptor + sizeof(AvbHashtreeDescriptor); - verity_data.hash_algorithm = + verityData.hash_algorithm = reinterpret_cast<const char*>((*descriptor)->hash_algorithm); - verity_data.salt = GetSalt(*verity_data.desc, trailing_data); - verity_data.root_digest = GetDigest(*verity_data.desc, trailing_data); + verityData.salt = getSalt(*verityData.desc, trailingData); + verityData.root_digest = getDigest(*verityData.desc, trailingData); - return verity_data; + return verityData; } -Result<void> ApexFile::Decompress(const std::string& dest_path) const { - const std::string& src_path = GetPath(); - - LOG(INFO) << "Decompressing" << src_path << " to " << dest_path; - - // We should decompress compressed APEX files only - if (!IsCompressed()) { - return ErrnoError() << "Cannot decompress an uncompressed APEX"; +Result<void> ApexFile::VerifyManifestMatches( + const std::string& mount_path) const { + Result<ApexManifest> verifiedManifest = + ReadManifest(mount_path + "/" + kManifestFilenamePb); + if (!verifiedManifest.ok()) { + return verifiedManifest.error(); } - // Get file descriptor of the compressed apex file - unique_fd src_fd(open(src_path.c_str(), O_RDONLY | O_CLOEXEC)); - if (src_fd.get() == -1) { - return ErrnoError() << "Failed to open compressed APEX " << GetPath(); - } - - // Open it as a zip file - ZipArchiveHandle handle; - int ret = OpenArchiveFd(src_fd.get(), src_path.c_str(), &handle, false); - if (ret < 0) { - return Error() << "Failed to open package " << src_path << ": " - << ErrorCodeString(ret); + if (!MessageDifferencer::Equals(manifest_, *verifiedManifest)) { + return Errorf( + "Manifest inside filesystem does not match manifest outside it"); } - auto handle_guard = - android::base::make_scope_guard([&handle] { CloseArchive(handle); }); - // Find the original apex file inside the zip and extract to dest - ZipEntry entry; - ret = FindEntry(handle, kCompressedApexFilename, &entry); - if (ret < 0) { - return Error() << "Could not find entry \"" << kCompressedApexFilename - << "\" in package " << src_path << ": " - << ErrorCodeString(ret); - } + return {}; +} - // Open destination file descriptor - unique_fd dest_fd( - open(dest_path.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0644)); - if (dest_fd.get() == -1) { - return ErrnoError() << "Failed to open decompression destination " - << dest_path.c_str(); - } +Result<std::vector<std::string>> FindApexes( + const std::vector<std::string>& paths) { + std::vector<std::string> result; + for (const auto& path : paths) { + auto exist = PathExists(path); + if (!exist.ok()) { + return exist.error(); + } + if (!*exist) continue; - // Prepare a guard that deletes the extracted file if anything goes wrong - auto decompressed_guard = android::base::make_scope_guard( - [&dest_path] { RemoveFileIfExists(dest_path); }); + const auto& apexes = FindApexFilesByName(path); + if (!apexes.ok()) { + return apexes; + } - // Extract the original_apex to dest_path - ret = ExtractEntryToFile(handle, &entry, dest_fd.get()); - if (ret < 0) { - return Error() << "Could not decompress to file " << dest_path << " " - << ErrorCodeString(ret); + result.insert(result.end(), apexes->begin(), apexes->end()); } + return result; +} - // Verification complete. Accept the decompressed file - decompressed_guard.Disable(); - LOG(VERBOSE) << "Decompressed " << src_path << " to " << dest_path; +Result<std::vector<std::string>> FindApexFilesByName(const std::string& path) { + auto filter_fn = [](const std::filesystem::directory_entry& entry) { + std::error_code ec; + if (entry.is_regular_file(ec) && + EndsWith(entry.path().filename().string(), kApexPackageSuffix)) { + return true; // APEX file, take. + } + return false; + }; + return ReadDir(path, filter_fn); +} - return {}; +bool isPathForBuiltinApexes(const std::string& path) { + for (const auto& dir : kApexPackageBuiltinDirs) { + if (StartsWith(path, dir)) { + return true; + } + } + return false; } } // namespace apex diff --git a/apexd/apex_file.h b/apexd/apex_file.h index 60d5feb2..0476911c 100644 --- a/apexd/apex_file.h +++ b/apexd/apex_file.h @@ -23,7 +23,9 @@ #include <android-base/result.h> #include <libavb/libavb.h> +#include <ziparchive/zip_archive.h> +#include "apex_constants.h" #include "apex_manifest.h" namespace android { @@ -44,42 +46,43 @@ class ApexFile { static android::base::Result<ApexFile> Open(const std::string& path); ApexFile() = delete; ApexFile(ApexFile&&) = default; - ApexFile& operator=(ApexFile&&) = default; const std::string& GetPath() const { return apex_path_; } - const std::optional<int32_t>& GetImageOffset() const { return image_offset_; } - const std::optional<size_t>& GetImageSize() const { return image_size_; } - const ::apex::proto::ApexManifest& GetManifest() const { return manifest_; } + int32_t GetImageOffset() const { return image_offset_; } + size_t GetImageSize() const { return image_size_; } + const ApexManifest& GetManifest() const { return manifest_; } const std::string& GetBundledPublicKey() const { return apex_pubkey_; } - const std::optional<std::string>& GetFsType() const { return fs_type_; } - android::base::Result<ApexVerityData> VerifyApexVerity( - const std::string& public_key) const; - bool IsCompressed() const { return is_compressed_; } - android::base::Result<void> Decompress(const std::string& output_path) const; + bool IsBuiltin() const { return is_builtin_; } + android::base::Result<ApexVerityData> VerifyApexVerity() const; + android::base::Result<void> VerifyManifestMatches( + const std::string& mount_path) const; private: - ApexFile(const std::string& apex_path, - const std::optional<int32_t>& image_offset, - const std::optional<size_t>& image_size, - ::apex::proto::ApexManifest manifest, const std::string& apex_pubkey, - const std::optional<std::string>& fs_type, bool is_compressed) + ApexFile(const std::string& apex_path, int32_t image_offset, + size_t image_size, ApexManifest manifest, + const std::string& apex_pubkey, bool is_builtin) : apex_path_(apex_path), image_offset_(image_offset), image_size_(image_size), manifest_(std::move(manifest)), apex_pubkey_(apex_pubkey), - fs_type_(fs_type), - is_compressed_(is_compressed) {} + is_builtin_(is_builtin) {} std::string apex_path_; - std::optional<int32_t> image_offset_; - std::optional<size_t> image_size_; - ::apex::proto::ApexManifest manifest_; + int32_t image_offset_; + size_t image_size_; + ApexManifest manifest_; std::string apex_pubkey_; - std::optional<std::string> fs_type_; - bool is_compressed_; + bool is_builtin_; }; +android::base::Result<std::vector<std::string>> FindApexes( + const std::vector<std::string>& paths); +android::base::Result<std::vector<std::string>> FindApexFilesByName( + const std::string& path); + +bool isPathForBuiltinApexes(const std::string& path); + } // namespace apex } // namespace android diff --git a/apexd/apex_file_repository.cpp b/apexd/apex_file_repository.cpp deleted file mode 100644 index c50d136f..00000000 --- a/apexd/apex_file_repository.cpp +++ /dev/null @@ -1,280 +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. - */ - -#define LOG_TAG "apexd" - -#include "apex_file_repository.h" - -#include <unordered_map> - -#include <android-base/file.h> -#include <android-base/properties.h> -#include <android-base/result.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> - -#include "apex_constants.h" -#include "apex_file.h" -#include "apexd_utils.h" - -using android::base::EndsWith; -using android::base::Error; -using android::base::GetProperty; -using android::base::Result; - -namespace android { -namespace apex { - -Result<void> ApexFileRepository::ScanBuiltInDir(const std::string& dir) { - LOG(INFO) << "Scanning " << dir << " for pre-installed ApexFiles"; - if (access(dir.c_str(), F_OK) != 0 && errno == ENOENT) { - LOG(WARNING) << dir << " does not exist. Skipping"; - return {}; - } - - Result<std::vector<std::string>> all_apex_files = FindFilesBySuffix( - dir, {kApexPackageSuffix, kCompressedApexPackageSuffix}); - if (!all_apex_files.ok()) { - return all_apex_files.error(); - } - - // TODO(b/179248390): scan parallelly if possible - for (const auto& file : *all_apex_files) { - LOG(INFO) << "Found pre-installed APEX " << file; - Result<ApexFile> apex_file = ApexFile::Open(file); - if (!apex_file.ok()) { - return Error() << "Failed to open " << file << " : " << apex_file.error(); - } - - const std::string& name = apex_file->GetManifest().name(); - auto it = pre_installed_store_.find(name); - if (it == pre_installed_store_.end()) { - pre_installed_store_.emplace(name, std::move(*apex_file)); - } else if (it->second.GetPath() != apex_file->GetPath()) { - auto level = base::FATAL; - // On some development (non-REL) builds the VNDK apex could be in /vendor. - // When testing CTS-on-GSI on these builds, there would be two VNDK apexes - // in the system, one in /system and one in /vendor. - static constexpr char kVndkApexModuleNamePrefix[] = "com.android.vndk."; - static constexpr char kPlatformVersionCodenameProperty[] = - "ro.build.version.codename"; - if (android::base::StartsWith(name, kVndkApexModuleNamePrefix) && - GetProperty(kPlatformVersionCodenameProperty, "REL") != "REL") { - level = android::base::INFO; - } - LOG(level) << "Found two apex packages " << it->second.GetPath() - << " and " << apex_file->GetPath() - << " with the same module name " << name; - } else if (it->second.GetBundledPublicKey() != - apex_file->GetBundledPublicKey()) { - LOG(FATAL) << "Public key of apex package " << it->second.GetPath() - << " (" << name << ") has unexpectedly changed"; - } - } - return {}; -} - -ApexFileRepository& ApexFileRepository::GetInstance() { - static ApexFileRepository instance; - return instance; -} - -android::base::Result<void> ApexFileRepository::AddPreInstalledApex( - const std::vector<std::string>& prebuilt_dirs) { - for (const auto& dir : prebuilt_dirs) { - if (auto result = ScanBuiltInDir(dir); !result.ok()) { - return result.error(); - } - } - return {}; -} - -// TODO(b/179497746): AddDataApex should not concern with filtering out invalid -// apex. -Result<void> ApexFileRepository::AddDataApex(const std::string& data_dir) { - LOG(INFO) << "Scanning " << data_dir << " for data ApexFiles"; - if (access(data_dir.c_str(), F_OK) != 0 && errno == ENOENT) { - LOG(WARNING) << data_dir << " does not exist. Skipping"; - return {}; - } - - Result<std::vector<std::string>> active_apex = - FindFilesBySuffix(data_dir, {kApexPackageSuffix}); - if (!active_apex.ok()) { - return active_apex.error(); - } - - // TODO(b/179248390): scan parallelly if possible - for (const auto& file : *active_apex) { - LOG(INFO) << "Found updated apex " << file; - Result<ApexFile> apex_file = ApexFile::Open(file); - if (!apex_file.ok()) { - LOG(ERROR) << "Failed to open " << file << " : " << apex_file.error(); - continue; - } - - const std::string& name = apex_file->GetManifest().name(); - if (!HasPreInstalledVersion(name)) { - LOG(ERROR) << "Skipping " << file << " : no preisntalled apex"; - // Ignore data apex without corresponding pre-installed apex - continue; - } - auto pre_installed_public_key = GetPublicKey(name); - if (!pre_installed_public_key.ok() || - apex_file->GetBundledPublicKey() != *pre_installed_public_key) { - // Ignore data apex if public key doesn't match with pre-installed apex - LOG(ERROR) << "Skipping " << file - << " : public key doesn't match pre-installed one"; - continue; - } - - if (EndsWith(apex_file->GetPath(), kDecompressedApexPackageSuffix)) { - LOG(WARNING) << "Skipping " << file - << " : Non-decompressed APEX should not have " - << kDecompressedApexPackageSuffix << " suffix"; - continue; - } - - auto it = data_store_.find(name); - if (it == data_store_.end()) { - data_store_.emplace(name, std::move(*apex_file)); - continue; - } - - const auto& existing_version = it->second.GetManifest().version(); - const auto new_version = apex_file->GetManifest().version(); - // If multiple data apexs are preset, select the one with highest version - bool prioritize_higher_version = new_version > existing_version; - // For same version, non-decompressed apex gets priority - if (prioritize_higher_version) { - it->second = std::move(*apex_file); - } - } - return {}; -} - -// TODO(b/179497746): remove this method when we add api for fetching ApexFile -// by name -Result<const std::string> ApexFileRepository::GetPublicKey( - const std::string& name) const { - auto it = pre_installed_store_.find(name); - if (it == pre_installed_store_.end()) { - return Error() << "No preinstalled apex found for package " << name; - } - return it->second.GetBundledPublicKey(); -} - -// TODO(b/179497746): remove this method when we add api for fetching ApexFile -// by name -Result<const std::string> ApexFileRepository::GetPreinstalledPath( - const std::string& name) const { - auto it = pre_installed_store_.find(name); - if (it == pre_installed_store_.end()) { - return Error() << "No preinstalled data found for package " << name; - } - return it->second.GetPath(); -} - -// TODO(b/179497746): remove this method when we add api for fetching ApexFile -// by name -Result<const std::string> ApexFileRepository::GetDataPath( - const std::string& name) const { - auto it = data_store_.find(name); - if (it == data_store_.end()) { - return Error() << "No data apex found for package " << name; - } - return it->second.GetPath(); -} - -bool ApexFileRepository::HasPreInstalledVersion(const std::string& name) const { - return pre_installed_store_.find(name) != pre_installed_store_.end(); -} - -bool ApexFileRepository::HasDataVersion(const std::string& name) const { - return data_store_.find(name) != data_store_.end(); -} - -// ApexFile is considered a decompressed APEX if it is located in decompression -// dir -bool ApexFileRepository::IsDecompressedApex(const ApexFile& apex) const { - return apex.GetPath().starts_with(decompression_dir_); -} - -bool ApexFileRepository::IsPreInstalledApex(const ApexFile& apex) const { - auto it = pre_installed_store_.find(apex.GetManifest().name()); - if (it == pre_installed_store_.end()) { - return false; - } - return it->second.GetPath() == apex.GetPath() || IsDecompressedApex(apex); -} - -std::vector<ApexFileRef> ApexFileRepository::GetPreInstalledApexFiles() const { - std::vector<ApexFileRef> result; - for (const auto& it : pre_installed_store_) { - result.emplace_back(std::cref(it.second)); - } - return std::move(result); -} - -std::vector<ApexFileRef> ApexFileRepository::GetDataApexFiles() const { - std::vector<ApexFileRef> result; - for (const auto& it : data_store_) { - result.emplace_back(std::cref(it.second)); - } - return std::move(result); -} - -// Group pre-installed APEX and data APEX by name -std::unordered_map<std::string, std::vector<ApexFileRef>> -ApexFileRepository::AllApexFilesByName() const { - // Collect all apex files - std::vector<ApexFileRef> all_apex_files; - auto pre_installed_apexs = GetPreInstalledApexFiles(); - auto data_apexs = GetDataApexFiles(); - std::move(pre_installed_apexs.begin(), pre_installed_apexs.end(), - std::back_inserter(all_apex_files)); - std::move(data_apexs.begin(), data_apexs.end(), - std::back_inserter(all_apex_files)); - - // Group them by name - std::unordered_map<std::string, std::vector<ApexFileRef>> result; - for (const auto& apex_file_ref : all_apex_files) { - const ApexFile& apex_file = apex_file_ref.get(); - const std::string& package_name = apex_file.GetManifest().name(); - if (result.find(package_name) == result.end()) { - result[package_name] = std::vector<ApexFileRef>{}; - } - result[package_name].emplace_back(apex_file_ref); - } - - return std::move(result); -} - -ApexFileRef ApexFileRepository::GetDataApex(const std::string& name) const { - auto it = data_store_.find(name); - CHECK(it != data_store_.end()); - return std::cref(it->second); -} - -ApexFileRef ApexFileRepository::GetPreInstalledApex( - const std::string& name) const { - auto it = pre_installed_store_.find(name); - CHECK(it != pre_installed_store_.end()); - return std::cref(it->second); -} - -} // namespace apex -} // namespace android diff --git a/apexd/apex_file_repository.h b/apexd/apex_file_repository.h deleted file mode 100644 index e9ccf7ee..00000000 --- a/apexd/apex_file_repository.h +++ /dev/null @@ -1,139 +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. - */ - -#pragma once - -#include <functional> -#include <string> -#include <unordered_map> -#include <vector> -#include "apex_constants.h" -#include "apex_file.h" - -#include <android-base/result.h> - -namespace android { -namespace apex { - -using ApexFileRef = std::reference_wrapper<const android::apex::ApexFile>; - -// This class serves as a ApexFile repository for all apexes on device. It also -// provides information about the ApexFiles it hosts, such as which are -// pre-installed and which are data. Such information can be used, for example, -// to verify validity of an apex before trying to mount it. -// -// It's expected to have a single instance of this class in a process that -// mounts apexes (e.g. apexd, otapreopt_chroot). -class ApexFileRepository final { - public: - // c-tor and d-tor are exposed for testing. - explicit ApexFileRepository( - const std::string& decompression_dir = kApexDecompressedDir) - : decompression_dir_(decompression_dir){}; - - ~ApexFileRepository() { - pre_installed_store_.clear(); - data_store_.clear(); - }; - - // Returns a singletone instance of this class. - static ApexFileRepository& GetInstance(); - - // Populate instance by collecting pre-installed apex files from the given - // |prebuilt_dirs|. - // Note: this call is **not thread safe** and is expected to be performed in a - // single thread during initialization of apexd. After initialization is - // finished, all queries to the instance are thread safe. - android::base::Result<void> AddPreInstalledApex( - const std::vector<std::string>& prebuilt_dirs); - - // Populate instance by collecting data apex files from the given |data_dir|. - // Note: this call is **not thread safe** and is expected to be performed in a - // single thread during initialization of apexd. After initialization is - // finished, all queries to the instance are thread safe. - android::base::Result<void> AddDataApex(const std::string& data_dir); - - // Returns trusted public key for an apex with the given |name|. - android::base::Result<const std::string> GetPublicKey( - const std::string& name) const; - - // Returns path to the pre-installed version of an apex with the given |name|. - android::base::Result<const std::string> GetPreinstalledPath( - const std::string& name) const; - - // Returns path to the data version of an apex with the given |name|. - android::base::Result<const std::string> GetDataPath( - const std::string& name) const; - - // Checks whether there is a pre-installed version of an apex with the given - // |name|. - bool HasPreInstalledVersion(const std::string& name) const; - - // Checks whether there is a data version of an apex with the given |name|. - bool HasDataVersion(const std::string& name) const; - - // Checks if given |apex| is pre-installed. - bool IsPreInstalledApex(const ApexFile& apex) const; - - // Checks if given |apex| is decompressed from a pre-installed APEX - bool IsDecompressedApex(const ApexFile& apex) const; - - // Returns reference to all pre-installed APEX on device - std::vector<ApexFileRef> GetPreInstalledApexFiles() const; - - // Returns reference to all data APEX on device - std::vector<ApexFileRef> GetDataApexFiles() const; - - // Group all ApexFiles on device by their package name - std::unordered_map<std::string, std::vector<ApexFileRef>> AllApexFilesByName() - const; - - // Returns a pre-installed version of apex with the given name. Caller is - // expected to check if there is a pre-installed apex with the given name - // using |HasPreinstalledVersion| function. - ApexFileRef GetPreInstalledApex(const std::string& name) const; - // Returns a data version of apex with the given name. Caller is - // expected to check if there is a data apex with the given name - // using |HasDataVersion| function. - ApexFileRef GetDataApex(const std::string& name) const; - - // Clears ApexFileRepostiry. - // Only use in tests. - void Reset(const std::string& decompression_dir = kApexDecompressedDir) { - pre_installed_store_.clear(); - data_store_.clear(); - decompression_dir_ = decompression_dir; - } - - private: - // Non-copyable && non-moveable. - ApexFileRepository(const ApexFileRepository&) = delete; - ApexFileRepository& operator=(const ApexFileRepository&) = delete; - ApexFileRepository& operator=(ApexFileRepository&&) = delete; - ApexFileRepository(ApexFileRepository&&) = delete; - - // Scans apexes in the given directory and adds collected data into - // |pre_installed_store_|. - android::base::Result<void> ScanBuiltInDir(const std::string& dir); - - std::unordered_map<std::string, ApexFile> pre_installed_store_, data_store_; - // Decompression directory which will be used to determine if apex is - // decompressed or not - std::string decompression_dir_; -}; - -} // namespace apex -} // namespace android diff --git a/apexd/apex_file_repository_test.cpp b/apexd/apex_file_repository_test.cpp deleted file mode 100644 index c1f5d533..00000000 --- a/apexd/apex_file_repository_test.cpp +++ /dev/null @@ -1,506 +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 <filesystem> -#include <string> - -#include <errno.h> -#include <sys/stat.h> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/stringprintf.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include "apex_file.h" -#include "apex_file_repository.h" -#include "apexd_test_utils.h" -#include "apexd_verity.h" - -namespace android { -namespace apex { - -using namespace std::literals; - -namespace fs = std::filesystem; - -using android::apex::testing::ApexFileEq; -using android::apex::testing::IsOk; -using android::base::GetExecutableDirectory; -using android::base::StringPrintf; -using ::testing::ByRef; -using ::testing::UnorderedElementsAre; - -static std::string GetTestDataDir() { return GetExecutableDirectory(); } -static std::string GetTestFile(const std::string& name) { - return GetTestDataDir() + "/" + name; -} - -namespace { -// Copies the compressed apex to |built_in_dir| and decompresses it to -// |decompression_dir -void PrepareCompressedApex(const std::string& name, - const std::string& built_in_dir, - const std::string& decompression_dir) { - fs::copy(GetTestFile(name), built_in_dir); - auto compressed_apex = - ApexFile::Open(StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str())); - - const auto& pkg_name = compressed_apex->GetManifest().name(); - const int version = compressed_apex->GetManifest().version(); - - auto decompression_path = - StringPrintf("%s/%s@%d%s", decompression_dir.c_str(), pkg_name.c_str(), - version, kDecompressedApexPackageSuffix); - compressed_apex->Decompress(decompression_path); -} -} // namespace - -TEST(ApexFileRepositoryTest, InitializeSuccess) { - // Prepare test data. - TemporaryDir built_in_dir, data_dir, decompression_dir; - fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path); - fs::copy(GetTestFile("apex.apexd_test_different_app.apex"), - built_in_dir.path); - - fs::copy(GetTestFile("apex.apexd_test.apex"), data_dir.path); - fs::copy(GetTestFile("apex.apexd_test_different_app.apex"), data_dir.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - // Now test that apexes were scanned correctly; - auto test_fn = [&](const std::string& apex_name) { - auto apex = ApexFile::Open(GetTestFile(apex_name)); - ASSERT_TRUE(IsOk(apex)); - - { - auto ret = instance.GetPublicKey(apex->GetManifest().name()); - ASSERT_TRUE(IsOk(ret)); - ASSERT_EQ(apex->GetBundledPublicKey(), *ret); - } - - { - auto ret = instance.GetPreinstalledPath(apex->GetManifest().name()); - ASSERT_TRUE(IsOk(ret)); - ASSERT_EQ(StringPrintf("%s/%s", built_in_dir.path, apex_name.c_str()), - *ret); - } - - { - auto ret = instance.GetDataPath(apex->GetManifest().name()); - ASSERT_TRUE(IsOk(ret)); - ASSERT_EQ(StringPrintf("%s/%s", data_dir.path, apex_name.c_str()), *ret); - } - - ASSERT_TRUE(instance.HasPreInstalledVersion(apex->GetManifest().name())); - ASSERT_TRUE(instance.HasDataVersion(apex->GetManifest().name())); - }; - - test_fn("apex.apexd_test.apex"); - test_fn("apex.apexd_test_different_app.apex"); - - // Check that second call will succeed as well. - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - test_fn("apex.apexd_test.apex"); - test_fn("apex.apexd_test_different_app.apex"); -} - -TEST(ApexFileRepositoryTest, InitializeFailureCorruptApex) { - // Prepare test data. - TemporaryDir td; - fs::copy(GetTestFile("apex.apexd_test.apex"), td.path); - fs::copy(GetTestFile("apex.apexd_test_corrupt_superblock_apex.apex"), - td.path); - - ApexFileRepository instance; - ASSERT_FALSE(IsOk(instance.AddPreInstalledApex({td.path}))); -} - -TEST(ApexFileRepositoryTest, InitializeCompressedApexWithoutApex) { - // Prepare test data. - TemporaryDir td; - fs::copy(GetTestFile("com.android.apex.compressed.v1_without_apex.capex"), - td.path); - - ApexFileRepository instance; - // Compressed APEX without APEX cannot be opened - ASSERT_FALSE(IsOk(instance.AddPreInstalledApex({td.path}))); -} - -TEST(ApexFileRepositoryTest, InitializeSameNameDifferentPathAborts) { - // Prepare test data. - TemporaryDir td; - fs::copy(GetTestFile("apex.apexd_test.apex"), td.path); - fs::copy(GetTestFile("apex.apexd_test.apex"), - StringPrintf("%s/other.apex", td.path)); - - ASSERT_DEATH( - { - ApexFileRepository instance; - instance.AddPreInstalledApex({td.path}); - }, - ""); -} - -TEST(ApexFileRepositoryTest, - InitializeSameNameDifferentPathAbortsCompressedApex) { - // Prepare test data. - TemporaryDir td; - fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), td.path); - fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), - StringPrintf("%s/other.capex", td.path)); - - ASSERT_DEATH( - { - ApexFileRepository instance; - instance.AddPreInstalledApex({td.path}); - }, - ""); -} - -TEST(ApexFileRepositoryTest, InitializePublicKeyUnexpectdlyChangedAborts) { - // Prepare test data. - TemporaryDir td; - fs::copy(GetTestFile("apex.apexd_test.apex"), td.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({td.path}))); - - // Check that apex was loaded. - auto path = instance.GetPreinstalledPath("com.android.apex.test_package"); - ASSERT_TRUE(IsOk(path)); - ASSERT_EQ(StringPrintf("%s/apex.apexd_test.apex", td.path), *path); - - auto public_key = instance.GetPublicKey("com.android.apex.test_package"); - ASSERT_TRUE(IsOk(public_key)); - - // Substitute it with another apex with the same name, but different public - // key. - fs::copy(GetTestFile("apex.apexd_test_different_key.apex"), *path, - fs::copy_options::overwrite_existing); - - { - auto apex = ApexFile::Open(*path); - ASSERT_TRUE(IsOk(apex)); - // Check module name hasn't changed. - ASSERT_EQ("com.android.apex.test_package", apex->GetManifest().name()); - // Check public key has changed. - ASSERT_NE(*public_key, apex->GetBundledPublicKey()); - } - - ASSERT_DEATH({ instance.AddPreInstalledApex({td.path}); }, ""); -} - -TEST(ApexFileRepositoryTest, - InitializePublicKeyUnexpectdlyChangedAbortsCompressedApex) { - // Prepare test data. - TemporaryDir td; - fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), td.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({td.path}))); - - // Check that apex was loaded. - auto path = instance.GetPreinstalledPath("com.android.apex.compressed"); - ASSERT_TRUE(IsOk(path)); - ASSERT_EQ(StringPrintf("%s/com.android.apex.compressed.v1.capex", td.path), - *path); - - auto public_key = instance.GetPublicKey("com.android.apex.compressed"); - ASSERT_TRUE(IsOk(public_key)); - - // Substitute it with another apex with the same name, but different public - // key. - fs::copy(GetTestFile("com.android.apex.compressed_different_key.capex"), - *path, fs::copy_options::overwrite_existing); - - { - auto apex = ApexFile::Open(*path); - ASSERT_TRUE(IsOk(apex)); - // Check module name hasn't changed. - ASSERT_EQ("com.android.apex.compressed", apex->GetManifest().name()); - // Check public key has changed. - ASSERT_NE(*public_key, apex->GetBundledPublicKey()); - } - - ASSERT_DEATH({ instance.AddPreInstalledApex({td.path}); }, ""); -} - -TEST(ApexFileRepositoryTest, IsPreInstalledApex) { - // Prepare test data. - TemporaryDir td; - fs::copy(GetTestFile("apex.apexd_test.apex"), td.path); - fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), td.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({td.path}))); - - auto compressed_apex = ApexFile::Open( - StringPrintf("%s/com.android.apex.compressed.v1.capex", td.path)); - ASSERT_TRUE(IsOk(compressed_apex)); - ASSERT_TRUE(instance.IsPreInstalledApex(*compressed_apex)); - - auto apex1 = ApexFile::Open(StringPrintf("%s/apex.apexd_test.apex", td.path)); - ASSERT_TRUE(IsOk(apex1)); - ASSERT_TRUE(instance.IsPreInstalledApex(*apex1)); - - // It's same apex, but path is different. Shouldn't be treated as - // pre-installed. - auto apex2 = ApexFile::Open(GetTestFile("apex.apexd_test.apex")); - ASSERT_TRUE(IsOk(apex2)); - ASSERT_FALSE(instance.IsPreInstalledApex(*apex2)); - - auto apex3 = - ApexFile::Open(GetTestFile("apex.apexd_test_different_app.apex")); - ASSERT_TRUE(IsOk(apex3)); - ASSERT_FALSE(instance.IsPreInstalledApex(*apex3)); -} - -TEST(ApexFileRepositoryTest, IsDecompressedApex) { - // Prepare instance - TemporaryDir decompression_dir; - ApexFileRepository instance(decompression_dir.path); - - // Prepare decompressed apex - std::string filename = "com.android.apex.compressed.v1_original.apex"; - fs::copy(GetTestFile(filename), decompression_dir.path); - auto decompressed_path = - StringPrintf("%s/%s", decompression_dir.path, filename.c_str()); - auto decompressed_apex = ApexFile::Open(decompressed_path); - - // Any file which is already located in |decompression_dir| should be - // considered decompressed - ASSERT_TRUE(instance.IsDecompressedApex(*decompressed_apex)); - - // Hard links with same file name is not considered decompressed - TemporaryDir active_dir; - auto active_path = StringPrintf("%s/%s", active_dir.path, filename.c_str()); - std::error_code ec; - fs::create_hard_link(decompressed_path, active_path, ec); - ASSERT_FALSE(ec) << "Failed to create hardlink"; - auto active_apex = ApexFile::Open(active_path); - ASSERT_FALSE(instance.IsDecompressedApex(*active_apex)); -} - -TEST(ApexFileRepositoryTest, AddAndGetDataApex) { - // Prepare test data. - TemporaryDir built_in_dir, data_dir, decompression_dir; - fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path); - fs::copy(GetTestFile("apex.apexd_test_v2.apex"), data_dir.path); - PrepareCompressedApex("com.android.apex.compressed.v1.capex", - built_in_dir.path, decompression_dir.path); - // Add a data apex that has kDecompressedApexPackageSuffix - fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"), - StringPrintf("%s/com.android.apex.compressed@1%s", data_dir.path, - kDecompressedApexPackageSuffix)); - - ApexFileRepository instance(decompression_dir.path); - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - // ApexFileRepository should only deal with APEX in /data/apex/active. - // Decompressed APEX should not be included - auto data_apexs = instance.GetDataApexFiles(); - auto normal_apex = - ApexFile::Open(StringPrintf("%s/apex.apexd_test_v2.apex", data_dir.path)); - ASSERT_THAT(data_apexs, - UnorderedElementsAre(ApexFileEq(ByRef(*normal_apex)))); -} - -TEST(ApexFileRepositoryTest, AddDataApexIgnoreCompressedApex) { - // Prepare test data. - TemporaryDir data_dir, decompression_dir; - fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), data_dir.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - auto data_apexs = instance.GetDataApexFiles(); - ASSERT_EQ(data_apexs.size(), 0u); -} - -TEST(ApexFileRepositoryTest, AddDataApexIgnoreIfNotPreInstalled) { - // Prepare test data. - TemporaryDir data_dir, decompression_dir; - fs::copy(GetTestFile("apex.apexd_test.apex"), data_dir.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - auto data_apexs = instance.GetDataApexFiles(); - ASSERT_EQ(data_apexs.size(), 0u); -} - -TEST(ApexFileRepositoryTest, AddDataApexPrioritizeHigherVersionApex) { - // Prepare test data. - TemporaryDir built_in_dir, data_dir, decompression_dir; - fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path); - fs::copy(GetTestFile("apex.apexd_test.apex"), data_dir.path); - fs::copy(GetTestFile("apex.apexd_test_v2.apex"), data_dir.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - auto data_apexs = instance.GetDataApexFiles(); - auto normal_apex = - ApexFile::Open(StringPrintf("%s/apex.apexd_test_v2.apex", data_dir.path)); - ASSERT_THAT(data_apexs, - UnorderedElementsAre(ApexFileEq(ByRef(*normal_apex)))); -} - -TEST(ApexFileRepositoryTest, AddDataApexDoesNotScanDecompressedApex) { - // Prepare test data. - TemporaryDir built_in_dir, data_dir, decompression_dir; - PrepareCompressedApex("com.android.apex.compressed.v1.capex", - built_in_dir.path, decompression_dir.path); - - ApexFileRepository instance(decompression_dir.path); - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - auto data_apexs = instance.GetDataApexFiles(); - ASSERT_EQ(data_apexs.size(), 0u); -} - -TEST(ApexFileRepositoryTest, AddDataApexIgnoreWrongPublicKey) { - // Prepare test data. - TemporaryDir built_in_dir, data_dir, decompression_dir; - fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path); - fs::copy(GetTestFile("apex.apexd_test_different_key.apex"), data_dir.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - auto data_apexs = instance.GetDataApexFiles(); - ASSERT_EQ(data_apexs.size(), 0u); -} - -TEST(ApexFileRepositoryTest, GetPreInstalledApexFiles) { - // Prepare test data. - TemporaryDir built_in_dir; - fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path); - fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), - built_in_dir.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - - auto pre_installed_apexs = instance.GetPreInstalledApexFiles(); - auto pre_apex_1 = ApexFile::Open( - StringPrintf("%s/apex.apexd_test.apex", built_in_dir.path)); - auto pre_apex_2 = ApexFile::Open(StringPrintf( - "%s/com.android.apex.compressed.v1.capex", built_in_dir.path)); - ASSERT_THAT(pre_installed_apexs, - UnorderedElementsAre(ApexFileEq(ByRef(*pre_apex_1)), - ApexFileEq(ByRef(*pre_apex_2)))); -} - -TEST(ApexFileRepositoryTest, AllApexFilesByName) { - TemporaryDir built_in_dir, decompression_dir; - fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path); - fs::copy(GetTestFile("com.android.apex.cts.shim.apex"), built_in_dir.path); - fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), - built_in_dir.path); - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - - TemporaryDir data_dir; - fs::copy(GetTestFile("com.android.apex.cts.shim.v2.apex"), data_dir.path); - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - auto result = instance.AllApexFilesByName(); - - // Verify the contents of result - auto apexd_test_file = ApexFile::Open( - StringPrintf("%s/apex.apexd_test.apex", built_in_dir.path)); - auto shim_v1 = ApexFile::Open( - StringPrintf("%s/com.android.apex.cts.shim.apex", built_in_dir.path)); - auto compressed_apex = ApexFile::Open(StringPrintf( - "%s/com.android.apex.compressed.v1.capex", built_in_dir.path)); - auto shim_v2 = ApexFile::Open( - StringPrintf("%s/com.android.apex.cts.shim.v2.apex", data_dir.path)); - - ASSERT_EQ(result.size(), 3u); - ASSERT_THAT(result[apexd_test_file->GetManifest().name()], - UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)))); - ASSERT_THAT(result[shim_v1->GetManifest().name()], - UnorderedElementsAre(ApexFileEq(ByRef(*shim_v1)), - ApexFileEq(ByRef(*shim_v2)))); - ASSERT_THAT(result[compressed_apex->GetManifest().name()], - UnorderedElementsAre(ApexFileEq(ByRef(*compressed_apex)))); -} - -TEST(ApexFileRepositoryTest, GetDataApex) { - // Prepare test data. - TemporaryDir built_in_dir, data_dir; - fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path); - fs::copy(GetTestFile("apex.apexd_test_v2.apex"), data_dir.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - ASSERT_TRUE(IsOk(instance.AddDataApex(data_dir.path))); - - auto apex = - ApexFile::Open(StringPrintf("%s/apex.apexd_test_v2.apex", data_dir.path)); - ASSERT_RESULT_OK(apex); - - auto ret = instance.GetDataApex("com.android.apex.test_package"); - ASSERT_THAT(ret, ApexFileEq(ByRef(*apex))); -} - -TEST(ApexFileRepositoryTest, GetDataApexNoSuchApexAborts) { - ASSERT_DEATH( - { - ApexFileRepository instance; - instance.GetDataApex("whatever"); - }, - ""); -} - -TEST(ApexFileRepositoryTest, GetPreInstalledApex) { - // Prepare test data. - TemporaryDir built_in_dir; - fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path); - - ApexFileRepository instance; - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path}))); - - auto apex = ApexFile::Open( - StringPrintf("%s/apex.apexd_test.apex", built_in_dir.path)); - ASSERT_RESULT_OK(apex); - - auto ret = instance.GetPreInstalledApex("com.android.apex.test_package"); - ASSERT_THAT(ret, ApexFileEq(ByRef(*apex))); -} - -TEST(ApexFileRepositoryTest, GetPreInstalledApexNoSuchApexAborts) { - ASSERT_DEATH( - { - ApexFileRepository instance; - instance.GetPreInstalledApex("whatever"); - }, - ""); -} - -} // namespace apex -} // namespace android diff --git a/apexd/apex_file_test.cpp b/apexd/apex_file_test.cpp index 3da74484..711d0246 100644 --- a/apexd/apex_file_test.cpp +++ b/apexd/apex_file_test.cpp @@ -19,47 +19,31 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/scopeguard.h> -#include <android-base/strings.h> -#include <gmock/gmock.h> #include <gtest/gtest.h> #include <libavb/libavb.h> #include <ziparchive/zip_archive.h> #include "apex_file.h" -#include "apexd_test_utils.h" -#include "apexd_utils.h" +#include "apex_preinstalled_data.h" -using android::base::GetExecutableDirectory; using android::base::Result; -static const std::string kTestDataDir = GetExecutableDirectory() + "/"; +static std::string testDataDir = android::base::GetExecutableDirectory() + "/"; namespace android { namespace apex { namespace { -struct ApexFileTestParam { - const char* type; - const char* prefix; -}; - -constexpr const ApexFileTestParam kParameters[] = { - {"ext4", "apex.apexd_test"}, {"f2fs", "apex.apexd_test_f2fs"}}; - -class ApexFileTest : public ::testing::TestWithParam<ApexFileTestParam> {}; - -INSTANTIATE_TEST_SUITE_P(Apex, ApexFileTest, ::testing::ValuesIn(kParameters)); - -TEST_P(ApexFileTest, GetOffsetOfSimplePackage) { - const std::string file_path = kTestDataDir + GetParam().prefix + ".apex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_TRUE(apex_file.ok()); +TEST(ApexFileTest, GetOffsetOfSimplePackage) { + const std::string filePath = testDataDir + "apex.apexd_test.apex"; + Result<ApexFile> apexFile = ApexFile::Open(filePath); + ASSERT_TRUE(apexFile.ok()); int32_t zip_image_offset; size_t zip_image_size; { ZipArchiveHandle handle; - int32_t rc = OpenArchive(file_path.c_str(), &handle); + int32_t rc = OpenArchive(filePath.c_str(), &handle); ASSERT_EQ(0, rc); auto close_guard = android::base::make_scope_guard([&handle]() { CloseArchive(handle); }); @@ -74,33 +58,34 @@ TEST_P(ApexFileTest, GetOffsetOfSimplePackage) { EXPECT_EQ(zip_image_size, entry.compressed_length); } - EXPECT_EQ(zip_image_offset, apex_file->GetImageOffset().value()); - EXPECT_EQ(zip_image_size, apex_file->GetImageSize().value()); + EXPECT_EQ(zip_image_offset, apexFile->GetImageOffset()); + EXPECT_EQ(zip_image_size, apexFile->GetImageSize()); } TEST(ApexFileTest, GetOffsetMissingFile) { - const std::string file_path = kTestDataDir + "missing.apex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_FALSE(apex_file.ok()); - ASSERT_THAT(apex_file.error().message(), - ::testing::HasSubstr("Failed to open package")); + const std::string filePath = testDataDir + "missing.apex"; + Result<ApexFile> apexFile = ApexFile::Open(filePath); + ASSERT_FALSE(apexFile.ok()); + EXPECT_NE(std::string::npos, + apexFile.error().message().find("Failed to open package")) + << apexFile.error(); } -TEST_P(ApexFileTest, GetApexManifest) { - const std::string file_path = kTestDataDir + GetParam().prefix + ".apex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_RESULT_OK(apex_file); - EXPECT_EQ("com.android.apex.test_package", apex_file->GetManifest().name()); - EXPECT_EQ(1u, apex_file->GetManifest().version()); +TEST(ApexFileTest, GetApexManifest) { + const std::string filePath = testDataDir + "apex.apexd_test.apex"; + Result<ApexFile> apexFile = ApexFile::Open(filePath); + ASSERT_RESULT_OK(apexFile); + EXPECT_EQ("com.android.apex.test_package", apexFile->GetManifest().name()); + EXPECT_EQ(1u, apexFile->GetManifest().version()); } -TEST_P(ApexFileTest, VerifyApexVerity) { - const std::string file_path = kTestDataDir + GetParam().prefix + ".apex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_RESULT_OK(apex_file); +TEST(ApexFileTest, VerifyApexVerity) { + ASSERT_RESULT_OK(collectPreinstalledData({"/system_ext/apex"})); + const std::string filePath = testDataDir + "apex.apexd_test.apex"; + Result<ApexFile> apexFile = ApexFile::Open(filePath); + ASSERT_RESULT_OK(apexFile); - auto verity_or = - apex_file->VerifyApexVerity(apex_file->GetBundledPublicKey()); + auto verity_or = apexFile->VerifyApexVerity(); ASSERT_RESULT_OK(verity_or); const ApexVerityData& data = *verity_or; @@ -108,229 +93,52 @@ TEST_P(ApexFileTest, VerifyApexVerity) { EXPECT_EQ(std::string("368a22e64858647bc45498e92f749f85482ac468" "50ca7ec8071f49dfa47a243c"), data.salt); - - const std::string digest_path = - kTestDataDir + GetParam().prefix + "_digest.txt"; - std::string root_digest; - ASSERT_TRUE(android::base::ReadFileToString(digest_path, &root_digest)) - << "Failed to read " << digest_path; - root_digest = android::base::Trim(root_digest); - - EXPECT_EQ(std::string(root_digest), data.root_digest); + EXPECT_EQ( + std::string( + "8e841019e41e8c40bca6dd6304cbf163ea257ba0a268304832c4105eba1c2747"), + data.root_digest); } -TEST_P(ApexFileTest, VerifyApexVerityWrongKey) { - const std::string file_path = kTestDataDir + GetParam().prefix + ".apex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_RESULT_OK(apex_file); +// TODO: May consider packaging a debug key in debug builds (again). +TEST(ApexFileTest, DISABLED_VerifyApexVerityNoKeyDir) { + const std::string filePath = testDataDir + "apex.apexd_test.apex"; + Result<ApexFile> apexFile = ApexFile::Open(filePath); + ASSERT_RESULT_OK(apexFile); - auto verity_or = apex_file->VerifyApexVerity("wrong-key"); + auto verity_or = apexFile->VerifyApexVerity(); ASSERT_FALSE(verity_or.ok()); } -TEST_P(ApexFileTest, GetBundledPublicKey) { - const std::string file_path = kTestDataDir + GetParam().prefix + ".apex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_RESULT_OK(apex_file); - - const std::string key_path = - kTestDataDir + "apexd_testdata/com.android.apex.test_package.avbpubkey"; - std::string key_content; - ASSERT_TRUE(android::base::ReadFileToString(key_path, &key_content)) - << "Failed to read " << key_path; - - EXPECT_EQ(key_content, apex_file->GetBundledPublicKey()); -} - -TEST(ApexFileTest, CorrutedApexB146895998) { - const std::string apex_path = kTestDataDir + "corrupted_b146895998.apex"; - Result<ApexFile> apex = ApexFile::Open(apex_path); - ASSERT_RESULT_OK(apex); - ASSERT_FALSE(apex->VerifyApexVerity("ignored").ok()); -} +TEST(ApexFileTest, VerifyApexVerityNoKeyInst) { + const std::string filePath = testDataDir + "apex.apexd_test_no_inst_key.apex"; + Result<ApexFile> apexFile = ApexFile::Open(filePath); + ASSERT_RESULT_OK(apexFile); -TEST_P(ApexFileTest, RetrieveFsType) { - const std::string file_path = kTestDataDir + GetParam().prefix + ".apex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_TRUE(apex_file.ok()); - - EXPECT_EQ(std::string(GetParam().type), apex_file->GetFsType().value()); -} - -TEST(ApexFileTest, OpenInvalidFilesystem) { - const std::string file_path = - kTestDataDir + "apex.apexd_test_corrupt_superblock_apex.apex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_FALSE(apex_file.ok()); - ASSERT_THAT(apex_file.error().message(), - ::testing::HasSubstr("Failed to retrieve filesystem type")); -} - -TEST(ApexFileTest, OpenCompressedApexFile) { - const std::string file_path = - kTestDataDir + "com.android.apex.compressed.v1.capex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_TRUE(apex_file.ok()); - - ASSERT_TRUE(apex_file->IsCompressed()); - ASSERT_FALSE(apex_file->GetImageOffset().has_value()); - ASSERT_FALSE(apex_file->GetImageSize().has_value()); - ASSERT_FALSE(apex_file->GetFsType().has_value()); -} - -TEST(ApexFileTest, OpenFailureForCompressedApexWithoutApex) { - const std::string file_path = - kTestDataDir + "com.android.apex.compressed.v1_without_apex.capex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_FALSE(apex_file.ok()); - ASSERT_THAT(apex_file.error().message(), - ::testing::HasSubstr("Could not find entry")); -} - -TEST(ApexFileTest, GetCompressedApexManifest) { - const std::string file_path = - kTestDataDir + "com.android.apex.compressed.v1.capex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_RESULT_OK(apex_file); - EXPECT_EQ("com.android.apex.compressed", apex_file->GetManifest().name()); - EXPECT_EQ(1u, apex_file->GetManifest().version()); + auto verity_or = apexFile->VerifyApexVerity(); + ASSERT_FALSE(verity_or.ok()); } -TEST(ApexFileTest, GetBundledPublicKeyOfCompressedApex) { - const std::string file_path = - kTestDataDir + "com.android.apex.compressed.v1.capex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_RESULT_OK(apex_file); +TEST(ApexFileTest, GetBundledPublicKey) { + const std::string filePath = testDataDir + "apex.apexd_test.apex"; + Result<ApexFile> apexFile = ApexFile::Open(filePath); + ASSERT_RESULT_OK(apexFile); - const std::string key_path = - kTestDataDir + "apexd_testdata/com.android.apex.compressed.avbpubkey"; - std::string key_content; - ASSERT_TRUE(android::base::ReadFileToString(key_path, &key_content)) - << "Failed to read " << key_path; + const std::string keyPath = + testDataDir + "apexd_testdata/com.android.apex.test_package.avbpubkey"; + std::string keyContent; + ASSERT_TRUE(android::base::ReadFileToString(keyPath, &keyContent)) + << "Failed to read " << keyPath; - EXPECT_EQ(key_content, apex_file->GetBundledPublicKey()); + EXPECT_EQ(keyContent, apexFile->GetBundledPublicKey()); } -TEST(ApexFileTest, CannotVerifyApexVerityForCompressedApex) { - const std::string file_path = - kTestDataDir + "com.android.apex.compressed.v1.capex"; - auto apex = ApexFile::Open(file_path); +TEST(ApexFileTest, CorrutedApex_b146895998) { + const std::string apex_path = testDataDir + "corrupted_b146895998.apex"; + Result<ApexFile> apex = ApexFile::Open(apex_path); ASSERT_RESULT_OK(apex); - auto result = apex->VerifyApexVerity(apex->GetBundledPublicKey()); - ASSERT_FALSE(result.ok()); - ASSERT_THAT( - result.error().message(), - ::testing::HasSubstr("Cannot verify ApexVerity of compressed APEX")); + ASSERT_FALSE(apex->VerifyApexVerity()); } -TEST(ApexFileTest, DecompressCompressedApex) { - const std::string file_path = - kTestDataDir + "com.android.apex.compressed.v1.capex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_RESULT_OK(apex_file); - - // Create a temp dir for decompression - TemporaryDir tmp_dir; - - const std::string package_name = apex_file->GetManifest().name(); - const std::string decompression_file_path = - tmp_dir.path + package_name + ".capex"; - - auto result = apex_file->Decompress(decompression_file_path); - ASSERT_RESULT_OK(result); - - // Assert output path is not empty - auto exists = PathExists(decompression_file_path); - ASSERT_RESULT_OK(exists); - ASSERT_TRUE(*exists) << decompression_file_path << " does not exist"; - - // Assert that decompressed apex is same as original apex - const std::string original_apex_file_path = - kTestDataDir + "com.android.apex.compressed.v1_original.apex"; - auto comparison_result = - CompareFiles(original_apex_file_path, decompression_file_path); - ASSERT_RESULT_OK(comparison_result); - ASSERT_TRUE(*comparison_result); -} - -TEST(ApexFileTest, DecompressFailForNormalApex) { - const std::string file_path = - kTestDataDir + "com.android.apex.compressed.v1_original.apex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - ASSERT_RESULT_OK(apex_file); - - TemporaryFile decompression_file; - - auto result = apex_file->Decompress(decompression_file.path); - ASSERT_FALSE(result.ok()); - ASSERT_THAT(result.error().message(), - ::testing::HasSubstr("Cannot decompress an uncompressed APEX")); -} - -TEST(ApexFileTest, DecompressFailIfDecompressionPathExists) { - const std::string file_path = - kTestDataDir + "com.android.apex.compressed.v1.capex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - - // Attempt to decompress in a path that already exists - TemporaryFile decompression_file; - auto exists = PathExists(decompression_file.path); - ASSERT_RESULT_OK(exists); - ASSERT_TRUE(*exists) << decompression_file.path << " does not exist"; - - auto result = apex_file->Decompress(decompression_file.path); - ASSERT_FALSE(result.ok()); - ASSERT_THAT(result.error().message(), - ::testing::HasSubstr("Failed to open decompression destination")); -} - -TEST(ApexFileTest, GetPathReturnsRealpath) { - const std::string real_path = kTestDataDir + "apex.apexd_test.apex"; - const std::string symlink_path = - kTestDataDir + "apex.apexd_test.symlink.apex"; - - // In case the link already exists - int ret = unlink(symlink_path.c_str()); - ASSERT_TRUE(ret == 0 || errno == ENOENT) - << "failed to unlink " << symlink_path; - - ret = symlink(real_path.c_str(), symlink_path.c_str()); - ASSERT_EQ(0, ret) << "failed to create symlink at " << symlink_path; - - // Open with the symlink. Realpath is expected. - Result<ApexFile> apex_file = ApexFile::Open(symlink_path); - ASSERT_RESULT_OK(apex_file); - ASSERT_EQ(real_path, apex_file->GetPath()); -} - -TEST(ApexFileTest, CompressedSharedLibsApexIsRejected) { - const std::string file_path = - kTestDataDir + "com.android.apex.compressed_sharedlibs.capex"; - Result<ApexFile> apex_file = ApexFile::Open(file_path); - - ASSERT_FALSE(apex_file.ok()); - ASSERT_THAT(apex_file.error().message(), - ::testing::HasSubstr("Apex providing sharedlibs shouldn't " - "be compressed")); -} - -// Check if CAPEX contains originalApexDigest in its manifest -TEST(ApexFileTest, OriginalApexDigest) { - const std::string capex_path = - kTestDataDir + "com.android.apex.compressed.v1.capex"; - auto capex = ApexFile::Open(capex_path); - ASSERT_TRUE(capex.ok()); - const std::string decompressed_apex_path = - kTestDataDir + "com.android.apex.compressed.v1_original.apex"; - auto decompressed_apex = ApexFile::Open(decompressed_apex_path); - ASSERT_TRUE(decompressed_apex.ok()); - // Validate root digest - auto digest = decompressed_apex->VerifyApexVerity( - decompressed_apex->GetBundledPublicKey()); - ASSERT_TRUE(digest.ok()); - ASSERT_EQ(digest->root_digest, - capex->GetManifest().capexmetadata().originalapexdigest()); -} } // namespace } // namespace apex } // namespace android diff --git a/apexd/apex_manifest.cpp b/apexd/apex_manifest.cpp index bfba216e..1994633c 100644 --- a/apexd/apex_manifest.cpp +++ b/apexd/apex_manifest.cpp @@ -22,7 +22,6 @@ using android::base::Error; using android::base::Result; -using ::apex::proto::ApexManifest; namespace android { namespace apex { @@ -47,8 +46,8 @@ Result<ApexManifest> ParseManifest(const std::string& content) { return apex_manifest; } -std::string GetPackageId(const ApexManifest& apex_manifest) { - return apex_manifest.name() + "@" + std::to_string(apex_manifest.version()); +std::string GetPackageId(const ApexManifest& apexManifest) { + return apexManifest.name() + "@" + std::to_string(apexManifest.version()); } Result<ApexManifest> ReadManifest(const std::string& path) { diff --git a/apexd/apex_manifest.h b/apexd/apex_manifest.h index 5bdd43bd..f2996483 100644 --- a/apexd/apex_manifest.h +++ b/apexd/apex_manifest.h @@ -23,16 +23,16 @@ #include <string> +using ::apex::proto::ApexManifest; + namespace android { namespace apex { // Parses and validates APEX manifest. -android::base::Result<::apex::proto::ApexManifest> ParseManifest( - const std::string& content); +android::base::Result<ApexManifest> ParseManifest(const std::string& content); // Returns package id of an ApexManifest -std::string GetPackageId(const ::apex::proto::ApexManifest& apex_manifest); +std::string GetPackageId(const ApexManifest& apex_manifest); // Reads and parses APEX manifest from the file on disk. -android::base::Result<::apex::proto::ApexManifest> ReadManifest( - const std::string& path); +android::base::Result<ApexManifest> ReadManifest(const std::string& path); } // namespace apex } // namespace android diff --git a/apexd/apex_manifest_test.cpp b/apexd/apex_manifest_test.cpp index f5bc93ca..5a4e3da5 100644 --- a/apexd/apex_manifest_test.cpp +++ b/apexd/apex_manifest_test.cpp @@ -22,8 +22,6 @@ #include "apex_manifest.h" -using ::apex::proto::ApexManifest; - namespace android { namespace apex { diff --git a/apexd/apex_preinstalled_data.cpp b/apexd/apex_preinstalled_data.cpp new file mode 100644 index 00000000..d62c28c5 --- /dev/null +++ b/apexd/apex_preinstalled_data.cpp @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#define LOG_TAG "apexd" + +#include "apex_preinstalled_data.h" + +#include <unordered_map> + +#include <android-base/file.h> +#include <android-base/result.h> +#include <android-base/strings.h> + +#include "apex_constants.h" +#include "apex_file.h" +#include "apexd_utils.h" +#include "string_log.h" + +using android::base::Error; +using android::base::Result; + +namespace android { +namespace apex { + +namespace { + +struct ApexPreinstalledData { + std::string name; + std::string key; + std::string path; +}; + +std::unordered_map<std::string, ApexPreinstalledData> gScannedPreinstalledData; + +Result<std::vector<ApexPreinstalledData>> collectPreinstalleDataFromDir( + const std::string& dir) { + LOG(INFO) << "Scanning " << dir << " for preinstalled data"; + std::vector<ApexPreinstalledData> ret; + if (access(dir.c_str(), F_OK) != 0 && errno == ENOENT) { + LOG(INFO) << "... does not exist. Skipping"; + return ret; + } + const bool scanBuiltinApexes = isPathForBuiltinApexes(dir); + if (!scanBuiltinApexes) { + return Error() << "Can't scan preinstalled APEX data from " << dir; + } + Result<std::vector<std::string>> apex_files = FindApexFilesByName(dir); + if (!apex_files.ok()) { + return apex_files.error(); + } + + for (const auto& file : *apex_files) { + Result<ApexFile> apex_file = ApexFile::Open(file); + if (!apex_file.ok()) { + return Error() << "Failed to open " << file << " : " << apex_file.error(); + } + ApexPreinstalledData apexPreInstalledData; + apexPreInstalledData.name = apex_file->GetManifest().name(); + apexPreInstalledData.key = apex_file->GetBundledPublicKey(); + apexPreInstalledData.path = apex_file->GetPath(); + ret.push_back(apexPreInstalledData); + } + return ret; +} + +Result<void> updatePreinstalledData( + const std::vector<ApexPreinstalledData>& apexes) { + for (const ApexPreinstalledData& apex : apexes) { + if (gScannedPreinstalledData.find(apex.name) == + gScannedPreinstalledData.end()) { + gScannedPreinstalledData.insert({apex.name, apex}); + } else { + const std::string& existing_key = + gScannedPreinstalledData.at(apex.name).key; + if (existing_key != apex.key) { + return Error() << "Key for package " << apex.name + << " does not match with previously scanned key"; + } + } + } + return {}; +} + +} // namespace + +Result<void> collectPreinstalledData(const std::vector<std::string>& dirs) { + for (const auto& dir : dirs) { + Result<std::vector<ApexPreinstalledData>> preinstalledData = + collectPreinstalleDataFromDir(dir); + if (!preinstalledData.ok()) { + return Error() << "Failed to collect keys from " << dir << " : " + << preinstalledData.error(); + } + Result<void> st = updatePreinstalledData(*preinstalledData); + if (!st.ok()) { + return st; + } + } + return {}; +} + +Result<const std::string> getApexKey(const std::string& name) { + if (gScannedPreinstalledData.find(name) == gScannedPreinstalledData.end()) { + return Error() << "No preinstalled data found for package " << name; + } + return gScannedPreinstalledData[name].key; +} + +Result<const std::string> getApexPreinstalledPath(const std::string& name) { + if (gScannedPreinstalledData.find(name) == gScannedPreinstalledData.end()) { + return Error() << "No preinstalled data found for package " << name; + } + return gScannedPreinstalledData[name].path; +} + +bool HasPreInstalledVersion(const std::string& name) { + return gScannedPreinstalledData.find(name) != gScannedPreinstalledData.end(); +} + +} // namespace apex +} // namespace android diff --git a/apexd/apex_preinstalled_data.h b/apexd/apex_preinstalled_data.h new file mode 100644 index 00000000..1b80aad5 --- /dev/null +++ b/apexd/apex_preinstalled_data.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#pragma once + +#include <string> +#include <vector> + +#include <android-base/result.h> + +namespace android { +namespace apex { + +android::base::Result<void> collectPreinstalledData( + const std::vector<std::string>& apex_dirs); +android::base::Result<const std::string> getApexKey(const std::string& name); +android::base::Result<const std::string> getApexPreinstalledPath( + const std::string& name); +bool HasPreInstalledVersion(const std::string& name); +} // namespace apex +} // namespace android diff --git a/apexd/apex_shim.cpp b/apexd/apex_shim.cpp index 0031c3f7..d1ff90d9 100644 --- a/apexd/apex_shim.cpp +++ b/apexd/apex_shim.cpp @@ -33,7 +33,6 @@ using android::base::ErrnoError; using android::base::Error; using android::base::Result; -using ::apex::proto::ApexManifest; namespace android { namespace apex { @@ -127,6 +126,7 @@ Result<void> ValidateShimApex(const std::string& mount_point, // Unfortunately fs::recursive_directory_iterator::operator++ can throw an // exception, which means that it's impossible to use range-based for loop // here. + // TODO: wrap into a non-throwing iterator to support range-based for loop. while (iter != fs::end(iter)) { auto path = iter->path(); // Resolve the mount point to ensure any trailing slash is removed. diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp index e0562f02..80e075a1 100644 --- a/apexd/apexd.cpp +++ b/apexd/apexd.cpp @@ -15,26 +15,25 @@ */ #include "apexd.h" -#include "apex_file_repository.h" #include "apexd_private.h" #include "apex_constants.h" #include "apex_database.h" #include "apex_file.h" #include "apex_manifest.h" +#include "apex_preinstalled_data.h" #include "apex_shim.h" #include "apexd_checkpoint.h" -#include "apexd_lifecycle.h" #include "apexd_loop.h" #include "apexd_prepostinstall.h" +#include "apexd_prop.h" #include "apexd_rollback_utils.h" #include "apexd_session.h" #include "apexd_utils.h" #include "apexd_verity.h" -#include "com_android_apex.h" +#include "string_log.h" #include <ApexProperties.sysprop.h> -#include <android-base/chrono_utils.h> #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/macros.h> @@ -44,7 +43,6 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> -#include <google/protobuf/util/message_differencer.h> #include <libavb/libavb.h> #include <libdm/dm.h> #include <libdm/dm_table.h> @@ -53,17 +51,15 @@ #include <dirent.h> #include <fcntl.h> -#include <linux/f2fs.h> #include <linux/loop.h> +#include <log/log.h> #include <stdlib.h> #include <sys/inotify.h> #include <sys/ioctl.h> #include <sys/mount.h> #include <sys/stat.h> -#include <sys/sysinfo.h> #include <sys/types.h> #include <unistd.h> -#include <algorithm> #include <algorithm> #include <array> @@ -71,40 +67,31 @@ #include <cstdlib> #include <filesystem> #include <fstream> -#include <future> #include <iomanip> #include <iterator> #include <memory> -#include <mutex> #include <optional> -#include <queue> -#include <sstream> #include <string> -#include <string_view> #include <thread> #include <unordered_map> #include <unordered_set> -using android::base::boot_clock; -using android::base::ConsumePrefix; using android::base::ErrnoError; using android::base::Error; using android::base::GetProperty; using android::base::Join; using android::base::ParseUint; using android::base::ReadFully; -using android::base::RemoveFileIfExists; using android::base::Result; -using android::base::SetProperty; +using android::base::StartsWith; using android::base::StringPrintf; using android::base::unique_fd; using android::dm::DeviceMapper; using android::dm::DmDeviceState; using android::dm::DmTable; using android::dm::DmTargetVerity; -using ::apex::proto::ApexManifest; + using apex::proto::SessionState; -using google::protobuf::util::MessageDifferencer; namespace android { namespace apex { @@ -113,6 +100,12 @@ using MountedApexData = MountedApexDatabase::MountedApexData; namespace { +// These should be in-sync with system/sepolicy/public/property_contexts +static constexpr const char* kApexStatusSysprop = "apexd.status"; +static constexpr const char* kApexStatusStarting = "starting"; +static constexpr const char* kApexStatusActivated = "activated"; +static constexpr const char* kApexStatusReady = "ready"; + static constexpr const char* kBuildFingerprintSysprop = "ro.build.fingerprint"; // This should be in UAPI, but it's not :-( @@ -121,20 +114,20 @@ static constexpr const char* kDmVerityRestartOnCorruption = MountedApexDatabase gMountedApexes; -std::optional<ApexdConfig> gConfig; - CheckpointInterface* gVoldService; bool gSupportsFsCheckpoints = false; bool gInFsCheckpointMode = false; static constexpr size_t kLoopDeviceSetupAttempts = 3u; -// Please DO NOT add new modules to this list without contacting mainline-modularization@ first. +bool gBootstrap = false; static const std::vector<std::string> kBootstrapApexes = ([]() { std::vector<std::string> ret = { + "com.android.art", "com.android.i18n", "com.android.runtime", "com.android.tzdata", + "com.android.os.statsd", }; auto vendor_vndk_ver = GetProperty("ro.vndk.version", ""); @@ -150,39 +143,14 @@ static const std::vector<std::string> kBootstrapApexes = ([]() { static constexpr const int kNumRetriesWhenCheckpointingEnabled = 1; -bool IsBootstrapApex(const ApexFile& apex) { +bool isBootstrapApex(const ApexFile& apex) { return std::find(kBootstrapApexes.begin(), kBootstrapApexes.end(), apex.GetManifest().name()) != kBootstrapApexes.end(); } -void ReleaseF2fsCompressedBlocks(const std::string& file_path) { - unique_fd fd( - TEMP_FAILURE_RETRY(open(file_path.c_str(), O_RDONLY | O_CLOEXEC, 0))); - if (fd.get() == -1) { - PLOG(ERROR) << "Failed to open " << file_path; - return; - } - unsigned int flags; - if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) { - PLOG(ERROR) << "Failed to call FS_IOC_GETFLAGS on " << file_path; - return; - } - if ((flags & FS_COMPR_FL) == 0) { - // Doesn't support f2fs-compression. - return; - } - uint64_t blk_cnt; - if (ioctl(fd, F2FS_IOC_RELEASE_COMPRESS_BLOCKS, &blk_cnt) == -1) { - PLOG(ERROR) << "Failed to call F2FS_IOC_RELEASE_COMPRESS_BLOCKS on " - << file_path; - } - LOG(INFO) << "Released " << blk_cnt << " compressed blocks from " - << file_path; -} - // Pre-allocate loop devices so that we don't have to wait for them // later when actually activating APEXes. -Result<void> PreAllocateLoopDevices() { +Result<void> preAllocateLoopDevices() { auto scan = FindApexes(kApexPackageBuiltinDirs); if (!scan.ok()) { return scan.error(); @@ -190,28 +158,28 @@ Result<void> PreAllocateLoopDevices() { auto size = 0; for (const auto& path : *scan) { - auto apex_file = ApexFile::Open(path); - if (!apex_file.ok()) { + auto apexFile = ApexFile::Open(path); + if (!apexFile.ok()) { continue; } size++; // bootstrap Apexes may be activated on separate namespaces. - if (IsBootstrapApex(*apex_file)) { + if (isBootstrapApex(*apexFile)) { size++; } } - // note: do not call PreAllocateLoopDevices() if size == 0. + // note: do not call preAllocateLoopDevices() if size == 0. // For devices (e.g. ARC) which doesn't support loop-control - // PreAllocateLoopDevices() can cause problem when it tries + // preAllocateLoopDevices() can cause problem when it tries // to access /dev/loop-control. if (size == 0) { return {}; } - return loop::PreAllocateLoopDevices(size); + return loop::preAllocateLoopDevices(size); } -std::unique_ptr<DmTable> CreateVerityTable(const ApexVerityData& verity_data, +std::unique_ptr<DmTable> createVerityTable(const ApexVerityData& verity_data, const std::string& block_device, const std::string& hash_device, bool restart_on_corruption) { @@ -242,18 +210,9 @@ std::unique_ptr<DmTable> CreateVerityTable(const ApexVerityData& verity_data, // Deletes a dm-verity device with a given name and path // Synchronizes on the device actually being deleted from userspace. -Result<void> DeleteVerityDevice(const std::string& name, bool deferred) { +Result<void> DeleteVerityDevice(const std::string& name) { DeviceMapper& dm = DeviceMapper::Instance(); - if (deferred) { - if (!dm.DeleteDeviceDeferred(name)) { - return ErrnoError() << "Failed to issue deferred delete of verity device " - << name; - } - return {}; - } - auto timeout = std::chrono::milliseconds( - android::sysprop::ApexProperties::dm_delete_timeout().value_or(750)); - if (!dm.DeleteDevice(name, timeout)) { + if (!dm.DeleteDevice(name, 750ms)) { return Error() << "Failed to delete dm-device " << name; } return {}; @@ -286,7 +245,7 @@ class DmVerityDevice { ~DmVerityDevice() { if (!cleared_) { - Result<void> ret = DeleteVerityDevice(name_, /* deferred= */ false); + Result<void> ret = DeleteVerityDevice(name_); if (!ret.ok()) { LOG(ERROR) << ret.error(); } @@ -304,46 +263,32 @@ class DmVerityDevice { bool cleared_; }; -Result<DmVerityDevice> CreateVerityDevice(const std::string& name, +Result<DmVerityDevice> createVerityDevice(const std::string& name, const DmTable& table) { DeviceMapper& dm = DeviceMapper::Instance(); if (dm.GetState(name) != DmDeviceState::INVALID) { - // Delete dangling dm-device. This can happen if apexd fails to delete it - // while unmounting an apex. + // TODO: since apexd tears down devices during unmount, can this happen? LOG(WARNING) << "Deleting existing dm device " << name; - auto result = DeleteVerityDevice(name, /* deferred= */ false); + const Result<void>& result = DeleteVerityDevice(name); if (!result.ok()) { - return result.error(); + // TODO: should we fail instead? + LOG(ERROR) << "Failed to delete device " << name << " : " + << result.error(); } } - auto timeout = std::chrono::milliseconds( - android::sysprop::ApexProperties::dm_create_timeout().value_or(1000)); std::string dev_path; - if (!dm.CreateDevice(name, table, &dev_path, timeout)) { + if (!dm.CreateDevice(name, table, &dev_path, 500ms)) { return Errorf("Couldn't create verity device."); } return DmVerityDevice(name, dev_path); } -/** - * When we create hardlink for a new apex package in kActiveApexPackagesDataDir, - * there might be an older version of the same package already present in there. - * Since a new version of the same package is being installed on this boot, the - * old one needs to deleted so that we don't end up activating same package - * twice. - * - * @param affected_packages package names of the news apex that are being - * installed in this boot - * @param files_to_keep path to the new apex packages in - * kActiveApexPackagesDataDir - */ Result<void> RemovePreviouslyActiveApexFiles( const std::unordered_set<std::string>& affected_packages, const std::unordered_set<std::string>& files_to_keep) { - auto all_active_apex_files = - FindFilesBySuffix(gConfig->active_apex_data_dir, {kApexPackageSuffix}); + auto all_active_apex_files = FindApexFilesByName(kActiveApexPackagesDataDir); if (!all_active_apex_files.ok()) { return all_active_apex_files.error(); @@ -377,14 +322,13 @@ Result<void> RemovePreviouslyActiveApexFiles( } // Reads the entire device to verify the image is authenticatic -Result<void> ReadVerityDevice(const std::string& verity_device, +Result<void> readVerityDevice(const std::string& verity_device, uint64_t device_size) { static constexpr int kBlockSize = 4096; static constexpr size_t kBufSize = 1024 * kBlockSize; std::vector<uint8_t> buffer(kBufSize); - unique_fd fd( - TEMP_FAILURE_RETRY(open(verity_device.c_str(), O_RDONLY | O_CLOEXEC))); + unique_fd fd(TEMP_FAILURE_RETRY(open(verity_device.c_str(), O_RDONLY))); if (fd.get() == -1) { return ErrnoError() << "Can't open " << verity_device; } @@ -403,16 +347,9 @@ Result<void> ReadVerityDevice(const std::string& verity_device, Result<void> VerifyMountedImage(const ApexFile& apex, const std::string& mount_point) { - // Verify that apex_manifest.pb inside mounted image matches the one in the - // outer .apex container. - Result<ApexManifest> verified_manifest = - ReadManifest(mount_point + "/" + kManifestFilenamePb); - if (!verified_manifest.ok()) { - return verified_manifest.error(); - } - if (!MessageDifferencer::Equals(*verified_manifest, apex.GetManifest())) { - return Errorf( - "Manifest inside filesystem does not match manifest outside it"); + auto result = apex.VerifyManifestMatches(mount_point); + if (!result.ok()) { + return result; } if (shim::IsShimApex(apex)) { return shim::ValidateShimApex(mount_point, apex); @@ -421,18 +358,11 @@ Result<void> VerifyMountedImage(const ApexFile& apex, } Result<MountedApexData> MountPackageImpl(const ApexFile& apex, - const std::string& mount_point, + const std::string& mountPoint, const std::string& device_name, const std::string& hashtree_file, - bool verify_image, - bool temp_mount = false) { - if (apex.IsCompressed()) { - return Error() << "Cannot directly mount compressed APEX " - << apex.GetPath(); - } - - LOG(VERBOSE) << "Creating mount point: " << mount_point; - auto time_started = boot_clock::now(); + bool verifyImage) { + LOG(VERBOSE) << "Creating mount point: " << mountPoint; // Note: the mount point could exist in case when the APEX was activated // during the bootstrap phase (e.g., the runtime or tzdata APEX). // Although we have separate mount namespaces to separate the early activated @@ -441,34 +371,31 @@ Result<MountedApexData> MountPackageImpl(const ApexFile& apex, // mounted at / which is (and has to be) a shared mount. Therefore, if apexd // finds an empty directory under /apex, it's not a problem and apexd can use // it. - auto exists = PathExists(mount_point); + auto exists = PathExists(mountPoint); if (!exists.ok()) { return exists.error(); } - if (!*exists && mkdir(mount_point.c_str(), kMkdirMode) != 0) { - return ErrnoError() << "Could not create mount point " << mount_point; + if (!*exists && mkdir(mountPoint.c_str(), kMkdirMode) != 0) { + return ErrnoError() << "Could not create mount point " << mountPoint; } - auto deleter = [&mount_point]() { - if (rmdir(mount_point.c_str()) != 0) { - PLOG(WARNING) << "Could not rmdir " << mount_point; + auto deleter = [&mountPoint]() { + if (rmdir(mountPoint.c_str()) != 0) { + PLOG(WARNING) << "Could not rmdir " << mountPoint; } }; auto scope_guard = android::base::make_scope_guard(deleter); - if (!IsEmptyDirectory(mount_point)) { - return ErrnoError() << mount_point << " is not empty"; + if (!IsEmptyDirectory(mountPoint)) { + return ErrnoError() << mountPoint << " is not empty"; } const std::string& full_path = apex.GetPath(); - if (!apex.GetImageOffset() || !apex.GetImageSize()) { - return Error() << "Cannot create mount point without image offset and size"; - } - loop::LoopbackDeviceUniqueFd loopback_device; + loop::LoopbackDeviceUniqueFd loopbackDevice; for (size_t attempts = 1;; ++attempts) { - Result<loop::LoopbackDeviceUniqueFd> ret = loop::CreateLoopDevice( - full_path, apex.GetImageOffset().value(), apex.GetImageSize().value()); + Result<loop::LoopbackDeviceUniqueFd> ret = loop::createLoopDevice( + full_path, apex.GetImageOffset(), apex.GetImageSize()); if (ret.ok()) { - loopback_device = std::move(*ret); + loopbackDevice = std::move(*ret); break; } if (attempts >= kLoopDeviceSetupAttempts) { @@ -476,43 +403,33 @@ Result<MountedApexData> MountPackageImpl(const ApexFile& apex, << ret.error(); } } - LOG(VERBOSE) << "Loopback device created: " << loopback_device.name; + LOG(VERBOSE) << "Loopback device created: " << loopbackDevice.name; - auto& instance = ApexFileRepository::GetInstance(); - - auto public_key = instance.GetPublicKey(apex.GetManifest().name()); - if (!public_key.ok()) { - return public_key.error(); - } - - auto verity_data = apex.VerifyApexVerity(*public_key); - if (!verity_data.ok()) { + auto verityData = apex.VerifyApexVerity(); + if (!verityData.ok()) { return Error() << "Failed to verify Apex Verity data for " << full_path - << ": " << verity_data.error(); + << ": " << verityData.error(); } - std::string block_device = loopback_device.name; - MountedApexData apex_data(loopback_device.name, apex.GetPath(), mount_point, + std::string blockDevice = loopbackDevice.name; + MountedApexData apex_data(loopbackDevice.name, apex.GetPath(), mountPoint, /* device_name = */ "", - /* hashtree_loop_name = */ "", - /* is_temp_mount */ temp_mount); + /* hashtree_loop_name = */ ""); // for APEXes in immutable partitions, we don't need to mount them on // dm-verity because they are already in the dm-verity protected partition; // system. However, note that we don't skip verification to ensure that APEXes // are correctly signed. - const bool mount_on_verity = - !instance.IsPreInstalledApex(apex) || instance.IsDecompressedApex(apex); - - DmVerityDevice verity_dev; + const bool mountOnVerity = !isPathForBuiltinApexes(full_path); + DmVerityDevice verityDev; loop::LoopbackDeviceUniqueFd loop_for_hash; - if (mount_on_verity) { - std::string hash_device = loopback_device.name; - if (verity_data->desc->tree_size == 0) { - if (auto st = PrepareHashTree(apex, *verity_data, hashtree_file); + if (mountOnVerity) { + std::string hash_device = loopbackDevice.name; + if (verityData->desc->tree_size == 0) { + if (auto st = PrepareHashTree(apex, *verityData, hashtree_file); !st.ok()) { return st.error(); } - auto create_loop_status = loop::CreateLoopDevice(hashtree_file, 0, 0); + auto create_loop_status = loop::createLoopDevice(hashtree_file, 0, 0); if (!create_loop_status.ok()) { return create_loop_status.error(); } @@ -520,59 +437,54 @@ Result<MountedApexData> MountPackageImpl(const ApexFile& apex, hash_device = loop_for_hash.name; apex_data.hashtree_loop_name = hash_device; } - auto verity_table = - CreateVerityTable(*verity_data, loopback_device.name, hash_device, - /* restart_on_corruption = */ !verify_image); - Result<DmVerityDevice> verity_dev_res = - CreateVerityDevice(device_name, *verity_table); - if (!verity_dev_res.ok()) { + auto verityTable = + createVerityTable(*verityData, loopbackDevice.name, hash_device, + /* restart_on_corruption = */ !verifyImage); + Result<DmVerityDevice> verityDevRes = + createVerityDevice(device_name, *verityTable); + if (!verityDevRes.ok()) { return Error() << "Failed to create Apex Verity device " << full_path - << ": " << verity_dev_res.error(); + << ": " << verityDevRes.error(); } - verity_dev = std::move(*verity_dev_res); + verityDev = std::move(*verityDevRes); apex_data.device_name = device_name; - block_device = verity_dev.GetDevPath(); + blockDevice = verityDev.GetDevPath(); - Result<void> read_ahead_status = - loop::ConfigureReadAhead(verity_dev.GetDevPath()); - if (!read_ahead_status.ok()) { - return read_ahead_status.error(); + Result<void> readAheadStatus = + loop::configureReadAhead(verityDev.GetDevPath()); + if (!readAheadStatus.ok()) { + return readAheadStatus.error(); } } - // TODO(b/158467418): consider moving this inside RunVerifyFnInsideTempMount. - if (mount_on_verity && verify_image) { - Result<void> verity_status = - ReadVerityDevice(block_device, (*verity_data).desc->image_size); - if (!verity_status.ok()) { - return verity_status.error(); + // TODO: consider moving this inside RunVerifyFnInsideTempMount. + if (mountOnVerity && verifyImage) { + Result<void> verityStatus = + readVerityDevice(blockDevice, (*verityData).desc->image_size); + if (!verityStatus.ok()) { + return verityStatus.error(); } } - uint32_t mount_flags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY; + uint32_t mountFlags = MS_NOATIME | MS_NODEV | MS_DIRSYNC | MS_RDONLY; if (apex.GetManifest().nocode()) { - mount_flags |= MS_NOEXEC; + mountFlags |= MS_NOEXEC; } - if (!apex.GetFsType()) { - return Error() << "Cannot mount package without FsType"; - } - if (mount(block_device.c_str(), mount_point.c_str(), - apex.GetFsType().value().c_str(), mount_flags, nullptr) == 0) { - auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( - boot_clock::now() - time_started).count(); + if (mount(blockDevice.c_str(), mountPoint.c_str(), "ext4", mountFlags, + nullptr) == 0) { LOG(INFO) << "Successfully mounted package " << full_path << " on " - << mount_point << " duration=" << time_elapsed; - auto status = VerifyMountedImage(apex, mount_point); + << mountPoint; + auto status = VerifyMountedImage(apex, mountPoint); if (!status.ok()) { - if (umount2(mount_point.c_str(), UMOUNT_NOFOLLOW) != 0) { - PLOG(ERROR) << "Failed to umount " << mount_point; + if (umount2(mountPoint.c_str(), UMOUNT_NOFOLLOW) != 0) { + PLOG(ERROR) << "Failed to umount " << mountPoint; } return Error() << "Failed to verify " << full_path << ": " << status.error(); } // Time to accept the temporaries as good. - verity_dev.Release(); - loopback_device.CloseGood(); + verityDev.Release(); + loopbackDevice.CloseGood(); loop_for_hash.CloseGood(); scope_guard.Disable(); // Accept the mount. @@ -583,9 +495,8 @@ Result<MountedApexData> MountPackageImpl(const ApexFile& apex, } std::string GetHashTreeFileName(const ApexFile& apex, bool is_new) { - const std::string& id = GetPackageId(apex.GetManifest()); std::string ret = - StringPrintf("%s/%s", gConfig->apex_hash_tree_dir, id.c_str()); + std::string(kApexHashTreeDir) + "/" + GetPackageId(apex.GetManifest()); return is_new ? ret + ".new" : ret; } @@ -601,40 +512,28 @@ Result<MountedApexData> VerifyAndTempMountPackage( return ErrnoError() << "Failed to unlink " << hashtree_file; } } - auto ret = - MountPackageImpl(apex, mount_point, temp_device_name, hashtree_file, - /* verify_image = */ true, /* temp_mount = */ true); - if (!ret.ok()) { - LOG(DEBUG) << "Cleaning up " << hashtree_file; - if (TEMP_FAILURE_RETRY(unlink(hashtree_file.c_str())) != 0) { - PLOG(ERROR) << "Failed to unlink " << hashtree_file; - } - } else { - gMountedApexes.AddMountedApex(apex.GetManifest().name(), false, *ret); - } - return ret; + return MountPackageImpl(apex, mount_point, temp_device_name, + GetHashTreeFileName(apex, /* is_new = */ true), + /* verifyImage = */ true); } -} // namespace - -Result<void> Unmount(const MountedApexData& data, bool deferred) { +Result<void> Unmount(const MountedApexData& data) { LOG(DEBUG) << "Unmounting " << data.full_path << " from mount point " - << data.mount_point << " deferred = " << deferred; + << data.mount_point; // Lazily try to umount whatever is mounted. if (umount2(data.mount_point.c_str(), UMOUNT_NOFOLLOW) != 0 && errno != EINVAL && errno != ENOENT) { return ErrnoError() << "Failed to unmount directory " << data.mount_point; } - - if (!deferred) { - if (rmdir(data.mount_point.c_str()) != 0) { - PLOG(ERROR) << "Failed to rmdir " << data.mount_point; - } + // Attempt to delete the folder. If the folder is retained, other + // data may be incorrect. + if (rmdir(data.mount_point.c_str()) != 0) { + PLOG(ERROR) << "Failed to rmdir directory " << data.mount_point; } // Try to free up the device-mapper device. if (!data.device_name.empty()) { - const auto& result = DeleteVerityDevice(data.device_name, deferred); + const auto& result = DeleteVerityDevice(data.device_name); if (!result.ok()) { return result; } @@ -644,28 +543,19 @@ Result<void> Unmount(const MountedApexData& data, bool deferred) { auto log_fn = [](const std::string& path, const std::string& /*id*/) { LOG(VERBOSE) << "Freeing loop device " << path << " for unmount."; }; - - // Since we now use LO_FLAGS_AUTOCLEAR when configuring loop devices, in - // theory we don't need to manually call DestroyLoopDevice here even if - // |deferred| is false. However we prefer to call it to ensure the invariant - // of SubmitStagedSession (after it's done, loop devices created for temp - // mount are freed). - if (!data.loop_name.empty() && !deferred) { + if (!data.loop_name.empty()) { loop::DestroyLoopDevice(data.loop_name, log_fn); } - if (!data.hashtree_loop_name.empty() && !deferred) { + if (!data.hashtree_loop_name.empty()) { loop::DestroyLoopDevice(data.hashtree_loop_name, log_fn); } return {}; } -namespace { - template <typename VerifyFn> Result<void> RunVerifyFnInsideTempMount(const ApexFile& apex, - const VerifyFn& verify_fn, - bool unmount_during_cleanup) { + const VerifyFn& verify_fn) { // Temp mount image of this apex to validate it was properly signed; // this will also read the entire block device through dm-verity, so // we can be sure there is no corruption. @@ -681,33 +571,19 @@ Result<void> RunVerifyFnInsideTempMount(const ApexFile& apex, } auto cleaner = [&]() { LOG(DEBUG) << "Unmounting " << temp_mount_point; - Result<void> result = Unmount(*mount_status, /* deferred= */ false); + Result<void> result = Unmount(*mount_status); if (!result.ok()) { LOG(WARNING) << "Failed to unmount " << temp_mount_point << " : " << result.error(); } - gMountedApexes.RemoveMountedApex(apex.GetManifest().name(), apex.GetPath(), - true); }; auto scope_guard = android::base::make_scope_guard(cleaner); - auto result = verify_fn(temp_mount_point); - if (!result.ok()) { - return result.error(); - } - if (!unmount_during_cleanup) { - scope_guard.Disable(); - } - return {}; + return verify_fn(temp_mount_point); } template <typename HookFn, typename HookCall> Result<void> PrePostinstallPackages(const std::vector<ApexFile>& apexes, HookFn fn, HookCall call) { - auto scope_guard = android::base::make_scope_guard([&]() { - for (const ApexFile& apex_file : apexes) { - apexd_private::UnmountTempMount(apex_file); - } - }); if (apexes.empty()) { return Errorf("Empty set of inputs"); } @@ -721,26 +597,9 @@ Result<void> PrePostinstallPackages(const std::vector<ApexFile>& apexes, } } - // 2) If we found hooks, temp mount if required, and run the pre/post-install. + // 2) If we found hooks, run the pre/post-install. if (has_hooks) { - std::vector<std::string> mount_points; - for (const ApexFile& apex : apexes) { - // Retrieve the mount data if the apex is already temp mounted, temp - // mount it otherwise. - std::string mount_point = - apexd_private::GetPackageTempMountPoint(apex.GetManifest()); - Result<MountedApexData> mount_data = - apexd_private::GetTempMountedApexData(apex.GetManifest().name()); - if (!mount_data.ok()) { - mount_data = VerifyAndTempMountPackage(apex, mount_point); - if (!mount_data.ok()) { - return mount_data.error(); - } - } - mount_points.push_back(mount_point); - } - - Result<void> install_status = (*call)(apexes, mount_points); + Result<void> install_status = (*call)(apexes); if (!install_status.ok()) { return install_status; } @@ -759,23 +618,20 @@ Result<void> PostinstallPackages(const std::vector<ApexFile>& apexes) { &StagePostInstall); } -// Converts a list of apex file paths into a list of ApexFile objects -// -// Returns error when trying to open empty set of inputs. -Result<std::vector<ApexFile>> OpenApexFiles( - const std::vector<std::string>& paths) { - if (paths.empty()) { - return Errorf("Empty set of inputs"); - } - std::vector<ApexFile> ret; +template <typename RetType, typename Fn> +RetType HandlePackages(const std::vector<std::string>& paths, Fn fn) { + // 1) Open all APEXes. + std::vector<ApexFile> apex_files; for (const std::string& path : paths) { Result<ApexFile> apex_file = ApexFile::Open(path); if (!apex_file.ok()) { return apex_file.error(); } - ret.emplace_back(std::move(*apex_file)); + apex_files.emplace_back(std::move(*apex_file)); } - return ret; + + // 2) Dispatch. + return fn(apex_files); } Result<void> ValidateStagingShimApex(const ApexFile& to) { @@ -788,20 +644,14 @@ Result<void> ValidateStagingShimApex(const ApexFile& to) { auto verify_fn = [&](const std::string& system_apex_path) { return shim::ValidateUpdate(system_apex_path, to.GetPath()); }; - return RunVerifyFnInsideTempMount(*system_shim, verify_fn, true); + return RunVerifyFnInsideTempMount(*system_shim, verify_fn); } // A version of apex verification that happens during boot. // This function should only verification checks that are necessary to run on // each boot. Try to avoid putting expensive checks inside this function. Result<void> VerifyPackageBoot(const ApexFile& apex_file) { - // TODO(ioffe): why do we need this here? - auto& instance = ApexFileRepository::GetInstance(); - auto public_key = instance.GetPublicKey(apex_file.GetManifest().name()); - if (!public_key.ok()) { - return public_key.error(); - } - Result<ApexVerityData> verity_or = apex_file.VerifyApexVerity(*public_key); + Result<ApexVerityData> verity_or = apex_file.VerifyApexVerity(); if (!verity_or.ok()) { return verity_or.error(); } @@ -818,48 +668,49 @@ Result<void> VerifyPackageBoot(const ApexFile& apex_file) { return {}; } -// A version of apex verification that happens on SubmitStagedSession. +// A version of apex verification that happens on submitStagedSession. // This function contains checks that might be expensive to perform, e.g. temp // mounting a package and reading entire dm-verity device, and shouldn't be run // during boot. -Result<void> VerifyPackageStagedInstall(const ApexFile& apex_file) { +Result<void> VerifyPackageInstall(const ApexFile& apex_file) { const auto& verify_package_boot_status = VerifyPackageBoot(apex_file); if (!verify_package_boot_status.ok()) { return verify_package_boot_status; } + Result<ApexVerityData> verity_or = apex_file.VerifyApexVerity(); constexpr const auto kSuccessFn = [](const std::string& /*mount_point*/) { return Result<void>{}; }; - return RunVerifyFnInsideTempMount(apex_file, kSuccessFn, false); + return RunVerifyFnInsideTempMount(apex_file, kSuccessFn); } template <typename VerifyApexFn> -Result<std::vector<ApexFile>> VerifyPackages( +Result<std::vector<ApexFile>> verifyPackages( const std::vector<std::string>& paths, const VerifyApexFn& verify_apex_fn) { - Result<std::vector<ApexFile>> apex_files = OpenApexFiles(paths); - if (!apex_files.ok()) { - return apex_files.error(); + if (paths.empty()) { + return Errorf("Empty set of inputs"); } + LOG(DEBUG) << "verifyPackages() for " << Join(paths, ','); - LOG(DEBUG) << "VerifyPackages() for " << Join(paths, ','); - - for (const ApexFile& apex_file : *apex_files) { - Result<void> result = verify_apex_fn(apex_file); - if (!result.ok()) { - return result.error(); + auto verify_fn = [&](std::vector<ApexFile>& apexes) { + for (const ApexFile& apex_file : apexes) { + Result<void> result = verify_apex_fn(apex_file); + if (!result.ok()) { + return Result<std::vector<ApexFile>>(result.error()); + } } - } - return std::move(*apex_files); + return Result<std::vector<ApexFile>>(std::move(apexes)); + }; + return HandlePackages<Result<std::vector<ApexFile>>>(paths, verify_fn); } -Result<ApexFile> VerifySessionDir(const int session_id) { - std::string session_dir_path = std::string(kStagedSessionsDir) + "/session_" + - std::to_string(session_id); - LOG(INFO) << "Scanning " << session_dir_path +Result<ApexFile> verifySessionDir(const int session_id) { + std::string sessionDirPath = std::string(kStagedSessionsDir) + "/session_" + + std::to_string(session_id); + LOG(INFO) << "Scanning " << sessionDirPath << " looking for packages to be validated"; - Result<std::vector<std::string>> scan = - FindFilesBySuffix(session_dir_path, {kApexPackageSuffix}); + Result<std::vector<std::string>> scan = FindApexFilesByName(sessionDirPath); if (!scan.ok()) { LOG(WARNING) << scan.error(); return scan.error(); @@ -870,7 +721,7 @@ Result<ApexFile> VerifySessionDir(const int session_id) { "More than one APEX package found in the same session directory."); } - auto verified = VerifyPackages(*scan, VerifyPackageStagedInstall); + auto verified = verifyPackages(*scan, VerifyPackageInstall); if (!verified.ok()) { return verified.error(); } @@ -891,27 +742,25 @@ Result<void> DeleteBackup() { } Result<void> BackupActivePackages() { - LOG(DEBUG) << "Initializing backup of " << gConfig->active_apex_data_dir; + LOG(DEBUG) << "Initializing backup of " << kActiveApexPackagesDataDir; // Previous restore might've delete backups folder. - auto create_status = CreateDirIfNeeded(kApexBackupDir, 0700); + auto create_status = createDirIfNeeded(kApexBackupDir, 0700); if (!create_status.ok()) { return Error() << "Backup failed : " << create_status.error(); } - auto apex_active_exists = - PathExists(std::string(gConfig->active_apex_data_dir)); + auto apex_active_exists = PathExists(std::string(kActiveApexPackagesDataDir)); if (!apex_active_exists.ok()) { return Error() << "Backup failed : " << apex_active_exists.error(); } if (!*apex_active_exists) { - LOG(DEBUG) << gConfig->active_apex_data_dir + LOG(DEBUG) << kActiveApexPackagesDataDir << " does not exist. Nothing to backup"; return {}; } - auto active_packages = - FindFilesBySuffix(gConfig->active_apex_data_dir, {kApexPackageSuffix}); + auto active_packages = FindApexFilesByName(kActiveApexPackagesDataDir); if (!active_packages.ok()) { return Error() << "Backup failed : " << active_packages.error(); } @@ -952,7 +801,7 @@ Result<void> BackupActivePackages() { } Result<void> RestoreActivePackages() { - LOG(DEBUG) << "Initializing restore of " << gConfig->active_apex_data_dir; + LOG(DEBUG) << "Initializing restore of " << kActiveApexPackagesDataDir; auto backup_exists = PathExists(std::string(kApexBackupDir)); if (!backup_exists.ok()) { @@ -963,38 +812,37 @@ Result<void> RestoreActivePackages() { } struct stat stat_data; - if (stat(gConfig->active_apex_data_dir, &stat_data) != 0) { - return ErrnoError() << "Failed to access " << gConfig->active_apex_data_dir; + if (stat(kActiveApexPackagesDataDir, &stat_data) != 0) { + return ErrnoError() << "Failed to access " << kActiveApexPackagesDataDir; } - LOG(DEBUG) << "Deleting existing packages in " - << gConfig->active_apex_data_dir; + LOG(DEBUG) << "Deleting existing packages in " << kActiveApexPackagesDataDir; auto delete_status = - DeleteDirContent(std::string(gConfig->active_apex_data_dir)); + DeleteDirContent(std::string(kActiveApexPackagesDataDir)); if (!delete_status.ok()) { return delete_status; } LOG(DEBUG) << "Renaming " << kApexBackupDir << " to " - << gConfig->active_apex_data_dir; - if (rename(kApexBackupDir, gConfig->active_apex_data_dir) != 0) { + << kActiveApexPackagesDataDir; + if (rename(kApexBackupDir, kActiveApexPackagesDataDir) != 0) { return ErrnoError() << "Failed to rename " << kApexBackupDir << " to " - << gConfig->active_apex_data_dir; + << kActiveApexPackagesDataDir; } LOG(DEBUG) << "Restoring original permissions for " - << gConfig->active_apex_data_dir; - if (chmod(gConfig->active_apex_data_dir, stat_data.st_mode & ALLPERMS) != 0) { + << kActiveApexPackagesDataDir; + if (chmod(kActiveApexPackagesDataDir, stat_data.st_mode & ALLPERMS) != 0) { + // TODO: should we wipe out /data/apex/active if chmod fails? return ErrnoError() << "Failed to restore original permissions for " - << gConfig->active_apex_data_dir; + << kActiveApexPackagesDataDir; } return {}; } -Result<void> UnmountPackage(const ApexFile& apex, bool allow_latest, - bool deferred) { - LOG(INFO) << "Unmounting " << GetPackageId(apex.GetManifest()); +Result<void> UnmountPackage(const ApexFile& apex, bool allow_latest) { + LOG(VERBOSE) << "Unmounting " << GetPackageId(apex.GetManifest()); const ApexManifest& manifest = apex.GetManifest(); @@ -1013,39 +861,33 @@ Result<void> UnmountPackage(const ApexFile& apex, bool allow_latest, return Error() << "Did not find " << apex.GetPath(); } - // Concept of latest sharedlibs apex is somewhat blurred. Since this is only - // used in testing, it is ok to always allow unmounting sharedlibs apex. - if (latest && !manifest.providesharedapexlibs()) { + if (latest) { if (!allow_latest) { return Error() << "Package " << apex.GetPath() << " is active"; } std::string mount_point = apexd_private::GetActiveMountPoint(manifest); - LOG(INFO) << "Unmounting " << mount_point; + LOG(VERBOSE) << "Unmounting and deleting " << mount_point; if (umount2(mount_point.c_str(), UMOUNT_NOFOLLOW) != 0) { return ErrnoError() << "Failed to unmount " << mount_point; } - - if (!deferred) { - if (rmdir(mount_point.c_str()) != 0) { - PLOG(ERROR) << "Failed to rmdir " << mount_point; - } + if (rmdir(mount_point.c_str()) != 0) { + PLOG(ERROR) << "Could not rmdir " << mount_point; + // Continue here. } } // Clean up gMountedApexes now, even though we're not fully done. gMountedApexes.RemoveMountedApex(manifest.name(), apex.GetPath()); - return Unmount(*data, deferred); + return Unmount(*data); } } // namespace -void SetConfig(const ApexdConfig& config) { gConfig = config; } - -Result<void> MountPackage(const ApexFile& apex, const std::string& mount_point, - const std::string& device_name) { - auto ret = MountPackageImpl(apex, mount_point, device_name, - GetHashTreeFileName(apex, /* is_new= */ false), - /* verify_image = */ false); +Result<void> MountPackage(const ApexFile& apex, const std::string& mountPoint) { + auto ret = + MountPackageImpl(apex, mountPoint, GetPackageId(apex.GetManifest()), + GetHashTreeFileName(apex, /* is_new = */ false), + /* verifyImage = */ false); if (!ret.ok()) { return ret.error(); } @@ -1056,41 +898,15 @@ Result<void> MountPackage(const ApexFile& apex, const std::string& mount_point, namespace apexd_private { -Result<void> UnmountTempMount(const ApexFile& apex) { - const ApexManifest& manifest = apex.GetManifest(); - LOG(VERBOSE) << "Unmounting all temp mounts for package " << manifest.name(); - - bool finished_unmounting = false; - // If multiple temp mounts exist, ensure that all are unmounted. - while (!finished_unmounting) { - Result<MountedApexData> data = - apexd_private::GetTempMountedApexData(manifest.name()); - if (!data.ok()) { - finished_unmounting = true; - } else { - gMountedApexes.RemoveMountedApex(manifest.name(), data->full_path, true); - Unmount(*data, /* deferred= */ false); - } - } - return {}; +Result<MountedApexData> TempMountPackage(const ApexFile& apex, + const std::string& mount_point) { + // TODO(ioffe): consolidate these two methods. + return android::apex::VerifyAndTempMountPackage(apex, mount_point); } -Result<MountedApexData> GetTempMountedApexData(const std::string& package) { - bool found = false; - Result<MountedApexData> mount_data; - gMountedApexes.ForallMountedApexes( - package, - [&](const MountedApexData& data, [[maybe_unused]] bool latest) { - if (!found) { - mount_data = data; - found = true; - } - }, - true); - if (found) { - return mount_data; - } - return Error() << "No temp mount data found for " << package; +Result<void> Unmount(const MountedApexData& data) { + // TODO(ioffe): consolidate these two methods. + return android::apex::Unmount(data); } bool IsMounted(const std::string& full_path) { @@ -1119,219 +935,77 @@ std::string GetActiveMountPoint(const ApexManifest& manifest) { } // namespace apexd_private -Result<void> ResumeRevertIfNeeded() { +Result<void> resumeRevertIfNeeded() { auto sessions = ApexSession::GetSessionsInState(SessionState::REVERT_IN_PROGRESS); if (sessions.empty()) { return {}; } - return RevertActiveSessions("", ""); -} - -Result<void> ActivateSharedLibsPackage(const std::string& mount_point) { - for (const auto& lib_path : {"lib", "lib64"}) { - std::string apex_lib_path = mount_point + "/" + lib_path; - auto lib_dir = PathExists(apex_lib_path); - if (!lib_dir.ok() || !*lib_dir) { - continue; - } - - auto iter = std::filesystem::directory_iterator(apex_lib_path); - std::error_code ec; - - while (iter != std::filesystem::end(iter)) { - const auto& lib_entry = *iter; - if (!lib_entry.is_directory()) { - iter = iter.increment(ec); - if (ec) { - return Error() << "Failed to scan " << apex_lib_path << " : " - << ec.message(); - } - continue; - } - - const auto library_name = lib_entry.path().filename(); - const std::string library_symlink_dir = - StringPrintf("%s/%s/%s/%s", kApexRoot, kApexSharedLibsSubDir, - lib_path, library_name.c_str()); - - auto symlink_dir = PathExists(library_symlink_dir); - if (!symlink_dir.ok() || !*symlink_dir) { - std::filesystem::create_directory(library_symlink_dir, ec); - if (ec) { - return Error() << "Failed to create directory " << library_symlink_dir - << ": " << ec.message(); - } - } - - auto inner_iter = - std::filesystem::directory_iterator(lib_entry.path().string()); - - while (inner_iter != std::filesystem::end(inner_iter)) { - const auto& lib_items = *inner_iter; - const auto hash_value = lib_items.path().filename(); - const std::string library_symlink_hash = StringPrintf( - "%s/%s", library_symlink_dir.c_str(), hash_value.c_str()); - - auto hash_dir = PathExists(library_symlink_hash); - if (hash_dir.ok() && *hash_dir) { - // Compare file size for two library files with same name and hash - // value - auto existing_file_path = - library_symlink_hash + "/" + library_name.string(); - auto existing_file_size = GetFileSize(existing_file_path); - if (!existing_file_size.ok()) { - return existing_file_size.error(); - } - - auto new_file_path = - lib_items.path().string() + "/" + library_name.string(); - auto new_file_size = GetFileSize(new_file_path); - if (!new_file_size.ok()) { - return new_file_size.error(); - } - - if (*existing_file_size != *new_file_size) { - return Error() << "There are two libraries with same hash and " - "different file size : " - << existing_file_path << " and " << new_file_path; - } - - inner_iter = inner_iter.increment(ec); - if (ec) { - return Error() << "Failed to scan " << lib_entry.path().string() - << " : " << ec.message(); - } - continue; - } - std::filesystem::create_directory_symlink(lib_items.path(), - library_symlink_hash, ec); - if (ec) { - return Error() << "Failed to create symlink from " << lib_items.path() - << " to " << library_symlink_hash << ec.message(); - } - - inner_iter = inner_iter.increment(ec); - if (ec) { - return Error() << "Failed to scan " << lib_entry.path().string() - << " : " << ec.message(); - } - } - - iter = iter.increment(ec); - if (ec) { - return Error() << "Failed to scan " << apex_lib_path << " : " - << ec.message(); - } - } - } - - return {}; + return revertActiveSessions(""); } -bool IsValidPackageName(const std::string& package_name) { - return kBannedApexName.count(package_name) == 0; -} - -Result<void> ActivatePackageImpl(const ApexFile& apex_file, - const std::string& device_name) { +Result<void> activatePackageImpl(const ApexFile& apex_file) { const ApexManifest& manifest = apex_file.GetManifest(); - if (!IsValidPackageName(manifest.name())) { - return Errorf("Package name {} is not allowed.", manifest.name()); - } - - // Validate upgraded shim apex - if (shim::IsShimApex(apex_file) && - !ApexFileRepository::GetInstance().IsPreInstalledApex(apex_file)) { - // This is not cheap for shim apex, but it is fine here since we have - // upgraded shim apex only during CTS tests. - Result<void> result = VerifyPackageBoot(apex_file); - if (!result.ok()) { - LOG(ERROR) << "Failed to validate shim apex: " << apex_file.GetPath(); - return result; - } + if (gBootstrap && !isBootstrapApex(apex_file)) { + return {}; } // See whether we think it's active, and do not allow to activate the same // version. Also detect whether this is the highest version. // We roll this into a single check. bool is_newest_version = true; + bool found_other_version = false; bool version_found_mounted = false; { uint64_t new_version = manifest.version(); bool version_found_active = false; gMountedApexes.ForallMountedApexes( manifest.name(), [&](const MountedApexData& data, bool latest) { - Result<ApexFile> other_apex = ApexFile::Open(data.full_path); - if (!other_apex.ok()) { + Result<ApexFile> otherApex = ApexFile::Open(data.full_path); + if (!otherApex.ok()) { return; } - if (static_cast<uint64_t>(other_apex->GetManifest().version()) == + found_other_version = true; + if (static_cast<uint64_t>(otherApex->GetManifest().version()) == new_version) { version_found_mounted = true; version_found_active = latest; } - if (static_cast<uint64_t>(other_apex->GetManifest().version()) > + if (static_cast<uint64_t>(otherApex->GetManifest().version()) > new_version) { is_newest_version = false; } }); - // If the package provides shared libraries to other APEXs, we need to - // activate all versions available (i.e. preloaded on /system/apex and - // available on /data/apex/active). The reason is that there might be some - // APEXs loaded from /system/apex that reference the libraries contained on - // the preloaded version of the apex providing shared libraries. - if (version_found_active && !manifest.providesharedapexlibs()) { + if (version_found_active) { LOG(DEBUG) << "Package " << manifest.name() << " with version " << manifest.version() << " already active"; return {}; } } - const std::string& mount_point = - apexd_private::GetPackageMountPoint(manifest); + const std::string& mountPoint = apexd_private::GetPackageMountPoint(manifest); if (!version_found_mounted) { - auto mount_status = MountPackage(apex_file, mount_point, device_name); - if (!mount_status.ok()) { - return mount_status; + auto mountStatus = MountPackage(apex_file, mountPoint); + if (!mountStatus.ok()) { + return mountStatus; } } - // For packages providing shared libraries, avoid creating a bindmount since - // there is no use for the /apex/<package_name> directory. However, mark the - // highest version as latest so that the latest version of the package can be - // properly reported to PackageManager. - if (manifest.providesharedapexlibs()) { - if (is_newest_version) { - gMountedApexes.SetLatest(manifest.name(), apex_file.GetPath()); - } - } else { - bool mounted_latest = false; - // Bind mount the latest version to /apex/<package_name>, unless the - // package provides shared libraries to other APEXs. - if (is_newest_version) { - const Result<void>& update_st = apexd_private::BindMount( - apexd_private::GetActiveMountPoint(manifest), mount_point); - mounted_latest = update_st.has_value(); - if (!update_st.ok()) { - return Error() << "Failed to update package " << manifest.name() - << " to version " << manifest.version() << " : " - << update_st.error(); - } - } - if (mounted_latest) { - gMountedApexes.SetLatest(manifest.name(), apex_file.GetPath()); + bool mounted_latest = false; + if (is_newest_version) { + const Result<void>& update_st = apexd_private::BindMount( + apexd_private::GetActiveMountPoint(manifest), mountPoint); + mounted_latest = update_st.has_value(); + if (!update_st.ok()) { + return Error() << "Failed to update package " << manifest.name() + << " to version " << manifest.version() << " : " + << update_st.error(); } } - - if (manifest.providesharedapexlibs()) { - const auto& handle_shared_libs_apex = - ActivateSharedLibsPackage(mount_point); - if (!handle_shared_libs_apex.ok()) { - return handle_shared_libs_apex; - } + if (mounted_latest) { + gMountedApexes.SetLatest(manifest.name(), apex_file.GetPath()); } LOG(DEBUG) << "Successfully activated " << apex_file.GetPath() @@ -1340,30 +1014,28 @@ Result<void> ActivatePackageImpl(const ApexFile& apex_file, return {}; } -Result<void> ActivatePackage(const std::string& full_path) { +Result<void> activatePackage(const std::string& full_path) { LOG(INFO) << "Trying to activate " << full_path; Result<ApexFile> apex_file = ApexFile::Open(full_path); if (!apex_file.ok()) { return apex_file.error(); } - return ActivatePackageImpl(*apex_file, - GetPackageId(apex_file->GetManifest())); + return activatePackageImpl(*apex_file); } -Result<void> DeactivatePackage(const std::string& full_path) { +Result<void> deactivatePackage(const std::string& full_path) { LOG(INFO) << "Trying to deactivate " << full_path; - Result<ApexFile> apex_file = ApexFile::Open(full_path); - if (!apex_file.ok()) { - return apex_file.error(); + Result<ApexFile> apexFile = ApexFile::Open(full_path); + if (!apexFile.ok()) { + return apexFile.error(); } - return UnmountPackage(*apex_file, /* allow_latest= */ true, - /* deferred= */ false); + return UnmountPackage(*apexFile, /* allow_latest= */ true); } -std::vector<ApexFile> GetActivePackages() { +std::vector<ApexFile> getActivePackages() { std::vector<ApexFile> ret; gMountedApexes.ForallMountedApexes( [&](const std::string&, const MountedApexData& data, bool latest) { @@ -1371,84 +1043,20 @@ std::vector<ApexFile> GetActivePackages() { return; } - Result<ApexFile> apex_file = ApexFile::Open(data.full_path); - if (!apex_file.ok()) { + Result<ApexFile> apexFile = ApexFile::Open(data.full_path); + if (!apexFile.ok()) { + // TODO: Fail? return; } - ret.emplace_back(std::move(*apex_file)); + ret.emplace_back(std::move(*apexFile)); }); return ret; } -std::vector<ApexFile> CalculateInactivePackages( - const std::vector<ApexFile>& active) { - std::vector<ApexFile> inactive = GetFactoryPackages(); - auto new_end = std::remove_if( - inactive.begin(), inactive.end(), [&active](const ApexFile& apex) { - return std::any_of(active.begin(), active.end(), - [&apex](const ApexFile& active_apex) { - return apex.GetPath() == active_apex.GetPath(); - }); - }); - inactive.erase(new_end, inactive.end()); - return std::move(inactive); -} - -Result<void> EmitApexInfoList(bool is_bootstrap) { - // on a non-updatable device, we don't have APEX database to emit - if (!android::sysprop::ApexProperties::updatable().value_or(false)) { - return {}; - } - - // Apexd runs both in "bootstrap" and "default" mount namespace. - // To expose /apex/apex-info-list.xml separately in each mount namespaces, - // we write /apex/.<namespace>-apex-info-list .xml file first and then - // bind mount it to the canonical file (/apex/apex-info-list.xml). - const std::string file_name = - fmt::format("{}/.{}-{}", kApexRoot, - is_bootstrap ? "bootstrap" : "default", kApexInfoList); - - unique_fd fd(TEMP_FAILURE_RETRY( - open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644))); - if (fd.get() == -1) { - return ErrnoErrorf("Can't open {}", file_name); - } - - const std::vector<ApexFile> active(GetActivePackages()); - - std::vector<ApexFile> inactive; - // we skip for non-activated built-in apexes in bootstrap mode - // in order to avoid boottime increase - if (!is_bootstrap) { - inactive = CalculateInactivePackages(active); - } - - std::stringstream xml; - CollectApexInfoList(xml, active, inactive); - - if (!android::base::WriteStringToFd(xml.str(), fd)) { - return ErrnoErrorf("Can't write to {}", file_name); - } - - fd.reset(); - - const std::string mount_point = - fmt::format("{}/{}", kApexRoot, kApexInfoList); - if (access(mount_point.c_str(), F_OK) != 0) { - close(open(mount_point.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, - 0644)); - } - if (mount(file_name.c_str(), mount_point.c_str(), nullptr, MS_BIND, - nullptr) == -1) { - return ErrnoErrorf("Can't bind mount {} to {}", file_name, mount_point); - } - return RestoreconPath(file_name); -} - namespace { std::unordered_map<std::string, uint64_t> GetActivePackagesMap() { - std::vector<ApexFile> active_packages = GetActivePackages(); + std::vector<ApexFile> active_packages = getActivePackages(); std::unordered_map<std::string, uint64_t> ret; for (const auto& package : active_packages) { const ApexManifest& manifest = package.GetManifest(); @@ -1459,50 +1067,28 @@ std::unordered_map<std::string, uint64_t> GetActivePackagesMap() { } // namespace -std::vector<ApexFile> GetFactoryPackages() { +std::vector<ApexFile> getFactoryPackages() { std::vector<ApexFile> ret; - - // Decompressed APEX is considered factory package - std::vector<std::string> decompressed_pkg_names; - auto active_pkgs = GetActivePackages(); - for (ApexFile& apex : active_pkgs) { - if (ApexFileRepository::GetInstance().IsDecompressedApex(apex)) { - decompressed_pkg_names.push_back(apex.GetManifest().name()); - ret.emplace_back(std::move(apex)); - } - } - - for (const auto& dir : gConfig->apex_built_in_dirs) { - auto all_apex_files = FindFilesBySuffix( - dir, {kApexPackageSuffix, kCompressedApexPackageSuffix}); - if (!all_apex_files.ok()) { - LOG(ERROR) << all_apex_files.error(); + for (const auto& dir : kApexPackageBuiltinDirs) { + auto apex_files = FindApexFilesByName(dir); + if (!apex_files.ok()) { + LOG(ERROR) << apex_files.error(); continue; } - - for (const std::string& path : *all_apex_files) { + for (const std::string& path : *apex_files) { Result<ApexFile> apex_file = ApexFile::Open(path); if (!apex_file.ok()) { LOG(ERROR) << apex_file.error(); - continue; - } - // Ignore compressed APEX if it has been decompressed already - if (apex_file->IsCompressed() && - std::find(decompressed_pkg_names.begin(), - decompressed_pkg_names.end(), - apex_file->GetManifest().name()) != - decompressed_pkg_names.end()) { - continue; + } else { + ret.emplace_back(std::move(*apex_file)); } - - ret.emplace_back(std::move(*apex_file)); } } return ret; } -Result<ApexFile> GetActivePackage(const std::string& packageName) { - std::vector<ApexFile> packages = GetActivePackages(); +Result<ApexFile> getActivePackage(const std::string& packageName) { + std::vector<ApexFile> packages = getActivePackages(); for (ApexFile& apex : packages) { if (apex.GetManifest().name() == packageName) { return std::move(apex); @@ -1517,7 +1103,7 @@ Result<ApexFile> GetActivePackage(const std::string& packageName) { * * Returns without error only if session was successfully aborted. **/ -Result<void> AbortStagedSession(int session_id) { +Result<void> abortStagedSession(int session_id) { auto session = ApexSession::GetSession(session_id); if (!session.ok()) { return Error() << "No session found with id " << session_id; @@ -1532,29 +1118,23 @@ Result<void> AbortStagedSession(int session_id) { } } +// TODO(ioffe): cleanup activation logic to avoid unnecessary scanning. namespace { -// TODO(b/179497746): Avoid scanning apex directly here -// Only used in OnBootstrap. Should we remove this function? -Result<std::vector<ApexFile>> ScanApexFiles(const char* apex_package_dir, - bool include_compressed = false) { +Result<std::vector<ApexFile>> ScanApexFiles(const char* apex_package_dir) { LOG(INFO) << "Scanning " << apex_package_dir << " looking for APEX packages."; if (access(apex_package_dir, F_OK) != 0 && errno == ENOENT) { LOG(INFO) << "... does not exist. Skipping"; return {}; } - std::vector<std::string> suffix_list = {kApexPackageSuffix}; - if (include_compressed) { - suffix_list.push_back(kCompressedApexPackageSuffix); - } - Result<std::vector<std::string>> scan = - FindFilesBySuffix(apex_package_dir, suffix_list); + Result<std::vector<std::string>> scan = FindApexFilesByName(apex_package_dir); if (!scan.ok()) { return Error() << "Failed to scan " << apex_package_dir << " : " << scan.error(); } std::vector<ApexFile> ret; for (const auto& name : *scan) { + LOG(INFO) << "Found " << name; Result<ApexFile> apex_file = ApexFile::Open(name); if (!apex_file.ok()) { LOG(ERROR) << "Failed to scan " << name << " : " << apex_file.error(); @@ -1565,152 +1145,64 @@ Result<std::vector<ApexFile>> ScanApexFiles(const char* apex_package_dir, return ret; } -std::vector<Result<void>> ActivateApexWorker( - bool is_ota_chroot, std::queue<const ApexFile*>& apex_queue, - std::mutex& mutex) { - std::vector<Result<void>> ret; - - while (true) { - const ApexFile* apex; - { - std::lock_guard lock(mutex); - if (apex_queue.empty()) break; - apex = apex_queue.front(); - apex_queue.pop(); +Result<void> ActivateApexPackages(const std::vector<ApexFile>& apexes) { + const auto& packages_with_code = GetActivePackagesMap(); + size_t failed_cnt = 0; + size_t skipped_cnt = 0; + size_t activated_cnt = 0; + for (const auto& apex : apexes) { + uint64_t new_version = static_cast<uint64_t>(apex.GetManifest().version()); + const auto& it = packages_with_code.find(apex.GetManifest().name()); + if (it != packages_with_code.end() && it->second >= new_version) { + LOG(INFO) << "Skipping activation of " << apex.GetPath() + << " same package with higher version " << it->second + << " is already active"; + skipped_cnt++; + continue; } - std::string device_name = GetPackageId(apex->GetManifest()); - if (is_ota_chroot) { - device_name += ".chroot"; - } - if (auto res = ActivatePackageImpl(*apex, device_name); !res.ok()) { - ret.push_back(Error() << "Failed to activate " << apex->GetPath() << " : " - << res.error()); + if (auto res = activatePackageImpl(apex); !res.ok()) { + LOG(ERROR) << "Failed to activate " << apex.GetPath() << " : " + << res.error(); + failed_cnt++; } else { - ret.push_back({}); - } - } - - return ret; -} - -Result<void> ActivateApexPackages(const std::vector<ApexFileRef>& apexes, - bool is_ota_chroot) { - std::queue<const ApexFile*> apex_queue; - std::mutex apex_queue_mutex; - - for (const ApexFile& apex : apexes) { - apex_queue.emplace(&apex); - } - - // Creates threads as many as half number of cores for the performance. - size_t worker_num = std::max(get_nprocs_conf() >> 1, 1); - worker_num = std::min(apex_queue.size(), worker_num); - - // On -eng builds there might be two different pre-installed art apexes. - // Attempting to activate them in parallel will result in UB (e.g. - // apexd-bootstrap might crash). In order to avoid this, for the time being on - // -eng builds activate apexes sequentially. - // TODO(b/176497601): remove this. - if (GetProperty("ro.build.type", "") == "eng") { - worker_num = 1; - } - - std::vector<std::future<std::vector<Result<void>>>> futures; - futures.reserve(worker_num); - for (size_t i = 0; i < worker_num; i++) { - futures.push_back(std::async(std::launch::async, ActivateApexWorker, - std::ref(is_ota_chroot), std::ref(apex_queue), - std::ref(apex_queue_mutex))); - } - - size_t activated_cnt = 0; - size_t failed_cnt = 0; - std::string error_message; - for (size_t i = 0; i < futures.size(); i++) { - for (const auto& res : futures[i].get()) { - if (res.ok()) { - ++activated_cnt; - } else { - ++failed_cnt; - LOG(ERROR) << res.error(); - if (failed_cnt == 1) { - error_message = res.error().message(); - } - } + activated_cnt++; } } - if (failed_cnt > 0) { - return Error() << "Failed to activate " << failed_cnt - << " APEX packages. One of the errors: " << error_message; + return Error() << "Failed to activate " << failed_cnt << " APEX packages"; } - LOG(INFO) << "Activated " << activated_cnt << " packages."; + LOG(INFO) << "Activated " << activated_cnt + << " packages. Skipped: " << skipped_cnt; return {}; } -// A fallback function in case some of the apexes failed to activate. For all -// such apexes that were coming from /data partition we will attempt to activate -// their corresponding pre-installed copies. -Result<void> ActivateMissingApexes(const std::vector<ApexFileRef>& apexes, - bool is_ota_chroot) { - LOG(INFO) << "Trying to activate pre-installed versions of missing apexes"; - const auto& file_repository = ApexFileRepository::GetInstance(); - const auto& activated_apexes = GetActivePackagesMap(); - std::vector<ApexFileRef> fallback_apexes; - for (const auto& apex_ref : apexes) { - const auto& apex = apex_ref.get(); - if (apex.GetManifest().providesharedapexlibs()) { - // We must mount both versions of sharedlibs apex anyway. Not much we can - // do here. - continue; - } - if (file_repository.IsPreInstalledApex(apex)) { - // We tried to activate pre-installed apex in the first place. No need to - // try again. - continue; - } - const std::string& name = apex.GetManifest().name(); - if (activated_apexes.find(name) == activated_apexes.end()) { - fallback_apexes.push_back(file_repository.GetPreInstalledApex(name)); - } - } - - // Process compressed APEX, if any - std::vector<ApexFileRef> compressed_apex; - for (auto it = fallback_apexes.begin(); it != fallback_apexes.end();) { - if (it->get().IsCompressed()) { - compressed_apex.emplace_back(*it); - it = fallback_apexes.erase(it); - } else { - it++; - } - } - std::vector<ApexFile> decompressed_apex; - if (!compressed_apex.empty()) { - decompressed_apex = - ProcessCompressedApex(compressed_apex, /* is_ota_chroot= */ false); - for (const ApexFile& apex_file : decompressed_apex) { - fallback_apexes.emplace_back(std::cref(apex_file)); - } - } - return ActivateApexPackages(fallback_apexes, is_ota_chroot); +bool ShouldActivateApexOnData(const ApexFile& apex) { + return HasPreInstalledVersion(apex.GetManifest().name()); } } // namespace +Result<void> scanPackagesDirAndActivate(const char* apex_package_dir) { + auto apexes = ScanApexFiles(apex_package_dir); + if (!apexes) { + return apexes.error(); + } + return ActivateApexPackages(*apexes); +} + /** * Snapshots data from base_dir/apexdata/<apex name> to * base_dir/apexrollback/<rollback id>/<apex name>. */ -Result<void> SnapshotDataDirectory(const std::string& base_dir, +Result<void> snapshotDataDirectory(const std::string& base_dir, const int rollback_id, const std::string& apex_name, bool pre_restore = false) { auto rollback_path = StringPrintf("%s/%s/%d%s", base_dir.c_str(), kApexSnapshotSubDir, rollback_id, pre_restore ? kPreRestoreSuffix : ""); - const Result<void> result = CreateDirIfNeeded(rollback_path, 0700); + const Result<void> result = createDirIfNeeded(rollback_path, 0700); if (!result.ok()) { return Error() << "Failed to create snapshot directory for rollback " << rollback_id << " : " << result.error(); @@ -1728,7 +1220,7 @@ Result<void> SnapshotDataDirectory(const std::string& base_dir, * to base_dir/apexdata/<apex name>. * Note the snapshot will be deleted after restoration succeeded. */ -Result<void> RestoreDataDirectory(const std::string& base_dir, +Result<void> restoreDataDirectory(const std::string& base_dir, const int rollback_id, const std::string& apex_name, bool pre_restore = false) { @@ -1752,13 +1244,13 @@ Result<void> RestoreDataDirectory(const std::string& base_dir, return {}; } -void SnapshotOrRestoreDeIfNeeded(const std::string& base_dir, +void snapshotOrRestoreDeIfNeeded(const std::string& base_dir, const ApexSession& session) { if (session.HasRollbackEnabled()) { for (const auto& apex_name : session.GetApexNames()) { Result<void> result = - SnapshotDataDirectory(base_dir, session.GetRollbackId(), apex_name); - if (!result.ok()) { + snapshotDataDirectory(base_dir, session.GetRollbackId(), apex_name); + if (!result) { LOG(ERROR) << "Snapshot failed for " << apex_name << ": " << result.error(); } @@ -1767,11 +1259,11 @@ void SnapshotOrRestoreDeIfNeeded(const std::string& base_dir, for (const auto& apex_name : session.GetApexNames()) { if (!gSupportsFsCheckpoints) { // Snapshot before restore so this rollback can be reverted. - SnapshotDataDirectory(base_dir, session.GetRollbackId(), apex_name, + snapshotDataDirectory(base_dir, session.GetRollbackId(), apex_name, true /* pre_restore */); } Result<void> result = - RestoreDataDirectory(base_dir, session.GetRollbackId(), apex_name); + restoreDataDirectory(base_dir, session.GetRollbackId(), apex_name); if (!result.ok()) { LOG(ERROR) << "Restore of data failed for " << apex_name << ": " << result.error(); @@ -1780,18 +1272,18 @@ void SnapshotOrRestoreDeIfNeeded(const std::string& base_dir, } } -void SnapshotOrRestoreDeSysData() { +void snapshotOrRestoreDeSysData() { auto sessions = ApexSession::GetSessionsInState(SessionState::ACTIVATED); for (const ApexSession& session : sessions) { - SnapshotOrRestoreDeIfNeeded(kDeSysDataDir, session); + snapshotOrRestoreDeIfNeeded(kDeSysDataDir, session); } } -int SnapshotOrRestoreDeUserData() { +int snapshotOrRestoreDeUserData() { auto user_dirs = GetDeUserDirs(); - if (!user_dirs.ok()) { + if (!user_dirs) { LOG(ERROR) << "Error reading dirs " << user_dirs.error(); return 1; } @@ -1800,69 +1292,93 @@ int SnapshotOrRestoreDeUserData() { for (const ApexSession& session : sessions) { for (const auto& user_dir : *user_dirs) { - SnapshotOrRestoreDeIfNeeded(user_dir, session); + snapshotOrRestoreDeIfNeeded(user_dir, session); } } return 0; } -Result<void> SnapshotCeData(const int user_id, const int rollback_id, - const std::string& apex_name) { +Result<ino_t> snapshotCeData(const int user_id, const int rollback_id, + const std::string& apex_name) { auto base_dir = StringPrintf("%s/%d", kCeDataDir, user_id); - return SnapshotDataDirectory(base_dir, rollback_id, apex_name); + Result<void> result = snapshotDataDirectory(base_dir, rollback_id, apex_name); + if (!result) { + return result.error(); + } + auto ce_snapshot_path = + StringPrintf("%s/%s/%d/%s", base_dir.c_str(), kApexSnapshotSubDir, + rollback_id, apex_name.c_str()); + return get_path_inode(ce_snapshot_path); } -Result<void> RestoreCeData(const int user_id, const int rollback_id, +Result<void> restoreCeData(const int user_id, const int rollback_id, const std::string& apex_name) { auto base_dir = StringPrintf("%s/%d", kCeDataDir, user_id); - return RestoreDataDirectory(base_dir, rollback_id, apex_name); + return restoreDataDirectory(base_dir, rollback_id, apex_name); } // Migrates sessions directory from /data/apex/sessions to // /metadata/apex/sessions, if necessary. -Result<void> MigrateSessionsDirIfNeeded() { - return ApexSession::MigrateToMetadataSessionsDir(); +Result<void> migrateSessionsDirIfNeeded() { + namespace fs = std::filesystem; + auto from_path = std::string(kApexDataDir) + "/sessions"; + auto exists = PathExists(from_path); + if (!exists) { + return Error() << "Failed to access " << from_path << ": " + << exists.error(); + } + if (!*exists) { + LOG(DEBUG) << from_path << " does not exist. Nothing to migrate."; + return {}; + } + auto to_path = kApexSessionsDir; + std::error_code error_code; + fs::copy(from_path, to_path, fs::copy_options::recursive, error_code); + if (error_code) { + return Error() << "Failed to copy old sessions directory" + << error_code.message(); + } + fs::remove_all(from_path, error_code); + if (error_code) { + return Error() << "Failed to delete old sessions directory " + << error_code.message(); + } + return {}; } -Result<void> DestroySnapshots(const std::string& base_dir, +Result<void> destroySnapshots(const std::string& base_dir, const int rollback_id) { auto path = StringPrintf("%s/%s/%d", base_dir.c_str(), kApexSnapshotSubDir, rollback_id); return DeleteDir(path); } -Result<void> DestroyDeSnapshots(const int rollback_id) { - DestroySnapshots(kDeSysDataDir, rollback_id); +Result<void> destroyDeSnapshots(const int rollback_id) { + destroySnapshots(kDeSysDataDir, rollback_id); auto user_dirs = GetDeUserDirs(); - if (!user_dirs.ok()) { + if (!user_dirs) { return Error() << "Error reading user dirs " << user_dirs.error(); } for (const auto& user_dir : *user_dirs) { - DestroySnapshots(user_dir, rollback_id); + destroySnapshots(user_dir, rollback_id); } return {}; } -Result<void> DestroyCeSnapshots(const int user_id, const int rollback_id) { - auto path = StringPrintf("%s/%d/%s/%d", kCeDataDir, user_id, - kApexSnapshotSubDir, rollback_id); - return DeleteDir(path); -} - /** * Deletes all credential-encrypted snapshots for the given user, except for * those listed in retain_rollback_ids. */ -Result<void> DestroyCeSnapshotsNotSpecified( +Result<void> destroyCeSnapshotsNotSpecified( int user_id, const std::vector<int>& retain_rollback_ids) { auto snapshot_root = StringPrintf("%s/%d/%s", kCeDataDir, user_id, kApexSnapshotSubDir); auto snapshot_dirs = GetSubdirs(snapshot_root); - if (!snapshot_dirs.ok()) { + if (!snapshot_dirs) { return Error() << "Error reading snapshot dirs " << snapshot_dirs.error(); } @@ -1874,7 +1390,7 @@ Result<void> DestroyCeSnapshotsNotSpecified( std::find(retain_rollback_ids.begin(), retain_rollback_ids.end(), snapshot_id) == retain_rollback_ids.end()) { Result<void> result = DeleteDir(snapshot_dir); - if (!result.ok()) { + if (!result) { return Error() << "Destroy CE snapshot failed for " << snapshot_dir << " : " << result.error(); } @@ -1883,16 +1399,16 @@ Result<void> DestroyCeSnapshotsNotSpecified( return {}; } -void RestorePreRestoreSnapshotsIfPresent(const std::string& base_dir, +void restorePreRestoreSnapshotsIfPresent(const std::string& base_dir, const ApexSession& session) { auto pre_restore_snapshot_path = StringPrintf("%s/%s/%d%s", base_dir.c_str(), kApexSnapshotSubDir, session.GetRollbackId(), kPreRestoreSuffix); - if (PathExists(pre_restore_snapshot_path).ok()) { + if (PathExists(pre_restore_snapshot_path)) { for (const auto& apex_name : session.GetApexNames()) { - Result<void> result = RestoreDataDirectory( + Result<void> result = restoreDataDirectory( base_dir, session.GetRollbackId(), apex_name, true /* pre_restore */); - if (!result.ok()) { + if (!result) { LOG(ERROR) << "Restore of pre-restore snapshot failed for " << apex_name << ": " << result.error(); } @@ -1900,77 +1416,71 @@ void RestorePreRestoreSnapshotsIfPresent(const std::string& base_dir, } } -void RestoreDePreRestoreSnapshotsIfPresent(const ApexSession& session) { - RestorePreRestoreSnapshotsIfPresent(kDeSysDataDir, session); +void restoreDePreRestoreSnapshotsIfPresent(const ApexSession& session) { + restorePreRestoreSnapshotsIfPresent(kDeSysDataDir, session); auto user_dirs = GetDeUserDirs(); - if (!user_dirs.ok()) { + if (!user_dirs) { LOG(ERROR) << "Error reading user dirs to restore pre-restore snapshots" << user_dirs.error(); } for (const auto& user_dir : *user_dirs) { - RestorePreRestoreSnapshotsIfPresent(user_dir, session); + restorePreRestoreSnapshotsIfPresent(user_dir, session); } } -void DeleteDePreRestoreSnapshots(const std::string& base_dir, +void deleteDePreRestoreSnapshots(const std::string& base_dir, const ApexSession& session) { auto pre_restore_snapshot_path = StringPrintf("%s/%s/%d%s", base_dir.c_str(), kApexSnapshotSubDir, session.GetRollbackId(), kPreRestoreSuffix); Result<void> result = DeleteDir(pre_restore_snapshot_path); - if (!result.ok()) { + if (!result) { LOG(ERROR) << "Deletion of pre-restore snapshot failed: " << result.error(); } } -void DeleteDePreRestoreSnapshots(const ApexSession& session) { - DeleteDePreRestoreSnapshots(kDeSysDataDir, session); +void deleteDePreRestoreSnapshots(const ApexSession& session) { + deleteDePreRestoreSnapshots(kDeSysDataDir, session); auto user_dirs = GetDeUserDirs(); - if (!user_dirs.ok()) { + if (!user_dirs) { LOG(ERROR) << "Error reading user dirs to delete pre-restore snapshots" << user_dirs.error(); } for (const auto& user_dir : *user_dirs) { - DeleteDePreRestoreSnapshots(user_dir, session); + deleteDePreRestoreSnapshots(user_dir, session); } } -void OnBootCompleted() { - ApexdLifecycle::GetInstance().MarkBootCompleted(); - BootCompletedCleanup(); -} - -// Returns true if any session gets staged -void ScanStagedSessionsDirAndStage() { - LOG(INFO) << "Scanning " << ApexSession::GetSessionsDir() +void scanStagedSessionsDirAndStage() { + LOG(INFO) << "Scanning " << kApexSessionsDir << " looking for sessions to be activated."; - auto sessions_to_activate = + auto sessionsToActivate = ApexSession::GetSessionsInState(SessionState::STAGED); if (gSupportsFsCheckpoints) { // A session that is in the ACTIVATED state should still be re-activated if // fs checkpointing is supported. In this case, a session may be in the // ACTIVATED state yet the data/apex/active directory may have been // reverted. The session should be reverted in this scenario. - auto activated_sessions = + auto activatedSessions = ApexSession::GetSessionsInState(SessionState::ACTIVATED); - sessions_to_activate.insert(sessions_to_activate.end(), - activated_sessions.begin(), - activated_sessions.end()); + sessionsToActivate.insert(sessionsToActivate.end(), + activatedSessions.begin(), + activatedSessions.end()); } - for (auto& session : sessions_to_activate) { - auto session_id = session.GetId(); + for (auto& session : sessionsToActivate) { + auto sessionId = session.GetId(); auto session_failed_fn = [&]() { - LOG(WARNING) << "Marking session " << session_id << " as failed."; + LOG(WARNING) << "Marking session " << sessionId << " as failed."; auto st = session.UpdateStateAndCommit(SessionState::ACTIVATION_FAILED); if (!st.ok()) { - LOG(WARNING) << "Failed to mark session " << session_id + LOG(WARNING) << "Failed to mark session " << sessionId << " as failed : " << st.error(); } }; @@ -1978,101 +1488,74 @@ void ScanStagedSessionsDirAndStage() { std::string build_fingerprint = GetProperty(kBuildFingerprintSysprop, ""); if (session.GetBuildFingerprint().compare(build_fingerprint) != 0) { - auto error_message = "APEX build fingerprint has changed"; - LOG(ERROR) << error_message; - session.SetErrorMessage(error_message); - continue; - } - - // If device supports fs-checkpoint, then apex session should only be - // installed when in checkpoint-mode. Otherwise, we will not be able to - // revert /data on error. - if (gSupportsFsCheckpoints && !gInFsCheckpointMode) { - auto error_message = - "Cannot install apex session if not in fs-checkpoint mode"; - LOG(ERROR) << error_message; - session.SetErrorMessage(error_message); + LOG(ERROR) << "APEX build fingerprint has changed"; continue; } - std::vector<std::string> dirs_to_scan; + std::vector<std::string> dirsToScan; if (session.GetChildSessionIds().empty()) { - dirs_to_scan.push_back(std::string(gConfig->staged_session_dir) + - "/session_" + std::to_string(session_id)); + dirsToScan.push_back(std::string(kStagedSessionsDir) + "/session_" + + std::to_string(sessionId)); } else { - for (auto child_session_id : session.GetChildSessionIds()) { - dirs_to_scan.push_back(std::string(gConfig->staged_session_dir) + - "/session_" + std::to_string(child_session_id)); + for (auto childSessionId : session.GetChildSessionIds()) { + dirsToScan.push_back(std::string(kStagedSessionsDir) + "/session_" + + std::to_string(childSessionId)); } } std::vector<std::string> apexes; - bool scan_successful = true; - for (const auto& dir_to_scan : dirs_to_scan) { - Result<std::vector<std::string>> scan = - FindFilesBySuffix(dir_to_scan, {kApexPackageSuffix}); + bool scanSuccessful = true; + for (const auto& dirToScan : dirsToScan) { + Result<std::vector<std::string>> scan = FindApexFilesByName(dirToScan); if (!scan.ok()) { LOG(WARNING) << scan.error(); - session.SetErrorMessage(scan.error().message()); - scan_successful = false; + scanSuccessful = false; break; } if (scan->size() > 1) { - std::string error_message = StringPrintf( - "More than one APEX package found in the same session directory %s " - ", skipping activation", - dir_to_scan.c_str()); - LOG(WARNING) << error_message; - session.SetErrorMessage(error_message); - scan_successful = false; + LOG(WARNING) << "More than one APEX package found in the same session " + << "directory " << dirToScan << ", skipping activation."; + scanSuccessful = false; break; } if (scan->empty()) { - std::string error_message = StringPrintf( - "No APEX packages found while scanning %s session id: %d.", - dir_to_scan.c_str(), session_id); - LOG(WARNING) << error_message; - session.SetErrorMessage(error_message); - scan_successful = false; + LOG(WARNING) << "No APEX packages found while scanning " << dirToScan + << " session id: " << sessionId << "."; + scanSuccessful = false; break; } apexes.push_back(std::move((*scan)[0])); } - if (!scan_successful) { + if (!scanSuccessful) { continue; } // Run postinstall, if necessary. - Result<void> postinstall_status = PostinstallPackages(apexes); + Result<void> postinstall_status = postinstallPackages(apexes); if (!postinstall_status.ok()) { - std::string error_message = - StringPrintf("Postinstall failed for session %d %s", session_id, - postinstall_status.error().message().c_str()); - LOG(ERROR) << error_message; - session.SetErrorMessage(error_message); + LOG(ERROR) << "Postinstall failed for session " + << std::to_string(sessionId) << ": " + << postinstall_status.error(); continue; } for (const auto& apex : apexes) { - // TODO(b/158470836): Avoid opening ApexFile repeatedly. + // TODO: Avoid opening ApexFile repeatedly. Result<ApexFile> apex_file = ApexFile::Open(apex); - if (!apex_file.ok()) { + if (!apex_file) { LOG(ERROR) << "Cannot open apex file during staging: " << apex; continue; } session.AddApexName(apex_file->GetManifest().name()); } - const Result<void> result = StagePackages(apexes); + const Result<void> result = stagePackages(apexes); if (!result.ok()) { - std::string error_message = StringPrintf( - "Activation failed for packages %s : %s", Join(apexes, ',').c_str(), - result.error().message().c_str()); - LOG(ERROR) << error_message; - session.SetErrorMessage(error_message); + LOG(ERROR) << "Activation failed for packages " << Join(apexes, ',') + << ": " << result.error(); continue; } @@ -2087,61 +1570,49 @@ void ScanStagedSessionsDirAndStage() { } } -Result<void> PreinstallPackages(const std::vector<std::string>& paths) { - Result<std::vector<ApexFile>> apex_files = OpenApexFiles(paths); - if (!apex_files.ok()) { - return apex_files.error(); +Result<void> preinstallPackages(const std::vector<std::string>& paths) { + if (paths.empty()) { + return Errorf("Empty set of inputs"); } - LOG(DEBUG) << "PreinstallPackages() for " << Join(paths, ','); - return PreinstallPackages(*apex_files); + LOG(DEBUG) << "preinstallPackages() for " << Join(paths, ','); + return HandlePackages<Result<void>>(paths, PreinstallPackages); } -Result<void> PostinstallPackages(const std::vector<std::string>& paths) { - Result<std::vector<ApexFile>> apex_files = OpenApexFiles(paths); - if (!apex_files.ok()) { - return apex_files.error(); +Result<void> postinstallPackages(const std::vector<std::string>& paths) { + if (paths.empty()) { + return Errorf("Empty set of inputs"); } - LOG(DEBUG) << "PostinstallPackages() for " << Join(paths, ','); - return PostinstallPackages(*apex_files); + LOG(DEBUG) << "postinstallPackages() for " << Join(paths, ','); + return HandlePackages<Result<void>>(paths, PostinstallPackages); } namespace { std::string StageDestPath(const ApexFile& apex_file) { - return StringPrintf("%s/%s%s", gConfig->active_apex_data_dir, + return StringPrintf("%s/%s%s", kActiveApexPackagesDataDir, GetPackageId(apex_file.GetManifest()).c_str(), kApexPackageSuffix); } } // namespace -Result<void> StagePackages(const std::vector<std::string>& tmp_paths) { - if (tmp_paths.empty()) { +Result<void> stagePackages(const std::vector<std::string>& tmpPaths) { + if (tmpPaths.empty()) { return Errorf("Empty set of inputs"); } - LOG(DEBUG) << "StagePackages() for " << Join(tmp_paths, ','); + LOG(DEBUG) << "stagePackages() for " << Join(tmpPaths, ','); // Note: this function is temporary. As such the code is not optimized, e.g., // it will open ApexFiles multiple times. // 1) Verify all packages. - Result<std::vector<ApexFile>> apex_files = OpenApexFiles(tmp_paths); - if (!apex_files.ok()) { - return apex_files.error(); - } - for (const ApexFile& apex_file : *apex_files) { - if (shim::IsShimApex(apex_file)) { - // Shim apex will be validated on every boot. No need to do it here. - continue; - } - Result<void> result = VerifyPackageBoot(apex_file); - if (!result.ok()) { - return result.error(); - } + auto verify_status = verifyPackages(tmpPaths, VerifyPackageBoot); + if (!verify_status.ok()) { + return verify_status.error(); } // Make sure that kActiveApexPackagesDataDir exists. auto create_dir_status = - CreateDirIfNeeded(std::string(gConfig->active_apex_data_dir), 0755); + createDirIfNeeded(std::string(kActiveApexPackagesDataDir), 0755); if (!create_dir_status.ok()) { return create_dir_status.error(); } @@ -2166,12 +1637,16 @@ Result<void> StagePackages(const std::vector<std::string>& tmp_paths) { auto scope_guard = android::base::make_scope_guard(deleter); std::unordered_set<std::string> staged_packages; - for (const ApexFile& apex_file : *apex_files) { + for (const std::string& path : tmpPaths) { + Result<ApexFile> apex_file = ApexFile::Open(path); + if (!apex_file.ok()) { + return apex_file.error(); + } // First promote new hashtree file to the one that will be used when // mounting apex. - std::string new_hashtree_file = GetHashTreeFileName(apex_file, + std::string new_hashtree_file = GetHashTreeFileName(*apex_file, /* is_new = */ true); - std::string old_hashtree_file = GetHashTreeFileName(apex_file, + std::string old_hashtree_file = GetHashTreeFileName(*apex_file, /* is_new = */ false); if (access(new_hashtree_file.c_str(), F_OK) == 0) { if (TEMP_FAILURE_RETRY(rename(new_hashtree_file.c_str(), @@ -2182,7 +1657,7 @@ Result<void> StagePackages(const std::vector<std::string>& tmp_paths) { changed_hashtree_files.emplace_back(std::move(old_hashtree_file)); } // And only then move apex to /data/apex/active. - std::string dest_path = StageDestPath(apex_file); + std::string dest_path = StageDestPath(*apex_file); if (access(dest_path.c_str(), F_OK) == 0) { LOG(DEBUG) << dest_path << " already exists. Deleting"; if (TEMP_FAILURE_RETRY(unlink(dest_path.c_str())) != 0) { @@ -2190,14 +1665,15 @@ Result<void> StagePackages(const std::vector<std::string>& tmp_paths) { } } - if (link(apex_file.GetPath().c_str(), dest_path.c_str()) != 0) { - return ErrnoError() << "Unable to link " << apex_file.GetPath() << " to " + if (link(apex_file->GetPath().c_str(), dest_path.c_str()) != 0) { + // TODO: Get correct binder error status. + return ErrnoError() << "Unable to link " << apex_file->GetPath() << " to " << dest_path; } staged_files.insert(dest_path); - staged_packages.insert(apex_file.GetManifest().name()); + staged_packages.insert(apex_file->GetManifest().name()); - LOG(DEBUG) << "Success linking " << apex_file.GetPath() << " to " + LOG(DEBUG) << "Success linking " << apex_file->GetPath() << " to " << dest_path; } @@ -2206,19 +1682,18 @@ Result<void> StagePackages(const std::vector<std::string>& tmp_paths) { return RemovePreviouslyActiveApexFiles(staged_packages, staged_files); } -Result<void> UnstagePackages(const std::vector<std::string>& paths) { +Result<void> unstagePackages(const std::vector<std::string>& paths) { if (paths.empty()) { return Errorf("Empty set of inputs"); } - LOG(DEBUG) << "UnstagePackages() for " << Join(paths, ','); + LOG(DEBUG) << "unstagePackages() for " << Join(paths, ','); + + // TODO: to make unstage safer, we can copy to be unstaged packages to a + // temporary folder and restore state from it in case unstagePackages fails. for (const std::string& path : paths) { - auto apex = ApexFile::Open(path); - if (!apex.ok()) { - return apex.error(); - } - if (ApexFileRepository::GetInstance().IsPreInstalledApex(*apex)) { - return Error() << "Can't uninstall pre-installed apex " << path; + if (access(path.c_str(), F_OK) != 0) { + return ErrnoError() << "Can't access " << path; } } @@ -2240,57 +1715,59 @@ Result<void> UnstagePackages(const std::vector<std::string>& paths) { * Also, we need to put staged sessions in /data/apex/sessions in REVERTED state * so that they do not get activated on next reboot. */ -Result<void> RevertActiveSessions(const std::string& crashing_native_process, - const std::string& error_message) { +Result<void> revertActiveSessions(const std::string& crashing_native_process) { // First check whenever there is anything to revert. If there is none, then // fail. This prevents apexd from boot looping a device in case a native // process is crashing and there are no apex updates. - auto active_sessions = ApexSession::GetActiveSessions(); - if (active_sessions.empty()) { + auto activeSessions = ApexSession::GetActiveSessions(); + if (activeSessions.empty()) { return Error() << "Revert requested, when there are no active sessions."; } - for (auto& session : active_sessions) { + for (auto& session : activeSessions) { if (!crashing_native_process.empty()) { session.SetCrashingNativeProcess(crashing_native_process); } - if (!error_message.empty()) { - session.SetErrorMessage(error_message); - } auto status = session.UpdateStateAndCommit(SessionState::REVERT_IN_PROGRESS); - if (!status.ok()) { + if (!status) { + // TODO: should we continue with a revert? return Error() << "Revert of session " << session << " failed : " << status.error(); } } + if (!gInFsCheckpointMode) { + // SafetyNet logging for b/19393765 + android_errorWriteLog(0x534e4554, "193932765"); + } + if (!gSupportsFsCheckpoints) { - auto restore_status = RestoreActivePackages(); - if (!restore_status.ok()) { - for (auto& session : active_sessions) { + auto restoreStatus = RestoreActivePackages(); + if (!restoreStatus.ok()) { + for (auto& session : activeSessions) { auto st = session.UpdateStateAndCommit(SessionState::REVERT_FAILED); LOG(DEBUG) << "Marking " << session << " as failed to revert"; - if (!st.ok()) { + if (!st) { LOG(WARNING) << "Failed to mark session " << session << " as failed to revert : " << st.error(); } } - return restore_status; + return restoreStatus; } } else { LOG(INFO) << "Not restoring active packages in checkpoint mode."; } - for (auto& session : active_sessions) { + for (auto& session : activeSessions) { if (!gSupportsFsCheckpoints && session.IsRollback()) { // If snapshots have already been restored, undo that by restoring the // pre-restore snapshot. - RestoreDePreRestoreSnapshotsIfPresent(session); + restoreDePreRestoreSnapshotsIfPresent(session); } auto status = session.UpdateStateAndCommit(SessionState::REVERTED); - if (!status.ok()) { + if (!status) { LOG(WARNING) << "Failed to mark session " << session << " as reverted : " << status.error(); } @@ -2299,10 +1776,9 @@ Result<void> RevertActiveSessions(const std::string& crashing_native_process, return {}; } -Result<void> RevertActiveSessionsAndReboot( - const std::string& crashing_native_process, - const std::string& error_message) { - auto status = RevertActiveSessions(crashing_native_process, error_message); +Result<void> revertActiveSessionsAndReboot( + const std::string& crashing_native_process) { + auto status = revertActiveSessions(crashing_native_process); if (!status.ok()) { return status; } @@ -2318,101 +1794,49 @@ Result<void> RevertActiveSessionsAndReboot( return {}; } -Result<void> CreateSharedLibsApexDir() { - // Creates /apex/sharedlibs/lib{,64} for SharedLibs APEXes. - std::string shared_libs_sub_dir = - StringPrintf("%s/%s", kApexRoot, kApexSharedLibsSubDir); - auto dir_exists = PathExists(shared_libs_sub_dir); - if (!dir_exists.ok() || !*dir_exists) { - std::error_code error_code; - std::filesystem::create_directory(shared_libs_sub_dir, error_code); - if (error_code) { - return Error() << "Failed to create directory " << shared_libs_sub_dir - << ": " << error_code.message(); - } - } - for (const auto& lib_path : {"lib", "lib64"}) { - std::string apex_lib_path = - StringPrintf("%s/%s", shared_libs_sub_dir.c_str(), lib_path); - auto lib_dir_exists = PathExists(apex_lib_path); - if (!lib_dir_exists.ok() || !*lib_dir_exists) { - std::error_code error_code; - std::filesystem::create_directory(apex_lib_path, error_code); - if (error_code) { - return Error() << "Failed to create directory " << apex_lib_path << ": " - << error_code.message(); - } - } - } +int onBootstrap() { + gBootstrap = true; - return {}; -} - -int OnBootstrap() { - auto time_started = boot_clock::now(); - Result<void> pre_allocate = PreAllocateLoopDevices(); - if (!pre_allocate.ok()) { + Result<void> preAllocate = preAllocateLoopDevices(); + if (!preAllocate.ok()) { LOG(ERROR) << "Failed to pre-allocate loop devices : " - << pre_allocate.error(); + << preAllocate.error(); } - ApexFileRepository& instance = ApexFileRepository::GetInstance(); - static const std::vector<std::string> kBootstrapApexDirs{ + std::vector<std::string> bootstrap_apex_dirs{ kApexPackageSystemDir, kApexPackageSystemExtDir, kApexPackageVendorDir}; - Result<void> status = instance.AddPreInstalledApex(kBootstrapApexDirs); + Result<void> status = collectPreinstalledData(bootstrap_apex_dirs); if (!status.ok()) { LOG(ERROR) << "Failed to collect APEX keys : " << status.error(); return 1; } - // Create directories for APEX shared libraries. - auto sharedlibs_apex_dir = CreateSharedLibsApexDir(); - if (!sharedlibs_apex_dir.ok()) { - LOG(ERROR) << sharedlibs_apex_dir.error(); - return 1; - } - - // Find all bootstrap apexes - std::vector<ApexFile> bootstrap_apexes; - for (const auto& dir : kBootstrapApexDirs) { - auto scan = ScanApexFiles(dir.c_str()); - if (!scan.ok()) { + // Activate built-in APEXes for processes launched before /data is mounted. + for (const auto& dir : bootstrap_apex_dirs) { + auto scan_status = ScanApexFiles(dir.c_str()); + if (!scan_status.ok()) { LOG(ERROR) << "Failed to scan APEX files in " << dir << " : " - << scan.error(); + << scan_status.error(); + return 1; + } + if (auto ret = ActivateApexPackages(*scan_status); !ret.ok()) { + LOG(ERROR) << "Failed to activate APEX files in " << dir << " : " + << ret.error(); return 1; } - std::copy_if(std::make_move_iterator(scan->begin()), - std::make_move_iterator(scan->end()), - std::back_inserter(bootstrap_apexes), IsBootstrapApex); - } - - // Now activate bootstrap apexes. - std::vector<ApexFileRef> bootstrap_apexes_ref; - std::transform(bootstrap_apexes.begin(), bootstrap_apexes.end(), - std::back_inserter(bootstrap_apexes_ref), - [](const auto& x) { return std::cref(x); }); - auto ret = ActivateApexPackages(bootstrap_apexes_ref, - /* is_ota_chroot= */ false); - if (!ret.ok()) { - LOG(ERROR) << "Failed to activate bootstrap apex files : " << ret.error(); - return 1; } - - OnAllPackagesActivated(/*is_bootstrap=*/true); - auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( - boot_clock::now() - time_started).count(); - LOG(INFO) << "OnBootstrap done, duration=" << time_elapsed; + LOG(INFO) << "Bootstrapping done"; return 0; } -Result<void> RemountApexFile(const std::string& path) { - if (auto ret = DeactivatePackage(path); !ret.ok()) { +Result<void> remountApexFile(const std::string& path) { + if (auto ret = deactivatePackage(path); !ret.ok()) { return ret; } - return ActivatePackage(path); + return activatePackage(path); } -void InitializeVold(CheckpointInterface* checkpoint_service) { +void initializeVold(CheckpointInterface* checkpoint_service) { if (checkpoint_service != nullptr) { gVoldService = checkpoint_service; Result<bool> supports_fs_checkpoints = @@ -2435,327 +1859,21 @@ void InitializeVold(CheckpointInterface* checkpoint_service) { } } -void Initialize(CheckpointInterface* checkpoint_service) { - InitializeVold(checkpoint_service); - ApexFileRepository& instance = ApexFileRepository::GetInstance(); - Result<void> status = instance.AddPreInstalledApex(kApexPackageBuiltinDirs); +void initialize(CheckpointInterface* checkpoint_service) { + initializeVold(checkpoint_service); + Result<void> status = collectPreinstalledData(kApexPackageBuiltinDirs); if (!status.ok()) { - LOG(ERROR) << "Failed to collect pre-installed APEX files : " - << status.error(); - return; - } - gMountedApexes.PopulateFromMounts(gConfig->active_apex_data_dir, - gConfig->decompression_dir, - gConfig->apex_hash_tree_dir); -} - -// Note: Pre-installed apex are initialized in Initialize(CheckpointInterface*) -// TODO(b/172911822): Consolidate this with Initialize() when -// ApexFileRepository can act as cache and re-scanning is not expensive -void InitializeDataApex() { - ApexFileRepository& instance = ApexFileRepository::GetInstance(); - Result<void> status = instance.AddDataApex(kActiveApexPackagesDataDir); - if (!status.ok()) { - LOG(ERROR) << "Failed to collect data APEX files : " << status.error(); + LOG(ERROR) << "Failed to collect APEX keys : " << status.error(); return; } -} - -/** - * For every package X, there can be at most two APEX, pre-installed vs - * installed on data. We usually select only one of these APEX for each package - * based on the following conditions: - * - Package X must be pre-installed on one of the built-in directories. - * - If there are multiple APEX, we select the one with highest version. - * - If there are multiple with same version, we give priority to APEX on - * /data partition. - * - * Typically, only one APEX is activated for each package, but APEX that provide - * shared libs are exceptions. We have to activate both APEX for them. - * - * @param all_apex all the APEX grouped by their package name - * @return list of ApexFile that needs to be activated - */ -std::vector<ApexFileRef> SelectApexForActivation( - const std::unordered_map<std::string, std::vector<ApexFileRef>>& all_apex, - const ApexFileRepository& instance) { - LOG(INFO) << "Selecting APEX for activation"; - std::vector<ApexFileRef> activation_list; - // For every package X, select which APEX to activate - for (auto& apex_it : all_apex) { - const std::string& package_name = apex_it.first; - const std::vector<ApexFileRef>& apex_files = apex_it.second; - - if (apex_files.size() > 2 || apex_files.size() == 0) { - LOG(FATAL) << "Unexpectedly found more than two versions or none for " - "APEX package " - << package_name; - continue; - } - // The package must have a pre-installed version before we consider it for - // activation - if (!instance.HasPreInstalledVersion(package_name)) { - LOG(INFO) << "Package " << package_name << " is not pre-installed"; - continue; - } - - if (apex_files.size() == 1) { - LOG(DEBUG) << "Selecting the only APEX: " << package_name << " " - << apex_files[0].get().GetPath(); - activation_list.emplace_back(apex_files[0]); - continue; - } - - // TODO(b/179497746): Now that we are dealing with list of reference, this - // selection process can be simplified by sorting the vector. - - // Given an APEX A and the version of the other APEX B, should we activate - // it? - auto select_apex = [&instance, &activation_list]( - const ApexFileRef& a_ref, - const int version_b) mutable { - const ApexFile& a = a_ref.get(); - // If A has higher version than B, then it should be activated - const bool higher_version = a.GetManifest().version() > version_b; - // If A has same version as B, then data version should get activated - const bool same_version_priority_to_data = - a.GetManifest().version() == version_b && - !instance.IsPreInstalledApex(a); - - // APEX that provides shared library are special: - // - if preinstalled version is lower than data version, both versions - // are activated. - // - if preinstalled version is equal to data version, data version only - // is activated. - // - if preinstalled version is higher than data version, preinstalled - // version only is activated. - const bool provides_shared_apex_libs = - a.GetManifest().providesharedapexlibs(); - bool activate = false; - if (provides_shared_apex_libs) { - // preinstalled version gets activated in all cases except when same - // version as data. - if (instance.IsPreInstalledApex(a) && - (a.GetManifest().version() != version_b)) { - LOG(DEBUG) << "Activating preinstalled shared libs APEX: " - << a.GetManifest().name() << " " << a.GetPath(); - activate = true; - } - // data version gets activated in all cases except when its version - // is lower than preinstalled version. - if (!instance.IsPreInstalledApex(a) && - (a.GetManifest().version() >= version_b)) { - LOG(DEBUG) << "Activating shared libs APEX: " - << a.GetManifest().name() << " " << a.GetPath(); - activate = true; - } - } else if (higher_version || same_version_priority_to_data) { - LOG(DEBUG) << "Selecting between two APEX: " << a.GetManifest().name() - << " " << a.GetPath(); - activate = true; - } - if (activate) { - activation_list.emplace_back(a_ref); - } - }; - const int version_0 = apex_files[0].get().GetManifest().version(); - const int version_1 = apex_files[1].get().GetManifest().version(); - select_apex(apex_files[0].get(), version_1); - select_apex(apex_files[1].get(), version_0); - } - return activation_list; + gMountedApexes.PopulateFromMounts(); } -namespace { - -Result<ApexFile> OpenAndValidateDecompressedApex(const ApexFile& capex, - const std::string& apex_path) { - auto apex = ApexFile::Open(apex_path); - if (!apex.ok()) { - return Error() << "Failed to open decompressed APEX: " << apex.error(); - } - auto result = ValidateDecompressedApex(capex, *apex); - if (!result.ok()) { - return result.error(); - } - return std::move(*apex); -} - -// Process a single compressed APEX. Returns the decompressed APEX if -// successful. -Result<ApexFile> ProcessCompressedApex(const ApexFile& capex, - bool is_ota_chroot) { - LOG(INFO) << "Processing compressed APEX " << capex.GetPath(); - const auto decompressed_apex_path = - StringPrintf("%s/%s%s", gConfig->decompression_dir, - GetPackageId(capex.GetManifest()).c_str(), - kDecompressedApexPackageSuffix); - // Check if decompressed APEX already exist - auto decompressed_path_exists = PathExists(decompressed_apex_path); - if (decompressed_path_exists.ok() && *decompressed_path_exists) { - // Check if existing decompressed APEX is valid - auto result = - OpenAndValidateDecompressedApex(capex, decompressed_apex_path); - if (result.ok()) { - LOG(INFO) << "Skipping decompression for " << capex.GetPath(); - return result; - } - // Do not delete existing decompressed APEX when is_ota_chroot is true - if (!is_ota_chroot) { - // Existing decompressed APEX is not valid. We will have to redecompress - LOG(WARNING) << "Existing decompressed APEX is invalid: " - << result.error(); - RemoveFileIfExists(decompressed_apex_path); - } - } - - // We can also reuse existing OTA APEX, depending on situation - auto ota_apex_path = StringPrintf("%s/%s%s", gConfig->decompression_dir, - GetPackageId(capex.GetManifest()).c_str(), - kOtaApexPackageSuffix); - auto ota_path_exists = PathExists(ota_apex_path); - if (ota_path_exists.ok() && *ota_path_exists) { - if (is_ota_chroot) { - // During ota_chroot, we try to reuse ota APEX as is - auto result = OpenAndValidateDecompressedApex(capex, ota_apex_path); - if (result.ok()) { - LOG(INFO) << "Skipping decompression for " << ota_apex_path; - return result; - } - // Existing ota_apex is not valid. We will have to decompress - LOG(WARNING) << "Existing decompressed OTA APEX is invalid: " - << result.error(); - RemoveFileIfExists(ota_apex_path); - } else { - // During boot, we can avoid decompression by renaming OTA apex - // to expected decompressed_apex path - - // Check if ota_apex APEX is valid - auto result = OpenAndValidateDecompressedApex(capex, ota_apex_path); - if (result.ok()) { - // ota_apex matches with capex. Slot has been switched. - - // Rename ota_apex to expected decompressed_apex path - if (rename(ota_apex_path.c_str(), decompressed_apex_path.c_str()) == - 0) { - // Check if renamed decompressed APEX is valid - result = - OpenAndValidateDecompressedApex(capex, decompressed_apex_path); - if (result.ok()) { - LOG(INFO) << "Renamed " << ota_apex_path << " to " - << decompressed_apex_path; - return result; - } - // Renamed ota_apex is not valid. We will have to decompress - LOG(WARNING) << "Renamed decompressed APEX from " << ota_apex_path - << " to " << decompressed_apex_path - << " is invalid: " << result.error(); - RemoveFileIfExists(decompressed_apex_path); - } else { - PLOG(ERROR) << "Failed to rename file " << ota_apex_path; - } - } - } - } - - // There was no way to avoid decompression - - // Clean up reserved space before decompressing capex - if (auto ret = DeleteDirContent(gConfig->ota_reserved_dir); !ret.ok()) { - LOG(ERROR) << "Failed to clean up reserved space: " << ret.error(); - } - - auto decompression_dest = - is_ota_chroot ? ota_apex_path : decompressed_apex_path; - auto scope_guard = android::base::make_scope_guard( - [&]() { RemoveFileIfExists(decompression_dest); }); - - auto decompression_result = capex.Decompress(decompression_dest); - if (!decompression_result.ok()) { - return Error() << "Failed to decompress : " << capex.GetPath().c_str() - << " " << decompression_result.error(); - } - - // Fix label of decompressed file - auto restore = RestoreconPath(decompression_dest); - if (!restore.ok()) { - return restore.error(); - } - - // Validate the newly decompressed APEX - auto return_apex = OpenAndValidateDecompressedApex(capex, decompression_dest); - if (!return_apex.ok()) { - return Error() << "Failed to decompress CAPEX: " << return_apex.error(); - } - - /// Release compressed blocks in case decompression_dest is on f2fs-compressed - // filesystem. - ReleaseF2fsCompressedBlocks(decompression_dest); - - scope_guard.Disable(); - return return_apex; -} -} // namespace - -/** - * For each compressed APEX, decompress it to kApexDecompressedDir - * and return the decompressed APEX. - * - * Returns list of decompressed APEX. - */ -std::vector<ApexFile> ProcessCompressedApex( - const std::vector<ApexFileRef>& compressed_apex, bool is_ota_chroot) { - LOG(INFO) << "Processing compressed APEX"; - - std::vector<ApexFile> decompressed_apex_list; - for (const ApexFile& capex : compressed_apex) { - if (!capex.IsCompressed()) { - continue; - } - - auto decompressed_apex = ProcessCompressedApex(capex, is_ota_chroot); - if (decompressed_apex.ok()) { - decompressed_apex_list.emplace_back(std::move(*decompressed_apex)); - continue; - } - LOG(ERROR) << "Failed to process compressed APEX: " - << decompressed_apex.error(); - } - return std::move(decompressed_apex_list); -} - -Result<void> ValidateDecompressedApex(const ApexFile& capex, - const ApexFile& apex) { - // Decompressed APEX must have same public key as CAPEX - if (capex.GetBundledPublicKey() != apex.GetBundledPublicKey()) { - return Error() - << "Public key of compressed APEX is different than original " - << "APEX for " << apex.GetPath(); - } - // Decompressed APEX must have same version as CAPEX - if (capex.GetManifest().version() != apex.GetManifest().version()) { - return Error() - << "Compressed APEX has different version than decompressed APEX " - << apex.GetPath(); - } - // Decompressed APEX must have same root digest as what is stored in CAPEX - auto apex_verity = apex.VerifyApexVerity(apex.GetBundledPublicKey()); - if (!apex_verity.ok() || - capex.GetManifest().capexmetadata().originalapexdigest() != - apex_verity->root_digest) { - return Error() << "Root digest of " << apex.GetPath() - << " does not match with" - << " expected root digest in " << capex.GetPath(); - } - return {}; -} - -void OnStart() { +void onStart() { LOG(INFO) << "Marking APEXd as starting"; - auto time_started = boot_clock::now(); - if (!SetProperty(gConfig->apex_status_sysprop, kApexStatusStarting)) { - PLOG(ERROR) << "Failed to set " << gConfig->apex_status_sysprop << " to " + if (!android::base::SetProperty(kApexStatusSysprop, kApexStatusStarting)) { + PLOG(ERROR) << "Failed to set " << kApexStatusSysprop << " to " << kApexStatusStarting; } @@ -2771,134 +1889,98 @@ void OnStart() { LOG(INFO) << "Exceeded number of session retries (" << kNumRetriesWhenCheckpointingEnabled << "). Starting a revert"; - RevertActiveSessions("", ""); + revertActiveSessions(""); } } - // Create directories for APEX shared libraries. - auto sharedlibs_apex_dir = CreateSharedLibsApexDir(); - if (!sharedlibs_apex_dir.ok()) { - LOG(ERROR) << sharedlibs_apex_dir.error(); - } - - // If there is any new apex to be installed on /data/app-staging, hardlink - // them to /data/apex/active first. - ScanStagedSessionsDirAndStage(); - if (auto status = ApexFileRepository::GetInstance().AddDataApex( - gConfig->active_apex_data_dir); - !status.ok()) { - LOG(ERROR) << "Failed to collect data APEX files : " << status.error(); - } - - auto status = ResumeRevertIfNeeded(); + // Activate APEXes from /data/apex. If one in the directory is newer than the + // system one, the new one will eclipse the old one. + scanStagedSessionsDirAndStage(); + auto status = resumeRevertIfNeeded(); if (!status.ok()) { LOG(ERROR) << "Failed to resume revert : " << status.error(); } - // Group every ApexFile on device by name - const auto& instance = ApexFileRepository::GetInstance(); - const auto& all_apex = instance.AllApexFilesByName(); - // There can be multiple APEX packages with package name X. Determine which - // one to activate. - auto activation_list = SelectApexForActivation(all_apex, instance); - - // Process compressed APEX, if any - std::vector<ApexFileRef> compressed_apex; - for (auto it = activation_list.begin(); it != activation_list.end();) { - if (it->get().IsCompressed()) { - compressed_apex.emplace_back(*it); - it = activation_list.erase(it); - } else { - it++; + std::vector<ApexFile> data_apex; + if (auto scan = ScanApexFiles(kActiveApexPackagesDataDir); !scan.ok()) { + LOG(ERROR) << "Failed to scan packages from " << kActiveApexPackagesDataDir + << " : " << scan.error(); + if (auto revert = revertActiveSessionsAndReboot(""); !revert.ok()) { + LOG(ERROR) << "Failed to revert : " << revert.error(); } + } else { + auto filter_fn = [](const ApexFile& apex) { + if (!ShouldActivateApexOnData(apex)) { + LOG(WARNING) << "Skipping " << apex.GetPath(); + return false; + } + return true; + }; + std::copy_if(std::make_move_iterator(scan->begin()), + std::make_move_iterator(scan->end()), + std::back_inserter(data_apex), filter_fn); } - std::vector<ApexFile> decompressed_apex; - if (!compressed_apex.empty()) { - decompressed_apex = - ProcessCompressedApex(compressed_apex, /* is_ota_chroot= */ false); - for (const ApexFile& apex_file : decompressed_apex) { - activation_list.emplace_back(std::cref(apex_file)); + + if (auto ret = ActivateApexPackages(data_apex); !ret.ok()) { + LOG(ERROR) << "Failed to activate packages from " + << kActiveApexPackagesDataDir << " : " << status.error(); + Result<void> revert_status = revertActiveSessionsAndReboot(""); + if (!revert_status.ok()) { + // TODO: should we kill apexd in this case? + LOG(ERROR) << "Failed to revert : " << revert_status.error() + << kActiveApexPackagesDataDir << " : " << ret.error(); } } - int data_apex_cnt = std::count_if( - activation_list.begin(), activation_list.end(), [](const auto& a) { - return !ApexFileRepository::GetInstance().IsPreInstalledApex(a.get()); - }); - if (data_apex_cnt > 0) { - Result<void> pre_allocate = loop::PreAllocateLoopDevices(data_apex_cnt); - if (!pre_allocate.ok()) { - LOG(ERROR) << "Failed to pre-allocate loop devices : " - << pre_allocate.error(); - } - } - - // TODO(b/179248390): activate parallelly if possible - auto activate_status = - ActivateApexPackages(activation_list, /* is_ota_chroot= */ false); - if (!activate_status.ok()) { - std::string error_message = - StringPrintf("Failed to activate packages: %s", - activate_status.error().message().c_str()); - LOG(ERROR) << error_message; - Result<void> revert_status = - RevertActiveSessionsAndReboot("", error_message); - if (!revert_status.ok()) { - LOG(ERROR) << "Failed to revert : " << revert_status.error(); + // Now also scan and activate APEXes from pre-installed directories. + for (const auto& dir : kApexPackageBuiltinDirs) { + auto scan_status = ScanApexFiles(dir.c_str()); + if (!scan_status.ok()) { + LOG(ERROR) << "Failed to scan APEX packages from " << dir << " : " + << scan_status.error(); + if (auto revert = revertActiveSessionsAndReboot(""); !revert.ok()) { + LOG(ERROR) << "Failed to revert : " << revert.error(); + } } - auto retry_status = ActivateMissingApexes(activation_list, - /* is_ota_chroot= */ false); - if (!retry_status.ok()) { - LOG(ERROR) << retry_status.error(); + if (auto activate = ActivateApexPackages(*scan_status); !activate.ok()) { + // This should never happen. Like **really** never. + // TODO: should we kill apexd in this case? + LOG(ERROR) << "Failed to activate packages from " << dir << " : " + << activate.error(); } } // Now that APEXes are mounted, snapshot or restore DE_sys data. - SnapshotOrRestoreDeSysData(); - - auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( - boot_clock::now() - time_started).count(); - LOG(INFO) << "OnStart done, duration=" << time_elapsed; + snapshotOrRestoreDeSysData(); } -void OnAllPackagesActivated(bool is_bootstrap) { - auto result = EmitApexInfoList(is_bootstrap); - if (!result.ok()) { - LOG(ERROR) << "cannot emit apex info list: " << result.error(); - } - - // Because apexd in bootstrap mode runs in blocking mode - // we don't have to set as activated. - if (is_bootstrap) { - return; - } - +void onAllPackagesActivated() { // Set a system property to let other components know that APEXs are // activated, but are not yet ready to be used. init is expected to wait // for this status before performing configuration based on activated // apexes. Other components that need to use APEXs should wait for the // ready state instead. LOG(INFO) << "Marking APEXd as activated"; - if (!SetProperty(gConfig->apex_status_sysprop, kApexStatusActivated)) { - PLOG(ERROR) << "Failed to set " << gConfig->apex_status_sysprop << " to " + if (!android::base::SetProperty(kApexStatusSysprop, kApexStatusActivated)) { + PLOG(ERROR) << "Failed to set " << kApexStatusSysprop << " to " << kApexStatusActivated; } } -void OnAllPackagesReady() { +void onAllPackagesReady() { // Set a system property to let other components know that APEXs are // correctly mounted and ready to be used. Before using any file from APEXs, // they can query this system property to ensure that they are okay to // access. Or they may have a on-property trigger to delay a task until // APEXs become ready. LOG(INFO) << "Marking APEXd as ready"; - if (!SetProperty(gConfig->apex_status_sysprop, kApexStatusReady)) { - PLOG(ERROR) << "Failed to set " << gConfig->apex_status_sysprop << " to " + if (!android::base::SetProperty(kApexStatusSysprop, kApexStatusReady)) { + PLOG(ERROR) << "Failed to set " << kApexStatusSysprop << " to " << kApexStatusReady; } } -Result<std::vector<ApexFile>> SubmitStagedSession( +Result<std::vector<ApexFile>> submitStagedSession( const int session_id, const std::vector<int>& child_session_ids, const bool has_rollback_enabled, const bool is_rollback, const int rollback_id) { @@ -2922,13 +2004,8 @@ Result<std::vector<ApexFile>> SubmitStagedSession( } std::vector<ApexFile> ret; - auto guard = android::base::make_scope_guard([&ret]() { - for (const auto& apex : ret) { - apexd_private::UnmountTempMount(apex); - } - }); for (int id_to_scan : ids_to_scan) { - auto verified = VerifySessionDir(id_to_scan); + auto verified = verifySessionDir(id_to_scan); if (!verified.ok()) { return verified.error(); } @@ -2962,15 +2039,10 @@ Result<std::vector<ApexFile>> SubmitStagedSession( return commit_status.error(); } - for (const auto& apex : ret) { - // Release compressed blocks in case /data is f2fs-compressed filesystem. - ReleaseF2fsCompressedBlocks(apex.GetPath()); - } - return ret; } -Result<void> MarkStagedSessionReady(const int session_id) { +Result<void> markStagedSessionReady(const int session_id) { auto session = ApexSession::GetSession(session_id); if (!session.ok()) { return session.error(); @@ -2989,7 +2061,7 @@ Result<void> MarkStagedSessionReady(const int session_id) { << ". Cannot mark it as ready."; } -Result<void> MarkStagedSessionSuccessful(const int session_id) { +Result<void> markStagedSessionSuccessful(const int session_id) { auto session = ApexSession::GetSession(session_id); if (!session.ok()) { return session.error(); @@ -3005,7 +2077,7 @@ Result<void> MarkStagedSessionSuccessful(const int session_id) { << " as successful : " << cleanup_status.error(); } if (session->IsRollback() && !gSupportsFsCheckpoints) { - DeleteDePreRestoreSnapshots(*session); + deleteDePreRestoreSnapshots(*session); } return session->UpdateStateAndCommit(SessionState::SUCCESS); } else { @@ -3013,63 +2085,86 @@ Result<void> MarkStagedSessionSuccessful(const int session_id) { } } -// Removes APEXes on /data that have not been activated -void RemoveInactiveDataApex() { - std::vector<std::string> all_apex_files; - Result<std::vector<std::string>> active_apex = - FindFilesBySuffix(gConfig->active_apex_data_dir, {kApexPackageSuffix}); - if (!active_apex.ok()) { - LOG(ERROR) << "Failed to scan " << gConfig->active_apex_data_dir << " : " - << active_apex.error(); - } else { - all_apex_files.insert(all_apex_files.end(), - std::make_move_iterator(active_apex->begin()), - std::make_move_iterator(active_apex->end())); - } - Result<std::vector<std::string>> decompressed_apex = FindFilesBySuffix( - gConfig->decompression_dir, {kDecompressedApexPackageSuffix}); - if (!decompressed_apex.ok()) { - LOG(ERROR) << "Failed to scan " << gConfig->decompression_dir << " : " - << decompressed_apex.error(); - } else { - all_apex_files.insert(all_apex_files.end(), - std::make_move_iterator(decompressed_apex->begin()), - std::make_move_iterator(decompressed_apex->end())); +namespace { + +// Find dangling mounts and unmount them. +// If one is on /data/apex/active, remove it. +void UnmountDanglingMounts() { + std::multimap<std::string, MountedApexData> danglings; + gMountedApexes.ForallMountedApexes([&](const std::string& package, + const MountedApexData& data, + bool latest) { + if (!latest) { + danglings.insert({package, data}); + } + }); + + for (const auto& [package, data] : danglings) { + const std::string& path = data.full_path; + LOG(VERBOSE) << "Unmounting " << data.mount_point; + gMountedApexes.RemoveMountedApex(package, path); + if (auto st = Unmount(data); !st.ok()) { + LOG(ERROR) << st.error(); + } + if (StartsWith(path, kActiveApexPackagesDataDir)) { + LOG(VERBOSE) << "Deleting old APEX " << path; + if (unlink(path.c_str()) != 0) { + PLOG(ERROR) << "Failed to delete " << path; + } + } } - for (const auto& path : all_apex_files) { - if (!apexd_private::IsMounted(path)) { - LOG(INFO) << "Removing inactive data APEX " << path; + RemoveObsoleteHashTrees(); +} + +// Removes APEXes on /data that don't have corresponding pre-installed version +// or that are corrupt +void RemoveOrphanedApexes() { + auto data_apexes = FindApexFilesByName(kActiveApexPackagesDataDir); + if (!data_apexes.ok()) { + LOG(ERROR) << "Failed to scan " << kActiveApexPackagesDataDir << " : " + << data_apexes.error(); + return; + } + for (const auto& path : *data_apexes) { + auto apex = ApexFile::Open(path); + if (!apex.ok()) { + LOG(DEBUG) << "Failed to open APEX " << path << " : " << apex.error(); + // before removing, double-check if the path is active or not + // just in case ApexFile::Open() fails with valid APEX + if (!apexd_private::IsMounted(path)) { + LOG(DEBUG) << "Removing corrupt APEX " << path; + if (unlink(path.c_str()) != 0) { + PLOG(ERROR) << "Failed to unlink " << path; + } + } + continue; + } + if (!ShouldActivateApexOnData(*apex)) { + LOG(DEBUG) << "Removing orphaned APEX " << path; if (unlink(path.c_str()) != 0) { - PLOG(ERROR) << "Failed to unlink inactive data APEX " << path; + PLOG(ERROR) << "Failed to unlink " << path; } } } } -void BootCompletedCleanup() { - RemoveInactiveDataApex(); - ApexSession::DeleteFinalizedSessions(); +} // namespace + +void bootCompletedCleanup() { + UnmountDanglingMounts(); + RemoveOrphanedApexes(); } -int UnmountAll() { - gMountedApexes.PopulateFromMounts(gConfig->active_apex_data_dir, - gConfig->decompression_dir, - gConfig->apex_hash_tree_dir); +int unmountAll() { + gMountedApexes.PopulateFromMounts(); int ret = 0; gMountedApexes.ForallMountedApexes([&](const std::string& /*package*/, const MountedApexData& data, bool latest) { LOG(INFO) << "Unmounting " << data.full_path << " mounted on " << data.mount_point; - auto apex = ApexFile::Open(data.full_path); - if (!apex.ok()) { - LOG(ERROR) << "Failed to open " << data.full_path << " : " - << apex.error(); - ret = 1; - return; - } - if (latest && !apex->GetManifest().providesharedapexlibs()) { + if (latest) { auto pos = data.mount_point.find('@'); CHECK(pos != std::string::npos); std::string bind_mount = data.mount_point.substr(0, pos); @@ -3078,7 +2173,7 @@ int UnmountAll() { ret = 1; } } - if (auto status = Unmount(data, /* deferred= */ false); !status.ok()) { + if (auto status = Unmount(data); !status.ok()) { LOG(ERROR) << "Failed to unmount " << data.mount_point << " : " << status.error(); ret = 1; @@ -3087,7 +2182,12 @@ int UnmountAll() { return ret; } -Result<void> RemountPackages() { +bool isBooting() { + auto status = GetProperty(kApexStatusSysprop, ""); + return status != kApexStatusReady && status != kApexStatusActivated; +} + +Result<void> remountPackages() { std::vector<std::string> apexes; gMountedApexes.ForallMountedApexes([&apexes](const std::string& /*package*/, const MountedApexData& data, @@ -3101,7 +2201,7 @@ Result<void> RemountPackages() { for (const std::string& apex : apexes) { // Since this is only used during development workflow, we are trying to // remount as many apexes as possible instead of failing fast. - if (auto ret = RemountApexFile(apex); !ret.ok()) { + if (auto ret = remountApexFile(apex); !ret) { LOG(WARNING) << "Failed to remount " << apex << " : " << ret.error(); failed.emplace_back(apex); } @@ -3118,596 +2218,5 @@ Result<void> RemountPackages() { return {}; } -// Given a single new APEX incoming via OTA, should we allocate space for it? -Result<bool> ShouldAllocateSpaceForDecompression( - const std::string& new_apex_name, const int64_t new_apex_version, - const ApexFileRepository& instance) { - // An apex at most will have two versions on device: pre-installed and data. - - // Check if there is a pre-installed version for the new apex. - if (!instance.HasPreInstalledVersion(new_apex_name)) { - // We are introducing a new APEX that doesn't exist at all - return true; - } - - // Check if there is a data apex - if (!instance.HasDataVersion(new_apex_name)) { - // Data apex doesn't exist. Compare against pre-installed APEX - auto pre_installed_apex = instance.GetPreInstalledApex(new_apex_name); - if (!pre_installed_apex.get().IsCompressed()) { - // Compressing an existing uncompressed system APEX. - return true; - } - // Since there is no data apex, it means device is using the compressed - // pre-installed version. If new apex has higher version, we are upgrading - // the pre-install version and if new apex has lower version, we are - // downgrading it. So the current decompressed apex should be replaced - // with the new decompressed apex to reflect that. - const int64_t pre_installed_version = - instance.GetPreInstalledApex(new_apex_name) - .get() - .GetManifest() - .version(); - return new_apex_version != pre_installed_version; - } - - // From here on, data apex exists. So we should compare directly against data - // apex. - auto data_apex = instance.GetDataApex(new_apex_name); - // Compare the data apex version with new apex - const int64_t data_version = data_apex.get().GetManifest().version(); - // We only decompress the new_apex if it has higher version than data apex. - return new_apex_version > data_version; -} - -void CollectApexInfoList(std::ostream& os, - const std::vector<ApexFile>& active_apexs, - const std::vector<ApexFile>& inactive_apexs) { - std::vector<com::android::apex::ApexInfo> apex_infos; - - auto convert_to_autogen = [&apex_infos](const ApexFile& apex, - bool is_active) { - auto& instance = ApexFileRepository::GetInstance(); - - auto preinstalled_path = - instance.GetPreinstalledPath(apex.GetManifest().name()); - std::optional<std::string> preinstalled_module_path; - if (preinstalled_path.ok()) { - preinstalled_module_path = *preinstalled_path; - } - - std::optional<int64_t> mtime; - struct stat stat_buf; - if (stat(apex.GetPath().c_str(), &stat_buf) == 0) { - mtime.emplace(stat_buf.st_mtime); - } else { - PLOG(WARNING) << "Failed to stat " << apex.GetPath(); - } - com::android::apex::ApexInfo apex_info( - apex.GetManifest().name(), apex.GetPath(), preinstalled_module_path, - apex.GetManifest().version(), apex.GetManifest().versionname(), - instance.IsPreInstalledApex(apex), is_active, mtime); - apex_infos.emplace_back(apex_info); - }; - for (const auto& apex : active_apexs) { - convert_to_autogen(apex, /* is_active= */ true); - } - for (const auto& apex : inactive_apexs) { - convert_to_autogen(apex, /* is_active= */ false); - } - com::android::apex::ApexInfoList apex_info_list(apex_infos); - com::android::apex::write(os, apex_info_list); -} - -// Reserve |size| bytes in |dest_dir| by creating a zero-filled file. -// Also, we always clean up ota_apex that has been processed as -// part of pre-reboot decompression whenever we reserve space. -Result<void> ReserveSpaceForCompressedApex(int64_t size, - const std::string& dest_dir) { - if (size < 0) { - return Error() << "Cannot reserve negative byte of space"; - } - - // Since we are reserving space, then we must be preparing for a new OTA. - // Clean up any processed ota_apex from previous OTA. - auto ota_apex_files = - FindFilesBySuffix(gConfig->decompression_dir, {kOtaApexPackageSuffix}); - if (!ota_apex_files.ok()) { - return Error() << "Failed to clean up ota_apex: " << ota_apex_files.error(); - } - for (const std::string& ota_apex : *ota_apex_files) { - RemoveFileIfExists(ota_apex); - } - - auto file_path = StringPrintf("%s/full.tmp", dest_dir.c_str()); - if (size == 0) { - LOG(INFO) << "Cleaning up reserved space for compressed APEX"; - // Ota is being cancelled. Clean up reserved space - RemoveFileIfExists(file_path); - return {}; - } - - LOG(INFO) << "Reserving " << size << " bytes for compressed APEX"; - unique_fd dest_fd( - open(file_path.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT, 0644)); - if (dest_fd.get() == -1) { - return ErrnoError() << "Failed to open file for reservation " - << file_path.c_str(); - } - - // Resize to required size - std::error_code ec; - std::filesystem::resize_file(file_path, size, ec); - if (ec) { - RemoveFileIfExists(file_path); - return ErrnoError() << "Failed to resize file " << file_path.c_str() - << " : " << ec.message(); - } - - return {}; -} - -int OnOtaChrootBootstrap() { - auto& instance = ApexFileRepository::GetInstance(); - if (auto status = instance.AddPreInstalledApex(gConfig->apex_built_in_dirs); - !status.ok()) { - LOG(ERROR) << "Failed to scan pre-installed apexes from " - << Join(gConfig->apex_built_in_dirs, ','); - return 1; - } - if (auto status = instance.AddDataApex(gConfig->active_apex_data_dir); - !status.ok()) { - LOG(ERROR) << "Failed to scan upgraded apexes from " - << gConfig->active_apex_data_dir; - // Failing to scan upgraded apexes is not fatal, since we can still try to - // run otapreopt using only pre-installed apexes. Worst case, apps will be - // re-optimized on next boot. - } - - // Create directories for APEX shared libraries. - if (auto status = CreateSharedLibsApexDir(); !status.ok()) { - LOG(ERROR) << "Failed to create /apex/sharedlibs : " << status.ok(); - return 1; - } - - auto activation_list = - SelectApexForActivation(instance.AllApexFilesByName(), instance); - - // TODO(b/179497746): This is the third time we are duplicating this code - // block. This will be easier to dedup once we start opening ApexFiles via - // ApexFileRepository. That way, ProcessCompressedApex can return list of - // ApexFileRef, instead of ApexFile. - - // Process compressed APEX, if any - std::vector<ApexFileRef> compressed_apex; - for (auto it = activation_list.begin(); it != activation_list.end();) { - if (it->get().IsCompressed()) { - compressed_apex.emplace_back(*it); - it = activation_list.erase(it); - } else { - it++; - } - } - std::vector<ApexFile> decompressed_apex; - if (!compressed_apex.empty()) { - decompressed_apex = - ProcessCompressedApex(compressed_apex, /* is_ota_chroot= */ true); - - for (const ApexFile& apex_file : decompressed_apex) { - activation_list.emplace_back(std::cref(apex_file)); - } - } - - auto activate_status = ActivateApexPackages(activation_list, - /* is_ota_chroot= */ true); - if (!activate_status.ok()) { - LOG(ERROR) << "Failed to activate apex packages : " - << activate_status.error(); - auto retry_status = ActivateMissingApexes(activation_list, - /* is_ota_chroot= */ true); - if (!retry_status.ok()) { - LOG(ERROR) << retry_status.error(); - } - } - - // There are a bunch of places that are producing apex-info.xml file. - // We should consolidate the logic in one function and make all other places - // use it. - auto active_apexes = GetActivePackages(); - std::vector<ApexFile> inactive_apexes = GetFactoryPackages(); - auto new_end = std::remove_if( - inactive_apexes.begin(), inactive_apexes.end(), - [&active_apexes](const ApexFile& apex) { - return std::any_of(active_apexes.begin(), active_apexes.end(), - [&apex](const ApexFile& active_apex) { - return apex.GetPath() == active_apex.GetPath(); - }); - }); - inactive_apexes.erase(new_end, inactive_apexes.end()); - std::stringstream xml; - CollectApexInfoList(xml, active_apexes, inactive_apexes); - std::string file_name = StringPrintf("%s/%s", kApexRoot, kApexInfoList); - unique_fd fd(TEMP_FAILURE_RETRY( - open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644))); - if (fd.get() == -1) { - PLOG(ERROR) << "Can't open " << file_name; - return 1; - } - - if (!android::base::WriteStringToFd(xml.str(), fd)) { - PLOG(ERROR) << "Can't write to " << file_name; - return 1; - } - - fd.reset(); - - if (auto status = RestoreconPath(file_name); !status.ok()) { - LOG(ERROR) << "Failed to restorecon " << file_name << " : " - << status.error(); - return 1; - } - - return 0; -} - -int OnOtaChrootBootstrapFlattenedApex() { - LOG(INFO) << "OnOtaChrootBootstrapFlattenedApex"; - - std::vector<com::android::apex::ApexInfo> apex_infos; - - for (const std::string& dir : gConfig->apex_built_in_dirs) { - LOG(INFO) << "Scanning " << dir; - auto dir_content = ReadDir(dir, [](const auto& entry) { - std::error_code ec; - return entry.is_directory(ec); - }); - - if (!dir_content.ok()) { - LOG(ERROR) << "Failed to scan " << dir << " : " << dir_content.error(); - continue; - } - - // Sort to make sure that /apex/apex-info-list.xml generation doesn't depend - // on the unstable directory scan. - std::vector<std::string> entries = std::move(*dir_content); - std::sort(entries.begin(), entries.end()); - - for (const std::string& apex_dir : entries) { - std::string manifest_file = apex_dir + "/" + kManifestFilenamePb; - if (access(manifest_file.c_str(), F_OK) != 0) { - PLOG(ERROR) << "Failed to access " << manifest_file; - continue; - } - - auto manifest = ReadManifest(manifest_file); - if (!manifest.ok()) { - LOG(ERROR) << "Failed to read apex manifest from " << manifest_file - << " : " << manifest.error(); - continue; - } - - std::string mount_point = std::string(kApexRoot) + "/" + manifest->name(); - if (mkdir(mount_point.c_str(), 0755) != 0) { - PLOG(ERROR) << "Failed to mkdir " << mount_point; - continue; - } - - LOG(INFO) << "Bind mounting " << apex_dir << " onto " << mount_point; - if (mount(apex_dir.c_str(), mount_point.c_str(), nullptr, MS_BIND, - nullptr) != 0) { - PLOG(ERROR) << "Failed to bind mount " << apex_dir << " to " - << mount_point; - continue; - } - - apex_infos.emplace_back(manifest->name(), /* modulePath= */ apex_dir, - /* preinstalledModulePath= */ apex_dir, - /* versionCode= */ manifest->version(), - /* versionName= */ manifest->versionname(), - /* isFactory= */ true, /* isActive= */ true, - /* lastUpdateMillis= */ 0); - } - } - - std::string file_name = StringPrintf("%s/%s", kApexRoot, kApexInfoList); - unique_fd fd(TEMP_FAILURE_RETRY( - open(file_name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644))); - if (fd.get() == -1) { - PLOG(ERROR) << "Can't open " << file_name; - return 1; - } - - std::ostringstream xml; - com::android::apex::ApexInfoList apex_info_list(apex_infos); - com::android::apex::write(xml, apex_info_list); - if (!android::base::WriteStringToFd(xml.str(), fd)) { - PLOG(ERROR) << "Can't write to " << file_name; - return 1; - } - fd.reset(); - - if (auto status = RestoreconPath(file_name); !status.ok()) { - LOG(ERROR) << "Failed to restorecon " << file_name << " : " - << status.error(); - return 1; - } - - return 0; -} - -android::apex::MountedApexDatabase& GetApexDatabaseForTesting() { - return gMountedApexes; -} - -// A version of apex verification that happens during non-staged APEX -// installation. -Result<void> VerifyPackageNonStagedInstall(const ApexFile& apex_file) { - const auto& verify_package_boot_status = VerifyPackageBoot(apex_file); - if (!verify_package_boot_status.ok()) { - return verify_package_boot_status; - } - - auto check_fn = [&apex_file](const std::string& mount_point) -> Result<void> { - auto dirs = GetSubdirs(mount_point); - if (!dirs.ok()) { - return dirs.error(); - } - if (std::find(dirs->begin(), dirs->end(), mount_point + "/app") != - dirs->end()) { - return Error() << apex_file.GetPath() << " contains app inside"; - } - if (std::find(dirs->begin(), dirs->end(), mount_point + "/priv-app") != - dirs->end()) { - return Error() << apex_file.GetPath() << " contains priv-app inside"; - } - return Result<void>{}; - }; - return RunVerifyFnInsideTempMount(apex_file, check_fn, true); -} - -Result<void> CheckSupportsNonStagedInstall(const ApexFile& cur_apex, - const ApexFile& new_apex) { - const auto& cur_manifest = cur_apex.GetManifest(); - const auto& new_manifest = new_apex.GetManifest(); - - if (!new_manifest.supportsrebootlessupdate()) { - return Error() << new_apex.GetPath() - << " does not support non-staged update"; - } - - // Check if update will impact linkerconfig. - - // Updates to shared libs APEXes must be done via staged install flow. - if (new_manifest.providesharedapexlibs()) { - return Error() << new_apex.GetPath() << " is a shared libs APEX"; - } - - // This APEX provides native libs to other parts of the platform. It can only - // be updated via staged install flow. - if (new_manifest.providenativelibs_size() > 0) { - return Error() << new_apex.GetPath() << " provides native libs"; - } - - // This APEX requires libs provided by dynamic common library APEX, hence it - // can only be installed using staged install flow. - if (new_manifest.requiresharedapexlibs_size() > 0) { - return Error() << new_apex.GetPath() << " requires shared apex libs"; - } - - // We don't allow non-staged updates of APEXES that have java libs inside. - if (new_manifest.jnilibs_size() > 0) { - return Error() << new_apex.GetPath() << " requires JNI libs"; - } - - // For requireNativeLibs bit, we only allow updates that don't change list of - // required libs. - - std::vector<std::string> cur_required_libs( - cur_manifest.requirenativelibs().begin(), - cur_manifest.requirenativelibs().end()); - sort(cur_required_libs.begin(), cur_required_libs.end()); - - std::vector<std::string> new_required_libs( - new_manifest.requirenativelibs().begin(), - new_manifest.requirenativelibs().end()); - sort(new_required_libs.begin(), new_required_libs.end()); - - if (cur_required_libs != new_required_libs) { - return Error() << "Set of native libs required by " << new_apex.GetPath() - << " differs from the one required by the currently active " - << cur_apex.GetPath(); - } - - auto expected_public_key = - ApexFileRepository::GetInstance().GetPublicKey(new_manifest.name()); - if (!expected_public_key.ok()) { - return expected_public_key.error(); - } - auto verity_data = new_apex.VerifyApexVerity(*expected_public_key); - if (!verity_data.ok()) { - return verity_data.error(); - } - // Supporting non-staged install of APEXes without a hashtree is additional - // hassle, it's easier not to support it. - if (verity_data->desc->tree_size == 0) { - return Error() << new_apex.GetPath() - << " does not have an embedded hash tree"; - } - return {}; -} - -Result<size_t> ComputePackageIdMinor(const ApexFile& apex) { - static constexpr size_t kMaxVerityDevicesPerApexName = 3u; - DeviceMapper& dm = DeviceMapper::Instance(); - std::vector<DeviceMapper::DmBlockDevice> dm_devices; - if (!dm.GetAvailableDevices(&dm_devices)) { - return Error() << "Failed to list dm devices"; - } - size_t devices = 0; - size_t next_minor = 1; - for (const auto& dm_device : dm_devices) { - std::string_view dm_name(dm_device.name()); - // Format is <module_name>@<version_code>[_<minor>] - if (!ConsumePrefix(&dm_name, apex.GetManifest().name())) { - continue; - } - devices++; - auto pos = dm_name.find_last_of('_'); - if (pos == std::string_view::npos) { - continue; - } - size_t minor; - if (!ParseUint(std::string(dm_name.substr(pos + 1)), &minor)) { - return Error() << "Unexpected dm device name " << dm_device.name(); - } - if (next_minor < minor + 1) { - next_minor = minor + 1; - } - } - if (devices > kMaxVerityDevicesPerApexName) { - return Error() << "There are too many (" << devices - << ") dm block devices associated with package " - << apex.GetManifest().name(); - } - while (true) { - std::string target_file = - StringPrintf("%s/%s_%zu.apex", gConfig->active_apex_data_dir, - GetPackageId(apex.GetManifest()).c_str(), next_minor); - if (access(target_file.c_str(), F_OK) == 0) { - next_minor++; - } else { - break; - } - } - - return next_minor; -} - -Result<void> UpdateApexInfoList() { - std::vector<ApexFile> active(GetActivePackages()); - std::vector<ApexFile> inactive = CalculateInactivePackages(active); - - std::stringstream xml; - CollectApexInfoList(xml, active, inactive); - - std::string name = StringPrintf("%s/.default-%s", kApexRoot, kApexInfoList); - unique_fd fd(TEMP_FAILURE_RETRY( - open(name.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644))); - if (fd.get() == -1) { - return ErrnoError() << "Can't open " << name; - } - if (!WriteStringToFd(xml.str(), fd)) { - return ErrnoError() << "Failed to write to " << name; - } - - return {}; -} - -Result<ApexFile> InstallPackage(const std::string& package_path) { - LOG(INFO) << "Installing " << package_path; - auto temp_apex = ApexFile::Open(package_path); - if (!temp_apex.ok()) { - return temp_apex.error(); - } - - const std::string& module_name = temp_apex->GetManifest().name(); - // Don't allow non-staged update if there are no active versions of this APEX. - auto cur_mounted_data = gMountedApexes.GetLatestMountedApex(module_name); - if (!cur_mounted_data.has_value()) { - return Error() << "No active version found for package " << module_name; - } - - auto cur_apex = ApexFile::Open(cur_mounted_data->full_path); - if (!cur_apex.ok()) { - return cur_apex.error(); - } - - // Do a quick check if this APEX can be installed without a reboot. - // Note that passing this check doesn't guarantee that APEX will be - // successfully installed. - if (auto r = CheckSupportsNonStagedInstall(*cur_apex, *temp_apex); !r.ok()) { - return r.error(); - } - - // 1. Verify that APEX is correct. This is a heavy check that involves - // mounting an APEX on a temporary mount point and reading the entire - // dm-verity block device. - if (auto verify = VerifyPackageNonStagedInstall(*temp_apex); !verify.ok()) { - return verify.error(); - } - - // 2. Compute params for mounting new apex. - auto new_id_minor = ComputePackageIdMinor(*temp_apex); - if (!new_id_minor.ok()) { - return new_id_minor.error(); - } - - std::string new_id = GetPackageId(temp_apex->GetManifest()) + "_" + - std::to_string(*new_id_minor); - - // 2. Unmount currently active APEX. - if (auto res = UnmountPackage(*cur_apex, /* allow_latest= */ true, - /* deferred= */ true); - !res.ok()) { - return res.error(); - } - - // 3. Hard link to final destination. - std::string target_file = - StringPrintf("%s/%s.apex", gConfig->active_apex_data_dir, new_id.c_str()); - - auto guard = android::base::make_scope_guard([&]() { - if (unlink(target_file.c_str()) != 0 && errno != ENOENT) { - PLOG(ERROR) << "Failed to unlink " << target_file; - } - // We can't really rely on the fact that dm-verity device backing up - // previously active APEX is still around. We need to create a new one. - std::string old_new_id = GetPackageId(temp_apex->GetManifest()) + "_" + - std::to_string(*new_id_minor + 1); - if (auto res = ActivatePackageImpl(*cur_apex, old_new_id); !res.ok()) { - // At this point not much we can do... :( - LOG(ERROR) << res.error(); - } - }); - - // At this point it should be safe to hard link |temp_apex| to - // |params->target_file|. In case reboot happens during one of the stages - // below, then on next boot apexd will pick up the new verified APEX. - if (link(package_path.c_str(), target_file.c_str()) != 0) { - return ErrnoError() << "Failed to link " << package_path << " to " - << target_file; - } - - auto new_apex = ApexFile::Open(target_file); - if (!new_apex.ok()) { - return new_apex.error(); - } - - // 4. And activate new one. - if (auto res = ActivatePackageImpl(*new_apex, new_id); !res.ok()) { - return res.error(); - } - - // Accept the install. - guard.Disable(); - - // 4. Now we can unlink old APEX if it's not pre-installed. - if (!ApexFileRepository::GetInstance().IsPreInstalledApex(*cur_apex)) { - if (unlink(cur_mounted_data->full_path.c_str()) != 0) { - PLOG(ERROR) << "Failed to unlink " << cur_mounted_data->full_path; - } - } - - if (auto res = UpdateApexInfoList(); !res.ok()) { - LOG(ERROR) << res.error(); - } - - // Release compressed blocks in case target_file is on f2fs-compressed - // filesystem. - ReleaseF2fsCompressedBlocks(target_file); - - return new_apex; -} - } // namespace apex } // namespace android diff --git a/apexd/apexd.h b/apexd/apexd.h index 2465bde9..36f72beb 100644 --- a/apexd/apexd.h +++ b/apexd/apexd.h @@ -17,7 +17,6 @@ #ifndef ANDROID_APEXD_APEXD_H_ #define ANDROID_APEXD_APEXD_H_ -#include <ostream> #include <string> #include <vector> @@ -25,177 +24,101 @@ #include <android-base/result.h> #include "apex_constants.h" -#include "apex_database.h" #include "apex_file.h" -#include "apex_file_repository.h" -#include "apexd_session.h" namespace android { namespace apex { -// A structure containing all the values that might need to be injected for -// testing (e.g. apexd status property, etc.) -// -// Ideally we want to introduce Apexd class and use dependency injection for -// such values, but that will require a sizeable refactoring. For the time being -// this config should do the trick. -struct ApexdConfig { - const char* apex_status_sysprop; - std::vector<std::string> apex_built_in_dirs; - const char* active_apex_data_dir; - const char* decompression_dir; - const char* ota_reserved_dir; - const char* apex_hash_tree_dir; - const char* staged_session_dir; -}; - -static const ApexdConfig kDefaultConfig = { - kApexStatusSysprop, kApexPackageBuiltinDirs, kActiveApexPackagesDataDir, - kApexDecompressedDir, kOtaReservedDir, kApexHashTreeDir, - kStagedSessionsDir, -}; - class CheckpointInterface; -void SetConfig(const ApexdConfig& config); - -// Exposed only for testing. -android::base::Result<void> Unmount( - const MountedApexDatabase::MountedApexData& data, bool deferred); +android::base::Result<void> resumeRevertIfNeeded(); -android::base::Result<void> ResumeRevertIfNeeded(); - -android::base::Result<void> PreinstallPackages( +// Keep it for now to make otapreopt_chroot keep happy. +// TODO(b/137086602): remove this function. +android::base::Result<void> scanPackagesDirAndActivate( + const char* apex_package_dir); +void scanStagedSessionsDirAndStage(); +android::base::Result<void> preinstallPackages( const std::vector<std::string>& paths) WARN_UNUSED; -android::base::Result<void> PostinstallPackages( +android::base::Result<void> postinstallPackages( const std::vector<std::string>& paths) WARN_UNUSED; -android::base::Result<void> StagePackages( +android::base::Result<void> stagePackages( const std::vector<std::string>& tmpPaths) WARN_UNUSED; -android::base::Result<void> UnstagePackages( +android::base::Result<void> unstagePackages( const std::vector<std::string>& paths) WARN_UNUSED; -android::base::Result<std::vector<ApexFile>> SubmitStagedSession( +android::base::Result<std::vector<ApexFile>> submitStagedSession( const int session_id, const std::vector<int>& child_session_ids, const bool has_rollback_enabled, const bool is_rollback, const int rollback_id) WARN_UNUSED; -android::base::Result<void> MarkStagedSessionReady(const int session_id) +android::base::Result<void> markStagedSessionReady(const int session_id) WARN_UNUSED; -android::base::Result<void> MarkStagedSessionSuccessful(const int session_id) +android::base::Result<void> markStagedSessionSuccessful(const int session_id) WARN_UNUSED; -// Only only of the parameters should be passed during revert -android::base::Result<void> RevertActiveSessions( - const std::string& crashing_native_process, - const std::string& error_message); -// Only only of the parameters should be passed during revert -android::base::Result<void> RevertActiveSessionsAndReboot( - const std::string& crashing_native_process, - const std::string& error_message); - -android::base::Result<void> ActivatePackage(const std::string& full_path) +android::base::Result<void> revertActiveSessions( + const std::string& crashing_native_process); +android::base::Result<void> revertActiveSessionsAndReboot( + const std::string& crashing_native_process); + +android::base::Result<void> activatePackage(const std::string& full_path) WARN_UNUSED; -android::base::Result<void> DeactivatePackage(const std::string& full_path) +android::base::Result<void> deactivatePackage(const std::string& full_path) WARN_UNUSED; -std::vector<ApexFile> GetActivePackages(); -android::base::Result<ApexFile> GetActivePackage( +std::vector<ApexFile> getActivePackages(); +android::base::Result<ApexFile> getActivePackage( const std::string& package_name); -std::vector<ApexFile> GetFactoryPackages(); +std::vector<ApexFile> getFactoryPackages(); -android::base::Result<void> AbortStagedSession(const int session_id); +android::base::Result<void> abortStagedSession(const int session_id); +android::base::Result<void> abortActiveSession(); -android::base::Result<void> SnapshotCeData(const int user_id, - const int rollback_id, - const std::string& apex_name); -android::base::Result<void> RestoreCeData(const int user_id, +android::base::Result<ino_t> snapshotCeData(const int user_id, + const int rollback_id, + const std::string& apex_name); +android::base::Result<void> restoreCeData(const int user_id, const int rollback_id, const std::string& apex_name); - -android::base::Result<void> DestroyDeSnapshots(const int rollback_id); -android::base::Result<void> DestroyCeSnapshots(const int user_id, - const int rollback_id); -android::base::Result<void> DestroyCeSnapshotsNotSpecified( +android::base::Result<void> destroyDeSnapshots(const int rollback_id); +android::base::Result<void> destroyCeSnapshotsNotSpecified( int user_id, const std::vector<int>& retain_rollback_ids); -int OnBootstrap(); +int onBootstrap(); +// Small helper function to tell if device is currently booting. +bool isBooting(); // Sets the values of gVoldService and gInFsCheckpointMode. -void InitializeVold(CheckpointInterface* checkpoint_service); +void initializeVold(CheckpointInterface* checkpoint_service); // Initializes in-memory state (e.g. pre-installed data, activated apexes). // Must be called first before calling any other boot sequence related function. -void Initialize(CheckpointInterface* checkpoint_service); -// Initializes data apex as in-memory state. Should be called only if we are -// not booting, since initialization timing is different when booting -void InitializeDataApex(); +void initialize(CheckpointInterface* checkpoint_service); // Migrates sessions from /data/apex/session to /metadata/session.i // Must only be called during boot (i.e apexd.status is not "ready" or // "activated"). -android::base::Result<void> MigrateSessionsDirIfNeeded(); +android::base::Result<void> migrateSessionsDirIfNeeded(); // Apex activation logic. Scans staged apex sessions and activates apexes. // Must only be called during boot (i.e apexd.status is not "ready" or // "activated"). -void OnStart(); -// For every package X, there can be at most two APEX, pre-installed vs -// installed on data. We decide which ones should be activated and return them -// as a list -std::vector<ApexFileRef> SelectApexForActivation( - const std::unordered_map<std::string, std::vector<ApexFileRef>>& all_apex, - const ApexFileRepository& instance); -std::vector<ApexFile> ProcessCompressedApex( - const std::vector<ApexFileRef>& compressed_apex, bool is_ota_chroot); -// Validate |apex| is same as |capex| -android::base::Result<void> ValidateDecompressedApex(const ApexFile& capex, - const ApexFile& apex); +void onStart(); // Notifies system that apexes are activated by setting apexd.status property to // "activated". // Must only be called during boot (i.e. apexd.status is not "ready" or // "activated"). -void OnAllPackagesActivated(bool is_bootstrap); +void onAllPackagesActivated(); // Notifies system that apexes are ready by setting apexd.status property to // "ready". // Must only be called during boot (i.e. apexd.status is not "ready" or // "activated"). -void OnAllPackagesReady(); -void OnBootCompleted(); -// Exposed for testing -void RemoveInactiveDataApex(); -void BootCompletedCleanup(); -int SnapshotOrRestoreDeUserData(); - -int UnmountAll(); +void onAllPackagesReady(); +void bootCompletedCleanup(); +int snapshotOrRestoreDeUserData(); -android::base::Result<MountedApexDatabase::MountedApexData> -GetTempMountedApexData(const std::string& package); +int unmountAll(); // Optimistically tries to remount as many APEX packages as possible. // For more documentation see corresponding binder call in IApexService.aidl. -android::base::Result<void> RemountPackages(); - -// Exposed for unit tests -android::base::Result<bool> ShouldAllocateSpaceForDecompression( - const std::string& new_apex_name, int64_t new_apex_version, - const ApexFileRepository& instance); - -void CollectApexInfoList(std::ostream& os, - const std::vector<ApexFile>& active_apexs, - const std::vector<ApexFile>& inactive_apexs); - -// Reserve |size| bytes in |dest_dir| by creating a zero-filled file -android::base::Result<void> ReserveSpaceForCompressedApex( - int64_t size, const std::string& dest_dir); - -// Activates apexes in otapreot_chroot environment. -// TODO(b/172911822): support compressed apexes. -int OnOtaChrootBootstrap(); - -// Activates flattened apexes in otapreopt_chroot environment. -int OnOtaChrootBootstrapFlattenedApex(); - -android::apex::MountedApexDatabase& GetApexDatabaseForTesting(); - -// Performs a non-staged install of an APEX specified by |package_path|. -// TODO(ioffe): add more documentation. -android::base::Result<ApexFile> InstallPackage(const std::string& package_path); +android::base::Result<void> remountPackages(); } // namespace apex } // namespace android diff --git a/apexd/apexd_checkpoint.h b/apexd/apexd_checkpoint.h index 2b4c3a12..7177c047 100644 --- a/apexd/apexd_checkpoint.h +++ b/apexd/apexd_checkpoint.h @@ -32,7 +32,7 @@ class CheckpointInterface { virtual android::base::Result<bool> NeedsCheckpoint() = 0; virtual android::base::Result<bool> NeedsRollback() = 0; - virtual android::base::Result<void> StartCheckpoint(int32_t num_retries) = 0; + virtual android::base::Result<void> StartCheckpoint(int32_t numRetries) = 0; virtual android::base::Result<void> AbortChanges(const std::string& msg, bool retry) = 0; diff --git a/apexd/apexd_checkpoint_vold.cpp b/apexd/apexd_checkpoint_vold.cpp index 8a8d0ff1..ca29209d 100644 --- a/apexd/apexd_checkpoint_vold.cpp +++ b/apexd/apexd_checkpoint_vold.cpp @@ -31,11 +31,11 @@ namespace android { namespace apex { Result<VoldCheckpointInterface> VoldCheckpointInterface::Create() { - auto vold_service = + auto voldService = defaultServiceManager()->getService(android::String16("vold")); - if (vold_service != nullptr) { + if (voldService != nullptr) { return VoldCheckpointInterface( - android::interface_cast<android::os::IVold>(vold_service)); + android::interface_cast<android::os::IVold>(voldService)); } return Errorf("Failed to retrieve vold service."); } @@ -91,10 +91,9 @@ Result<bool> VoldCheckpointInterface::NeedsRollback() { return false; } -Result<void> VoldCheckpointInterface::StartCheckpoint(int32_t num_retries) { +Result<void> VoldCheckpointInterface::StartCheckpoint(int32_t numRetries) { if (supports_fs_checkpoints_) { - android::binder::Status status = - vold_service_->startCheckpoint(num_retries); + android::binder::Status status = vold_service_->startCheckpoint(numRetries); if (!status.isOk()) { return Error() << status.toString8().c_str(); } diff --git a/apexd/apexd_checkpoint_vold.h b/apexd/apexd_checkpoint_vold.h index f547532e..dbd190da 100644 --- a/apexd/apexd_checkpoint_vold.h +++ b/apexd/apexd_checkpoint_vold.h @@ -44,7 +44,7 @@ class VoldCheckpointInterface : public CheckpointInterface { android::base::Result<void> StartCheckpoint(int32_t retry) override; android::base::Result<void> AbortChanges(const std::string& msg, - bool num_retries) override; + bool numRetries) override; static android::base::Result<VoldCheckpointInterface> Create(); diff --git a/apexd/apexd_lifecycle.h b/apexd/apexd_lifecycle.h deleted file mode 100644 index ecdef96e..00000000 --- a/apexd/apexd_lifecycle.h +++ /dev/null @@ -1,48 +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. - */ - -#ifndef ANDROID_APEXD_APEXD_LIFECYCLE_H_ -#define ANDROID_APEXD_APEXD_LIFECYCLE_H_ - -#include <android-base/result.h> - -namespace android { -namespace apex { - -class ApexdLifecycle { - private: - ApexdLifecycle(){}; - std::atomic<bool> boot_completed_; - - // Non-copyable && non-moveable. - ApexdLifecycle(const ApexdLifecycle&) = delete; - ApexdLifecycle& operator=(const ApexdLifecycle&) = delete; - ApexdLifecycle& operator=(ApexdLifecycle&&) = delete; - - public: - static ApexdLifecycle& GetInstance() { - static ApexdLifecycle instance; - return instance; - } - bool IsBooting(); - void MarkBootCompleted(); - void WaitForBootStatus(android::base::Result<void> (&rollback_fn)( - const std::string&, const std::string&)); -}; -} // namespace apex -} // namespace android - -#endif // ANDROID_APEXD_APEXD_LIFECYCLE_H diff --git a/apexd/apexd_loop.cpp b/apexd/apexd_loop.cpp index 08805eaf..568eb055 100644 --- a/apexd/apexd_loop.cpp +++ b/apexd/apexd_loop.cpp @@ -18,49 +18,29 @@ #include "apexd_loop.h" -#include <mutex> - #include <dirent.h> #include <fcntl.h> #include <linux/fs.h> #include <linux/loop.h> #include <sys/ioctl.h> #include <sys/stat.h> -#include <sys/statfs.h> #include <sys/types.h> #include <unistd.h> #include <android-base/file.h> #include <android-base/logging.h> -#include <android-base/parseint.h> -#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include "apexd_utils.h" #include "string_log.h" -using android::base::Basename; -using android::base::ErrnoError; using android::base::Error; -using android::base::GetBoolProperty; -using android::base::ParseUint; using android::base::Result; using android::base::StartsWith; using android::base::StringPrintf; using android::base::unique_fd; -#ifndef LOOP_CONFIGURE -// These can be removed whenever we pull in the Linux v5.8 UAPI headers -struct loop_config { - __u32 fd; - __u32 block_size; - struct loop_info64 info; - __u64 __reserved[8]; -}; -#define LOOP_CONFIGURE 0x4C0A -#endif - namespace android { namespace apex { namespace loop { @@ -86,9 +66,13 @@ void LoopbackDeviceUniqueFd::MaybeCloseBad() { } } -Result<void> ConfigureReadAhead(const std::string& device_path) { - CHECK(StartsWith(device_path, "/dev/")); - std::string device_name = Basename(device_path); +Result<void> configureReadAhead(const std::string& device_path) { + auto pos = device_path.find("/dev/block/"); + if (pos != 0) { + return Error() << "Device path does not start with /dev/block."; + } + pos = device_path.find_last_of('/'); + std::string device_name = device_path.substr(pos + 1, std::string::npos); std::string sysfs_device = StringPrintf("/sys/block/%s/queue/read_ahead_kb", device_name.c_str()); @@ -106,10 +90,10 @@ Result<void> ConfigureReadAhead(const std::string& device_path) { return {}; } -Result<void> PreAllocateLoopDevices(size_t num) { - Result<void> loop_ready = WaitForFile("/dev/loop-control", 20s); - if (!loop_ready.ok()) { - return loop_ready; +Result<void> preAllocateLoopDevices(size_t num) { + Result<void> loopReady = WaitForFile("/dev/loop-control", 20s); + if (!loopReady.ok()) { + return loopReady; } unique_fd ctl_fd( TEMP_FAILURE_RETRY(open("/dev/loop-control", O_RDWR | O_CLOEXEC))); @@ -117,30 +101,13 @@ Result<void> PreAllocateLoopDevices(size_t num) { return ErrnoError() << "Failed to open loop-control"; } - bool found = false; - size_t start_id = 0; - constexpr const char* kLoopPrefix = "loop"; - WalkDir("/dev/block", [&](const std::filesystem::directory_entry& entry) { - std::string devname = entry.path().filename().string(); - if (StartsWith(devname, kLoopPrefix)) { - size_t id; - auto parse_ok = ParseUint( - devname.substr(std::char_traits<char>::length(kLoopPrefix)), &id); - if (parse_ok && id > start_id) { - start_id = id; - found = true; - } - } - }); - if (found) ++start_id; - // Assumption: loop device ID [0..num) is valid. // This is because pre-allocation happens during bootstrap. // Anyway Kernel pre-allocated loop devices // as many as CONFIG_BLK_DEV_LOOP_MIN_COUNT, // Within the amount of kernel-pre-allocation, // LOOP_CTL_ADD will fail with EEXIST - for (size_t id = start_id; id < num + start_id; ++id) { + for (size_t id = 0ul; id < num; ++id) { int ret = ioctl(ctl_fd.get(), LOOP_CTL_ADD, id); if (ret < 0 && errno != EEXIST) { return ErrnoError() << "Failed LOOP_CTL_ADD"; @@ -153,30 +120,25 @@ Result<void> PreAllocateLoopDevices(size_t num) { // just optimistally hope that they are all created when we actually // access them for activating APEXes. If the dev nodes are not ready // even then, we wait 50ms and warning message will be printed (see below - // CreateLoopDevice()). + // createLoopDevice()). LOG(INFO) << "Pre-allocated " << num << " loopback devices"; return {}; } -Result<void> ConfigureLoopDevice(const int device_fd, const std::string& target, - const int32_t image_offset, - const size_t image_size) { - static bool use_loop_configure; - static std::once_flag once_flag; - std::call_once(once_flag, [&]() { - // LOOP_CONFIGURE is a new ioctl in Linux 5.8 (and backported in Android - // common) that allows atomically configuring a loop device. It is a lot - // faster than the traditional LOOP_SET_FD/LOOP_SET_STATUS64 combo, but - // it may not be available on updating devices, so try once before - // deciding. - struct loop_config config; - memset(&config, 0, sizeof(config)); - config.fd = -1; - if (ioctl(device_fd, LOOP_CONFIGURE, &config) == -1 && errno == EBADF) { - // If the IOCTL exists, it will fail with EBADF for the -1 fd - use_loop_configure = true; - } - }); +Result<LoopbackDeviceUniqueFd> createLoopDevice(const std::string& target, + const int32_t imageOffset, + const size_t imageSize) { + unique_fd ctl_fd(open("/dev/loop-control", O_RDWR | O_CLOEXEC)); + if (ctl_fd.get() == -1) { + return ErrnoError() << "Failed to open loop-control"; + } + + int num = ioctl(ctl_fd.get(), LOOP_CTL_GET_FREE); + if (num == -1) { + return ErrnoError() << "Failed LOOP_CTL_GET_FREE"; + } + + std::string device = StringPrintf("/dev/block/loop%d", num); /* * Using O_DIRECT will tell the kernel that we want to use Direct I/O @@ -189,151 +151,74 @@ Result<void> ConfigureLoopDevice(const int device_fd, const std::string& target, */ unique_fd target_fd(open(target.c_str(), O_RDONLY | O_CLOEXEC | O_DIRECT)); if (target_fd.get() == -1) { - struct statfs stbuf; - int saved_errno = errno; - // let's give another try with buffered I/O for EROFS and squashfs - if (statfs(target.c_str(), &stbuf) != 0 || - (stbuf.f_type != EROFS_SUPER_MAGIC_V1 && - stbuf.f_type != SQUASHFS_MAGIC && - stbuf.f_type != OVERLAYFS_SUPER_MAGIC)) { - return Error(saved_errno) << "Failed to open " << target; - } - LOG(WARNING) << "Fallback to buffered I/O for " << target; - target_fd.reset(open(target.c_str(), O_RDONLY | O_CLOEXEC)); - if (target_fd.get() == -1) { - return ErrnoError() << "Failed to open " << target; - } - } - - struct loop_info64 li; - memset(&li, 0, sizeof(li)); - strlcpy((char*)li.lo_crypt_name, kApexLoopIdPrefix, LO_NAME_SIZE); - li.lo_offset = image_offset; - li.lo_sizelimit = image_size; - // Automatically free loop device on last close. - li.lo_flags |= LO_FLAGS_AUTOCLEAR; - - if (use_loop_configure) { - struct loop_config config; - memset(&config, 0, sizeof(config)); - li.lo_flags |= LO_FLAGS_DIRECT_IO; - config.fd = target_fd.get(); - config.info = li; - config.block_size = 4096; - - if (ioctl(device_fd, LOOP_CONFIGURE, &config) == -1) { - return ErrnoError() << "Failed to LOOP_CONFIGURE"; - } - - return {}; - } else { - if (ioctl(device_fd, LOOP_SET_FD, target_fd.get()) == -1) { - return ErrnoError() << "Failed to LOOP_SET_FD"; - } - - if (ioctl(device_fd, LOOP_SET_STATUS64, &li) == -1) { - return ErrnoError() << "Failed to LOOP_SET_STATUS64"; - } - - if (ioctl(device_fd, BLKFLSBUF, 0) == -1) { - // This works around a kernel bug where the following happens. - // 1) The device runs with a value of loop.max_part > 0 - // 2) As part of LOOP_SET_FD above, we do a partition scan, which loads - // the first 2 pages of the underlying file into the buffer cache - // 3) When we then change the offset with LOOP_SET_STATUS64, those pages - // are not invalidated from the cache. - // 4) When we try to mount an ext4 filesystem on the loop device, the ext4 - // code will try to find a superblock by reading 4k at offset 0; but, - // because we still have the old pages at offset 0 lying in the cache, - // those pages will be returned directly. However, those pages contain - // the data at offset 0 in the underlying file, not at the offset that - // we configured - // 5) the ext4 driver fails to find a superblock in the (wrong) data, and - // fails to mount the filesystem. - // - // To work around this, explicitly flush the block device, which will - // flush the buffer cache and make sure we actually read the data at the - // correct offset. - return ErrnoError() << "Failed to flush buffers on the loop device"; - } - - // Direct-IO requires the loop device to have the same block size as the - // underlying filesystem. - if (ioctl(device_fd, LOOP_SET_BLOCK_SIZE, 4096) == -1) { - PLOG(WARNING) << "Failed to LOOP_SET_BLOCK_SIZE"; - } + return ErrnoError() << "Failed to open " << target; } - return {}; -} - -Result<LoopbackDeviceUniqueFd> WaitForDevice(int num) { - std::string opened_device; - const std::vector<std::string> candidate_devices = { - StringPrintf("/dev/block/loop%d", num), - StringPrintf("/dev/loop%d", num), - }; - - // apexd-bootstrap runs in parallel with ueventd to optimize boot time. In - // rare cases apexd would try attempt to mount an apex before ueventd created - // a loop device for it. To work around this we keep polling for loop device - // to be created until ueventd's cold boot sequence is done. - // See comment on kLoopDeviceRetryAttempts. - unique_fd sysfs_fd; - bool cold_boot_done = GetBoolProperty("ro.cold_boot_done", false); - for (size_t i = 0; i != kLoopDeviceRetryAttempts; ++i) { - if (!cold_boot_done) { - cold_boot_done = GetBoolProperty("ro.cold_boot_done", false); - } - for (const auto& device : candidate_devices) { + LoopbackDeviceUniqueFd device_fd; + { + // See comment on kLoopDeviceRetryAttempts. + unique_fd sysfs_fd; + for (size_t i = 0; i != kLoopDeviceRetryAttempts; ++i) { sysfs_fd.reset(open(device.c_str(), O_RDWR | O_CLOEXEC)); if (sysfs_fd.get() != -1) { - return LoopbackDeviceUniqueFd(std::move(sysfs_fd), device); + break; } + PLOG(WARNING) << "Loopback device " << device + << " not ready. Waiting 50ms..."; + usleep(50000); } - PLOG(WARNING) << "Loopback device " << num << " not ready. Waiting 50ms..."; - usleep(50000); - if (!cold_boot_done) { - // ueventd hasn't finished cold boot yet, keep trying. - i = 0; + if (sysfs_fd.get() == -1) { + return ErrnoError() << "Failed to open " << device; } + device_fd = LoopbackDeviceUniqueFd(std::move(sysfs_fd), device); + CHECK_NE(device_fd.get(), -1); } - return Error() << "Faled to open loopback device " << num; -} - -Result<LoopbackDeviceUniqueFd> CreateLoopDevice(const std::string& target, - const int32_t image_offset, - const size_t image_size) { - unique_fd ctl_fd(open("/dev/loop-control", O_RDWR | O_CLOEXEC)); - if (ctl_fd.get() == -1) { - return ErrnoError() << "Failed to open loop-control"; - } - - static std::mutex mlock; - std::lock_guard lock(mlock); - int num = ioctl(ctl_fd.get(), LOOP_CTL_GET_FREE); - if (num == -1) { - return ErrnoError() << "Failed LOOP_CTL_GET_FREE"; - } - - Result<LoopbackDeviceUniqueFd> loop_device = WaitForDevice(num); - if (!loop_device.ok()) { - return loop_device.error(); + if (ioctl(device_fd.get(), LOOP_SET_FD, target_fd.get()) == -1) { + return ErrnoError() << "Failed to LOOP_SET_FD"; } - CHECK_NE(loop_device->device_fd.get(), -1); - Result<void> configureStatus = ConfigureLoopDevice( - loop_device->device_fd.get(), target, image_offset, image_size); - if (!configureStatus.ok()) { - return configureStatus.error(); - } - - Result<void> read_ahead_status = ConfigureReadAhead(loop_device->name); - if (!read_ahead_status.ok()) { - return read_ahead_status.error(); - } - - return loop_device; + struct loop_info64 li; + memset(&li, 0, sizeof(li)); + strlcpy((char*)li.lo_crypt_name, kApexLoopIdPrefix, LO_NAME_SIZE); + li.lo_offset = imageOffset; + li.lo_sizelimit = imageSize; + if (ioctl(device_fd.get(), LOOP_SET_STATUS64, &li) == -1) { + return ErrnoError() << "Failed to LOOP_SET_STATUS64"; + } + + if (ioctl(device_fd.get(), BLKFLSBUF, 0) == -1) { + // This works around a kernel bug where the following happens. + // 1) The device runs with a value of loop.max_part > 0 + // 2) As part of LOOP_SET_FD above, we do a partition scan, which loads + // the first 2 pages of the underlying file into the buffer cache + // 3) When we then change the offset with LOOP_SET_STATUS64, those pages + // are not invalidated from the cache. + // 4) When we try to mount an ext4 filesystem on the loop device, the ext4 + // code will try to find a superblock by reading 4k at offset 0; but, + // because we still have the old pages at offset 0 lying in the cache, + // those pages will be returned directly. However, those pages contain + // the data at offset 0 in the underlying file, not at the offset that + // we configured + // 5) the ext4 driver fails to find a superblock in the (wrong) data, and + // fails to mount the filesystem. + // + // To work around this, explicitly flush the block device, which will flush + // the buffer cache and make sure we actually read the data at the correct + // offset. + return ErrnoError() << "Failed to flush buffers on the loop device"; + } + + // Direct-IO requires the loop device to have the same block size as the + // underlying filesystem. + if (ioctl(device_fd.get(), LOOP_SET_BLOCK_SIZE, 4096) == -1) { + PLOG(WARNING) << "Failed to LOOP_SET_BLOCK_SIZE"; + } + + Result<void> readAheadStatus = configureReadAhead(device); + if (!readAheadStatus.ok()) { + return readAheadStatus.error(); + } + return device_fd; } void DestroyLoopDevice(const std::string& path, const DestroyLoopFn& extra) { diff --git a/apexd/apexd_loop.h b/apexd/apexd_loop.h index c727944a..8341675d 100644 --- a/apexd/apexd_loop.h +++ b/apexd/apexd_loop.h @@ -38,7 +38,7 @@ struct LoopbackDeviceUniqueFd { : device_fd(std::move(fd)), name(name) {} LoopbackDeviceUniqueFd(LoopbackDeviceUniqueFd&& fd) noexcept - : device_fd(std::move(fd.device_fd)), name(std::move(fd.name)) {} + : device_fd(std::move(fd.device_fd)), name(fd.name) {} LoopbackDeviceUniqueFd& operator=(LoopbackDeviceUniqueFd&& other) noexcept { MaybeCloseBad(); device_fd = std::move(other.device_fd); @@ -52,16 +52,16 @@ struct LoopbackDeviceUniqueFd { void CloseGood() { device_fd.reset(-1); } - int Get() { return device_fd.get(); } + int get() { return device_fd.get(); } }; -android::base::Result<void> ConfigureReadAhead(const std::string& device_path); +android::base::Result<void> configureReadAhead(const std::string& device_path); -android::base::Result<void> PreAllocateLoopDevices(size_t num); +android::base::Result<void> preAllocateLoopDevices(size_t num); -android::base::Result<LoopbackDeviceUniqueFd> CreateLoopDevice( - const std::string& target, const int32_t image_offset, - const size_t image_size); +android::base::Result<LoopbackDeviceUniqueFd> createLoopDevice( + const std::string& target, const int32_t imageOffset, + const size_t imageSize); using DestroyLoopFn = std::function<void(const std::string&, const std::string&)>; diff --git a/apexd/apexd_main.cpp b/apexd/apexd_main.cpp index 26d5732e..c60f4faf 100644 --- a/apexd/apexd_main.cpp +++ b/apexd/apexd_main.cpp @@ -17,15 +17,14 @@ #define LOG_TAG "apexd" #include <strings.h> -#include <sys/stat.h> #include <ApexProperties.sysprop.h> #include <android-base/logging.h> #include "apexd.h" #include "apexd_checkpoint_vold.h" -#include "apexd_lifecycle.h" #include "apexd_prepostinstall.h" +#include "apexd_prop.h" #include "apexservice.h" #include <android-base/properties.h> @@ -45,17 +44,12 @@ int HandleSubcommand(char** argv) { if (strcmp("--bootstrap", argv[1]) == 0) { LOG(INFO) << "Bootstrap subcommand detected"; - return android::apex::OnBootstrap(); + return android::apex::onBootstrap(); } if (strcmp("--unmount-all", argv[1]) == 0) { LOG(INFO) << "Unmount all subcommand detected"; - return android::apex::UnmountAll(); - } - - if (strcmp("--otachroot-bootstrap", argv[1]) == 0) { - LOG(INFO) << "OTA chroot bootstrap subcommand detected"; - return android::apex::OnOtaChrootBootstrap(); + return android::apex::unmountAll(); } if (strcmp("--snapshotde", argv[1]) == 0) { @@ -68,16 +62,16 @@ int HandleSubcommand(char** argv) { LOG(ERROR) << "Could not retrieve vold service: " << vold_service_st.error(); } else { - android::apex::InitializeVold(&*vold_service_st); + android::apex::initializeVold(&*vold_service_st); } - int result = android::apex::SnapshotOrRestoreDeUserData(); + int result = android::apex::snapshotOrRestoreDeUserData(); if (result == 0) { // Notify other components (e.g. init) that all APEXs are ready to be used // Note that it's important that the binder service is registered at this // point, since other system services might depend on it. - android::apex::OnAllPackagesReady(); + android::apex::onAllPackagesReady(); } return result; } @@ -104,40 +98,20 @@ void InstallSigtermSignalHandler() { int main(int /*argc*/, char** argv) { android::base::InitLogging(argv, &android::base::KernelLogger); - // TODO(b/158468454): add a -v flag or an external setting to change severity. - android::base::SetMinimumLogSeverity(android::base::INFO); - - // set umask to 022 so that files/dirs created are accessible to other - // processes e.g.) apex-info-file.xml is supposed to be read by other - // processes - umask(022); + // TODO: add a -v flag or an external setting to change LogSeverity. + android::base::SetMinimumLogSeverity(android::base::VERBOSE); InstallSigtermSignalHandler(); - android::apex::SetConfig(android::apex::kDefaultConfig); - - android::apex::ApexdLifecycle& lifecycle = - android::apex::ApexdLifecycle::GetInstance(); - bool booting = lifecycle.IsBooting(); - const bool has_subcommand = argv[1] != nullptr; if (!android::sysprop::ApexProperties::updatable().value_or(false)) { + LOG(INFO) << "This device does not support updatable APEX. Exiting"; if (!has_subcommand) { - if (!booting) { - // We've finished booting, but for some reason somebody tried to start - // apexd. Simply exit. - return 0; - } - - LOG(INFO) << "This device does not support updatable APEX. Exiting"; - // Mark apexd as activated so that init can proceed. - android::apex::OnAllPackagesActivated(/*is_bootstrap=*/false); + // mark apexd as activated so that init can proceed + android::apex::onAllPackagesActivated(); } else if (strcmp("--snapshotde", argv[1]) == 0) { - LOG(INFO) << "This device does not support updatable APEX. Exiting"; // mark apexd as ready - android::apex::OnAllPackagesReady(); - } else if (strcmp("--otachroot-bootstrap", argv[1]) == 0) { - return android::apex::OnOtaChrootBootstrapFlattenedApex(); + android::apex::onAllPackagesReady(); } return 0; } @@ -155,21 +129,12 @@ int main(int /*argc*/, char** argv) { } else { vold_service = &*vold_service_st; } - android::apex::Initialize(vold_service); + android::apex::initialize(vold_service); + bool booting = android::apex::isBooting(); if (booting) { - if (auto res = android::apex::MigrateSessionsDirIfNeeded(); !res.ok()) { - LOG(ERROR) << "Failed to migrate sessions to /metadata partition : " - << res.error(); - } - android::apex::OnStart(); - } else { - // TODO(b/172911822): Trying to use data apex related ApexFileRepository - // apis without initializing it should throw error. Also, unit tests should - // not pass without initialization. - // TODO(b/172911822): Consolidate this with Initialize() when - // ApexFileRepository can act as cache and re-scanning is not expensive - android::apex::InitializeDataApex(); + android::apex::migrateSessionsDirIfNeeded(); + android::apex::onStart(); } android::apex::binder::CreateAndRegisterService(); android::apex::binder::StartThreadPool(); @@ -181,8 +146,10 @@ int main(int /*argc*/, char** argv) { // themselves should wait for the ready status instead, which is set when // the "--snapshotde" subcommand is received and snapshot/restore is // complete. - android::apex::OnAllPackagesActivated(/*is_bootstrap=*/false); - lifecycle.WaitForBootStatus(android::apex::RevertActiveSessionsAndReboot); + android::apex::onAllPackagesActivated(); + android::apex::waitForBootStatus( + android::apex::revertActiveSessionsAndReboot, + android::apex::bootCompletedCleanup); } android::apex::binder::AllowServiceShutdown(); diff --git a/apexd/apexd_prepostinstall.cpp b/apexd/apexd_prepostinstall.cpp index 51b60b4b..21b8650a 100644 --- a/apexd/apexd_prepostinstall.cpp +++ b/apexd/apexd_prepostinstall.cpp @@ -35,7 +35,6 @@ #include "apex_database.h" #include "apex_file.h" -#include "apex_manifest.h" #include "apexd.h" #include "apexd_private.h" #include "apexd_utils.h" @@ -43,7 +42,6 @@ using android::base::Error; using android::base::Result; -using ::apex::proto::ApexManifest; namespace android { namespace apex { @@ -60,12 +58,14 @@ void CloseSTDDescriptors() { close(STDERR_FILENO); } +// Instead of temp mounting inside this fuction, we can make a caller do it. +// This will align with the plan of extending temp mounting to provide a +// way to run additional pre-reboot verification of an APEX. +// TODO(ioffe): pass mount points instead of apex files. template <typename Fn> -Result<void> StageFnInstall(const std::vector<ApexFile>& apexes, - const std::vector<std::string>& mount_points, Fn fn, +Result<void> StageFnInstall(const std::vector<ApexFile>& apexes, Fn fn, const char* arg, const char* name) { - // TODO(b/158470023): consider supporting a session with more than one - // pre-install hook. + // TODO: Support a session with more than one pre-install hook. int hook_idx = -1; for (size_t i = 0; i < apexes.size(); i++) { if (!(apexes[i].GetManifest().*fn)().empty()) { @@ -78,32 +78,73 @@ Result<void> StageFnInstall(const std::vector<ApexFile>& apexes, CHECK(hook_idx != -1); LOG(VERBOSE) << name << " for " << apexes[hook_idx].GetPath(); - // Create invocation args. + std::vector<MountedApexData> mounted_apexes; + std::vector<std::string> activation_dirs; + auto preinstall_guard = android::base::make_scope_guard([&]() { + for (const auto& mount : mounted_apexes) { + Result<void> st = apexd_private::Unmount(mount); + if (!st.ok()) { + LOG(ERROR) << "Failed to unmount " << mount.full_path << " from " + << mount.mount_point << " after " << name << ": " + << st.error(); + } + } + for (const std::string& active_point : activation_dirs) { + if (0 != rmdir(active_point.c_str())) { + PLOG(ERROR) << "Could not delete temporary active point " + << active_point; + } + } + }); + + for (const ApexFile& apex : apexes) { + // 1) Mount the package. + std::string mount_point = + apexd_private::GetPackageTempMountPoint(apex.GetManifest()); + + auto mount_data = apexd_private::TempMountPackage(apex, mount_point); + if (!mount_data.ok()) { + return mount_data.error(); + } + mounted_apexes.push_back(std::move(*mount_data)); + + // Given the fact, that we only allow updates of existing APEXes, all the + // activation points will always be already created. Only scenario, when it + // won't be the case might be apexservice_test. But even then, it might be + // safer to move active_point creation logic to run after unshare. + // TODO(ioffe): move creation of activation points inside RunFnInstall? + // 2) Ensure there is an activation point, and we will clean it up. + std::string active_point = + apexd_private::GetActiveMountPoint(apex.GetManifest()); + if (0 == mkdir(active_point.c_str(), kMkdirMode)) { + activation_dirs.emplace_back(std::move(active_point)); + } else { + int saved_errno = errno; + if (saved_errno != EEXIST) { + return Error() << "Unable to create mount point" << active_point << ": " + << strerror(saved_errno); + } + } + } + + // 3) Create invocation args. std::vector<std::string> args{ "/system/bin/apexd", arg, - mount_points[hook_idx] // Make the APEX with hook first. + mounted_apexes[hook_idx].mount_point, // Make the APEX with hook first. }; - for (size_t i = 0; i < mount_points.size(); i++) { + for (size_t i = 0; i < mounted_apexes.size(); i++) { if ((int)i != hook_idx) { - args.push_back(mount_points[i]); + args.push_back(mounted_apexes[i].mount_point); } } - return ForkAndRun(args); + std::string error_msg; + int res = ForkAndRun(args, &error_msg); + return res == 0 ? Result<void>{} : Error() << error_msg; } template <typename Fn> int RunFnInstall(char** in_argv, Fn fn, const char* name) { - std::vector<std::string> activation_dirs; - auto preinstall_guard = android::base::make_scope_guard([&]() { - for (const std::string& active_point : activation_dirs) { - if (0 != rmdir(active_point.c_str())) { - PLOG(ERROR) << "Could not delete temporary active point " - << active_point; - } - } - }); - // 1) Unshare. if (unshare(CLONE_NEWNS) != 0) { PLOG(ERROR) << "Failed to unshare() for apex " << name; @@ -119,8 +160,7 @@ int RunFnInstall(char** in_argv, Fn fn, const char* name) { std::string hook_path; { - auto bind_fn = [&fn, name, - activation_dirs](const std::string& mount_point) mutable { + auto bind_fn = [&fn, name](const std::string& mount_point) { std::string hook; std::string active_point; { @@ -143,14 +183,6 @@ int RunFnInstall(char** in_argv, Fn fn, const char* name) { const auto& manifest = *manifest_or; hook = (manifest.*fn)(); active_point = apexd_private::GetActiveMountPoint(manifest); - // Ensure there is an activation point. If not, create one and delete - // later. - if (0 == mkdir(active_point.c_str(), kMkdirMode)) { - activation_dirs.push_back(active_point); - } else if (errno != EEXIST) { - PLOG(ERROR) << "Unable to create mount point " << active_point; - _exit(205); - } } // 3) Activate the new apex. @@ -201,19 +233,17 @@ int RunFnInstall(char** in_argv, Fn fn, const char* name) { } // namespace -Result<void> StagePreInstall(const std::vector<ApexFile>& apexes, - const std::vector<std::string>& mount_points) { - return StageFnInstall(apexes, mount_points, &ApexManifest::preinstallhook, - "--pre-install", "pre-install"); +Result<void> StagePreInstall(const std::vector<ApexFile>& apexes) { + return StageFnInstall(apexes, &ApexManifest::preinstallhook, "--pre-install", + "pre-install"); } int RunPreInstall(char** in_argv) { return RunFnInstall(in_argv, &ApexManifest::preinstallhook, "pre-install"); } -Result<void> StagePostInstall(const std::vector<ApexFile>& apexes, - const std::vector<std::string>& mount_points) { - return StageFnInstall(apexes, mount_points, &ApexManifest::postinstallhook, +Result<void> StagePostInstall(const std::vector<ApexFile>& apexes) { + return StageFnInstall(apexes, &ApexManifest::postinstallhook, "--post-install", "post-install"); } diff --git a/apexd/apexd_prepostinstall.h b/apexd/apexd_prepostinstall.h index 65125bae..66cd2f57 100644 --- a/apexd/apexd_prepostinstall.h +++ b/apexd/apexd_prepostinstall.h @@ -27,20 +27,16 @@ namespace apex { class ApexFile; -// Forks into: apexd --pre-install <mount-point-of-apex-with-hook> -// [<other-mount-points>] The caller must pass the temp mount point for each -// apex file. +// Temp mounts given apexes and then forks into: +// apexd --pre-install <mount-point-of-apex-with-hook> [<other-mount-points>] android::base::Result<void> StagePreInstall( - const std::vector<ApexFile>& apexes, - const std::vector<std::string>& mount_points); + const std::vector<ApexFile>& apexes); int RunPreInstall(char** argv); -// Forks into: apexd --post-install <mount-point-of-apex-with-hook> -// [<other-mount-points>] The caller must pass the temp mount point for each -// apex file. +// Temp mounts given apexes and then forks into: +// apexd --post-install <mount-point-of-apex-with-hook> [<other-mount-points>] android::base::Result<void> StagePostInstall( - const std::vector<ApexFile>& apexes, - const std::vector<std::string>& mount_points); + const std::vector<ApexFile>& apexes); int RunPostInstall(char** argv); } // namespace apex diff --git a/apexd/apexd_private.h b/apexd/apexd_private.h index b3108cff..1706e08b 100644 --- a/apexd/apexd_private.h +++ b/apexd/apexd_private.h @@ -21,7 +21,6 @@ #include <android-base/result.h> #include "apex_database.h" -#include "apex_file.h" #include "apex_manifest.h" namespace android { @@ -33,16 +32,16 @@ static constexpr int kMkdirMode = 0755; namespace apexd_private { -std::string GetPackageMountPoint(const ::apex::proto::ApexManifest& manifest); -std::string GetPackageTempMountPoint( - const ::apex::proto::ApexManifest& manifest); -std::string GetActiveMountPoint(const ::apex::proto::ApexManifest& manifest); +std::string GetPackageMountPoint(const ApexManifest& manifest); +std::string GetPackageTempMountPoint(const ApexManifest& manifest); +std::string GetActiveMountPoint(const ApexManifest& manifest); android::base::Result<void> BindMount(const std::string& target, const std::string& source); -android::base::Result<MountedApexDatabase::MountedApexData> -GetTempMountedApexData(const std::string& package); -android::base::Result<void> UnmountTempMount(const ApexFile& apex); +android::base::Result<MountedApexDatabase::MountedApexData> TempMountPackage( + const ApexFile& apex, const std::string& mount_point); +android::base::Result<void> Unmount( + const MountedApexDatabase::MountedApexData& data); } // namespace apexd_private } // namespace apex diff --git a/apexd/apexd_lifecycle.cpp b/apexd/apexd_prop.cpp index 41c1abef..31030e10 100644 --- a/apexd/apexd_lifecycle.cpp +++ b/apexd/apexd_prop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,28 +16,23 @@ #define LOG_TAG "apexd" -#include "apexd_lifecycle.h" +#include "apexd_prop.h" #include <android-base/logging.h> #include <android-base/properties.h> #include "apexd_utils.h" +using android::base::GetBoolProperty; using android::base::GetProperty; using android::base::Result; using android::base::WaitForProperty; namespace android { namespace apex { - -bool ApexdLifecycle::IsBooting() { - auto status = GetProperty(kApexStatusSysprop, ""); - return status != kApexStatusReady && status != kApexStatusActivated; -} - -void ApexdLifecycle::WaitForBootStatus( - Result<void> (&revert_fn)(const std::string&, const std::string&)) { - while (!boot_completed_) { +void waitForBootStatus(Result<void> (&revert_fn)(const std::string&), + void (&complete_fn)()) { + while (!GetBoolProperty("sys.boot_completed", false)) { // Check for change in either crashing property or sys.boot_completed // Wait for updatable_crashing property change for most of the time // (arbitrary 30s), briefly check if boot has completed successfully, @@ -49,7 +44,7 @@ void ApexdLifecycle::WaitForBootStatus( auto name = GetProperty("sys.init.updatable_crashing_process_name", ""); LOG(ERROR) << "Native process '" << (name.empty() ? "[unknown]" : name) << "' is crashing. Attempting a revert"; - auto result = revert_fn(name, ""); + auto result = revert_fn(name); if (!result.ok()) { LOG(ERROR) << "Revert failed : " << result.error(); break; @@ -64,9 +59,15 @@ void ApexdLifecycle::WaitForBootStatus( } } } + // Wait for boot to complete, and then run complete_fn. + // TODO(ioffe): this is a hack, instead we should have a binder call from + // system_server into apexd when boot completes. + if (WaitForProperty("sys.boot_completed", "1", std::chrono::minutes(5))) { + complete_fn(); + return; + } else { + LOG(ERROR) << "Boot never completed"; + } } - -void ApexdLifecycle::MarkBootCompleted() { boot_completed_ = true; } - } // namespace apex } // namespace android diff --git a/apexd/apexd_prop.h b/apexd/apexd_prop.h new file mode 100644 index 00000000..4b8a7661 --- /dev/null +++ b/apexd/apexd_prop.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef ANDROID_APEXD_APEXD_PROP_H_ +#define ANDROID_APEXD_APEXD_PROP_H_ + +#include <android-base/result.h> + +namespace android { +namespace apex { + +void waitForBootStatus( + android::base::Result<void> (&rollback_fn)(const std::string&), + void (&complete_fn)()); + +} // namespace apex +} // namespace android + +#endif // ANDROID_APEXD_APEXD_PROP_H diff --git a/apexd/apexd_rollback_utils.h b/apexd/apexd_rollback_utils.h index c7fa05ec..9990be26 100644 --- a/apexd/apexd_rollback_utils.h +++ b/apexd/apexd_rollback_utils.h @@ -25,6 +25,10 @@ #include <android-base/result.h> #include <android-base/scopeguard.h> #include <logwrap/logwrap.h> +#include <selinux/android.h> + +using android::base::Error; +using android::base::Result; namespace android { namespace apex { @@ -36,7 +40,7 @@ static constexpr const char* kCpPath = "/system/bin/cp"; * path. Note that this will fail if run before APEXes are mounted, due to a * dependency on runtime. */ -inline int32_t CopyDirectoryRecursive(const char* from, const char* to) { +int32_t copy_directory_recursive(const char* from, const char* to) { const char* const argv[] = { kCpPath, "-F", /* delete any existing destination file first @@ -58,15 +62,15 @@ inline int32_t CopyDirectoryRecursive(const char* from, const char* to) { * from from_path into to_path. Note that this must be run after APEXes are * mounted. */ -inline android::base::Result<void> ReplaceFiles(const std::string& from_path, - const std::string& to_path) { +inline Result<void> ReplaceFiles(const std::string& from_path, + const std::string& to_path) { namespace fs = std::filesystem; std::error_code error_code; fs::remove_all(to_path, error_code); if (error_code) { - return android::base::Error() << "Failed to delete existing files at " - << to_path << " : " << error_code.message(); + return Error() << "Failed to delete existing files at " << to_path << " : " + << error_code.message(); } auto deleter = [&] { @@ -79,15 +83,23 @@ inline android::base::Result<void> ReplaceFiles(const std::string& from_path, }; auto scope_guard = android::base::make_scope_guard(deleter); - int rc = CopyDirectoryRecursive(from_path.c_str(), to_path.c_str()); + int rc = copy_directory_recursive(from_path.c_str(), to_path.c_str()); if (rc != 0) { - return android::base::Error() << "Failed to copy from [" << from_path - << "] to [" << to_path << "]"; + return Error() << "Failed to copy from [" << from_path << "] to [" + << to_path << "]"; } scope_guard.Disable(); return {}; } +inline Result<void> RestoreconPath(const std::string& path) { + unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE; + if (selinux_android_restorecon(path.c_str(), seflags) < 0) { + return Error() << "Failed to restorecon " << path; + } + return {}; +} + } // namespace apex } // namespace android diff --git a/apexd/apexd_session.cpp b/apexd/apexd_session.cpp index defef6fa..3d916c48 100644 --- a/apexd/apexd_session.cpp +++ b/apexd/apexd_session.cpp @@ -22,7 +22,6 @@ #include "session_state.pb.h" #include <android-base/logging.h> -#include <android-base/stringprintf.h> #include <dirent.h> #include <sys/stat.h> @@ -33,7 +32,6 @@ using android::base::Error; using android::base::Result; -using android::base::StringPrintf; using apex::proto::SessionState; namespace android { @@ -41,57 +39,68 @@ namespace apex { namespace { -// Starting from R, apexd prefers /metadata partition (kNewApexSessionsDir) as -// location for sessions-related information. For devices that don't have -// /metadata partition, apexd will fallback to the /data one -// (kOldApexSessionsDir). -static constexpr const char* kOldApexSessionsDir = "/data/apex/sessions"; -static constexpr const char* kNewApexSessionsDir = "/metadata/apex/sessions"; - static constexpr const char* kStateFileName = "state"; -} // namespace +std::string getSessionDir(int session_id) { + return kApexSessionsDir + "/" + std::to_string(session_id); +} -ApexSession::ApexSession(SessionState state) : state_(std::move(state)) {} +std::string getSessionStateFilePath(int session_id) { + return getSessionDir(session_id) + "/" + kStateFileName; +} -std::string ApexSession::GetSessionsDir() { - static std::string result; - static std::once_flag once_flag; - std::call_once(once_flag, [&]() { - auto status = - FindFirstExistingDirectory(kNewApexSessionsDir, kOldApexSessionsDir); - if (!status.ok()) { - LOG(FATAL) << status.error(); - } - result = std::move(*status); - }); - return result; +Result<std::string> createSessionDirIfNeeded(int session_id) { + // create /data/sessions + auto res = createDirIfNeeded(kApexSessionsDir, 0700); + if (!res.ok()) { + return res.error(); + } + // create /data/sessions/session_id + std::string sessionDir = getSessionDir(session_id); + res = createDirIfNeeded(sessionDir, 0700); + if (!res.ok()) { + return res.error(); + } + + return sessionDir; } -Result<void> ApexSession::MigrateToMetadataSessionsDir() { - return MoveDir(kOldApexSessionsDir, kNewApexSessionsDir); +Result<void> deleteSessionDir(int session_id) { + std::string session_dir = getSessionDir(session_id); + LOG(DEBUG) << "Deleting " << session_dir; + auto path = std::filesystem::path(session_dir); + std::error_code error_code; + std::filesystem::remove_all(path, error_code); + if (error_code) { + return Error() << "Failed to delete " << session_dir << " : " + << error_code.message(); + } + return {}; } +} // namespace + +ApexSession::ApexSession(SessionState state) : state_(std::move(state)) {} + Result<ApexSession> ApexSession::CreateSession(int session_id) { SessionState state; // Create session directory - std::string session_dir = GetSessionsDir() + "/" + std::to_string(session_id); - if (auto status = CreateDirIfNeeded(session_dir, 0700); !status.ok()) { - return status.error(); + auto sessionPath = createSessionDirIfNeeded(session_id); + if (!sessionPath.ok()) { + return sessionPath.error(); } state.set_id(session_id); return ApexSession(state); } - Result<ApexSession> ApexSession::GetSessionFromFile(const std::string& path) { SessionState state; - std::fstream state_file(path, std::ios::in | std::ios::binary); - if (!state_file) { + std::fstream stateFile(path, std::ios::in | std::ios::binary); + if (!stateFile) { return Error() << "Failed to open " << path; } - if (!state.ParseFromIstream(&state_file)) { + if (!state.ParseFromIstream(&stateFile)) { return Error() << "Failed to parse " << path; } @@ -99,8 +108,7 @@ Result<ApexSession> ApexSession::GetSessionFromFile(const std::string& path) { } Result<ApexSession> ApexSession::GetSession(int session_id) { - auto path = StringPrintf("%s/%d/%s", GetSessionsDir().c_str(), session_id, - kStateFileName); + auto path = getSessionStateFilePath(session_id); return GetSessionFromFile(path); } @@ -108,19 +116,19 @@ Result<ApexSession> ApexSession::GetSession(int session_id) { std::vector<ApexSession> ApexSession::GetSessions() { std::vector<ApexSession> sessions; - Result<std::vector<std::string>> session_paths = ReadDir( - GetSessionsDir(), [](const std::filesystem::directory_entry& entry) { + Result<std::vector<std::string>> sessionPaths = ReadDir( + kApexSessionsDir, [](const std::filesystem::directory_entry& entry) { std::error_code ec; return entry.is_directory(ec); }); - if (!session_paths.ok()) { + if (!sessionPaths.ok()) { return sessions; } - for (const std::string& session_dir_path : *session_paths) { + for (const std::string& sessionDirPath : *sessionPaths) { // Try to read session state - auto session = GetSessionFromFile(session_dir_path + "/" + kStateFileName); + auto session = GetSessionFromFile(sessionDirPath + "/" + kStateFileName); if (!session.ok()) { LOG(WARNING) << session.error(); continue; @@ -144,20 +152,20 @@ std::vector<ApexSession> ApexSession::GetSessionsInState( std::vector<ApexSession> ApexSession::GetActiveSessions() { auto sessions = GetSessions(); - std::vector<ApexSession> active_sessions; + std::vector<ApexSession> activeSessions; for (const ApexSession& session : sessions) { if (!session.IsFinalized() && session.GetState() != SessionState::UNKNOWN) { - active_sessions.push_back(session); + activeSessions.push_back(session); } } - return active_sessions; + return activeSessions; } SessionState::State ApexSession::GetState() const { return state_.state(); } int ApexSession::GetId() const { return state_.id(); } -const std::string& ApexSession::GetBuildFingerprint() const { +std::string ApexSession::GetBuildFingerprint() const { return state_.expected_build_fingerprint(); } @@ -181,14 +189,10 @@ bool ApexSession::IsRollback() const { return state_.is_rollback(); } int ApexSession::GetRollbackId() const { return state_.rollback_id(); } -const std::string& ApexSession::GetCrashingNativeProcess() const { +std::string ApexSession::GetCrashingNativeProcess() const { return state_.crashing_native_process(); } -const std::string& ApexSession::GetErrorMessage() const { - return state_.error_message(); -} - const google::protobuf::RepeatedField<int> ApexSession::GetChildSessionIds() const { return state_.child_session_ids(); @@ -226,10 +230,6 @@ void ApexSession::SetCrashingNativeProcess( state_.set_crashing_native_process(crashing_process); } -void ApexSession::SetErrorMessage(const std::string& error_message) { - state_.set_error_message(error_message); -} - void ApexSession::AddApexName(const std::string& apex_name) { state_.add_apex_names(apex_name); } @@ -238,29 +238,19 @@ Result<void> ApexSession::UpdateStateAndCommit( const SessionState::State& session_state) { state_.set_state(session_state); - auto state_file_path = StringPrintf("%s/%d/%s", GetSessionsDir().c_str(), - state_.id(), kStateFileName); + auto stateFilePath = getSessionStateFilePath(state_.id()); - std::fstream state_file(state_file_path, - std::ios::out | std::ios::trunc | std::ios::binary); - if (!state_.SerializeToOstream(&state_file)) { - return Error() << "Failed to write state file " << state_file_path; + std::fstream stateFile(stateFilePath, + std::ios::out | std::ios::trunc | std::ios::binary); + if (!state_.SerializeToOstream(&stateFile)) { + return Error() << "Failed to write state file " << stateFilePath; } return {}; } Result<void> ApexSession::DeleteSession() const { - std::string session_dir = GetSessionsDir() + "/" + std::to_string(GetId()); - LOG(INFO) << "Deleting " << session_dir; - auto path = std::filesystem::path(session_dir); - std::error_code error_code; - std::filesystem::remove_all(path, error_code); - if (error_code) { - return Error() << "Failed to delete " << session_dir << " : " - << error_code.message(); - } - return {}; + return deleteSessionDir(GetId()); } std::ostream& operator<<(std::ostream& out, const ApexSession& session) { @@ -269,18 +259,5 @@ std::ostream& operator<<(std::ostream& out, const ApexSession& session) { << "]"; } -void ApexSession::DeleteFinalizedSessions() { - auto sessions = GetSessions(); - for (const ApexSession& session : sessions) { - if (!session.IsFinalized()) { - continue; - } - auto result = session.DeleteSession(); - if (!result.ok()) { - LOG(WARNING) << "Failed to delete finalized session: " << session.GetId(); - } - } -} - } // namespace apex } // namespace android diff --git a/apexd/apexd_session.h b/apexd/apexd_session.h index 5c1f1e3e..e0cea91c 100644 --- a/apexd/apexd_session.h +++ b/apexd/apexd_session.h @@ -28,18 +28,10 @@ namespace android { namespace apex { +static const std::string kApexSessionsDir = "/metadata/apex/sessions"; + class ApexSession { public: - // Returns top-level directory to store sessions metadata in. - // If device has /metadata partition, this will return - // /metadata/apex/sessions, on all other devices it will return - // /data/apex/sessions. - static std::string GetSessionsDir(); - // Migrates content of /data/apex/sessions to /metadata/apex/sessions. - // If device doesn't have /metadata partition this call will be a no-op. - // If /data/apex/sessions this call will also be a no-op. - static android::base::Result<void> MigrateToMetadataSessionsDir(); - static android::base::Result<ApexSession> CreateSession(int session_id); static android::base::Result<ApexSession> GetSession(int session_id); static std::vector<ApexSession> GetSessions(); @@ -52,9 +44,8 @@ class ApexSession { const google::protobuf::RepeatedField<int> GetChildSessionIds() const; ::apex::proto::SessionState::State GetState() const; int GetId() const; - const std::string& GetBuildFingerprint() const; - const std::string& GetCrashingNativeProcess() const; - const std::string& GetErrorMessage() const; + std::string GetBuildFingerprint() const; + std::string GetCrashingNativeProcess() const; bool IsFinalized() const; bool HasRollbackEnabled() const; bool IsRollback() const; @@ -67,17 +58,15 @@ class ApexSession { void SetIsRollback(const bool is_rollback); void SetRollbackId(const int rollback_id); void SetCrashingNativeProcess(const std::string& crashing_process); - void SetErrorMessage(const std::string& error_message); void AddApexName(const std::string& apex_name); android::base::Result<void> UpdateStateAndCommit( const ::apex::proto::SessionState::State& state); android::base::Result<void> DeleteSession() const; - static void DeleteFinalizedSessions(); private: - explicit ApexSession(::apex::proto::SessionState state); + ApexSession(::apex::proto::SessionState state); ::apex::proto::SessionState state_; static android::base::Result<ApexSession> GetSessionFromFile( diff --git a/apexd/apexd_session_test.cpp b/apexd/apexd_session_test.cpp deleted file mode 100644 index 50d0f858..00000000 --- a/apexd/apexd_session_test.cpp +++ /dev/null @@ -1,126 +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 <filesystem> -#include <fstream> -#include <string> - -#include <errno.h> - -#include <android-base/file.h> -#include <android-base/result.h> -#include <android-base/scopeguard.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <gtest/gtest.h> - -#include "apexd_session.h" -#include "apexd_test_utils.h" -#include "apexd_utils.h" -#include "session_state.pb.h" - -namespace android { -namespace apex { -namespace { - -using android::apex::testing::IsOk; -using android::base::Join; -using android::base::make_scope_guard; - -// TODO(b/170329726): add unit tests for apexd_sessions.h - -TEST(ApexdSessionTest, GetSessionsDirSessionsStoredInMetadata) { - if (access("/metadata", F_OK) != 0) { - GTEST_SKIP() << "Device doesn't have /metadata partition"; - } - - std::string result = ApexSession::GetSessionsDir(); - ASSERT_EQ(result, "/metadata/apex/sessions"); -} - -TEST(ApexdSessionTest, GetSessionsDirNoMetadataPartitionFallbackToData) { - if (access("/metadata", F_OK) == 0) { - GTEST_SKIP() << "Device has /metadata partition"; - } - - std::string result = ApexSession::GetSessionsDir(); - ASSERT_EQ(result, "/data/apex/sessions"); -} - -TEST(ApexdSessionTest, MigrateToMetadataSessionsDir) { - namespace fs = std::filesystem; - - if (access("/metadata", F_OK) != 0) { - GTEST_SKIP() << "Device doesn't have /metadata partition"; - } - - // This is ugly, but does the job. To have a truly hermetic unit tests we - // need to refactor ApexSession class. - for (const auto& entry : fs::directory_iterator("/metadata/apex/sessions")) { - fs::remove_all(entry.path()); - } - - // This is a very ugly test set up, but to have something better we need to - // refactor ApexSession class. - class TestApexSession { - public: - TestApexSession(int id, const SessionState::State& state) { - path_ = "/data/apex/sessions/" + std::to_string(id); - if (auto status = CreateDirIfNeeded(path_, 0700); !status.ok()) { - ADD_FAILURE() << "Failed to create " << path_ << " : " - << status.error(); - } - SessionState session; - session.set_id(id); - session.set_state(state); - std::fstream state_file( - path_ + "/state", std::ios::out | std::ios::trunc | std::ios::binary); - if (!session.SerializeToOstream(&state_file)) { - ADD_FAILURE() << "Failed to write to " << path_; - } - } - - ~TestApexSession() { fs::remove_all(path_); } - - private: - std::string path_; - }; - - auto deleter = make_scope_guard([&]() { - fs::remove_all("/metadata/apex/sessions/239"); - fs::remove_all("/metadata/apex/sessions/1543"); - }); - - TestApexSession session1(239, SessionState::SUCCESS); - TestApexSession session2(1543, SessionState::ACTIVATION_FAILED); - - ASSERT_TRUE(IsOk(ApexSession::MigrateToMetadataSessionsDir())); - - auto sessions = ApexSession::GetSessions(); - ASSERT_EQ(2u, sessions.size()) << Join(sessions, ','); - - auto migrated_session_1 = ApexSession::GetSession(239); - ASSERT_TRUE(IsOk(migrated_session_1)); - ASSERT_EQ(SessionState::SUCCESS, migrated_session_1->GetState()); - - auto migrated_session_2 = ApexSession::GetSession(1543); - ASSERT_TRUE(IsOk(migrated_session_2)); - ASSERT_EQ(SessionState::ACTIVATION_FAILED, migrated_session_2->GetState()); -} - -} // namespace -} // namespace apex -} // namespace android diff --git a/apexd/apexd_test.cpp b/apexd/apexd_test.cpp deleted file mode 100644 index a3ebd03a..00000000 --- a/apexd/apexd_test.cpp +++ /dev/null @@ -1,3603 +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. - */ - -#include <string> -#include <vector> - -#include <android-base/file.h> -#include <android-base/properties.h> -#include <android-base/scopeguard.h> -#include <android-base/stringprintf.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include "apex_database.h" -#include "apex_file_repository.h" -#include "apexd.h" -#include "apexd_checkpoint.h" -#include "apexd_session.h" -#include "apexd_test_utils.h" -#include "apexd_utils.h" - -#include "apex_manifest.pb.h" -#include "com_android_apex.h" -#include "gmock/gmock-matchers.h" - -namespace android { -namespace apex { - -namespace fs = std::filesystem; - -using MountedApexData = MountedApexDatabase::MountedApexData; -using android::apex::testing::ApexFileEq; -using android::apex::testing::IsOk; -using android::base::GetExecutableDirectory; -using android::base::GetProperty; -using android::base::make_scope_guard; -using android::base::RemoveFileIfExists; -using android::base::Result; -using android::base::StringPrintf; -using android::base::unique_fd; -using android::base::WriteStringToFile; -using com::android::apex::testing::ApexInfoXmlEq; -using ::testing::ByRef; -using ::testing::HasSubstr; -using ::testing::IsEmpty; -using ::testing::StartsWith; -using ::testing::UnorderedElementsAre; -using ::testing::UnorderedElementsAreArray; - -static std::string GetTestDataDir() { return GetExecutableDirectory(); } -static std::string GetTestFile(const std::string& name) { - return GetTestDataDir() + "/" + name; -} - -static int64_t GetMTime(const std::string& path) { - struct stat st_buf; - if (stat(path.c_str(), &st_buf) != 0) { - PLOG(ERROR) << "Failed to stat " << path; - return 0; - } - return st_buf.st_mtime; -} - -// A very basic mock of CheckpointInterface. -class MockCheckpointInterface : public CheckpointInterface { - public: - Result<bool> SupportsFsCheckpoints() override { - return supports_fs_checkpoint_; - } - - Result<bool> NeedsCheckpoint() override { return needs_checkpoint_; } - - Result<bool> NeedsRollback() override { return needs_rollback_; } - - Result<void> StartCheckpoint(int32_t num_retries) override { return {}; } - - Result<void> AbortChanges(const std::string& msg, bool retry) override { - return {}; - } - - void SetSupportsCheckpoint(bool value) { supports_fs_checkpoint_ = value; } - - void SetNeedsCheckpoint(bool value) { needs_checkpoint_ = value; } - - void SetNeedsRollback(bool value) { needs_rollback_ = value; } - - private: - bool supports_fs_checkpoint_, needs_checkpoint_, needs_rollback_; -}; - -static constexpr const char* kTestApexdStatusSysprop = "apexd.status.test"; - -// A test fixture that provides frequently required temp directories for tests -class ApexdUnitTest : public ::testing::Test { - public: - ApexdUnitTest() { - built_in_dir_ = StringPrintf("%s/pre-installed-apex", td_.path); - data_dir_ = StringPrintf("%s/data-apex", td_.path); - decompression_dir_ = StringPrintf("%s/decompressed-apex", td_.path); - ota_reserved_dir_ = StringPrintf("%s/ota-reserved", td_.path); - hash_tree_dir_ = StringPrintf("%s/apex-hash-tree", td_.path); - staged_session_dir_ = StringPrintf("%s/staged-session-dir", td_.path); - config_ = {kTestApexdStatusSysprop, {built_in_dir_}, - data_dir_.c_str(), decompression_dir_.c_str(), - ota_reserved_dir_.c_str(), hash_tree_dir_.c_str(), - staged_session_dir_.c_str()}; - } - - const std::string& GetBuiltInDir() { return built_in_dir_; } - const std::string& GetDataDir() { return data_dir_; } - const std::string& GetDecompressionDir() { return decompression_dir_; } - const std::string& GetOtaReservedDir() { return ota_reserved_dir_; } - const std::string& GetHashTreeDir() { return hash_tree_dir_; } - const std::string GetStagedDir(int session_id) { - return StringPrintf("%s/session_%d", staged_session_dir_.c_str(), - session_id); - } - - std::string GetRootDigest(const ApexFile& apex) { - if (apex.IsCompressed()) { - return ""; - } - auto digest = apex.VerifyApexVerity(apex.GetBundledPublicKey()); - if (!digest.ok()) { - return ""; - } - return digest->root_digest; - } - - std::string AddPreInstalledApex(const std::string& apex_name) { - fs::copy(GetTestFile(apex_name), built_in_dir_); - return StringPrintf("%s/%s", built_in_dir_.c_str(), apex_name.c_str()); - } - - std::string AddDataApex(const std::string& apex_name) { - fs::copy(GetTestFile(apex_name), data_dir_); - return StringPrintf("%s/%s", data_dir_.c_str(), apex_name.c_str()); - } - - std::string AddDataApex(const std::string& apex_name, - const std::string& target_name) { - fs::copy(GetTestFile(apex_name), data_dir_ + "/" + target_name); - return StringPrintf("%s/%s", data_dir_.c_str(), target_name.c_str()); - } - - // Copies the compressed apex to |built_in_dir| and decompresses it to - // |decompressed_dir| and then hard links to |target_dir| - std::string PrepareCompressedApex(const std::string& name, - const std::string& built_in_dir) { - fs::copy(GetTestFile(name), built_in_dir); - auto compressed_apex = ApexFile::Open( - StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str())); - std::vector<ApexFileRef> compressed_apex_list; - compressed_apex_list.emplace_back(std::cref(*compressed_apex)); - auto return_value = - ProcessCompressedApex(compressed_apex_list, /*is_ota_chroot*/ false); - return StringPrintf("%s/%s", built_in_dir.c_str(), name.c_str()); - } - - std::string PrepareCompressedApex(const std::string& name) { - return PrepareCompressedApex(name, built_in_dir_); - } - - Result<ApexSession> CreateStagedSession(const std::string& apex_name, - int session_id) { - CreateDirIfNeeded(GetStagedDir(session_id), 0755); - fs::copy(GetTestFile(apex_name), GetStagedDir(session_id)); - auto result = ApexSession::CreateSession(session_id); - result->SetBuildFingerprint(GetProperty("ro.build.fingerprint", "")); - return result; - } - - protected: - void SetUp() override { - SetConfig(config_); - ApexFileRepository::GetInstance().Reset(decompression_dir_); - ASSERT_EQ(mkdir(built_in_dir_.c_str(), 0755), 0); - ASSERT_EQ(mkdir(data_dir_.c_str(), 0755), 0); - ASSERT_EQ(mkdir(decompression_dir_.c_str(), 0755), 0); - ASSERT_EQ(mkdir(ota_reserved_dir_.c_str(), 0755), 0); - ASSERT_EQ(mkdir(hash_tree_dir_.c_str(), 0755), 0); - ASSERT_EQ(mkdir(staged_session_dir_.c_str(), 0755), 0); - - DeleteDirContent(ApexSession::GetSessionsDir()); - } - - void TearDown() override { DeleteDirContent(ApexSession::GetSessionsDir()); } - - private: - TemporaryDir td_; - std::string built_in_dir_; - std::string data_dir_; - std::string decompression_dir_; - std::string ota_reserved_dir_; - std::string hash_tree_dir_; - std::string staged_session_dir_; - ApexdConfig config_; -}; - -// Apex that does not have pre-installed version, does not get selected -TEST_F(ApexdUnitTest, ApexMustHavePreInstalledVersionForSelection) { - AddPreInstalledApex("apex.apexd_test.apex"); - AddPreInstalledApex("com.android.apex.cts.shim.apex"); - auto shared_lib_1 = ApexFile::Open(AddPreInstalledApex( - "com.android.apex.test.sharedlibs_generated.v1.libvX.apex")); - auto& instance = ApexFileRepository::GetInstance(); - // Pre-installed data needs to be present so that we can add data apex - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); - - auto apexd_test_file = ApexFile::Open(AddDataApex("apex.apexd_test.apex")); - auto shim_v1 = ApexFile::Open(AddDataApex("com.android.apex.cts.shim.apex")); - // Normally both pre-installed and data apex would be activated for a shared - // libs apex, but if they are the same version only the data apex will be. - auto shared_lib_2 = ApexFile::Open( - AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex")); - ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); - - const auto all_apex = instance.AllApexFilesByName(); - // Pass a blank instance so that the data apex files are not considered - // pre-installed - const ApexFileRepository instance_blank; - auto result = SelectApexForActivation(all_apex, instance_blank); - ASSERT_EQ(result.size(), 0u); - // When passed proper instance they should get selected - result = SelectApexForActivation(all_apex, instance); - ASSERT_EQ(result.size(), 3u); - ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)), - ApexFileEq(ByRef(*shim_v1)), - ApexFileEq(ByRef(*shared_lib_2)))); -} - -// Higher version gets priority when selecting for activation -TEST_F(ApexdUnitTest, HigherVersionOfApexIsSelected) { - auto apexd_test_file_v2 = - ApexFile::Open(AddPreInstalledApex("apex.apexd_test_v2.apex")); - AddPreInstalledApex("com.android.apex.cts.shim.apex"); - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); - - TemporaryDir data_dir; - AddDataApex("apex.apexd_test.apex"); - auto shim_v2 = - ApexFile::Open(AddDataApex("com.android.apex.cts.shim.v2.apex")); - ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); - - auto all_apex = instance.AllApexFilesByName(); - auto result = SelectApexForActivation(all_apex, instance); - ASSERT_EQ(result.size(), 2u); - - ASSERT_THAT(result, - UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file_v2)), - ApexFileEq(ByRef(*shim_v2)))); -} - -// When versions are equal, non-pre-installed version gets priority -TEST_F(ApexdUnitTest, DataApexGetsPriorityForSameVersions) { - AddPreInstalledApex("apex.apexd_test.apex"); - AddPreInstalledApex("com.android.apex.cts.shim.apex"); - // Initialize pre-installed APEX information - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); - - auto apexd_test_file = ApexFile::Open(AddDataApex("apex.apexd_test.apex")); - auto shim_v1 = ApexFile::Open(AddDataApex("com.android.apex.cts.shim.apex")); - // Initialize ApexFile repo - ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); - - auto all_apex = instance.AllApexFilesByName(); - auto result = SelectApexForActivation(all_apex, instance); - ASSERT_EQ(result.size(), 2u); - - ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*apexd_test_file)), - ApexFileEq(ByRef(*shim_v1)))); -} - -// Both versions of shared libs can be selected when preinstalled version is -// lower than data version -TEST_F(ApexdUnitTest, SharedLibsCanHaveBothVersionSelected) { - auto shared_lib_v1 = ApexFile::Open(AddPreInstalledApex( - "com.android.apex.test.sharedlibs_generated.v1.libvX.apex")); - // Initialize pre-installed APEX information - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); - - auto shared_lib_v2 = ApexFile::Open( - AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex")); - // Initialize data APEX information - ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); - - auto all_apex = instance.AllApexFilesByName(); - auto result = SelectApexForActivation(all_apex, instance); - ASSERT_EQ(result.size(), 2u); - - ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*shared_lib_v1)), - ApexFileEq(ByRef(*shared_lib_v2)))); -} - -// Data version of shared libs should not be selected if lower than -// preinstalled version -TEST_F(ApexdUnitTest, SharedLibsDataVersionDeletedIfLower) { - auto shared_lib_v2 = ApexFile::Open(AddPreInstalledApex( - "com.android.apex.test.sharedlibs_generated.v2.libvY.apex")); - // Initialize pre-installed APEX information - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); - - auto shared_lib_v1 = ApexFile::Open( - AddDataApex("com.android.apex.test.sharedlibs_generated.v1.libvX.apex")); - // Initialize data APEX information - ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); - - auto all_apex = instance.AllApexFilesByName(); - auto result = SelectApexForActivation(all_apex, instance); - ASSERT_EQ(result.size(), 1u); - - ASSERT_THAT(result, UnorderedElementsAre(ApexFileEq(ByRef(*shared_lib_v2)))); -} - -TEST_F(ApexdUnitTest, ProcessCompressedApex) { - auto compressed_apex = ApexFile::Open( - AddPreInstalledApex("com.android.apex.compressed.v1.capex")); - - std::vector<ApexFileRef> compressed_apex_list; - compressed_apex_list.emplace_back(std::cref(*compressed_apex)); - auto return_value = - ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); - - std::string decompressed_file_path = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - // Assert output path is not empty - auto exists = PathExists(decompressed_file_path); - ASSERT_TRUE(IsOk(exists)); - ASSERT_TRUE(*exists) << decompressed_file_path << " does not exist"; - - // Assert that decompressed apex is same as original apex - const std::string original_apex_file_path = - GetTestFile("com.android.apex.compressed.v1_original.apex"); - auto comparison_result = - CompareFiles(original_apex_file_path, decompressed_file_path); - ASSERT_TRUE(IsOk(comparison_result)); - ASSERT_TRUE(*comparison_result); - - // Assert that return value contains decompressed APEX - auto decompressed_apex = ApexFile::Open(decompressed_file_path); - ASSERT_THAT(return_value, - UnorderedElementsAre(ApexFileEq(ByRef(*decompressed_apex)))); -} - -TEST_F(ApexdUnitTest, ProcessCompressedApexRunsVerification) { - auto compressed_apex_mismatch_key = ApexFile::Open(AddPreInstalledApex( - "com.android.apex.compressed_key_mismatch_with_original.capex")); - auto compressed_apex_version_mismatch = ApexFile::Open( - AddPreInstalledApex("com.android.apex.compressed.v1_with_v2_apex.capex")); - - std::vector<ApexFileRef> compressed_apex_list; - compressed_apex_list.emplace_back(std::cref(*compressed_apex_mismatch_key)); - compressed_apex_list.emplace_back( - std::cref(*compressed_apex_version_mismatch)); - auto return_value = - ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); - ASSERT_EQ(return_value.size(), 0u); -} - -TEST_F(ApexdUnitTest, ValidateDecompressedApex) { - auto capex = ApexFile::Open( - AddPreInstalledApex("com.android.apex.compressed.v1.capex")); - auto decompressed_v1 = ApexFile::Open( - AddDataApex("com.android.apex.compressed.v1_original.apex")); - - auto result = - ValidateDecompressedApex(std::cref(*capex), std::cref(*decompressed_v1)); - ASSERT_TRUE(IsOk(result)); - - // Validation checks version - auto decompressed_v2 = ApexFile::Open( - AddDataApex("com.android.apex.compressed.v2_original.apex")); - result = - ValidateDecompressedApex(std::cref(*capex), std::cref(*decompressed_v2)); - ASSERT_FALSE(IsOk(result)); - ASSERT_THAT( - result.error().message(), - HasSubstr( - "Compressed APEX has different version than decompressed APEX")); - - // Validation check root digest - auto decompressed_v1_different_digest = ApexFile::Open(AddDataApex( - "com.android.apex.compressed.v1_different_digest_original.apex")); - result = ValidateDecompressedApex( - std::cref(*capex), std::cref(*decompressed_v1_different_digest)); - ASSERT_FALSE(IsOk(result)); - ASSERT_THAT(result.error().message(), - HasSubstr("does not match with expected root digest")); - - // Validation checks key - auto capex_different_key = ApexFile::Open( - AddDataApex("com.android.apex.compressed_different_key.capex")); - result = ValidateDecompressedApex(std::cref(*capex_different_key), - std::cref(*decompressed_v1)); - ASSERT_FALSE(IsOk(result)); - ASSERT_THAT( - result.error().message(), - HasSubstr("Public key of compressed APEX is different than original")); -} - -TEST_F(ApexdUnitTest, ProcessCompressedApexCanBeCalledMultipleTimes) { - auto compressed_apex = ApexFile::Open( - AddPreInstalledApex("com.android.apex.compressed.v1.capex")); - - std::vector<ApexFileRef> compressed_apex_list; - compressed_apex_list.emplace_back(std::cref(*compressed_apex)); - auto return_value = - ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); - ASSERT_EQ(return_value.size(), 1u); - - // Capture the creation time of the decompressed APEX - std::error_code ec; - auto decompressed_apex_path = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - auto last_write_time_1 = fs::last_write_time(decompressed_apex_path, ec); - ASSERT_FALSE(ec) << "Failed to capture last write time of " - << decompressed_apex_path; - - // Now try to decompress the same capex again. It should not fail. - return_value = - ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); - ASSERT_EQ(return_value.size(), 1u); - - // Ensure the decompressed APEX file did not change - auto last_write_time_2 = fs::last_write_time(decompressed_apex_path, ec); - ASSERT_FALSE(ec) << "Failed to capture last write time of " - << decompressed_apex_path; - ASSERT_EQ(last_write_time_1, last_write_time_2); -} - -// Test behavior of ProcessCompressedApex when is_ota_chroot is true -TEST_F(ApexdUnitTest, ProcessCompressedApexOnOtaChroot) { - auto compressed_apex = ApexFile::Open( - AddPreInstalledApex("com.android.apex.compressed.v1.capex")); - - std::vector<ApexFileRef> compressed_apex_list; - compressed_apex_list.emplace_back(std::cref(*compressed_apex)); - auto return_value = - ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ true); - ASSERT_EQ(return_value.size(), 1u); - - // Decompressed APEX should be located in decompression_dir - std::string decompressed_file_path = - StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - // Assert output path is not empty - auto exists = PathExists(decompressed_file_path); - ASSERT_TRUE(IsOk(exists)); - ASSERT_TRUE(*exists) << decompressed_file_path << " does not exist"; - - // Assert that decompressed apex is same as original apex - const std::string original_apex_file_path = - GetTestFile("com.android.apex.compressed.v1_original.apex"); - auto comparison_result = - CompareFiles(original_apex_file_path, decompressed_file_path); - ASSERT_TRUE(IsOk(comparison_result)); - ASSERT_TRUE(*comparison_result); - - // Assert that return value contains the decompressed APEX - auto apex_file = ApexFile::Open(decompressed_file_path); - ASSERT_THAT(return_value, - UnorderedElementsAre(ApexFileEq(ByRef(*apex_file)))); -} - -// When decompressing APEX, reuse existing OTA APEX -TEST_F(ApexdUnitTest, ProcessCompressedApexReuseOtaApex) { - // Push a compressed APEX that will fail to decompress - auto compressed_apex = ApexFile::Open(AddPreInstalledApex( - "com.android.apex.compressed.v1_not_decompressible.capex")); - - std::vector<ApexFileRef> compressed_apex_list; - compressed_apex_list.emplace_back(std::cref(*compressed_apex)); - - // If we try to decompress capex directly, it should fail since the capex - // pushed is faulty and cannot be decompressed - auto return_value = - ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); - ASSERT_EQ(return_value.size(), 0u); - - // But, if there is an ota_apex present for reuse, it should reuse that - // and avoid decompressing the faulty capex - - // Push an OTA apex that should be reused to skip decompression - auto ota_apex_path = - StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"), - ota_apex_path); - return_value = - ProcessCompressedApex(compressed_apex_list, /* is_ota_chroot= */ false); - ASSERT_EQ(return_value.size(), 1u); - - // Ota Apex should be cleaned up - ASSERT_FALSE(*PathExists(ota_apex_path)); - ASSERT_EQ(return_value[0].GetPath(), - StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix)); -} - -TEST_F(ApexdUnitTest, ShouldAllocateSpaceForDecompressionNewApex) { - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); - - // A brand new compressed APEX is being introduced: selected - auto result = - ShouldAllocateSpaceForDecompression("com.android.brand.new", 1, instance); - ASSERT_TRUE(IsOk(result)); - ASSERT_TRUE(*result); -} - -TEST_F(ApexdUnitTest, - ShouldAllocateSpaceForDecompressionWasNotCompressedBefore) { - // Prepare fake pre-installed apex - AddPreInstalledApex("apex.apexd_test.apex"); - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); - - // An existing pre-installed APEX is now compressed in the OTA: selected - { - auto result = ShouldAllocateSpaceForDecompression( - "com.android.apex.test_package", 1, instance); - ASSERT_TRUE(IsOk(result)); - ASSERT_TRUE(*result); - } - - // Even if there is a data apex (lower version) - // Include data apex within calculation now - AddDataApex("apex.apexd_test_v2.apex"); - ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); - { - auto result = ShouldAllocateSpaceForDecompression( - "com.android.apex.test_package", 3, instance); - ASSERT_TRUE(IsOk(result)); - ASSERT_TRUE(*result); - } - - // But not if data apex has equal or higher version - { - auto result = ShouldAllocateSpaceForDecompression( - "com.android.apex.test_package", 2, instance); - ASSERT_TRUE(IsOk(result)); - ASSERT_FALSE(*result); - } -} - -TEST_F(ApexdUnitTest, ShouldAllocateSpaceForDecompressionVersionCompare) { - // Prepare fake pre-installed apex - PrepareCompressedApex("com.android.apex.compressed.v1.capex"); - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({GetBuiltInDir()}))); - ASSERT_TRUE(IsOk(instance.AddDataApex(GetDataDir()))); - - { - // New Compressed apex has higher version than decompressed data apex: - // selected - auto result = ShouldAllocateSpaceForDecompression( - "com.android.apex.compressed", 2, instance); - ASSERT_TRUE(IsOk(result)); - ASSERT_TRUE(*result) - << "Higher version test with decompressed data returned false"; - } - - // Compare against decompressed data apex - { - // New Compressed apex has same version as decompressed data apex: not - // selected - auto result = ShouldAllocateSpaceForDecompression( - "com.android.apex.compressed", 1, instance); - ASSERT_TRUE(IsOk(result)); - ASSERT_FALSE(*result) - << "Same version test with decompressed data returned true"; - } - - { - // New Compressed apex has lower version than decompressed data apex: - // selected - auto result = ShouldAllocateSpaceForDecompression( - "com.android.apex.compressed", 0, instance); - ASSERT_TRUE(IsOk(result)); - ASSERT_TRUE(*result) - << "lower version test with decompressed data returned false"; - } - - // Replace decompressed data apex with a higher version - ApexFileRepository instance_new(GetDecompressionDir()); - ASSERT_TRUE(IsOk(instance_new.AddPreInstalledApex({GetBuiltInDir()}))); - TemporaryDir data_dir_new; - fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"), - data_dir_new.path); - ASSERT_TRUE(IsOk(instance_new.AddDataApex(data_dir_new.path))); - - { - // New Compressed apex has higher version as data apex: selected - auto result = ShouldAllocateSpaceForDecompression( - "com.android.apex.compressed", 3, instance_new); - ASSERT_TRUE(IsOk(result)); - ASSERT_TRUE(*result) << "Higher version test with new data returned false"; - } - - { - // New Compressed apex has same version as data apex: not selected - auto result = ShouldAllocateSpaceForDecompression( - "com.android.apex.compressed", 2, instance_new); - ASSERT_TRUE(IsOk(result)); - ASSERT_FALSE(*result) << "Same version test with new data returned true"; - } - - { - // New Compressed apex has lower version than data apex: not selected - auto result = ShouldAllocateSpaceForDecompression( - "com.android.apex.compressed", 1, instance_new); - ASSERT_TRUE(IsOk(result)); - ASSERT_FALSE(*result) << "lower version test with new data returned true"; - } -} - -TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexCreatesSingleFile) { - TemporaryDir dest_dir; - // Reserving space should create a single file in dest_dir with exact size - - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); - auto files = ReadDir(dest_dir.path, [](auto _) { return true; }); - ASSERT_TRUE(IsOk(files)); - ASSERT_EQ(files->size(), 1u); - EXPECT_EQ(fs::file_size((*files)[0]), 100u); -} - -TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexSafeToCallMultipleTimes) { - TemporaryDir dest_dir; - // Calling ReserveSpaceForCompressedApex multiple times should still create - // a single file - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); - auto files = ReadDir(dest_dir.path, [](auto _) { return true; }); - ASSERT_TRUE(IsOk(files)); - ASSERT_EQ(files->size(), 1u); - EXPECT_EQ(fs::file_size((*files)[0]), 100u); -} - -TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexShrinkAndGrow) { - TemporaryDir dest_dir; - - // Create a 100 byte file - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); - - // Should be able to shrink and grow the reserved space - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(1000, dest_dir.path))); - auto files = ReadDir(dest_dir.path, [](auto _) { return true; }); - ASSERT_TRUE(IsOk(files)); - ASSERT_EQ(files->size(), 1u); - EXPECT_EQ(fs::file_size((*files)[0]), 1000u); - - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(10, dest_dir.path))); - files = ReadDir(dest_dir.path, [](auto _) { return true; }); - ASSERT_TRUE(IsOk(files)); - ASSERT_EQ(files->size(), 1u); - EXPECT_EQ(fs::file_size((*files)[0]), 10u); -} - -TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexDeallocateIfPassedZero) { - TemporaryDir dest_dir; - - // Create a file first - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(100, dest_dir.path))); - auto files = ReadDir(dest_dir.path, [](auto _) { return true; }); - ASSERT_TRUE(IsOk(files)); - ASSERT_EQ(files->size(), 1u); - - // Should delete the reserved file if size passed is 0 - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(0, dest_dir.path))); - files = ReadDir(dest_dir.path, [](auto _) { return true; }); - ASSERT_TRUE(IsOk(files)); - ASSERT_EQ(files->size(), 0u); -} - -TEST_F(ApexdUnitTest, ReserveSpaceForCapexCleansOtaApex) { - TemporaryDir dest_dir; - - auto ota_apex_path = StringPrintf( - "%s/ota_apex%s", GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - auto create_ota_apex = [&]() { - // Create an ota_apex first - fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"), - ota_apex_path); - auto path_exists = PathExists(ota_apex_path); - ASSERT_TRUE(*path_exists); - }; - create_ota_apex(); - - // Should not delete the reserved file if size passed is negative - ASSERT_FALSE(IsOk(ReserveSpaceForCompressedApex(-1, dest_dir.path))); - auto path_exists = PathExists(ota_apex_path); - ASSERT_TRUE(*path_exists); - - // Should delete the reserved file if size passed is 0 - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(0, dest_dir.path))); - path_exists = PathExists(ota_apex_path); - ASSERT_FALSE(*path_exists); - - create_ota_apex(); - // Should delete the reserved file if size passed is positive - ASSERT_TRUE(IsOk(ReserveSpaceForCompressedApex(10, dest_dir.path))); - path_exists = PathExists(ota_apex_path); - ASSERT_FALSE(*path_exists); -} - -TEST_F(ApexdUnitTest, ReserveSpaceForCompressedApexErrorForNegativeValue) { - TemporaryDir dest_dir; - // Should return error if negative value is passed - ASSERT_FALSE(IsOk(ReserveSpaceForCompressedApex(-1, dest_dir.path))); -} - -// A test fixture to use for tests that mount/unmount apexes. -class ApexdMountTest : public ApexdUnitTest { - public: - - void UnmountOnTearDown(const std::string& apex_file) { - to_unmount_.push_back(apex_file); - } - - protected: - void SetUp() final { - ApexdUnitTest::SetUp(); - GetApexDatabaseForTesting().Reset(); - ASSERT_TRUE(IsOk(SetUpApexTestEnvironment())); - } - - void TearDown() final { - ApexdUnitTest::TearDown(); - for (const auto& apex : to_unmount_) { - if (auto status = DeactivatePackage(apex); !status.ok()) { - LOG(ERROR) << "Failed to unmount " << apex << " : " << status.error(); - } - } - } - - private: - MountNamespaceRestorer restorer_; - std::vector<std::string> to_unmount_; -}; - -// TODO(b/187864524): cover other negative scenarios. -TEST_F(ApexdMountTest, InstallPackageRejectsApexWithoutRebootlessSupport) { - std::string file_path = AddPreInstalledApex("apex.apexd_test.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = InstallPackage(GetTestFile("apex.apexd_test.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), - HasSubstr("does not support non-staged update")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsNoPreInstalledApex) { - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT( - ret.error().message(), - HasSubstr("No active version found for package test.apex.rebootless")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsNoHashtree) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = - InstallPackage(GetTestFile("test.rebootless_apex_v2_no_hashtree.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), - HasSubstr(" does not have an embedded hash tree")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsNoActiveApex) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT( - ret.error().message(), - HasSubstr("No active version found for package test.apex.rebootless")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsManifestMismatch) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = InstallPackage( - GetTestFile("test.rebootless_apex_manifest_mismatch.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT( - ret.error().message(), - HasSubstr( - "Manifest inside filesystem does not match manifest outside it")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsCorrupted) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_corrupted.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), HasSubstr("Can't verify /dev/block/dm-")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsProvidesSharedLibs) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = InstallPackage( - GetTestFile("test.rebootless_apex_provides_sharedlibs.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), HasSubstr(" is a shared libs APEX")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsProvidesNativeLibs) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = InstallPackage( - GetTestFile("test.rebootless_apex_provides_native_libs.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), HasSubstr(" provides native libs")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsRequiresSharedApexLibs) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = InstallPackage( - GetTestFile("test.rebootless_apex_requires_shared_apex_libs.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), HasSubstr(" requires shared apex libs")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsJniLibs) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_jni_libs.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), HasSubstr(" requires JNI libs")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsAddRequiredNativeLib) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = - InstallPackage(GetTestFile("test.rebootless_apex_add_native_lib.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), - HasSubstr("Set of native libs required by")); - ASSERT_THAT( - ret.error().message(), - HasSubstr("differs from the one required by the currently active")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsRemovesRequiredNativeLib) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = InstallPackage( - GetTestFile("test.rebootless_apex_remove_native_lib.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), - HasSubstr("Set of native libs required by")); - ASSERT_THAT( - ret.error().message(), - HasSubstr("differs from the one required by the currently active")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsAppInApex) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = - InstallPackage(GetTestFile("test.rebootless_apex_app_in_apex.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), HasSubstr("contains app inside")); -} - -TEST_F(ApexdMountTest, InstallPackageRejectsPrivAppInApex) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto ret = - InstallPackage(GetTestFile("test.rebootless_apex_priv_app_in_apex.apex")); - ASSERT_FALSE(IsOk(ret)); - ASSERT_THAT(ret.error().message(), HasSubstr("contains priv-app inside")); -} - -TEST_F(ApexdMountTest, InstallPackagePreInstallVersionActive) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - { - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - } - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); - ASSERT_TRUE(IsOk(ret)); - UnmountOnTearDown(ret->GetPath()); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/test.apex.rebootless", - "/apex/test.apex.rebootless@2")); - - // Check that /apex/test.apex.rebootless is a bind mount of - // /apex/test.apex.rebootless@2. - auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb"); - ASSERT_TRUE(IsOk(manifest)); - ASSERT_EQ(2u, manifest->version()); - - // Check that GetActivePackage correctly reports upgraded version. - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); - - // Check that pre-installed APEX is still around - ASSERT_EQ(0, access(file_path.c_str(), F_OK)) - << "Can't access " << file_path << " : " << strerror(errno); - - auto& db = GetApexDatabaseForTesting(); - // Check that upgraded APEX is mounted on top of dm-verity device. - db.ForallMountedApexes( - "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, ret->GetPath()); - ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1"); - }); -} - -TEST_F(ApexdMountTest, InstallPackagePreInstallVersionActiveSamegrade) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - { - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - } - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex")); - ASSERT_TRUE(IsOk(ret)); - UnmountOnTearDown(ret->GetPath()); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/test.apex.rebootless", - "/apex/test.apex.rebootless@1")); - - // Check that GetActivePackage correctly reports upgraded version. - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); - - // Check that pre-installed APEX is still around - ASSERT_EQ(0, access(file_path.c_str(), F_OK)) - << "Can't access " << file_path << " : " << strerror(errno); - - auto& db = GetApexDatabaseForTesting(); - // Check that upgraded APEX is mounted on top of dm-verity device. - db.ForallMountedApexes( - "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, ret->GetPath()); - ASSERT_EQ(data.device_name, "test.apex.rebootless@1_1"); - }); -} - -TEST_F(ApexdMountTest, InstallPackageDataVersionActive) { - AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - std::string file_path = AddDataApex("test.rebootless_apex_v1.apex"); - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - { - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - } - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); - ASSERT_TRUE(IsOk(ret)); - UnmountOnTearDown(ret->GetPath()); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/test.apex.rebootless", - "/apex/test.apex.rebootless@2")); - - // Check that /apex/test.apex.rebootless is a bind mount of - // /apex/test.apex.rebootless@2. - auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb"); - ASSERT_TRUE(IsOk(manifest)); - ASSERT_EQ(2u, manifest->version()); - - // Check that GetActivePackage correctly reports upgraded version. - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); - - // Check that previously active APEX was deleted. - ASSERT_EQ(-1, access(file_path.c_str(), F_OK)); - ASSERT_EQ(ENOENT, errno); - - auto& db = GetApexDatabaseForTesting(); - // Check that upgraded APEX is mounted on top of dm-verity device. - db.ForallMountedApexes( - "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, ret->GetPath()); - ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1"); - }); -} - -TEST_F(ApexdMountTest, InstallPackageResolvesPathCollision) { - AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - std::string file_path = AddDataApex("test.rebootless_apex_v1.apex", - "test.apex.rebootless@1_1.apex"); - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - { - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - } - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v1.apex")); - ASSERT_TRUE(IsOk(ret)); - UnmountOnTearDown(ret->GetPath()); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/test.apex.rebootless", - "/apex/test.apex.rebootless@1")); - - // Check that /apex/test.apex.rebootless is a bind mount of - // /apex/test.apex.rebootless@2. - auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb"); - ASSERT_TRUE(IsOk(manifest)); - ASSERT_EQ(1u, manifest->version()); - - // Check that GetActivePackage correctly reports upgraded version. - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); - - // Check that we correctly resolved active apex path collision. - ASSERT_EQ(active_apex->GetPath(), - GetDataDir() + "/test.apex.rebootless@1_2.apex"); - - // Check that previously active APEX was deleted. - ASSERT_EQ(-1, access(file_path.c_str(), F_OK)); - ASSERT_EQ(ENOENT, errno); - - auto& db = GetApexDatabaseForTesting(); - // Check that upgraded APEX is mounted on top of dm-verity device. - db.ForallMountedApexes( - "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, ret->GetPath()); - ASSERT_EQ(data.device_name, "test.apex.rebootless@1_2"); - }); -} - -TEST_F(ApexdMountTest, InstallPackageDataVersionActiveSamegrade) { - AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - std::string file_path = AddDataApex("test.rebootless_apex_v2.apex"); - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - { - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - } - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); - ASSERT_TRUE(IsOk(ret)); - UnmountOnTearDown(ret->GetPath()); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/test.apex.rebootless", - "/apex/test.apex.rebootless@2")); - - // Check that /apex/test.apex.rebootless is a bind mount of - // /apex/test.apex.rebootless@2. - auto manifest = ReadManifest("/apex/test.apex.rebootless/apex_manifest.pb"); - ASSERT_TRUE(IsOk(manifest)); - ASSERT_EQ(2u, manifest->version()); - - // Check that GetActivePackage correctly reports upgraded version. - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), ret->GetPath()); - - // Check that previously active APEX was deleted. - ASSERT_EQ(-1, access(file_path.c_str(), F_OK)); - ASSERT_EQ(ENOENT, errno); - - auto& db = GetApexDatabaseForTesting(); - // Check that upgraded APEX is mounted on top of dm-verity device. - db.ForallMountedApexes( - "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, ret->GetPath()); - ASSERT_EQ(data.device_name, "test.apex.rebootless@2_1"); - }); -} - -TEST_F(ApexdMountTest, InstallPackageUnmountFailsPreInstalledApexActive) { - std::string file_path = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - { - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - } - - unique_fd fd(open("/apex/test.apex.rebootless/apex_manifest.pb", - O_RDONLY | O_CLOEXEC)); - ASSERT_NE(-1, fd.get()); - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); - ASSERT_FALSE(IsOk(ret)); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/test.apex.rebootless", - "/apex/test.apex.rebootless@1")); - - // Check that GetActivePackage correctly reports upgraded version. - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - - // Check that old APEX is still around - ASSERT_EQ(0, access(file_path.c_str(), F_OK)) - << "Can't access " << file_path << " : " << strerror(errno); - - auto& db = GetApexDatabaseForTesting(); - // Check that upgraded APEX is mounted on top of dm-verity device. - db.ForallMountedApexes("test.apex.rebootless", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, file_path); - }); -} - -TEST_F(ApexdMountTest, InstallPackageUnmountFailedUpdatedApexActive) { - AddPreInstalledApex("test.rebootless_apex_v1.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - std::string file_path = AddDataApex("test.rebootless_apex_v1.apex"); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - { - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - } - - unique_fd fd(open("/apex/test.apex.rebootless/apex_manifest.pb", - O_RDONLY | O_CLOEXEC)); - ASSERT_NE(-1, fd.get()); - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); - ASSERT_FALSE(IsOk(ret)); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/test.apex.rebootless", - "/apex/test.apex.rebootless@1")); - - // Check that GetActivePackage correctly reports old apex. - auto active_apex = GetActivePackage("test.apex.rebootless"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - - // Check that old APEX is still around - ASSERT_EQ(0, access(file_path.c_str(), F_OK)) - << "Can't access " << file_path << " : " << strerror(errno); - - auto& db = GetApexDatabaseForTesting(); - db.ForallMountedApexes( - "test.apex.rebootless", [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, file_path); - ASSERT_EQ(data.device_name, "test.apex.rebootless@1"); - }); -} - -TEST_F(ApexdMountTest, InstallPackageUpdatesApexInfoList) { - auto apex_1 = AddPreInstalledApex("test.rebootless_apex_v1.apex"); - auto apex_2 = AddPreInstalledApex("apex.apexd_test.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - UnmountOnTearDown(apex_1); - UnmountOnTearDown(apex_2); - ASSERT_TRUE(IsOk(ActivatePackage(apex_1))); - ASSERT_TRUE(IsOk(ActivatePackage(apex_2))); - - // Call OnAllPackagesActivated to create /apex/apex-info-list.xml. - OnAllPackagesActivated(/* is_bootstrap= */ false); - // Check /apex/apex-info-list.xml was created. - ASSERT_EQ(0, access("/apex/apex-info-list.xml", F_OK)); - - auto ret = InstallPackage(GetTestFile("test.rebootless_apex_v2.apex")); - ASSERT_TRUE(IsOk(ret)); - UnmountOnTearDown(ret->GetPath()); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "test.apex.rebootless", - /* modulePath= */ apex_1, - /* preinstalledModulePath= */ apex_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_2, /* preinstalledModulePath= */ apex_2, - /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, - /* isActive= */ true, GetMTime(apex_2)); - auto apex_info_xml_3 = com::android::apex::ApexInfo( - /* moduleName= */ "test.apex.rebootless", - /* modulePath= */ ret->GetPath(), - /* preinstalledModulePath= */ apex_1, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ false, /* isActive= */ true, GetMTime(ret->GetPath())); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2), - ApexInfoXmlEq(apex_info_xml_3))); -} - -TEST_F(ApexdMountTest, ActivatePackage) { - std::string file_path = AddPreInstalledApex("apex.apexd_test.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - UnmountOnTearDown(file_path); - - auto active_apex = GetActivePackage("com.android.apex.test_package"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1")); - - ASSERT_TRUE(IsOk(DeactivatePackage(file_path))); - ASSERT_FALSE(IsOk(GetActivePackage("com.android.apex.test_package"))); - - auto new_apex_mounts = GetApexMounts(); - ASSERT_EQ(new_apex_mounts.size(), 0u); -} - -TEST_F(ApexdMountTest, ActivateDeactivateSharedLibsApex) { - ASSERT_EQ(mkdir("/apex/sharedlibs", 0755), 0); - ASSERT_EQ(mkdir("/apex/sharedlibs/lib", 0755), 0); - ASSERT_EQ(mkdir("/apex/sharedlibs/lib64", 0755), 0); - auto deleter = make_scope_guard([]() { - std::error_code ec; - fs::remove_all("/apex/sharedlibs", ec); - if (ec) { - LOG(ERROR) << "Failed to delete /apex/sharedlibs : " << ec; - } - }); - - std::string file_path = AddPreInstalledApex( - "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - - UnmountOnTearDown(file_path); - ASSERT_TRUE(IsOk(ActivatePackage(file_path))); - - auto active_apex = GetActivePackage("com.android.apex.test.sharedlibs"); - ASSERT_TRUE(IsOk(active_apex)); - ASSERT_EQ(active_apex->GetPath(), file_path); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test.sharedlibs@1")); - - ASSERT_TRUE(IsOk(DeactivatePackage(file_path))); - ASSERT_FALSE(IsOk(GetActivePackage("com.android.apex.test.sharedlibs"))); - - auto new_apex_mounts = GetApexMounts(); - ASSERT_EQ(new_apex_mounts.size(), 0u); -} - -TEST_F(ApexdMountTest, RemoveInactiveDataApex) { - AddPreInstalledApex("com.android.apex.compressed.v2.capex"); - // Add a decompressed apex that will not be mounted, so should be removed - auto decompressed_apex = StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"), - decompressed_apex); - // Add a decompressed apex that will be mounted, so should be not be removed - auto active_decompressed_apex = StringPrintf( - "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"), - active_decompressed_apex); - // Apex that do not have kDecompressedApexPackageSuffix, should not be removed - // from decompression_dir - auto decompressed_different_suffix = - StringPrintf("%s/com.android.apex.compressed@2%s", - GetDecompressionDir().c_str(), kApexPackageSuffix); - fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"), - decompressed_different_suffix); - - AddPreInstalledApex("apex.apexd_test.apex"); - auto data_apex = AddDataApex("apex.apexd_test.apex"); - auto active_data_apex = AddDataApex("apex.apexd_test_v2.apex"); - - // Activate some of the apex - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}); - UnmountOnTearDown(active_decompressed_apex); - UnmountOnTearDown(active_data_apex); - ASSERT_TRUE(IsOk(ActivatePackage(active_decompressed_apex))); - ASSERT_TRUE(IsOk(ActivatePackage(active_data_apex))); - // Clean up inactive apex packages - RemoveInactiveDataApex(); - - // Verify inactive apex packages have been deleted - ASSERT_TRUE(*PathExists(active_decompressed_apex)); - ASSERT_TRUE(*PathExists(active_data_apex)); - ASSERT_TRUE(*PathExists(decompressed_different_suffix)); - ASSERT_FALSE(*PathExists(decompressed_apex)); - ASSERT_FALSE(*PathExists(data_apex)); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapOnlyPreInstalledApexes) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package_2", - /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, - /* isActive= */ true, GetMTime(apex_path_2)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2))); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapFailsToScanPreInstalledApexes) { - AddPreInstalledApex("apex.apexd_test.apex"); - AddPreInstalledApex("apex.apexd_test_corrupt_superblock_apex.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 1); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasHigherVersion) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - UnmountOnTearDown(apex_path_2); - UnmountOnTearDown(apex_path_3); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@2", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package_2", - /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, - /* isActive= */ true, GetMTime(apex_path_2)); - auto apex_info_xml_3 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_3, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2), - ApexInfoXmlEq(apex_info_xml_3))); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasSameVersion) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - std::string apex_path_3 = AddDataApex("apex.apexd_test.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - UnmountOnTearDown(apex_path_2); - UnmountOnTearDown(apex_path_3); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package_2", - /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, - /* isActive= */ true, GetMTime(apex_path_2)); - auto apex_info_xml_3 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_3, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2), - ApexInfoXmlEq(apex_info_xml_3))); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapSystemHasHigherVersion) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test_v2.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - AddDataApex("apex.apexd_test.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@2", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package_2", - /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, - /* isActive= */ true, GetMTime(apex_path_2)); - - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2))); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasSameVersionButDifferentKey) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - AddDataApex("apex.apexd_test_different_key.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package_2", - /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, - /* isActive= */ true, GetMTime(apex_path_2)); - - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2))); -} - -TEST_F(ApexdMountTest, - OnOtaChrootBootstrapDataHasHigherVersionButDifferentKey) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - std::string apex_path_3 = - AddDataApex("apex.apexd_test_different_key_v2.apex"); - - { - auto apex = ApexFile::Open(apex_path_3); - ASSERT_TRUE(IsOk(apex)); - ASSERT_EQ(static_cast<uint64_t>(apex->GetManifest().version()), 2ULL); - } - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package_2", - /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, - /* isActive= */ true, GetMTime(apex_path_2)); - - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2))); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataApexWithoutPreInstalledApex) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - AddDataApex("apex.apexd_test_different_app.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - UnmountOnTearDown(apex_path_1); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); - - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1))); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapPreInstalledSharedLibsApex) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = AddPreInstalledApex( - "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); - std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - UnmountOnTearDown(apex_path_2); - UnmountOnTearDown(apex_path_3); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@2", - "/apex/com.android.apex.test.sharedlibs@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test.sharedlibs", - /* modulePath= */ apex_path_2, - /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_2)); - auto apex_info_xml_3 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_3, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3)); - - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2), - ApexInfoXmlEq(apex_info_xml_3))); - - ASSERT_EQ(access("/apex/sharedlibs", F_OK), 0); - - // Check /apex/sharedlibs is populated properly. - std::vector<std::string> sharedlibs; - for (const auto& p : fs::recursive_directory_iterator("/apex/sharedlibs")) { - if (fs::is_symlink(p)) { - auto src = fs::read_symlink(p.path()); - ASSERT_EQ(p.path().filename(), src.filename()); - sharedlibs.push_back(p.path().parent_path().string() + "->" + - src.parent_path().string()); - } - } - - std::vector<std::string> expected = { - "/apex/sharedlibs/lib/libsharedlibtest.so->" - "/apex/com.android.apex.test.sharedlibs@1/lib/libsharedlibtest.so", - "/apex/sharedlibs/lib/libc++.so->" - "/apex/com.android.apex.test.sharedlibs@1/lib/libc++.so", - }; - - // On 64bit devices we also have lib64. - if (!GetProperty("ro.product.cpu.abilist64", "").empty()) { - expected.push_back( - "/apex/sharedlibs/lib64/libsharedlibtest.so->" - "/apex/com.android.apex.test.sharedlibs@1/lib64/libsharedlibtest.so"); - expected.push_back( - "/apex/sharedlibs/lib64/libc++.so->" - "/apex/com.android.apex.test.sharedlibs@1/lib64/libc++.so"); - } - ASSERT_THAT(sharedlibs, UnorderedElementsAreArray(expected)); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapSharedLibsApexBothVersions) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = AddPreInstalledApex( - "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); - std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); - std::string apex_path_4 = - AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - UnmountOnTearDown(apex_path_2); - UnmountOnTearDown(apex_path_3); - UnmountOnTearDown(apex_path_4); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@2", - "/apex/com.android.apex.test.sharedlibs@1", - "/apex/com.android.apex.test.sharedlibs@2")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test.sharedlibs", - /* modulePath= */ apex_path_2, - /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_2)); - auto apex_info_xml_3 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_3, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_3)); - auto apex_info_xml_4 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test.sharedlibs", - /* modulePath= */ apex_path_4, - /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ false, /* isActive= */ true, GetMTime(apex_path_4)); - - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2), - ApexInfoXmlEq(apex_info_xml_3), - ApexInfoXmlEq(apex_info_xml_4))); - - ASSERT_EQ(access("/apex/sharedlibs", F_OK), 0); - - // Check /apex/sharedlibs is populated properly. - // Because we don't want to hardcode full paths (they are pretty long and have - // a hash in them which might change if new prebuilts are dropped in), the - // assertion logic is a little bit clunky. - std::vector<std::string> sharedlibs; - for (const auto& p : fs::recursive_directory_iterator("/apex/sharedlibs")) { - if (fs::is_symlink(p)) { - auto src = fs::read_symlink(p.path()); - ASSERT_EQ(p.path().filename(), src.filename()); - sharedlibs.push_back(p.path().parent_path().string() + "->" + - src.parent_path().string()); - } - } - - std::vector<std::string> expected = { - "/apex/sharedlibs/lib/libsharedlibtest.so->" - "/apex/com.android.apex.test.sharedlibs@2/lib/libsharedlibtest.so", - "/apex/sharedlibs/lib/libsharedlibtest.so->" - "/apex/com.android.apex.test.sharedlibs@1/lib/libsharedlibtest.so", - "/apex/sharedlibs/lib/libc++.so->" - "/apex/com.android.apex.test.sharedlibs@1/lib/libc++.so", - }; - // On 64bit devices we also have lib64. - if (!GetProperty("ro.product.cpu.abilist64", "").empty()) { - expected.push_back( - "/apex/sharedlibs/lib64/libsharedlibtest.so->" - "/apex/com.android.apex.test.sharedlibs@2/lib64/libsharedlibtest.so"); - expected.push_back( - "/apex/sharedlibs/lib64/libsharedlibtest.so->" - "/apex/com.android.apex.test.sharedlibs@1/lib64/libsharedlibtest.so"); - expected.push_back( - "/apex/sharedlibs/lib64/libc++.so->" - "/apex/com.android.apex.test.sharedlibs@1/lib64/libc++.so"); - } - - ASSERT_THAT(sharedlibs, UnorderedElementsAreArray(expected)); -} - -// Test when we move from uncompressed APEX to CAPEX via ota -TEST_F(ApexdMountTest, OnOtaChrootBootstrapOnlyCompressedApexes) { - std::string apex_path = - AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // Decompressed APEX should be mounted from decompression_dir - std::string decompressed_apex = - StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - UnmountOnTearDown(decompressed_apex); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_decompressed = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ decompressed_apex, - /* preinstalledModulePath= */ apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, GetMTime(decompressed_apex)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1.chroot"); - }); -} - -// Test we decompress only once even if OnOtaChrootBootstrap is called multiple -// times -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDecompressOnlyOnceMultipleCalls) { - std::string apex_path = - AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // Decompressed OTA APEX should be mounted - std::string decompressed_ota_apex = - StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - UnmountOnTearDown(decompressed_ota_apex); - - // Capture the creation time of the OTA APEX - std::error_code ec; - auto last_write_time_1 = fs::last_write_time(decompressed_ota_apex, ec); - ASSERT_FALSE(ec) << "Failed to capture last write time of " - << decompressed_ota_apex; - - // Call OnOtaChrootBootstrap again. Since we do not hardlink decompressed APEX - // to /data/apex/active directory when in chroot, when selecting apex for - // activation, we will end up selecting compressed APEX again. - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // Compare write time to ensure we did not decompress again - auto last_write_time_2 = fs::last_write_time(decompressed_ota_apex, ec); - ASSERT_FALSE(ec) << "Failed to capture last write time of " - << decompressed_ota_apex << ec.message(); - ASSERT_EQ(last_write_time_1, last_write_time_2); -} - -// Test when we upgrade existing CAPEX to higher version via OTA -TEST_F(ApexdMountTest, OnOtaChrootBootstrapUpgradeCapex) { - TemporaryDir previous_built_in_dir; - PrepareCompressedApex("com.android.apex.compressed.v1.capex", - previous_built_in_dir.path); - // Place a higher version capex in current built_in_dir - std::string apex_path = - AddPreInstalledApex("com.android.apex.compressed.v2.capex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // Upgraded decompressed APEX should be mounted from decompression dir - std::string decompressed_active_apex = - StringPrintf("%s/com.android.apex.compressed@2%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@2")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_decompressed = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ decompressed_active_apex, - /* preinstalledModulePath= */ apex_path, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ true, /* isActive= */ true, - GetMTime(decompressed_active_apex)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@2.chroot"); - }); -} - -// Test when we update existing CAPEX to same version via OTA -TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapex) { - TemporaryDir previous_built_in_dir; - PrepareCompressedApex("com.android.apex.compressed.v1.capex", - previous_built_in_dir.path); - // Place a same version capex in current built_in_dir, under a different name - auto apex_path = - StringPrintf("%s/different-name.capex", GetBuiltInDir().c_str()); - fs::copy(GetTestFile("com.android.apex.compressed.v1.capex"), apex_path); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // Previously decompressed APEX should be mounted from decompression_dir - std::string decompressed_active_apex = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_decompressed = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ decompressed_active_apex, - /* preinstalledModulePath= */ apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, - GetMTime(decompressed_active_apex)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1.chroot"); - }); -} - -// Test when we update existing CAPEX to same version, but different digest -TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapexDifferentDigest) { - TemporaryDir previous_built_in_dir; - auto different_digest_apex_path = PrepareCompressedApex( - "com.android.apex.compressed.v1_different_digest.capex", - previous_built_in_dir.path); - // Place a same version capex in current built_in_dir, which has different - // digest - auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // New decompressed ota APEX should be mounted with kOtaApexPackageSuffix - std::string decompressed_ota_apex = - StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - UnmountOnTearDown(decompressed_ota_apex); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_decompressed = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ decompressed_ota_apex, - /* preinstalledModulePath= */ apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, - GetMTime(decompressed_ota_apex)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_ota_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1.chroot"); - }); - - // Ensure decompressed apex has same digest as pre-installed - auto pre_installed_apex = ApexFile::Open(apex_path); - auto decompressed_apex = ApexFile::Open(decompressed_ota_apex); - auto different_digest_apex = ApexFile::Open(different_digest_apex_path); - ASSERT_EQ( - pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), - GetRootDigest(*decompressed_apex)); - ASSERT_NE( - pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), - GetRootDigest(*different_digest_apex)); - - // Ensure we didn't remove previous decompressed APEX - std::string previous_decompressed_apex = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - auto path_exists = PathExists(previous_decompressed_apex); - ASSERT_TRUE(*path_exists); -} - -// Test when we update existing CAPEX to same version, but different key via OTA -TEST_F(ApexdMountTest, OnOtaChrootBootstrapSamegradeCapexDifferentKey) { - TemporaryDir previous_built_in_dir; - PrepareCompressedApex("com.android.apex.compressed_different_key.capex", - previous_built_in_dir.path); - // Place a same version capex in current built_in_dir, which has different key - auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // New decompressed APEX should be mounted from ota_reserved directory - std::string decompressed_active_apex = - StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_decompressed = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ decompressed_active_apex, - /* preinstalledModulePath= */ apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, - GetMTime(decompressed_active_apex)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1.chroot"); - }); -} - -// Test when we remove CAPEX via OTA -TEST_F(ApexdMountTest, OnOtaChrootBootstrapCapexToApex) { - TemporaryDir previous_built_in_dir; - PrepareCompressedApex("com.android.apex.compressed.v1.capex", - previous_built_in_dir.path); - // Place a uncompressed version apex in current built_in_dir - std::string apex_path = - AddPreInstalledApex("com.android.apex.compressed.v1_original.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // New uncompressed APEX should be mounted - UnmountOnTearDown(apex_path); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_uncompressed = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ apex_path, - /* preinstalledModulePath= */ apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_uncompressed))); -} - -TEST_F(ApexdMountTest, - OnOtaChrootBootstrapDecompressedApexVersionDifferentThanCapex) { - TemporaryDir previous_built_in_dir; - PrepareCompressedApex("com.android.apex.compressed.v2.capex", - previous_built_in_dir.path); - // Place a lower version capex in current built_in_dir, so that previously - // decompressed APEX has higher version but still doesn't get picked during - // selection. - std::string apex_path = - AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // Pre-installed CAPEX should be decompressed again and mounted from - // decompression_dir - std::string decompressed_active_apex = - StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_decompressed = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ decompressed_active_apex, - /* preinstalledModulePath= */ apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, - GetMTime(decompressed_active_apex)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); -} - -// Test when we update CAPEX and there is a higher version present in data -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHigherThanCapex) { - auto system_apex_path = - PrepareCompressedApex("com.android.apex.compressed.v1.capex"); - auto data_apex_path = - AddDataApex("com.android.apex.compressed.v2_original.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // Data APEX should be mounted - UnmountOnTearDown(data_apex_path); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@2")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_data = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ data_apex_path, - /* preinstalledModulePath= */ system_apex_path, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ false, /* isActive= */ true, GetMTime(data_apex_path)); - auto apex_info_xml_system = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ system_apex_path, - /* preinstalledModulePath= */ system_apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ false, GetMTime(system_apex_path)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_data), - ApexInfoXmlEq(apex_info_xml_system))); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, data_apex_path); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@2.chroot"); - }); -} - -// Test when we update CAPEX and there is a lower version present in data -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataLowerThanCapex) { - auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v2.capex"); - AddDataApex("com.android.apex.compressed.v1_original.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // Decompressed APEX should be mounted from reserved dir - std::string decompressed_active_apex = - StringPrintf("%s/com.android.apex.compressed@2%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@2")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ decompressed_active_apex, - /* preinstalledModulePath= */ apex_path, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ true, /* isActive= */ true, - GetMTime(decompressed_active_apex)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml))); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@2.chroot"); - }); -} - -// Test when we update CAPEX and there is a same version present in data -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataSameAsCapex) { - auto system_apex_path = - PrepareCompressedApex("com.android.apex.compressed.v1.capex"); - auto data_apex_path = - AddDataApex("com.android.apex.compressed.v1_original.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // Data APEX should be mounted - UnmountOnTearDown(data_apex_path); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_data = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ data_apex_path, - /* preinstalledModulePath= */ system_apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ false, /* isActive= */ true, GetMTime(data_apex_path)); - auto apex_info_xml_system = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ system_apex_path, - /* preinstalledModulePath= */ system_apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ false, GetMTime(system_apex_path)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_data), - ApexInfoXmlEq(apex_info_xml_system))); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, data_apex_path); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1.chroot"); - }); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDataHasDifferentKeyThanCapex) { - AddDataApex("com.android.apex.compressed_different_key.capex"); - // Place a same version capex in current built_in_dir, which has different key - auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - // New decompressed APEX should be mounted from ota_reserved directory - std::string decompressed_active_apex = - StringPrintf("%s/com.android.apex.compressed@1%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_decompressed = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.compressed", - /* modulePath= */ decompressed_active_apex, - /* preinstalledModulePath= */ apex_path, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, - GetMTime(decompressed_active_apex)); - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_decompressed))); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1.chroot"); - }); -} - -static std::string GetSelinuxContext(const std::string& file) { - char* ctx; - if (getfilecon(file.c_str(), &ctx) < 0) { - PLOG(ERROR) << "Failed to getfilecon " << file; - return ""; - } - std::string result(ctx); - freecon(ctx); - return result; -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapSelinuxLabelsAreCorrect) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = AddPreInstalledApex( - "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); - std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); - - UnmountOnTearDown(apex_path_2); - UnmountOnTearDown(apex_path_3); - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - - EXPECT_EQ(GetSelinuxContext("/apex/apex-info-list.xml"), - "u:object_r:apex_info_file:s0"); - - EXPECT_EQ(GetSelinuxContext("/apex/sharedlibs"), - "u:object_r:apex_mnt_dir:s0"); - - EXPECT_EQ(GetSelinuxContext("/apex/com.android.apex.test_package"), - "u:object_r:system_file:s0"); - EXPECT_EQ(GetSelinuxContext("/apex/com.android.apex.test_package@2"), - "u:object_r:system_file:s0"); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapDmDevicesHaveCorrectName) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - UnmountOnTearDown(apex_path_2); - UnmountOnTearDown(apex_path_3); - - MountedApexDatabase& db = GetApexDatabaseForTesting(); - // com.android.apex.test_package_2 should be mounted directly on top of loop - // device. - db.ForallMountedApexes("com.android.apex.test_package_2", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_THAT(data.device_name, IsEmpty()); - ASSERT_THAT(data.loop_name, StartsWith("/dev")); - }); - // com.android.apex.test_package should be mounted on top of dm-verity device. - db.ForallMountedApexes("com.android.apex.test_package", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.device_name, - "com.android.apex.test_package@2.chroot"); - ASSERT_THAT(data.loop_name, StartsWith("/dev")); - }); -} - -TEST_F(ApexdMountTest, - OnOtaChrootBootstrapFailsToActivatePreInstalledApexKeepsGoing) { - std::string apex_path_1 = - AddPreInstalledApex("apex.apexd_test_manifest_mismatch.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - UnmountOnTearDown(apex_path_2); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 137, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ false, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package_2", - /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, - /* isActive= */ true, GetMTime(apex_path_2)); - - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2))); -} - -TEST_F(ApexdMountTest, - OnOtaChrootBootstrapFailsToActivateDataApexFallsBackToPreInstalled) { - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - std::string apex_path_3 = - AddDataApex("apex.apexd_test_manifest_mismatch.apex"); - - ASSERT_EQ(OnOtaChrootBootstrap(), 0); - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_path_1, - /* preinstalledModulePath= */ apex_path_1, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, GetMTime(apex_path_1)); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package_2", - /* modulePath= */ apex_path_2, /* preinstalledModulePath= */ apex_path_2, - /* versionCode= */ 1, /* versionName= */ "1", /* isFactory= */ true, - /* isActive= */ true, GetMTime(apex_path_2)); - - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2))); -} - -TEST_F(ApexdMountTest, OnOtaChrootBootstrapFlattenedApex) { - std::string apex_dir_1 = GetBuiltInDir() + "/com.android.apex.test_package"; - std::string apex_dir_2 = GetBuiltInDir() + "/com.android.apex.test_package_2"; - - ASSERT_EQ(mkdir(apex_dir_1.c_str(), 0755), 0); - ASSERT_EQ(mkdir(apex_dir_2.c_str(), 0755), 0); - - auto write_manifest_fn = [&](const std::string& apex_dir, - const std::string& module_name, int version) { - using ::apex::proto::ApexManifest; - - ApexManifest manifest; - manifest.set_name(module_name); - manifest.set_version(version); - manifest.set_versionname(std::to_string(version)); - - std::string out; - manifest.SerializeToString(&out); - ASSERT_TRUE(WriteStringToFile(out, apex_dir + "/apex_manifest.pb")); - }; - - write_manifest_fn(apex_dir_1, "com.android.apex.test_package", 2); - write_manifest_fn(apex_dir_2, "com.android.apex.test_package_2", 1); - - ASSERT_EQ(OnOtaChrootBootstrapFlattenedApex(), 0); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package_2")); - - ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0); - ASSERT_EQ(GetSelinuxContext("/apex/apex-info-list.xml"), - "u:object_r:apex_info_file:s0"); - - auto info_list = - com::android::apex::readApexInfoList("/apex/apex-info-list.xml"); - ASSERT_TRUE(info_list.has_value()); - auto apex_info_xml_1 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package", - /* modulePath= */ apex_dir_1, - /* preinstalledModulePath= */ apex_dir_1, - /* versionCode= */ 2, /* versionName= */ "2", - /* isFactory= */ true, /* isActive= */ true, - /* lastUpdateMillis= */ 0); - auto apex_info_xml_2 = com::android::apex::ApexInfo( - /* moduleName= */ "com.android.apex.test_package_2", - /* modulePath= */ apex_dir_2, - /* preinstalledModulePath= */ apex_dir_2, - /* versionCode= */ 1, /* versionName= */ "1", - /* isFactory= */ true, /* isActive= */ true, - /* lastUpdateMillis= */ 0); - - ASSERT_THAT(info_list->getApexInfo(), - UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1), - ApexInfoXmlEq(apex_info_xml_2))); -} - -TEST_F(ApexdMountTest, OnStartOnlyPreInstalledApexes) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); -} - -TEST_F(ApexdMountTest, OnStartDataHasHigherVersion) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - UnmountOnTearDown(apex_path_2); - UnmountOnTearDown(apex_path_3); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@2", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); -} - -TEST_F(ApexdMountTest, OnStartDataHasWrongSHA) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - std::string apex_path = AddPreInstalledApex("com.android.apex.cts.shim.apex"); - AddDataApex("com.android.apex.cts.shim.v2_wrong_sha.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - UnmountOnTearDown(apex_path); - OnStart(); - - // Check system shim apex is activated instead of the data one. - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.cts.shim", - "/apex/com.android.apex.cts.shim@1")); -} - -TEST_F(ApexdMountTest, OnStartDataHasSameVersion) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - std::string apex_path_3 = AddDataApex("apex.apexd_test.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - UnmountOnTearDown(apex_path_2); - UnmountOnTearDown(apex_path_3); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from data apex, not pre-installed one. - db.ForallMountedApexes("com.android.apex.test_package", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, apex_path_3); - }); -} - -TEST_F(ApexdMountTest, OnStartSystemHasHigherVersion) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test_v2.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - AddDataApex("apex.apexd_test.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@2", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from pre-installed one. - db.ForallMountedApexes("com.android.apex.test_package", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, apex_path_1); - }); -} - -TEST_F(ApexdMountTest, OnStartFailsToActivateApexOnDataFallsBackToBuiltIn) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - AddDataApex("apex.apexd_test_manifest_mismatch.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from pre-installed apex. - db.ForallMountedApexes("com.android.apex.test_package", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, apex_path_1); - }); -} - -TEST_F(ApexdMountTest, OnStartApexOnDataHasWrongKeyFallsBackToBuiltIn) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - std::string apex_path_3 = - AddDataApex("apex.apexd_test_different_key_v2.apex"); - - { - auto apex = ApexFile::Open(apex_path_3); - ASSERT_TRUE(IsOk(apex)); - ASSERT_EQ(static_cast<uint64_t>(apex->GetManifest().version()), 2ULL); - } - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from pre-installed apex. - db.ForallMountedApexes("com.android.apex.test_package", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, apex_path_1); - }); -} - -TEST_F(ApexdMountTest, OnStartOnlyPreInstalledCapexes) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - std::string apex_path_1 = - AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Decompressed APEX should be mounted - std::string decompressed_active_apex = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1"); - }); -} - -TEST_F(ApexdMountTest, OnStartDataHasHigherVersionThanCapex) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - std::string apex_path_2 = - AddDataApex("com.android.apex.compressed.v2_original.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - UnmountOnTearDown(apex_path_2); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@2")); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from data apex. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, apex_path_2); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@2"); - }); -} - -TEST_F(ApexdMountTest, OnStartDataHasSameVersionAsCapex) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - std::string apex_path_2 = - AddDataApex("com.android.apex.compressed.v1_original.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Data APEX should be mounted - UnmountOnTearDown(apex_path_2); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from data apex, not pre-installed one. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, apex_path_2); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1"); - }); -} - -TEST_F(ApexdMountTest, OnStartSystemHasHigherVersionCapexThanData) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - std::string apex_path_1 = - AddPreInstalledApex("com.android.apex.compressed.v2.capex"); - AddDataApex("com.android.apex.compressed.v1_original.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Decompressed APEX should be mounted - std::string decompressed_active_apex = StringPrintf( - "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@2")); - - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from compressed apex - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@2"); - }); -} - -TEST_F(ApexdMountTest, OnStartFailsToActivateApexOnDataFallsBackToCapex) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - AddDataApex("com.android.apex.compressed.v2_manifest_mismatch.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Decompressed APEX should be mounted - std::string decompressed_active_apex = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. It should also be mounted - // on dm-verity device. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1"); - }); -} - -// Test scenario when we fallback to capex but it already has a decompressed -// version on data -TEST_F(ApexdMountTest, OnStartFallbackToAlreadyDecompressedCapex) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - PrepareCompressedApex("com.android.apex.compressed.v1.capex"); - AddDataApex("com.android.apex.compressed.v2_manifest_mismatch.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Decompressed APEX should be mounted - std::string decompressed_active_apex = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1"); - }); -} - -// Test scenario when we fallback to capex but it has same version as corrupt -// data apex -TEST_F(ApexdMountTest, OnStartFallbackToCapexSameVersion) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - AddPreInstalledApex("com.android.apex.compressed.v2.capex"); - // Add data apex using the common naming convention for /data/apex/active - // directory - fs::copy(GetTestFile("com.android.apex.compressed.v2_manifest_mismatch.apex"), - GetDataDir() + "/com.android.apex.compressed@2.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Decompressed APEX should be mounted - std::string decompressed_active_apex = StringPrintf( - "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@2")); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@2"); - }); -} - -TEST_F(ApexdMountTest, OnStartCapexToApex) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - TemporaryDir previous_built_in_dir; - PrepareCompressedApex("com.android.apex.compressed.v1.capex", - previous_built_in_dir.path); - auto apex_path = - AddPreInstalledApex("com.android.apex.compressed.v1_original.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Uncompressed APEX should be mounted - UnmountOnTearDown(apex_path); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from decompressed apex. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, apex_path); - ASSERT_THAT(data.device_name, IsEmpty()); - }); -} - -// Test to ensure we do not mount decompressed APEX from /data/apex/active -TEST_F(ApexdMountTest, OnStartOrphanedDecompressedApexInActiveDirectory) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - // Place a decompressed APEX in /data/apex/active. This apex should not - // be mounted since it's not in correct location. Instead, the - // pre-installed APEX should be mounted. - auto decompressed_apex_in_active_dir = - StringPrintf("%s/com.android.apex.compressed@1%s", GetDataDir().c_str(), - kDecompressedApexPackageSuffix); - fs::copy(GetTestFile("com.android.apex.compressed.v1_original.apex"), - decompressed_apex_in_active_dir); - auto apex_path = - AddPreInstalledApex("com.android.apex.compressed.v1_original.apex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Pre-installed APEX should be mounted - UnmountOnTearDown(apex_path); - auto& db = GetApexDatabaseForTesting(); - // Check that pre-installed APEX has been activated - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, apex_path); - ASSERT_THAT(data.device_name, IsEmpty()); - }); -} - -// Test scenario when decompressed version has different version than -// pre-installed CAPEX -TEST_F(ApexdMountTest, OnStartDecompressedApexVersionDifferentThanCapex) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - TemporaryDir previous_built_in_dir; - PrepareCompressedApex("com.android.apex.compressed.v2.capex", - previous_built_in_dir.path); - auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Existing higher version decompressed APEX should be ignored and new - // pre-installed CAPEX should be decompressed and mounted - std::string decompressed_active_apex = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - ASSERT_EQ(GetProperty(kTestApexdStatusSysprop, ""), "starting"); - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1")); - auto& db = GetApexDatabaseForTesting(); - // Check that it was mounted from newly decompressed apex. - db.ForallMountedApexes("com.android.apex.compressed", - [&](const MountedApexData& data, bool latest) { - ASSERT_TRUE(latest); - ASSERT_EQ(data.full_path, decompressed_active_apex); - ASSERT_EQ(data.device_name, - "com.android.apex.compressed@1"); - }); -} - -// Test that ota_apex is persisted until slot switch -TEST_F(ApexdMountTest, OnStartOtaApexKeptUntilSlotSwitch) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - // Imagine current system has v1 capex and we have v2 incoming via ota - auto old_capex = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - auto ota_apex_path = - StringPrintf("%s/com.android.apex.compressed@2%s", - GetDecompressionDir().c_str(), kOtaApexPackageSuffix); - fs::copy(GetTestFile("com.android.apex.compressed.v2_original.apex"), - ota_apex_path.c_str()); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - // When we call OnStart for the first time, it will decompress v1 capex and - // activate it, while after second call it will decompress v2 capex and - // activate it. We need to make sure that activated APEXes are cleaned up - // after test finishes. - auto old_decompressed_apex = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - auto new_decompressed_apex = StringPrintf( - "%s/com.android.apex.compressed@2%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - UnmountOnTearDown(old_decompressed_apex); - UnmountOnTearDown(new_decompressed_apex); - - // First try starting without slot switch. Since we are booting with - // old pre-installed capex, ota_apex should not be deleted - OnStart(); - auto path_exists = PathExists(ota_apex_path); - ASSERT_TRUE(*path_exists); - - // When we switch slot, the pre-installed APEX will match ota_apex - // and the ota_apex will end up getting renamed. - RemoveFileIfExists(old_capex); - AddPreInstalledApex("com.android.apex.compressed.v2.capex"); - ApexFileRepository::GetInstance().Reset(GetDecompressionDir()); - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - OnStart(); - path_exists = PathExists(ota_apex_path); - ASSERT_FALSE(*path_exists); -} - -// Test scenario when decompressed version has same version but different -// digest -TEST_F(ApexdMountTest, - OnStartDecompressedApexVersionSameAsCapexDifferentDigest) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - // Push a CAPEX to system without decompressing it - auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - auto pre_installed_apex = ApexFile::Open(apex_path); - // Now push an APEX with different root digest as decompressed APEX - auto decompressed_apex_path = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - fs::copy(GetTestFile( - "com.android.apex.compressed.v1_different_digest_original.apex"), - decompressed_apex_path); - auto different_digest_apex = ApexFile::Open(decompressed_apex_path); - auto different_digest = GetRootDigest(*different_digest_apex); - ASSERT_NE( - pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), - different_digest); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Existing same version decompressed APEX with different root digest should - // be ignored and the pre-installed CAPEX should be decompressed again. - UnmountOnTearDown(decompressed_apex_path); - - // Ensure decompressed apex has same digest as pre-installed - auto decompressed_apex = ApexFile::Open(decompressed_apex_path); - ASSERT_EQ( - pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), - GetRootDigest(*decompressed_apex)); - ASSERT_NE(GetRootDigest(*decompressed_apex), different_digest); -} - -// Test when decompressed APEX has different key than CAPEX -TEST_F(ApexdMountTest, OnStartDecompressedApexVersionSameAsCapexDifferentKey) { - MockCheckpointInterface checkpoint_interface; - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - TemporaryDir previous_built_in_dir; - auto different_key_apex_path = - PrepareCompressedApex("com.android.apex.compressed_different_key.capex", - previous_built_in_dir.path); - // Place a same version capex in current built_in_dir, which has different key - auto apex_path = AddPreInstalledApex("com.android.apex.compressed.v1.capex"); - - ASSERT_RESULT_OK( - ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()})); - - OnStart(); - - // Existing same version decompressed APEX should be ignored and new - // pre-installed CAPEX should be decompressed and mounted - std::string decompressed_active_apex = StringPrintf( - "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), - kDecompressedApexPackageSuffix); - UnmountOnTearDown(decompressed_active_apex); - - // Ensure decompressed apex has same digest as pre-installed - auto pre_installed_apex = ApexFile::Open(apex_path); - auto decompressed_apex = ApexFile::Open(decompressed_active_apex); - auto different_key_apex = ApexFile::Open(different_key_apex_path); - ASSERT_EQ( - pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), - GetRootDigest(*decompressed_apex)); - ASSERT_NE( - pre_installed_apex->GetManifest().capexmetadata().originalapexdigest(), - GetRootDigest(*different_key_apex)); -} - -TEST_F(ApexdMountTest, PopulateFromMountsChecksPathPrefix) { - AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path = AddDataApex("apex.apexd_test_v2.apex"); - - // Mount an apex from decomrpession_dir - PrepareCompressedApex("com.android.apex.compressed.v1.capex"); - std::string decompressed_apex = - StringPrintf("%s/com.android.apex.compressed@1.decompressed.apex", - GetDecompressionDir().c_str()); - - // Mount an apex from some other directory - TemporaryDir td; - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - fs::copy(GetTestFile("apex.apexd_test_different_app.apex"), td.path); - std::string other_apex = - StringPrintf("%s/apex.apexd_test_different_app.apex", td.path); - - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); - - ASSERT_TRUE(IsOk(ActivatePackage(apex_path))); - ASSERT_TRUE(IsOk(ActivatePackage(decompressed_apex))); - ASSERT_TRUE(IsOk(ActivatePackage(other_apex))); - - auto& db = GetApexDatabaseForTesting(); - // Remember mount information for |other_apex|, since it won't be available in - // the database. We will need to tear it down manually. - std::optional<MountedApexData> other_apex_mount_data; - db.ForallMountedApexes( - "com.android.apex.test_package_2", - [&other_apex_mount_data](const MountedApexData& data, bool latest) { - if (latest) { - other_apex_mount_data.emplace(data); - } - }); - UnmountOnTearDown(apex_path); - UnmountOnTearDown(decompressed_apex); - ASSERT_TRUE(other_apex_mount_data.has_value()); - auto deleter = make_scope_guard([&other_apex_mount_data]() { - if (!other_apex_mount_data.has_value()) { - return; - } - if (umount2("/apex/com.android.apex.test_package_2", 0) != 0) { - PLOG(ERROR) << "Failed to unmount /apex/com.android.apex.test_package_2"; - } - auto res = Unmount(*other_apex_mount_data, /* deferred= */ false); - if (!res.ok()) { - LOG(ERROR) << res.error(); - } - }); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@2", - "/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - // Clear the database before calling PopulateFromMounts - db.Reset(); - - // Populate from mount - db.PopulateFromMounts(GetDataDir(), GetDecompressionDir(), GetHashTreeDir()); - - // Count number of package and collect package names - int package_count = 0; - std::vector<std::string> mounted_paths; - db.ForallMountedApexes([&](const std::string& package, - const MountedApexData& data, bool latest) { - package_count++; - mounted_paths.push_back(data.full_path); - }); - ASSERT_EQ(package_count, 2); - ASSERT_THAT(mounted_paths, - UnorderedElementsAre(apex_path, decompressed_apex)); -} - -TEST_F(ApexdMountTest, UnmountAll) { - AddPreInstalledApex("apex.apexd_test.apex"); - std::string apex_path_2 = - AddPreInstalledApex("apex.apexd_test_different_app.apex"); - std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); - - // Mount an apex from decomrpession_dir - PrepareCompressedApex("com.android.apex.compressed.v1.capex"); - std::string decompressed_apex = - StringPrintf("%s/com.android.apex.compressed@1.decompressed.apex", - GetDecompressionDir().c_str()); - - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); - - ASSERT_TRUE(IsOk(ActivatePackage(apex_path_2))); - ASSERT_TRUE(IsOk(ActivatePackage(apex_path_3))); - ASSERT_TRUE(IsOk(ActivatePackage(decompressed_apex))); - UnmountOnTearDown(apex_path_2); - UnmountOnTearDown(apex_path_3); - UnmountOnTearDown(decompressed_apex); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test_package", - "/apex/com.android.apex.test_package@2", - "/apex/com.android.apex.compressed", - "/apex/com.android.apex.compressed@1", - "/apex/com.android.apex.test_package_2", - "/apex/com.android.apex.test_package_2@1")); - - auto& db = GetApexDatabaseForTesting(); - // UnmountAll expects apex database to empty, hence this reset. - db.Reset(); - - ASSERT_EQ(0, UnmountAll()); - - auto new_apex_mounts = GetApexMounts(); - ASSERT_EQ(new_apex_mounts.size(), 0u); -} - -TEST_F(ApexdMountTest, UnmountAllSharedLibsApex) { - ASSERT_EQ(mkdir("/apex/sharedlibs", 0755), 0); - ASSERT_EQ(mkdir("/apex/sharedlibs/lib", 0755), 0); - ASSERT_EQ(mkdir("/apex/sharedlibs/lib64", 0755), 0); - auto deleter = make_scope_guard([]() { - std::error_code ec; - fs::remove_all("/apex/sharedlibs", ec); - if (ec) { - LOG(ERROR) << "Failed to delete /apex/sharedlibs : " << ec; - } - }); - - std::string apex_path_1 = AddPreInstalledApex( - "com.android.apex.test.sharedlibs_generated.v1.libvX.apex"); - std::string apex_path_2 = - AddDataApex("com.android.apex.test.sharedlibs_generated.v2.libvY.apex"); - - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); - - ASSERT_TRUE(IsOk(ActivatePackage(apex_path_1))); - ASSERT_TRUE(IsOk(ActivatePackage(apex_path_2))); - UnmountOnTearDown(apex_path_1); - UnmountOnTearDown(apex_path_2); - - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, - UnorderedElementsAre("/apex/com.android.apex.test.sharedlibs@1", - "/apex/com.android.apex.test.sharedlibs@2")); - - auto& db = GetApexDatabaseForTesting(); - // UnmountAll expects apex database to empty, hence this reset. - db.Reset(); - - ASSERT_EQ(0, UnmountAll()); - - auto new_apex_mounts = GetApexMounts(); - ASSERT_EQ(new_apex_mounts.size(), 0u); -} - -class ApexActivationFailureTests : public ApexdMountTest {}; - -TEST_F(ApexActivationFailureTests, BuildFingerprintDifferent) { - auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); - apex_session->SetBuildFingerprint("wrong fingerprint"); - apex_session->UpdateStateAndCommit(SessionState::STAGED); - - OnStart(); - - apex_session = ApexSession::GetSession(123); - ASSERT_THAT(apex_session->GetErrorMessage(), - HasSubstr("APEX build fingerprint has changed")); -} - -TEST_F(ApexActivationFailureTests, ApexFileMissingInStagingDirectory) { - auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); - apex_session->UpdateStateAndCommit(SessionState::STAGED); - // Delete the apex file in staging directory - DeleteDirContent(GetStagedDir(123)); - - OnStart(); - - apex_session = ApexSession::GetSession(123); - ASSERT_THAT(apex_session->GetErrorMessage(), - HasSubstr("No APEX packages found")); -} - -TEST_F(ApexActivationFailureTests, MultipleApexFileInStagingDirectory) { - auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); - CreateStagedSession("com.android.apex.compressed.v1_original.apex", 123); - apex_session->UpdateStateAndCommit(SessionState::STAGED); - - OnStart(); - - apex_session = ApexSession::GetSession(123); - ASSERT_THAT(apex_session->GetErrorMessage(), - HasSubstr("More than one APEX package found")); -} - -TEST_F(ApexActivationFailureTests, PostInstallFailsForApex) { - auto apex_session = - CreateStagedSession("apex.apexd_test_corrupt_superblock_apex.apex", 123); - apex_session->UpdateStateAndCommit(SessionState::STAGED); - - OnStart(); - - apex_session = ApexSession::GetSession(123); - ASSERT_THAT(apex_session->GetErrorMessage(), - HasSubstr("Postinstall failed for session")); -} - -TEST_F(ApexActivationFailureTests, CorruptedApexCannotBeStaged) { - auto apex_session = CreateStagedSession("corrupted_b146895998.apex", 123); - apex_session->UpdateStateAndCommit(SessionState::STAGED); - - OnStart(); - - apex_session = ApexSession::GetSession(123); - ASSERT_THAT(apex_session->GetErrorMessage(), - HasSubstr("Activation failed for packages")); -} - -TEST_F(ApexActivationFailureTests, ActivatePackageImplFails) { - auto shim_path = AddPreInstalledApex("com.android.apex.cts.shim.apex"); - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); - - auto apex_session = - CreateStagedSession("com.android.apex.cts.shim.v2_wrong_sha.apex", 123); - apex_session->UpdateStateAndCommit(SessionState::STAGED); - - UnmountOnTearDown(shim_path); - OnStart(); - - apex_session = ApexSession::GetSession(123); - ASSERT_THAT(apex_session->GetErrorMessage(), - HasSubstr("Failed to activate packages")); - ASSERT_THAT(apex_session->GetErrorMessage(), - HasSubstr("has unexpected SHA512 hash")); -} - -TEST_F(ApexActivationFailureTests, - StagedSessionFailsWhenNotInFsCheckpointMode) { - MockCheckpointInterface checkpoint_interface; - checkpoint_interface.SetSupportsCheckpoint(true); - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - auto pre_installed_apex = AddPreInstalledApex("apex.apexd_test.apex"); - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); - - auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); - apex_session->UpdateStateAndCommit(SessionState::STAGED); - - UnmountOnTearDown(pre_installed_apex); - OnStart(); - - apex_session = ApexSession::GetSession(123); - ASSERT_EQ(apex_session->GetState(), SessionState::ACTIVATION_FAILED); - ASSERT_THAT( - apex_session->GetErrorMessage(), - HasSubstr("Cannot install apex session if not in fs-checkpoint mode")); -} - -TEST_F(ApexActivationFailureTests, StagedSessionRevertsWhenInFsRollbackMode) { - MockCheckpointInterface checkpoint_interface; - checkpoint_interface.SetSupportsCheckpoint(true); - checkpoint_interface.SetNeedsRollback(true); - // Need to call InitializeVold before calling OnStart - InitializeVold(&checkpoint_interface); - - auto pre_installed_apex = AddPreInstalledApex("apex.apexd_test.apex"); - auto& instance = ApexFileRepository::GetInstance(); - ASSERT_RESULT_OK(instance.AddPreInstalledApex({GetBuiltInDir()})); - - auto apex_session = CreateStagedSession("apex.apexd_test.apex", 123); - apex_session->UpdateStateAndCommit(SessionState::STAGED); - - UnmountOnTearDown(pre_installed_apex); - OnStart(); - - apex_session = ApexSession::GetSession(123); - ASSERT_EQ(apex_session->GetState(), SessionState::REVERTED); -} - -} // namespace apex -} // namespace android diff --git a/apexd/apexd_test_utils.h b/apexd/apexd_test_utils.h index 5e3689d3..0e43477d 100644 --- a/apexd/apexd_test_utils.h +++ b/apexd/apexd_test_utils.h @@ -14,34 +14,14 @@ * limitations under the License. */ -#include <filesystem> -#include <fstream> - -#include <asm-generic/errno-base.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <sched.h> -#include <sys/mount.h> - -#include <android-base/errors.h> -#include <android-base/logging.h> -#include <android-base/macros.h> -#include <android-base/result.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <android-base/unique_fd.h> #include <android/apex/ApexInfo.h> #include <android/apex/ApexSessionInfo.h> #include <binder/IServiceManager.h> -#include <selinux/android.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> -#include "apex_file.h" #include "session_state.pb.h" -#include "com_android_apex.h" - -using android::base::Error; -using android::base::Result; using apex::proto::SessionState; namespace android { @@ -52,7 +32,6 @@ using ::testing::AllOf; using ::testing::Eq; using ::testing::ExplainMatchResult; using ::testing::Field; -using ::testing::Property; template <typename T> inline ::testing::AssertionResult IsOk(const android::base::Result<T>& result) { @@ -106,22 +85,6 @@ MATCHER_P(ApexInfoEq, other, "") { arg, result_listener); } -MATCHER_P(ApexFileEq, other, "") { - return ExplainMatchResult( - AllOf(Property("path", &ApexFile::GetPath, Eq(other.get().GetPath())), - Property("image_offset", &ApexFile::GetImageOffset, - Eq(other.get().GetImageOffset())), - Property("image_size", &ApexFile::GetImageSize, - Eq(other.get().GetImageSize())), - Property("fs_type", &ApexFile::GetFsType, - Eq(other.get().GetFsType())), - Property("public_key", &ApexFile::GetBundledPublicKey, - Eq(other.get().GetBundledPublicKey())), - Property("is_compressed", &ApexFile::IsCompressed, - Eq(other.get().IsCompressed()))), - arg, result_listener); -} - inline ApexSessionInfo CreateSessionInfo(int session_id) { ApexSessionInfo info; info.sessionId = session_id; @@ -165,194 +128,5 @@ inline void PrintTo(const ApexInfo& apex, std::ostream* os) { *os << "}"; } -inline Result<bool> CompareFiles(const std::string& filename1, - const std::string& filename2) { - std::ifstream file1(filename1, std::ios::binary); - std::ifstream file2(filename2, std::ios::binary); - - if (file1.bad() || file2.bad()) { - return Error() << "Could not open one of the file"; - } - - std::istreambuf_iterator<char> begin1(file1); - std::istreambuf_iterator<char> begin2(file2); - - return std::equal(begin1, std::istreambuf_iterator<char>(), begin2); -} - -inline android::base::Result<std::string> GetCurrentMountNamespace() { - std::string result; - if (!android::base::Readlink("/proc/self/ns/mnt", &result)) { - return android::base::ErrnoError() << "Failed to read /proc/self/ns/mnt"; - } - return result; -} - -// A helper class to switch back to the original mount namespace of a process -// upon exiting current scope. -class MountNamespaceRestorer final { - public: - explicit MountNamespaceRestorer() { - original_namespace_.reset(open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC)); - if (original_namespace_.get() < 0) { - PLOG(ERROR) << "Failed to open /proc/self/ns/mnt"; - } - } - - ~MountNamespaceRestorer() { - if (original_namespace_.get() != -1) { - if (setns(original_namespace_.get(), CLONE_NEWNS) == -1) { - PLOG(ERROR) << "Failed to switch back to " << original_namespace_.get(); - } - } - } - - private: - android::base::unique_fd original_namespace_; - DISALLOW_COPY_AND_ASSIGN(MountNamespaceRestorer); -}; - -inline std::vector<std::string> GetApexMounts() { - std::vector<std::string> apex_mounts; - std::string mount_info; - if (!android::base::ReadFileToString("/proc/self/mountinfo", &mount_info)) { - return apex_mounts; - } - for (const auto& line : android::base::Split(mount_info, "\n")) { - std::vector<std::string> tokens = android::base::Split(line, " "); - // line format: - // mnt_id parent_mnt_id major:minor source target option propagation_type - // ex) 33 260:19 / /apex rw,nosuid,nodev - - if (tokens.size() >= 7 && android::base::StartsWith(tokens[4], "/apex/")) { - apex_mounts.push_back(tokens[4]); - } - } - return apex_mounts; -} - -// Sets up a test environment for unit testing logic around mounting/unmounting -// apexes. For examples of usage see apexd_test.cpp -inline android::base::Result<void> SetUpApexTestEnvironment() { - using android::base::ErrnoError; - - // 1. Switch to new mount namespace. - if (unshare(CLONE_NEWNS) != 0) { - return ErrnoError() << "Failed to unshare"; - } - - // 2. Make everything private, so that changes don't propagate. - if (mount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr) == -1) { - return ErrnoError() << "Failed to mount / as private"; - } - - // 3. Unmount all apexes. This needs to happen in two phases: - // Note: unlike regular unmount flow in apexd, we don't destroy dm and loop - // devices, since that would've propagated outside of the test environment. - std::vector<std::string> apex_mounts = GetApexMounts(); - - // 3a. First unmount all bind mounds (without @version_code). - for (const auto& mount : apex_mounts) { - if (mount.find('@') == std::string::npos) { - if (umount2(mount.c_str(), 0) != 0) { - return ErrnoError() << "Failed to unmount " << mount; - } - } - } - - // 3.b Now unmount versioned mounts. - for (const auto& mount : apex_mounts) { - if (mount.find('@') != std::string::npos) { - if (umount2(mount.c_str(), 0) != 0) { - return ErrnoError() << "Failed to unmount " << mount; - } - } - } - - static constexpr const char* kApexMountForTest = "/mnt/scratch/apex"; - - // Clean up in case previous test left directory behind. - if (access(kApexMountForTest, F_OK) == 0) { - if (umount2(kApexMountForTest, MNT_FORCE | UMOUNT_NOFOLLOW) != 0) { - PLOG(WARNING) << "Failed to unmount " << kApexMountForTest; - } - if (rmdir(kApexMountForTest) != 0) { - return ErrnoError() << "Failed to rmdir " << kApexMountForTest; - } - } - - // 4. Create an empty tmpfs that will substitute /apex in tests. - if (mkdir(kApexMountForTest, 0755) != 0) { - return ErrnoError() << "Failed to mkdir " << kApexMountForTest; - } - - if (mount("tmpfs", kApexMountForTest, "tmpfs", 0, nullptr) == -1) { - return ErrnoError() << "Failed to mount " << kApexMountForTest; - } - - // 5. Overlay it over /apex via bind mount. - if (mount(kApexMountForTest, "/apex", nullptr, MS_BIND, nullptr) == -1) { - return ErrnoError() << "Failed to bind mount " << kApexMountForTest - << " over /apex"; - } - - // Just in case, run restorecon -R on /apex. - if (selinux_android_restorecon("/apex", SELINUX_ANDROID_RESTORECON_RECURSE) < - 0) { - return android::base::ErrnoError() << "Failed to restorecon /apex"; - } - - return {}; -} - -} // namespace apex -} // namespace android - -namespace com { -namespace android { -namespace apex { - -namespace testing { -MATCHER_P(ApexInfoXmlEq, other, "") { - using ::testing::AllOf; - using ::testing::Eq; - using ::testing::ExplainMatchResult; - using ::testing::Field; - using ::testing::Property; - - return ExplainMatchResult( - AllOf( - Property("moduleName", &ApexInfo::getModuleName, - Eq(other.getModuleName())), - Property("modulePath", &ApexInfo::getModulePath, - Eq(other.getModulePath())), - Property("preinstalledModulePath", - &ApexInfo::getPreinstalledModulePath, - Eq(other.getPreinstalledModulePath())), - Property("versionCode", &ApexInfo::getVersionCode, - Eq(other.getVersionCode())), - Property("isFactory", &ApexInfo::getIsFactory, - Eq(other.getIsFactory())), - Property("isActive", &ApexInfo::getIsActive, Eq(other.getIsActive())), - Property("lastUpdateMillis", &ApexInfo::getLastUpdateMillis, - Eq(other.getLastUpdateMillis()))), - arg, result_listener); -} - -} // namespace testing - -// Must be in com::android::apex namespace for gtest to pick it up. -inline void PrintTo(const ApexInfo& apex, std::ostream* os) { - *os << "apex_info: {\n"; - *os << " moduleName : " << apex.getModuleName() << "\n"; - *os << " modulePath : " << apex.getModulePath() << "\n"; - *os << " preinstalledModulePath : " << apex.getPreinstalledModulePath() - << "\n"; - *os << " versionCode : " << apex.getVersionCode() << "\n"; - *os << " isFactory : " << apex.getIsFactory() << "\n"; - *os << " isActive : " << apex.getIsActive() << "\n"; - *os << "}"; -} - } // namespace apex } // namespace android -} // namespace com diff --git a/apexd/apexd_testdata/Android.bp b/apexd/apexd_testdata/Android.bp index 0e7039a0..1f74a532 100644 --- a/apexd/apexd_testdata/Android.bp +++ b/apexd/apexd_testdata/Android.bp @@ -15,10 +15,6 @@ // These apex definitions will generate the prebuilt test data. The modules // are disabled so as not to pollute the build. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - apex_key { name: "com.android.apex.test_package.key", public_key: "com.android.apex.test_package.avbpubkey", @@ -26,13 +22,6 @@ apex_key { installable: false, } -apex_key { - name: "com.android.apex.compressed.key", - public_key: "com.android.apex.compressed.avbpubkey", - private_key: "com.android.apex.compressed.pem", - installable: false, -} - apex { name: "apex.apexd_test", manifest: "manifest.json", @@ -44,173 +33,13 @@ apex { } apex { - name: "com.android.apex.compressed.v1", - manifest: "manifest_compressed.json", - file_contexts: ":apex.test-file_contexts", - prebuilts: ["sample_prebuilt_file"], - key: "com.android.apex.compressed.key", - installable: false, - test_only_force_compression: true, - updatable: false, -} - -apex { - name: "com.android.apex.compressed.v1_different_digest", - manifest: "manifest_compressed.json", - file_contexts: ":apex.test-file_contexts", - prebuilts: ["hash_of_dev_null"], - key: "com.android.apex.compressed.key", - installable: false, - test_only_force_compression: true, - updatable: false, -} - -genrule { - // Generates a compressed apex which doesn't have an original_apex file in it - name: "gen_capex_without_apex", - out: ["com.android.apex.compressed.v1_without_apex.capex"], - srcs: [":com.android.apex.compressed.v1"], - tools: ["soong_zip"], - cmd: "unzip -q $(in) -d $(genDir) && rm $(genDir)/original_apex && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) -L 9 " + - "-o $(genDir)/com.android.apex.compressed.v1_without_apex.capex" -} - -genrule { - // Generates a compressed apex which has different version of original_apex in it - name: "gen_capex_with_v2_apex", - out: ["com.android.apex.compressed.v1_with_v2_apex.capex"], - srcs: [":com.android.apex.compressed.v2"], - tools: ["soong_zip", "conv_apex_manifest"], - cmd: "unzip -q $(in) -d $(genDir) && " + - "$(location conv_apex_manifest) setprop version 1 $(genDir)/apex_manifest.pb && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) -L 9 " + - "-o $(genDir)/com.android.apex.compressed.v1_with_v2_apex.capex" -} - -genrule { - // Generates a compressed apex which can be opened but not decompressed - name: "gen_capex_not_decompressible", - out: ["com.android.apex.compressed.v1_not_decompressible.capex"], - srcs: [":com.android.apex.compressed.v1"], - tools: ["soong_zip", "conv_apex_manifest"], - cmd: "unzip -q $(in) -d $(genDir) && echo '' > $(genDir)/original_apex && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) -L 9 " + - "-o $(genDir)/com.android.apex.compressed.v1_not_decompressible.capex" -} - -genrule { - // Generates a capex which has same module name as com.android.apex.compressed, but - // is contains a different public key. - name: "gen_key_mismatch_capex", - out: ["com.android.apex.compressed_different_key.capex"], - srcs: [":apex.apexd_test_no_inst_key"], - tools: ["soong_zip", "zipalign", "conv_apex_manifest", "apex_compression_tool"], - cmd: "unzip -q $(in) -d $(genDir) && " + - "$(location conv_apex_manifest) setprop name com.android.apex.compressed $(genDir)/apex_manifest.pb && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) " + - "-s apex_manifest.pb -s apex_payload.img -s apex_pubkey " + - "-o $(genDir)/unaligned.apex && " + - "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " + - "$(genDir)/com.android.apex.compressed_different_key.apex && " + - "$(location apex_compression_tool) compress " + - "--apex_compression_tool_path='out/soong/host/linux-x86/bin:prebuilts/sdk/tools/linux/bin' " + - "--input=$(genDir)/com.android.apex.compressed_different_key.apex " + - "--output=$(genDir)/com.android.apex.compressed_different_key.capex" -} - -genrule { - // Generates a capex which has a different public key than original_apex - name: "gen_key_mismatch_with_original_capex", - out: ["com.android.apex.compressed_key_mismatch_with_original.capex"], - srcs: [":com.android.apex.compressed.v1"], - tools: ["soong_zip"], - cmd: "unzip -q $(in) -d $(genDir) && " + // unzip input - "echo 'different-key' >> $(genDir)/apex_pubkey && " + // modify the public key - "$(location soong_zip) -d -C $(genDir) -D $(genDir) -L 9 " +// repack the compressed APEX - "-o $(genDir)/com.android.apex.compressed_key_mismatch_with_original.capex", -} - -genrule { - // Generates an apex which has a different manifest outside the filesystem - // image. - name: "gen_manifest_mismatch_compressed_apex_v2", - out: ["com.android.apex.compressed.v2_manifest_mismatch.apex"], - srcs: [":com.android.apex.compressed.v2_original"], - tools: ["soong_zip", "zipalign", "conv_apex_manifest"], - cmd: "unzip -q $(in) -d $(genDir) && " + - "$(location conv_apex_manifest) setprop version 137 $(genDir)/apex_manifest.pb && " + - "$(location soong_zip) -d -C $(genDir) -D $(genDir) " + - "-s apex_manifest.pb -s apex_payload.img -s apex_pubkey " + - "-o $(genDir)/unaligned.apex && " + - "$(location zipalign) -f 4096 $(genDir)/unaligned.apex " + - "$(genDir)/com.android.apex.compressed.v2_manifest_mismatch.apex" -} - -apex { - name: "com.android.apex.compressed.v1_original", - manifest: "manifest_compressed.json", - file_contexts: ":apex.test-file_contexts", - prebuilts: ["sample_prebuilt_file"], - key: "com.android.apex.compressed.key", - installable: false, - compressible: false, - updatable: false, -} - -apex { - name: "com.android.apex.compressed.v1_different_digest_original", - manifest: "manifest_compressed.json", - file_contexts: ":apex.test-file_contexts", - prebuilts: ["hash_of_dev_null"], - key: "com.android.apex.compressed.key", - installable: false, - compressible: false, - updatable: false, -} - -apex { - name: "com.android.apex.compressed.v2", - manifest: "manifest_compressed_v2.json", - file_contexts: ":apex.test-file_contexts", - prebuilts: ["sample_prebuilt_file"], - key: "com.android.apex.compressed.key", - installable: false, - test_only_force_compression: true, - updatable: false, -} - -apex { - name: "com.android.apex.compressed.v2_original", - manifest: "manifest_compressed_v2.json", - file_contexts: ":apex.test-file_contexts", - prebuilts: ["sample_prebuilt_file"], - key: "com.android.apex.compressed.key", - installable: false, - compressible: false, - updatable: false, -} - -apex { - name: "apex.apexd_test_f2fs", - manifest: "manifest.json", - file_contexts: ":apex.test-file_contexts", - prebuilts: ["sample_prebuilt_file"], - key: "com.android.apex.test_package.key", - installable: false, - min_sdk_version: "current", - payload_fs_type: "f2fs", -} - -apex { name: "apex.apexd_test_no_hashtree", manifest: "manifest.json", file_contexts: ":apex.test-file_contexts", prebuilts: ["sample_prebuilt_file"], key: "com.android.apex.test_package.key", installable: false, - generate_hashtree: false, - updatable: false, + test_only_no_hashtree: true, } // This APEX has same name and version as apex.apexd_test_no_hashtree, but has @@ -226,8 +55,7 @@ apex { ], key: "com.android.apex.test_package.key", installable: false, - generate_hashtree: false, - updatable: false, + test_only_no_hashtree: true, } apex { @@ -237,7 +65,6 @@ apex { prebuilts: ["sample_prebuilt_file"], key: "com.android.apex.test_package.key", installable: false, - updatable: false, } apex { @@ -265,7 +92,6 @@ apex { prebuilts: ["sample_prebuilt_file"], key: "com.android.apex.test_package.key", installable: false, - updatable: false, } apex_key { @@ -283,7 +109,6 @@ apex { key: "com.android.apex.test_package.preinstall.key", binaries: ["apex_test_preInstallHook"], installable: false, - updatable: false, } apex_key { @@ -301,7 +126,6 @@ apex { key: "com.android.apex.test_package.postinstall.key", binaries: ["apex_test_postInstallHook"], installable: false, - updatable: false, } apex_key { @@ -319,7 +143,6 @@ apex { key: "com.android.apex.test_package.prepostinstall.fail.key", binaries: ["apex_test_prePostInstallHookFail"], installable: false, - updatable: false, } apex_key { @@ -336,18 +159,6 @@ apex { prebuilts: ["sample_prebuilt_file"], key: "com.android.apex.test_package.no_inst_key.key", installable: false, - updatable: false, -} - -apex { - name: "apex.apexd_test_f2fs_no_inst_key", - manifest: "manifest_no_inst_key.json", - file_contexts: ":apex.test-file_contexts", - prebuilts: ["sample_prebuilt_file"], - key: "com.android.apex.test_package.no_inst_key.key", - installable: false, - payload_fs_type: "f2fs", - updatable: false, } apex_key { @@ -364,7 +175,6 @@ apex { prebuilts: ["sample_prebuilt_file"], key: "com.android.apex.test_package_2.key", installable: false, - updatable: false, } sh_binary { @@ -389,7 +199,6 @@ apex { prebuilts: ["sample_prebuilt_file"], key: "com.android.apex.test_package.key", installable: false, - updatable: false, } prebuilt_etc { @@ -403,156 +212,3 @@ prebuilt_apex { filename: "corrupted_b146895998.apex", installable: false, } - -// APEX for banned name test cannot be generated at build time. -// This file can be generated manually by creating new apex target -// with manifest name 'sharedlibs', and modify aapt2 to skip validating -// package name from aapt::util::IsAndroidPackageName(). -prebuilt_apex { - name: "apex.banned_name", - src: "sharedlibs.apex", - filename: "sharedlibs.apex", - installable: false, -} - -// A compressed apex that also provides shared libs. -// Should be declined by ApexFile::Open. -apex { - name: "com.android.apex.compressed_sharedlibs", - manifest: "manifest_compressed_sharedlibs.json", - file_contexts: ":apex.test-file_contexts", - prebuilts: ["sample_prebuilt_file"], - key: "com.android.apex.compressed.key", - installable: false, - test_only_force_compression: true, - updatable: false, -} - -apex { - name: "test.rebootless_apex_v1", - manifest: "manifest_rebootless.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, - // TODO(ioffe): we should have a separate field to hashtree presence. - min_sdk_version: "29", // test requires hashtree to be present. -} - -apex { - name: "test.rebootless_apex_v2", - manifest: "manifest_rebootless_v2.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, - // TODO(ioffe): we should have a separate field to hashtree presence. - min_sdk_version: "29", // test requires hashtree to be present. -} - -apex { - name: "test.rebootless_apex_v2_no_hashtree", - manifest: "manifest_rebootless_v2.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, - generate_hashtree: false, -} - -apex { - name: "test.rebootless_apex_provides_sharedlibs", - manifest: "manifest_rebootless_provides_sharedlibs.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, -} - -apex { - name: "test.rebootless_apex_provides_native_libs", - manifest: "manifest_rebootless_provides_native_libs.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, -} - -apex { - name: "test.rebootless_apex_requires_shared_apex_libs", - manifest: "manifest_rebootless_requires_shared_apex_libs.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, -} - -apex { - name: "test.rebootless_apex_jni_libs", - manifest: "manifest_rebootless_jni_libs.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, -} - -apex { - name: "test.rebootless_apex_add_native_lib", - manifest: "manifest_rebootless_add_native_lib.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, -} - -apex { - name: "test.rebootless_apex_remove_native_lib", - manifest: "manifest_rebootless_remove_native_lib.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, -} - -apex { - name: "test.rebootless_apex_app_in_apex", - manifest: "manifest_rebootless_v2.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, - apps: ["AppInRebootlessApex"], - // TODO(ioffe): we should have a separate field to hashtree presence. - min_sdk_version: "29", // test requires hashtree to be present. -} - -apex { - name: "test.rebootless_apex_priv_app_in_apex", - manifest: "manifest_rebootless_v2.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test_package.key", - installable: false, - updatable: false, - apps: ["PrivAppInRebootlessApex"], - // TODO(ioffe): we should have a separate field to hashtree presence. - min_sdk_version: "29", // test requires hashtree to be present. -} - -android_app { - name: "AppInRebootlessApex", - sdk_version: "29", - manifest: "AppInRebootlessApex_AndroidManifest.xml", - apex_available: [ - "test.rebootless_apex_app_in_apex", - ], -} - -android_app { - name: "PrivAppInRebootlessApex", - sdk_version: "29", - privileged: true, - manifest: "AppInRebootlessApex_AndroidManifest.xml", - apex_available: [ - "test.rebootless_apex_priv_app_in_apex", - ], -} diff --git a/apexd/apexd_testdata/AppInRebootlessApex_AndroidManifest.xml b/apexd/apexd_testdata/AppInRebootlessApex_AndroidManifest.xml deleted file mode 100644 index 242d1be4..00000000 --- a/apexd/apexd_testdata/AppInRebootlessApex_AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (C) 2017 Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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="com.android.apex.rebootless_tests.app"> -</manifest> diff --git a/apexd/apexd_testdata/com.android.apex.compressed.avbpubkey b/apexd/apexd_testdata/com.android.apex.compressed.avbpubkey Binary files differdeleted file mode 100644 index 92767eac..00000000 --- a/apexd/apexd_testdata/com.android.apex.compressed.avbpubkey +++ /dev/null diff --git a/apexd/apexd_testdata/com.android.apex.compressed.pem b/apexd/apexd_testdata/com.android.apex.compressed.pem deleted file mode 100644 index bc2c0a72..00000000 --- a/apexd/apexd_testdata/com.android.apex.compressed.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKQIBAAKCAgEAqvAUWzu1+JmgUDTMx22v3Wcdv05CwE3y7roVbIhIFtfQjvjY -NNStwnl5r+2er0BmlNDWAPC20x46uF2kfSecIjoWMOAjMtkgWHYX6nZqE2BXo6Mj -KnxC+D+zdj6FO+QCbAZ1JKlfy/zGyq37VTIQu1OF9a3FyusOP2qapWr4jYiZ2WLr -gHhk/qvurN2GhpeTX7TMSEOVfg2Q7kKTxxXE5blw5lRaF9FzX4FMfU296qyXJ1qE -8pLjcVwVjTjrAO69AQ3S+lZZXA5WJwbHqU5WaRL5k+XjXASyoxTRvOHOsQ3mxQ+B -Ad227pk/x5lZLnnuc7d3Qe5cB2yJNDaENEptCA5VKaBG1QbRBL8ykD45w2OwIP7p -2H58b/2tEgtjA4bpWEFqwVa8LXMBg2x56Sbo8YWmQL4FE14XAYyI3eAs171JZV6e -nf9/ll+45oIXI2wY0jttfnYyGErLlbCIWYYByAnV5mwZ9eBrXl/xu65+MUKVnZOA -2y1KJch1eXUjLqMRAW6DBwOmAjsJhuiF8u2gZlF/zgnzPzWbbvfMWi/aztdCV2Yb -rRhKZT6i2A0a9o3lghwGpHJjXwJ23ELWmfWI6mxGGJyhrVRLvUlRp2a8QRmw6mcc -sWVWdichcjvx20fIPHanzC/TtgKe/smQjZ1GQClPbqRvbhoxleklLDk+NWkCAwEA -AQKCAgAJLwbY8/dN2OsdBAkwebsmGQEnIwxBCq8Pll3KS/QbjhK9a68p/3cqaJ5a -DlklLz+TpTiqKkSYSRp8h0NsGfGwgRBqJdCeTb4IIqgcR6phSh7LQtuDz9NFRR7e -LnO4CQL8TMiEZLkp23XOs9Q5+oudlNowndpvkXtdetu+IWYjLICfhkoCx/UdHZ2g -GfK9Tm1zieIjy8W/VPlBY8BOxOOkN/dR2JxShUU+j1LNtJyMfCNO0PUtlyizEXBy -+ujdTvZGlTiNjJAWewqz5BBcD0JjOMrB4Gr7qaDzVA4EqNkS+B063x9eO0w1u3QK -xDlfXtupyeSVXToCiBlC/d7SdGExJBqpebCKWLvdIXxDbgU5tNeKXcbHNp9zOduV -RTulPxseqKgvO/wDc27F1AChG68GYyWcEm7kWQd6BVmOOwxRurssrTgchFk/HrY+ -Vzlbqs+CTyrYoyZuMF8W7E0U7pQtLx4VMv1AEfHyzLmbPgsdwmlPZ4On+yks4rGt -jHGtUergxsAiiYBHZqXbmuK7UTg1wi5u8p4QgTUxC5jkKMetkgqGajWf5BDpf02E -+0P+58VGHkd3ACPFG4PcS8gnmjpx/EXicWq855w+nshr8Ff4Gs9LbUv0pg8s9mZA -2JGfGyjxp8h9/06OrKq5pM5HVuOCZ0AgEHEf++AeBSq24mrgkQKCAQEA28JaiH83 -7NCiK8XJebgPgVGl/wwugsA+Ea3Kv/prQGmFW/IVJNAbMAREkzDi4IaVGySPHDpo -OaDFFPOiC9zyveK8LdJyqaInEdN6nerGuM4ubA27DKP52MHfMBr6FDYgru5hcskw -4gHGZAWhD242rzhFGgfTTDbRXRhqn93f/6+yHbjyjybs5vPM2gU/qPlwdjUPxXFo -g3Nrx3TyrMtfUL/dhHniF+SiiVfu8hNqi4EuQ295jGtwMZl8U0/UVuQ6UQUMe6cT -VfKuunxyCq2AOvd4XLODZ9BcZDtTDLIsXaPyVzJUPGxugqlid/i7SuV+c3unT0dD -Ma2+7n6gdStuPQKCAQEAxyCeECT3Yhx1rB3SvloGVCMQY3ajs4/a3qIPLa18X8ZL -K1qUevlq7mpJVbnI0KmbuyNzPyGkPbQtuGU0g8kvchD7nH3Pi9SabB2ereIi9YO0 -s5a+ZVVPdtD69oCfhhKh9BPrjsSDnci2ZjCYkxzKHVtSvlJQmWAaxcUCwUKPvXen -LBQ4QNC9bnVj6v9XJwWy3YgrwpRetdBPU/jnSAjLOpjWKlXz4pokHpIR1kAsRY9O -In2gkP27PwSBMEZ4qxKa0uujQ5L4odqtDm4O15fF1IQ0/y+sNTrZArg54X3MYG6J -UyR9ReKXUL5nWYYSEsVJ6D9J8fiow21hiYnSt82inQKCAQEAlrW+QtgEcYtPfHeD -Sc96CrUFA1nGV/MhXhxy7J//h8gWJk1qRLnXu2Fh6hPftB14CopS/wfrTII+RrUj -D8GyU5k4drBZ5I4I/0eqUrydFkaIPaBZBD08bnPe7W3CzbOlTHK9L+xcctLGzPez -UhLCu/36HfT56s25XYAON58BLKfAnnOlHZmTZHwUo/xvgSG4B8kyDLVO+L9iTgKd -HvXGY2mBsIWqEbrB4TEF9MxuCEhKgwLjN/LCmbFqOvSHaiPQ/plYy0B0mT/6pngL -+ditFUN8Lw8JclvJ0Q+CUDWtCXcTDsu8S0gNrdweZKqXP7ENvIMz5cG4ikxeoc8D -mfdz/QKCAQBZkzGnX4mtJ9JDV7Maj9Ky/Ib9xzvCpZ62cb5UNOtzBfeAjCGo5BQX -JdbRal6Mhw+X8k2Ag7inTSsX/ObPtavTKxKUhf/cDgpdQkHERKqnONULyG7jlKnH -cCDEzH12SWFzM5bORVZTnxt2ArxPyS6eYBtrpAm/xPymJIaluzR/7ZhU+s+HUJ82 -VjZZWv2wfx7ECuJsiGPGc+uLgbdArzwEowYMS1gHgoFnAxxk/b8sl1d1qn+VWZ5m -rbcTqU/U3Oyqnvd1iWKxJHaevCGPGCYVAFf6x043L765O3hGGFncszyxGwQDcPfS -iaRiIC53JSqm//lakRoRt12eClKw/h79AoIBAQC0uu/kwB//HdQbzJkejoBZDWpu -2eTZKnLs/5hsU/CPfJnL2cJfStJIFh6WgutNoW++Xi79OfagEQx6hAyfl5HexV8V -2PNDMgcgxauAObhkZtg8x5ZDAyrOiXVHR+T3cm8qQbVtrHdiX97hrg9tTj923baK -TpI9qQw+2Jg627BR2t/rYIrZwdL2oqbznihIIPg6FkCT/53AfX8W1U9GWPnUyIkn -KvRdtY6osOS/C/Dju/Eg6710umACRPLa5928A5q+1jY6TntRIG8Q/agL4IXjR/0E -O0OkoRArOFt2NGN88Vqy7ses0VfP2UIpjjRP3/8xwUFmUo8vdoR5oy4kC/Sz ------END RSA PRIVATE KEY----- diff --git a/apexd/apexd_testdata/manifest_compressed.json b/apexd/apexd_testdata/manifest_compressed.json deleted file mode 100644 index 8c07cc82..00000000 --- a/apexd/apexd_testdata/manifest_compressed.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.apex.compressed", - "version": 1 -} diff --git a/apexd/apexd_testdata/manifest_compressed_sharedlibs.json b/apexd/apexd_testdata/manifest_compressed_sharedlibs.json deleted file mode 100644 index 30d2835f..00000000 --- a/apexd/apexd_testdata/manifest_compressed_sharedlibs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "com.android.apex.compressed", - "version": 1, - "provideSharedApexLibs": true -} diff --git a/apexd/apexd_testdata/manifest_compressed_v2.json b/apexd/apexd_testdata/manifest_compressed_v2.json deleted file mode 100644 index c5adb514..00000000 --- a/apexd/apexd_testdata/manifest_compressed_v2.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.apex.compressed", - "version": 2 -} diff --git a/apexd/apexd_testdata/manifest_rebootless.json b/apexd/apexd_testdata/manifest_rebootless.json deleted file mode 100644 index a37fa005..00000000 --- a/apexd/apexd_testdata/manifest_rebootless.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "test.apex.rebootless", - "version": 1, - "supportsRebootlessUpdate": true, - "requireNativeLibs": [ - "libfoo", - "libbar" - ] -} diff --git a/apexd/apexd_testdata/manifest_rebootless_add_native_lib.json b/apexd/apexd_testdata/manifest_rebootless_add_native_lib.json deleted file mode 100644 index eab172c9..00000000 --- a/apexd/apexd_testdata/manifest_rebootless_add_native_lib.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "test.apex.rebootless", - "version": 1, - "supportsRebootlessUpdate": true, - "requireNativeLibs": [ - "libfoo", - "libbar", - "libnew" - ] -} diff --git a/apexd/apexd_testdata/manifest_rebootless_jni_libs.json b/apexd/apexd_testdata/manifest_rebootless_jni_libs.json deleted file mode 100644 index bf88d2eb..00000000 --- a/apexd/apexd_testdata/manifest_rebootless_jni_libs.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "test.apex.rebootless", - "version": 1, - "supportsRebootlessUpdate": true, - "requireNativeLibs": [ - "libfoo", - "libbar" - ], - "jniLibs": [ - "libjniwow" - ] -} diff --git a/apexd/apexd_testdata/manifest_rebootless_provides_native_libs.json b/apexd/apexd_testdata/manifest_rebootless_provides_native_libs.json deleted file mode 100644 index ef829b07..00000000 --- a/apexd/apexd_testdata/manifest_rebootless_provides_native_libs.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "test.apex.rebootless", - "version": 1, - "supportsRebootlessUpdate": true, - "requireNativeLibs": [ - "libfoo", - "libbar" - ], - "provideNativeLibs": [ - "libbaz" - ] -} diff --git a/apexd/apexd_testdata/manifest_rebootless_provides_sharedlibs.json b/apexd/apexd_testdata/manifest_rebootless_provides_sharedlibs.json deleted file mode 100644 index d91c21e4..00000000 --- a/apexd/apexd_testdata/manifest_rebootless_provides_sharedlibs.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "test.apex.rebootless", - "version": 1, - "supportsRebootlessUpdate": true, - "requireNativeLibs": [ - "libfoo", - "libbar" - ], - "provideSharedApexLibs": true -} diff --git a/apexd/apexd_testdata/manifest_rebootless_remove_native_lib.json b/apexd/apexd_testdata/manifest_rebootless_remove_native_lib.json deleted file mode 100644 index 0926e70f..00000000 --- a/apexd/apexd_testdata/manifest_rebootless_remove_native_lib.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "test.apex.rebootless", - "version": 1, - "supportsRebootlessUpdate": true, - "requireNativeLibs": [ - "libbar" - ] -} diff --git a/apexd/apexd_testdata/manifest_rebootless_requires_shared_apex_libs.json b/apexd/apexd_testdata/manifest_rebootless_requires_shared_apex_libs.json deleted file mode 100644 index faaadfda..00000000 --- a/apexd/apexd_testdata/manifest_rebootless_requires_shared_apex_libs.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "test.apex.rebootless", - "version": 1, - "supportsRebootlessUpdate": true, - "requireNativeLibs": [ - "libfoo", - "libbar" - ], - "requireSharedApexLibs": [ - "libabc" - ] -} diff --git a/apexd/apexd_testdata/manifest_rebootless_v2.json b/apexd/apexd_testdata/manifest_rebootless_v2.json deleted file mode 100644 index dfcb21fe..00000000 --- a/apexd/apexd_testdata/manifest_rebootless_v2.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "test.apex.rebootless", - "version": 2, - "supportsRebootlessUpdate": true, - "requireNativeLibs": [ - "libfoo", - "libbar" - ] -} diff --git a/apexd/apexd_testdata/sharedlibs.apex b/apexd/apexd_testdata/sharedlibs.apex Binary files differdeleted file mode 100644 index cde11d51..00000000 --- a/apexd/apexd_testdata/sharedlibs.apex +++ /dev/null diff --git a/apexd/apexd_utils.h b/apexd/apexd_utils.h index 23194ebf..2759f9aa 100644 --- a/apexd/apexd_utils.h +++ b/apexd/apexd_utils.h @@ -18,11 +18,9 @@ #define ANDROID_APEXD_APEXD_UTILS_H_ #include <chrono> -#include <cstdint> #include <filesystem> #include <string> #include <thread> -#include <type_traits> #include <vector> #include <dirent.h> @@ -32,14 +30,17 @@ #include <android-base/chrono_utils.h> #include <android-base/logging.h> -#include <android-base/properties.h> #include <android-base/result.h> #include <android-base/scopeguard.h> #include <android-base/strings.h> #include <cutils/android_reboot.h> -#include <selinux/android.h> #include "apex_constants.h" +#include "string_log.h" + +using android::base::ErrnoError; +using android::base::Error; +using android::base::Result; namespace android { namespace apex { @@ -60,18 +61,21 @@ inline int WaitChild(pid_t pid) { } } -inline android::base::Result<void> ForkAndRun( - const std::vector<std::string>& args) { +// TODO(ioffe): change to Result<void>? +inline int ForkAndRun(const std::vector<std::string>& args, + std::string* error_msg) { LOG(DEBUG) << "Forking : " << android::base::Join(args, " "); std::vector<const char*> argv; argv.resize(args.size() + 1, nullptr); std::transform(args.begin(), args.end(), argv.begin(), [](const std::string& in) { return in.c_str(); }); + // 3) Fork. pid_t pid = fork(); if (pid == -1) { // Fork failed. - return android::base::ErrnoError() << "Unable to fork"; + *error_msg = PStringLog() << "Unable to fork"; + return -1; } if (pid == 0) { @@ -82,13 +86,13 @@ inline android::base::Result<void> ForkAndRun( int rc = WaitChild(pid); if (rc != 0) { - return android::base::Error() << "Failed run: status=" << rc; + *error_msg = StringLog() << "Failed run: status=" << rc; } - return {}; + return rc; } template <typename Fn> -android::base::Result<void> WalkDir(const std::string& path, Fn fn) { +Result<void> WalkDir(const std::string& path, Fn fn) { namespace fs = std::filesystem; std::error_code ec; auto it = fs::directory_iterator(path, ec); @@ -98,15 +102,14 @@ android::base::Result<void> WalkDir(const std::string& path, Fn fn) { it.increment(ec); } if (ec) { - return android::base::Error() - << "Can't open " << path << " for reading : " << ec.message(); + return Error() << "Can't open " << path + << " for reading : " << ec.message(); } return {}; } template <typename FilterFn> -android::base::Result<std::vector<std::string>> ReadDir(const std::string& path, - FilterFn fn) { +Result<std::vector<std::string>> ReadDir(const std::string& path, FilterFn fn) { namespace fs = std::filesystem; std::vector<std::string> ret; @@ -126,70 +129,72 @@ inline bool IsEmptyDirectory(const std::string& path) { return res.ok() && res->empty(); } -inline android::base::Result<void> CreateDirIfNeeded(const std::string& path, - mode_t mode) { +inline Result<void> createDirIfNeeded(const std::string& path, mode_t mode) { struct stat stat_data; if (stat(path.c_str(), &stat_data) != 0) { if (errno == ENOENT) { if (mkdir(path.c_str(), mode) != 0) { - return android::base::ErrnoError() << "Could not mkdir " << path; + return ErrnoError() << "Could not mkdir " << path; } } else { - return android::base::ErrnoError() << "Could not stat " << path; + return ErrnoError() << "Could not stat " << path; } } else { if (!S_ISDIR(stat_data.st_mode)) { - return android::base::Error() - << path << " exists and is not a directory."; + return Error() << path << " exists and is not a directory."; } } // Need to manually call chmod because mkdir will create a folder with // permissions mode & ~umask. if (chmod(path.c_str(), mode) != 0) { - return android::base::ErrnoError() << "Could not chmod " << path; + return ErrnoError() << "Could not chmod " << path; } return {}; } -inline android::base::Result<void> DeleteDirContent(const std::string& path) { +inline Result<void> DeleteDirContent(const std::string& path) { auto files = ReadDir(path, [](auto _) { return true; }); if (!files.ok()) { - return android::base::Error() - << "Failed to delete " << path << " : " << files.error(); + return Error() << "Failed to delete " << path << " : " << files.error(); } for (const std::string& file : *files) { - std::error_code ec; - std::filesystem::remove_all(file, ec); - if (ec) { - return android::base::Error() - << "Failed to delete path " << file << " : " << ec.message(); + if (unlink(file.c_str()) != 0) { + return ErrnoError() << "Failed to delete " << file; } } return {}; } -inline android::base::Result<void> DeleteDir(const std::string& path) { +inline Result<void> DeleteDir(const std::string& path) { namespace fs = std::filesystem; std::error_code ec; fs::remove_all(path, ec); if (ec) { - return android::base::Error() - << "Failed to delete path " << path << " : " << ec.message(); + return Error() << "Failed to delete path " << path << " : " << ec.message(); } return {}; } -inline android::base::Result<bool> PathExists(const std::string& path) { +inline Result<ino_t> get_path_inode(const std::string& path) { + struct stat buf; + memset(&buf, 0, sizeof(buf)); + if (stat(path.c_str(), &buf) != 0) { + return ErrnoError() << "Failed to stat " << path; + } else { + return buf.st_ino; + } +} + +inline Result<bool> PathExists(const std::string& path) { namespace fs = std::filesystem; std::error_code ec; if (!fs::exists(fs::path(path), ec)) { if (ec) { - return android::base::Error() - << "Failed to access " << path << " : " << ec.message(); + return Error() << "Failed to access " << path << " : " << ec.message(); } else { return false; } @@ -204,8 +209,8 @@ inline void Reboot() { } } -inline android::base::Result<void> WaitForFile( - const std::string& path, std::chrono::nanoseconds timeout) { +inline Result<void> WaitForFile(const std::string& path, + std::chrono::nanoseconds timeout) { android::base::Timer t; bool has_slept = false; while (t.duration() < timeout) { @@ -219,12 +224,10 @@ inline android::base::Result<void> WaitForFile( std::this_thread::sleep_for(5ms); has_slept = true; } - return android::base::ErrnoError() - << "wait for '" << path << "' timed out and took " << t; + return ErrnoError() << "wait for '" << path << "' timed out and took " << t; } -inline android::base::Result<std::vector<std::string>> GetSubdirs( - const std::string& path) { +inline Result<std::vector<std::string>> GetSubdirs(const std::string& path) { namespace fs = std::filesystem; auto filter_fn = [](const std::filesystem::directory_entry& entry) { std::error_code ec; @@ -238,140 +241,10 @@ inline android::base::Result<std::vector<std::string>> GetSubdirs( return ReadDir(path, filter_fn); } -inline android::base::Result<std::vector<std::string>> GetDeUserDirs() { +inline Result<std::vector<std::string>> GetDeUserDirs() { return GetSubdirs(kDeNDataDir); } -inline android::base::Result<std::vector<std::string>> FindFilesBySuffix( - const std::string& path, const std::vector<std::string>& suffix_list) { - auto filter_fn = - [&suffix_list](const std::filesystem::directory_entry& entry) { - for (const std::string& suffix : suffix_list) { - std::error_code ec; - auto name = entry.path().filename().string(); - if (entry.is_regular_file(ec) && - android::base::EndsWith(name, suffix)) { - return true; // suffix matches, take. - } - } - return false; - }; - return ReadDir(path, filter_fn); -} - -inline android::base::Result<std::vector<std::string>> FindApexes( - const std::vector<std::string>& paths) { - std::vector<std::string> result; - for (const auto& path : paths) { - auto exist = PathExists(path); - if (!exist.ok()) { - return exist.error(); - } - if (!*exist) continue; - - const auto& apexes = FindFilesBySuffix(path, {kApexPackageSuffix}); - if (!apexes.ok()) { - return apexes; - } - - result.insert(result.end(), apexes->begin(), apexes->end()); - } - return result; -} - -// Returns first path between |first_dir| and |second_dir| that correspond to a -// existing directory. Returns error if neither |first_dir| nor |second_dir| -// correspond to an existing directory. -inline android::base::Result<std::string> FindFirstExistingDirectory( - const std::string& first_dir, const std::string& second_dir) { - struct stat stat_buf; - if (stat(first_dir.c_str(), &stat_buf) != 0) { - PLOG(WARNING) << "Failed to stat " << first_dir; - if (stat(second_dir.c_str(), &stat_buf) != 0) { - return android::base::ErrnoError() << "Failed to stat " << second_dir; - } - if (!S_ISDIR(stat_buf.st_mode)) { - return android::base::Error() << second_dir << " is not a directory"; - } - return second_dir; - } - - if (S_ISDIR(stat_buf.st_mode)) { - return first_dir; - } - LOG(WARNING) << first_dir << " is not a directory"; - - if (stat(second_dir.c_str(), &stat_buf) != 0) { - return android::base::ErrnoError() << "Failed to stat " << second_dir; - } - if (!S_ISDIR(stat_buf.st_mode)) { - return android::base::Error() << second_dir << " is not a directory"; - } - return second_dir; -} - -// Copies all entries under |from| directory to |to| directory, and then them. -// Leaving |from| empty. -inline android::base::Result<void> MoveDir(const std::string& from, - const std::string& to) { - struct stat stat_buf; - if (stat(to.c_str(), &stat_buf) != 0) { - return android::base::ErrnoError() << "Failed to stat " << to; - } - if (!S_ISDIR(stat_buf.st_mode)) { - return android::base::Error() << to << " is not a directory"; - } - - namespace fs = std::filesystem; - std::error_code ec; - auto it = fs::directory_iterator(from, ec); - if (ec) { - return android::base::Error() - << "Can't read " << from << " : " << ec.message(); - } - - for (const auto& end = fs::directory_iterator(); it != end;) { - auto from_path = it->path(); - it.increment(ec); - if (ec) { - return android::base::Error() - << "Can't read " << from << " : " << ec.message(); - } - auto to_path = to / from_path.filename(); - fs::copy(from_path, to_path, fs::copy_options::recursive, ec); - if (ec) { - return android::base::Error() << "Failed to copy " << from_path << " to " - << to_path << " : " << ec.message(); - } - fs::remove_all(from_path, ec); - if (ec) { - return android::base::Error() - << "Failed to delete " << from_path << " : " << ec.message(); - } - } - return {}; -} - -inline android::base::Result<uintmax_t> GetFileSize( - const std::string& file_path) { - std::error_code ec; - auto value = std::filesystem::file_size(file_path, ec); - if (ec) { - return android::base::Error() << "Failed to get file size of " << file_path - << " : " << ec.message(); - } - - return value; -} - -inline android::base::Result<void> RestoreconPath(const std::string& path) { - unsigned int seflags = SELINUX_ANDROID_RESTORECON_RECURSE; - if (selinux_android_restorecon(path.c_str(), seflags) < 0) { - return android::base::ErrnoError() << "Failed to restorecon " << path; - } - return {}; -} - } // namespace apex } // namespace android diff --git a/apexd/apexd_utils_test.cpp b/apexd/apexd_utils_test.cpp deleted file mode 100644 index cbbc45e1..00000000 --- a/apexd/apexd_utils_test.cpp +++ /dev/null @@ -1,284 +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 <filesystem> -#include <fstream> -#include <new> -#include <string> - -#include <errno.h> - -#include <android-base/file.h> -#include <android-base/result.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <gtest/gtest.h> - -#include "apexd.h" -#include "apexd_test_utils.h" -#include "apexd_utils.h" - -namespace android { -namespace apex { -namespace { - -namespace fs = std::filesystem; - -using android::apex::testing::IsOk; -using android::base::Basename; -using android::base::Join; -using android::base::StringPrintf; -using ::testing::UnorderedElementsAre; -using ::testing::UnorderedElementsAreArray; - -// TODO(b/170327382): add unit tests for apexd_utils.h - -TEST(ApexdUtilTest, DeleteDirContent) { - TemporaryDir root_dir; - TemporaryFile child_file_1(root_dir.path); - TemporaryFile child_file_2(root_dir.path); - std::string child_dir = StringPrintf("%s/child-dir", root_dir.path); - CreateDirIfNeeded(child_dir, 0755); - TemporaryFile nested_file(child_dir); - - auto content = ReadDir(root_dir.path, [](auto _) { return true; }); - IsOk(content); - ASSERT_EQ(content->size(), 3u); - - auto del_result = DeleteDirContent(root_dir.path); - IsOk(del_result); - content = ReadDir(root_dir.path, [](auto _) { return true; }); - IsOk(content); - ASSERT_EQ(content->size(), 0u); -} - -TEST(ApexdUtilTest, FindFirstExistingDirectoryBothExist) { - TemporaryDir first_dir; - TemporaryDir second_dir; - auto result = FindFirstExistingDirectory(first_dir.path, second_dir.path); - ASSERT_TRUE(IsOk(result)); - ASSERT_EQ(*result, first_dir.path); -} - -TEST(ApexdUtilTest, FindFirstExistingDirectoryOnlyFirstExist) { - TemporaryDir first_dir; - auto second_dir = "/data/local/tmp/does/not/exist"; - auto result = FindFirstExistingDirectory(first_dir.path, second_dir); - ASSERT_TRUE(IsOk(result)); - ASSERT_EQ(*result, first_dir.path); -} - -TEST(ApexdUtilTest, FindFirstExistingDirectoryOnlySecondExist) { - auto first_dir = "/data/local/tmp/does/not/exist"; - TemporaryDir second_dir; - auto result = FindFirstExistingDirectory(first_dir, second_dir.path); - ASSERT_TRUE(IsOk(result)); - ASSERT_EQ(*result, second_dir.path); -} - -TEST(ApexdUtilTest, FindFirstExistingDirectoryNoneExist) { - auto first_dir = "/data/local/tmp/does/not/exist"; - auto second_dir = "/data/local/tmp/also/does/not/exist"; - auto result = FindFirstExistingDirectory(first_dir, second_dir); - ASSERT_FALSE(IsOk(result)); -} - -TEST(ApexdUtilTest, FindFirstExistingDirectoryFirstFileSecondDir) { - TemporaryFile first_file; - TemporaryDir second_dir; - auto result = FindFirstExistingDirectory(first_file.path, second_dir.path); - ASSERT_TRUE(IsOk(result)); - ASSERT_EQ(*result, second_dir.path); -} - -TEST(ApexdUtilTest, FindFirstExistingDirectoryFirstDirSecondFile) { - TemporaryDir first_dir; - TemporaryFile second_file; - auto result = FindFirstExistingDirectory(first_dir.path, second_file.path); - ASSERT_TRUE(IsOk(result)); - ASSERT_EQ(*result, first_dir.path); -} - -TEST(ApexdUtilTest, FindFirstExistingDirectoryBothFiles) { - TemporaryFile first_file; - TemporaryFile second_file; - auto result = FindFirstExistingDirectory(first_file.path, second_file.path); - ASSERT_FALSE(IsOk(result)); -} - -TEST(ApexdUtilTest, FindFirstExistingDirectoryFirstFileSecondDoesNotExist) { - TemporaryFile first_file; - auto second_dir = "/data/local/tmp/does/not/exist"; - auto result = FindFirstExistingDirectory(first_file.path, second_dir); - ASSERT_FALSE(IsOk(result)); -} - -TEST(ApexdUtilTest, FindFirstExistingDirectoryFirsDoesNotExistSecondFile) { - auto first_dir = "/data/local/tmp/does/not/exist"; - TemporaryFile second_file; - auto result = FindFirstExistingDirectory(first_dir, second_file.path); - ASSERT_FALSE(IsOk(result)); -} - -TEST(ApexdUtilTest, MoveDir) { - TemporaryDir from; - TemporaryDir to; - - TemporaryFile from_1(from.path); - auto from_subdir = StringPrintf("%s/subdir", from.path); - if (mkdir(from_subdir.c_str(), 07000) != 0) { - FAIL() << "Failed to mkdir " << from_subdir << " : " << strerror(errno); - } - TemporaryFile from_2(from_subdir); - - auto result = MoveDir(from.path, to.path); - ASSERT_TRUE(IsOk(result)); - ASSERT_TRUE(fs::is_empty(from.path)); - - std::vector<std::string> content; - for (const auto& it : fs::recursive_directory_iterator(to.path)) { - content.push_back(it.path()); - } - - static const std::vector<std::string> expected = { - StringPrintf("%s/%s", to.path, Basename(from_1.path).c_str()), - StringPrintf("%s/subdir", to.path), - StringPrintf("%s/subdir/%s", to.path, Basename(from_2.path).c_str()), - }; - ASSERT_THAT(content, UnorderedElementsAreArray(expected)); -} - -TEST(ApexdUtilTest, MoveDirFromIsNotDirectory) { - TemporaryFile from; - TemporaryDir to; - ASSERT_FALSE(IsOk(MoveDir(from.path, to.path))); -} - -TEST(ApexdUtilTest, MoveDirToIsNotDirectory) { - TemporaryDir from; - TemporaryFile to; - TemporaryFile from_1(from.path); - ASSERT_FALSE(IsOk(MoveDir(from.path, to.path))); -} - -TEST(ApexdUtilTest, MoveDirFromDoesNotExist) { - TemporaryDir to; - ASSERT_FALSE(IsOk(MoveDir("/data/local/tmp/does/not/exist", to.path))); -} - -TEST(ApexdUtilTest, MoveDirToDoesNotExist) { - namespace fs = std::filesystem; - - TemporaryDir from; - TemporaryFile from_1(from.path); - auto from_subdir = StringPrintf("%s/subdir", from.path); - if (mkdir(from_subdir.c_str(), 07000) != 0) { - FAIL() << "Failed to mkdir " << from_subdir << " : " << strerror(errno); - } - TemporaryFile from_2(from_subdir); - - ASSERT_FALSE(IsOk(MoveDir(from.path, "/data/local/tmp/does/not/exist"))); - - // Check that |from| directory is not empty. - std::vector<std::string> content; - for (const auto& it : fs::recursive_directory_iterator(from.path)) { - content.push_back(it.path()); - } - - ASSERT_THAT(content, - UnorderedElementsAre(from_1.path, from_subdir, from_2.path)); -} - -TEST(ApexdUtilTest, FindFilesBySuffix) { - TemporaryDir td; - - // create files with different suffix - const std::string first_filename = StringPrintf("%s/first.a", td.path); - const std::string second_filename = StringPrintf("%s/second.b", td.path); - const std::string third_filename = StringPrintf("%s/third.c", td.path); - const std::string fourth_filename = StringPrintf("%s/fourth.c", td.path); - - std::ofstream first_file(first_filename); - std::ofstream second_file(second_filename); - std::ofstream third_file(third_filename); - std::ofstream fourth_file(fourth_filename); - - auto result = FindFilesBySuffix(td.path, {".b", ".c"}); - ASSERT_TRUE(IsOk(result)); - ASSERT_THAT(*result, UnorderedElementsAre(second_filename, third_filename, - fourth_filename)); -} - -TEST(ApexdTestUtilsTest, MountNamespaceRestorer) { - auto original_namespace = GetCurrentMountNamespace(); - ASSERT_RESULT_OK(original_namespace); - { - MountNamespaceRestorer restorer; - // Switch to new mount namespace. - ASSERT_NE(-1, unshare(CLONE_NEWNS)); - auto current_namespace = GetCurrentMountNamespace(); - ASSERT_RESULT_OK(current_namespace); - ASSERT_NE(original_namespace, current_namespace); - } - // Check that we switched back to the original namespace upon exiting the - // scope. - auto current_namespace = GetCurrentMountNamespace(); - ASSERT_RESULT_OK(current_namespace); - ASSERT_EQ(*original_namespace, *current_namespace); -} - -TEST(ApexdTestUtilsTest, SetUpApexTestEnvironment) { - auto original_apex_mounts = GetApexMounts(); - ASSERT_GT(original_apex_mounts.size(), 0u); - auto original_dir_content = ReadDir("/apex", [](auto _) { return true; }); - ASSERT_TRUE(IsOk(original_dir_content)); - { - MountNamespaceRestorer restorer; - ASSERT_TRUE(IsOk(SetUpApexTestEnvironment())); - // Check /apex is apex_mnt_dir. - char* context; - ASSERT_GT(getfilecon("/apex", &context), 0); - EXPECT_EQ(std::string(context), "u:object_r:apex_mnt_dir:s0"); - freecon(context); - // Check no apexes are mounted in our test environment. - auto new_apex_mounts = GetApexMounts(); - ASSERT_EQ(new_apex_mounts.size(), 0u); - // Check that /apex is empty. - auto dir_content = ReadDir("/apex", [](auto _) { return true; }); - ASSERT_TRUE(IsOk(dir_content)); - ASSERT_EQ(dir_content->size(), 0u) - << "Found following entries: " << Join(*dir_content, ','); - // Check that we can still access /data. - std::string test_dir = android::base::GetExecutableDirectory(); - ASSERT_TRUE(android::base::StartsWith(test_dir, "/data")); - TemporaryFile tf(test_dir); - // Check that we can write. - ASSERT_TRUE(android::base::WriteStringToFile("secret", tf.path)); - // And check that we can still read it - std::string content; - ASSERT_TRUE(android::base::ReadFileToString(tf.path, &content)); - ASSERT_EQ(content, "secret"); - } - auto apex_mounts = GetApexMounts(); - ASSERT_THAT(apex_mounts, UnorderedElementsAreArray(original_apex_mounts)); - auto apex_dir_content = ReadDir("/apex", [](auto _) { return true; }); - ASSERT_TRUE(IsOk(apex_dir_content)); - ASSERT_EQ(apex_dir_content->size(), original_dir_content->size()); -} - -} // namespace -} // namespace apex -} // namespace android diff --git a/apexd/apexd_verity.cpp b/apexd/apexd_verity.cpp index 0008f6d2..a4be3363 100644 --- a/apexd/apexd_verity.cpp +++ b/apexd/apexd_verity.cpp @@ -29,7 +29,6 @@ #include "apex_file.h" #include "apexd_utils.h" -using android::base::Dirname; using android::base::ErrnoError; using android::base::Error; using android::base::ReadFully; @@ -60,8 +59,7 @@ std::vector<uint8_t> HexToBin(const std::string& hex) { Result<void> GenerateHashTree(const ApexFile& apex, const ApexVerityData& verity_data, const std::string& hashtree_file) { - unique_fd fd( - TEMP_FAILURE_RETRY(open(apex.GetPath().c_str(), O_RDONLY | O_CLOEXEC))); + unique_fd fd(TEMP_FAILURE_RETRY(open(apex.GetPath().c_str(), O_RDONLY))); if (fd.get() == -1) { return ErrnoError() << "Failed to open " << apex.GetPath(); } @@ -80,10 +78,7 @@ Result<void> GenerateHashTree(const ApexFile& apex, return Error() << "Invalid image size " << image_size; } - if (!apex.GetImageOffset()) { - return Error() << "Cannot generate HashTree without image offset"; - } - if (lseek(fd, apex.GetImageOffset().value(), SEEK_SET) == -1) { + if (lseek(fd, apex.GetImageOffset(), SEEK_SET) == -1) { return ErrnoError() << "Failed to seek"; } @@ -110,8 +105,8 @@ Result<void> GenerateHashTree(const ApexFile& apex, return Error() << "Failed to build hashtree: root digest mismatch"; } - unique_fd out_fd(TEMP_FAILURE_RETRY(open( - hashtree_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600))); + unique_fd out_fd(TEMP_FAILURE_RETRY( + open(hashtree_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0600))); if (!builder->WriteHashTreeToFd(out_fd, 0)) { return Error() << "Failed to write hashtree to " << hashtree_file; } @@ -120,8 +115,7 @@ Result<void> GenerateHashTree(const ApexFile& apex, Result<std::string> CalculateRootDigest(const std::string& hashtree_file, const ApexVerityData& verity_data) { - unique_fd fd( - TEMP_FAILURE_RETRY(open(hashtree_file.c_str(), O_RDONLY | O_CLOEXEC))); + unique_fd fd(TEMP_FAILURE_RETRY(open(hashtree_file.c_str(), O_RDONLY))); if (fd.get() == -1) { return ErrnoError() << "Failed to open " << hashtree_file; } @@ -155,11 +149,7 @@ Result<std::string> CalculateRootDigest(const std::string& hashtree_file, Result<PrepareHashTreeResult> PrepareHashTree( const ApexFile& apex, const ApexVerityData& verity_data, const std::string& hashtree_file) { - if (apex.IsCompressed()) { - return Error() << "Cannot prepare HashTree of compressed APEX"; - } - - if (auto st = CreateDirIfNeeded(Dirname(hashtree_file), 0700); !st.ok()) { + if (auto st = createDirIfNeeded(kApexHashTreeDir, 0700); !st.ok()) { return st.error(); } bool should_regenerate_hashtree = false; diff --git a/apexd/apexd_verity_test.cpp b/apexd/apexd_verity_test.cpp index 4e53ccbf..cf2317c8 100644 --- a/apexd/apexd_verity_test.cpp +++ b/apexd/apexd_verity_test.cpp @@ -25,6 +25,7 @@ #include <gtest/gtest.h> #include "apex_file.h" +#include "apex_preinstalled_data.h" #include "apexd_test_utils.h" #include "apexd_verity.h" @@ -44,11 +45,12 @@ static std::string GetTestFile(const std::string& name) { } TEST(ApexdVerityTest, ReusesHashtree) { + ASSERT_TRUE(IsOk(collectPreinstalledData({"/system_ext/apex"}))); TemporaryDir td; auto apex = ApexFile::Open(GetTestFile("apex.apexd_test_no_hashtree.apex")); ASSERT_TRUE(IsOk(apex)); - auto verity_data = apex->VerifyApexVerity(apex->GetBundledPublicKey()); + auto verity_data = apex->VerifyApexVerity(); ASSERT_TRUE(IsOk(verity_data)); auto hashtree_file = StringPrintf("%s/hashtree", td.path); @@ -76,11 +78,12 @@ TEST(ApexdVerityTest, ReusesHashtree) { } TEST(ApexdVerityTest, RegenerateHashree) { + ASSERT_TRUE(IsOk(collectPreinstalledData({"/system_ext/apex"}))); TemporaryDir td; auto apex = ApexFile::Open(GetTestFile("apex.apexd_test_no_hashtree.apex")); ASSERT_TRUE(IsOk(apex)); - auto verity_data = apex->VerifyApexVerity(apex->GetBundledPublicKey()); + auto verity_data = apex->VerifyApexVerity(); ASSERT_TRUE(IsOk(verity_data)); auto hashtree_file = StringPrintf("%s/hashtree", td.path); @@ -95,7 +98,7 @@ TEST(ApexdVerityTest, RegenerateHashree) { auto apex2 = ApexFile::Open(GetTestFile("apex.apexd_test_no_hashtree_2.apex")); ASSERT_TRUE(IsOk(apex2)); - auto verity_data2 = apex2->VerifyApexVerity(apex2->GetBundledPublicKey()); + auto verity_data2 = apex2->VerifyApexVerity(); ASSERT_TRUE(IsOk(verity_data2)); // Now call PrepareHashTree again. Since digest doesn't match, hashtree @@ -112,20 +115,5 @@ TEST(ApexdVerityTest, RegenerateHashree) { ASSERT_NE(first_hashtree, second_hashtree) << hashtree_file << " was reused"; } -TEST(ApexdVerityTest, CannotPrepareHashTreeForCompressedApex) { - TemporaryDir td; - - auto apex = - ApexFile::Open(GetTestFile("com.android.apex.compressed.v1.capex")); - ASSERT_TRUE(IsOk(apex)); - std::string hash_tree; - ApexVerityData verity_data; - auto result = PrepareHashTree(*apex, verity_data, hash_tree); - ASSERT_FALSE(IsOk(result)); - ASSERT_THAT( - result.error().message(), - ::testing::HasSubstr("Cannot prepare HashTree of compressed APEX")); -} - } // namespace apex } // namespace android diff --git a/apexd/apexservice.cpp b/apexd/apexservice.cpp index 4f3d2fc3..b8aced60 100644 --- a/apexd/apexservice.cpp +++ b/apexd/apexservice.cpp @@ -36,14 +36,13 @@ #include <utils/String16.h> #include "apex_file.h" -#include "apex_file_repository.h" +#include "apex_preinstalled_data.h" #include "apexd.h" #include "apexd_session.h" #include "string_log.h" #include <android/apex/BnApexService.h> -using android::base::Join; using android::base::Result; namespace android { @@ -79,10 +78,10 @@ class ApexService : public BnApexService { BinderStatus getSessions(std::vector<ApexSessionInfo>* aidl_return) override; BinderStatus getStagedSessionInfo( int session_id, ApexSessionInfo* apex_session_info) override; - BinderStatus activatePackage(const std::string& package_path) override; - BinderStatus deactivatePackage(const std::string& package_path) override; + BinderStatus activatePackage(const std::string& packagePath) override; + BinderStatus deactivatePackage(const std::string& packagePath) override; BinderStatus getActivePackages(std::vector<ApexInfo>* aidl_return) override; - BinderStatus getActivePackage(const std::string& package_name, + BinderStatus getActivePackage(const std::string& packageName, ApexInfo* aidl_return) override; BinderStatus getAllPackages(std::vector<ApexInfo>* aidl_return) override; BinderStatus preinstallPackages( @@ -93,26 +92,14 @@ class ApexService : public BnApexService { BinderStatus revertActiveSessions() override; BinderStatus resumeRevertIfNeeded() override; BinderStatus snapshotCeData(int user_id, int rollback_id, - const std::string& apex_name) override; + const std::string& apex_name, + int64_t* _aidl_return) override; BinderStatus restoreCeData(int user_id, int rollback_id, const std::string& apex_name) override; BinderStatus destroyDeSnapshots(int rollback_id) override; - BinderStatus destroyCeSnapshots(int user_id, int rollback_id) override; BinderStatus destroyCeSnapshotsNotSpecified( int user_id, const std::vector<int>& retain_rollback_ids) override; BinderStatus remountPackages() override; - BinderStatus recollectPreinstalledData( - const std::vector<std::string>& paths) override; - BinderStatus recollectDataApex(const std::string& path, - const std::string& decompression_dir) override; - BinderStatus markBootCompleted() override; - BinderStatus calculateSizeForCompressedApex( - const CompressedApexInfoList& compressed_apex_info_list, - int64_t* required_size) override; - BinderStatus reserveSpaceForCompressedApex( - const CompressedApexInfoList& compressed_apex_info_list) override; - BinderStatus installAndActivatePackage(const std::string& package_path, - ApexInfo* aidl_return) override; status_t dump(int fd, const Vector<String16>& args) override; @@ -133,37 +120,39 @@ BinderStatus CheckDebuggable(const std::string& name) { } BinderStatus ApexService::stagePackages(const std::vector<std::string>& paths) { - BinderStatus debug_check = CheckDebuggable("stagePackages"); - if (!debug_check.isOk()) { - return debug_check; + BinderStatus debugCheck = CheckDebuggable("stagePackages"); + if (!debugCheck.isOk()) { + return debugCheck; } LOG(DEBUG) << "stagePackages() received by ApexService, paths " << android::base::Join(paths, ','); - Result<void> res = ::android::apex::StagePackages(paths); + Result<void> res = ::android::apex::stagePackages(paths); if (res.ok()) { return BinderStatus::ok(); } + // TODO: Get correct binder error status. LOG(ERROR) << "Failed to stage " << android::base::Join(paths, ',') << ": " << res.error(); return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, + BinderStatus::EX_ILLEGAL_ARGUMENT, String8(res.error().message().c_str())); } BinderStatus ApexService::unstagePackages( const std::vector<std::string>& paths) { - Result<void> res = ::android::apex::UnstagePackages(paths); + Result<void> res = ::android::apex::unstagePackages(paths); if (res.ok()) { return BinderStatus::ok(); } + // TODO: Get correct binder error status. LOG(ERROR) << "Failed to unstage " << android::base::Join(paths, ',') << ": " << res.error(); return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, + BinderStatus::EX_ILLEGAL_ARGUMENT, String8(res.error().message().c_str())); } @@ -173,7 +162,7 @@ BinderStatus ApexService::submitStagedSession(const ApexSessionParams& params, << params.sessionId << " child sessions: [" << android::base::Join(params.childSessionIds, ',') << "]"; - Result<std::vector<ApexFile>> packages = ::android::apex::SubmitStagedSession( + Result<std::vector<ApexFile>> packages = ::android::apex::submitStagedSession( params.sessionId, params.childSessionIds, params.hasRollbackEnabled, params.isRollback, params.rollbackId); if (!packages.ok()) { @@ -197,7 +186,7 @@ BinderStatus ApexService::submitStagedSession(const ApexSessionParams& params, BinderStatus ApexService::markStagedSessionReady(int session_id) { LOG(DEBUG) << "markStagedSessionReady() received by ApexService, session id " << session_id; - Result<void> success = ::android::apex::MarkStagedSessionReady(session_id); + Result<void> success = ::android::apex::markStagedSessionReady(session_id); if (!success.ok()) { LOG(ERROR) << "Failed to mark session id " << session_id << " as ready: " << success.error(); @@ -212,7 +201,7 @@ BinderStatus ApexService::markStagedSessionSuccessful(int session_id) { LOG(DEBUG) << "markStagedSessionSuccessful() received by ApexService, session id " << session_id; - Result<void> ret = ::android::apex::MarkStagedSessionSuccessful(session_id); + Result<void> ret = ::android::apex::markStagedSessionSuccessful(session_id); if (!ret.ok()) { LOG(ERROR) << "Failed to mark session " << session_id << " as SUCCESS: " << ret.error(); @@ -223,43 +212,6 @@ BinderStatus ApexService::markStagedSessionSuccessful(int session_id) { return BinderStatus::ok(); } -BinderStatus ApexService::markBootCompleted() { - ::android::apex::OnBootCompleted(); - return BinderStatus::ok(); -} - -BinderStatus ApexService::calculateSizeForCompressedApex( - const CompressedApexInfoList& compressed_apex_info_list, - int64_t* required_size) { - *required_size = 0; - const auto& instance = ApexFileRepository::GetInstance(); - for (const auto& apex_info : compressed_apex_info_list.apexInfos) { - auto should_allocate_space = ShouldAllocateSpaceForDecompression( - apex_info.moduleName, apex_info.versionCode, instance); - if (!should_allocate_space.ok() || *should_allocate_space) { - *required_size += apex_info.decompressedSize; - } - } - return BinderStatus::ok(); -} - -BinderStatus ApexService::reserveSpaceForCompressedApex( - const CompressedApexInfoList& compressed_apex_info_list) { - int64_t required_size; - if (auto res = calculateSizeForCompressedApex(compressed_apex_info_list, - &required_size); - !res.isOk()) { - return res; - } - if (auto res = ReserveSpaceForCompressedApex(required_size, kOtaReservedDir); - !res.ok()) { - return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, - String8(res.error().message().c_str())); - } - return BinderStatus::ok(); -} - static void ClearSessionInfo(ApexSessionInfo* session_info) { session_info->sessionId = -1; session_info->isUnknown = false; @@ -273,14 +225,13 @@ static void ClearSessionInfo(ApexSessionInfo* session_info) { session_info->isRevertFailed = false; } -void ConvertToApexSessionInfo(const ApexSession& session, +void convertToApexSessionInfo(const ApexSession& session, ApexSessionInfo* session_info) { using SessionState = ::apex::proto::SessionState; ClearSessionInfo(session_info); session_info->sessionId = session.GetId(); session_info->crashingNativeProcess = session.GetCrashingNativeProcess(); - session_info->errorMessage = session.GetErrorMessage(); switch (session.GetState()) { case SessionState::VERIFIED: @@ -314,24 +265,23 @@ void ConvertToApexSessionInfo(const ApexSession& session, } } -static ApexInfo GetApexInfo(const ApexFile& package) { - auto& instance = ApexFileRepository::GetInstance(); +static ApexInfo getApexInfo(const ApexFile& package) { ApexInfo out; out.moduleName = package.GetManifest().name(); out.modulePath = package.GetPath(); out.versionCode = package.GetManifest().version(); out.versionName = package.GetManifest().versionname(); - out.isFactory = instance.IsPreInstalledApex(package); + out.isFactory = package.IsBuiltin(); out.isActive = false; - Result<std::string> preinstalled_path = - instance.GetPreinstalledPath(package.GetManifest().name()); - if (preinstalled_path.ok()) { - out.preinstalledModulePath = *preinstalled_path; + Result<std::string> preinstalledPath = + getApexPreinstalledPath(package.GetManifest().name()); + if (preinstalledPath.ok()) { + out.preinstalledModulePath = *preinstalledPath; } return out; } -static std::string ToString(const ApexInfo& package) { +static std::string toString(const ApexInfo& package) { std::string msg = StringLog() << "Module: " << package.moduleName << " Version: " << package.versionCode @@ -347,9 +297,9 @@ BinderStatus ApexService::getSessions( std::vector<ApexSessionInfo>* aidl_return) { auto sessions = ApexSession::GetSessions(); for (const auto& session : sessions) { - ApexSessionInfo session_info; - ConvertToApexSessionInfo(session, &session_info); - aidl_return->push_back(session_info); + ApexSessionInfo sessionInfo; + convertToApexSessionInfo(session, &sessionInfo); + aidl_return->push_back(sessionInfo); } return BinderStatus::ok(); @@ -367,80 +317,82 @@ BinderStatus ApexService::getStagedSessionInfo( return BinderStatus::ok(); } - ConvertToApexSessionInfo(*session, apex_session_info); + convertToApexSessionInfo(*session, apex_session_info); return BinderStatus::ok(); } -BinderStatus ApexService::activatePackage(const std::string& package_path) { - BinderStatus debug_check = CheckDebuggable("activatePackage"); - if (!debug_check.isOk()) { - return debug_check; +BinderStatus ApexService::activatePackage(const std::string& packagePath) { + BinderStatus debugCheck = CheckDebuggable("activatePackage"); + if (!debugCheck.isOk()) { + return debugCheck; } LOG(DEBUG) << "activatePackage() received by ApexService, path " - << package_path; + << packagePath; - Result<void> res = ::android::apex::ActivatePackage(package_path); + Result<void> res = ::android::apex::activatePackage(packagePath); if (res.ok()) { return BinderStatus::ok(); } - LOG(ERROR) << "Failed to activate " << package_path << ": " << res.error(); + // TODO: Get correct binder error status. + LOG(ERROR) << "Failed to activate " << packagePath << ": " << res.error(); return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, + BinderStatus::EX_ILLEGAL_ARGUMENT, String8(res.error().message().c_str())); } -BinderStatus ApexService::deactivatePackage(const std::string& package_path) { - BinderStatus debug_check = CheckDebuggable("deactivatePackage"); - if (!debug_check.isOk()) { - return debug_check; +BinderStatus ApexService::deactivatePackage(const std::string& packagePath) { + BinderStatus debugCheck = CheckDebuggable("deactivatePackage"); + if (!debugCheck.isOk()) { + return debugCheck; } LOG(DEBUG) << "deactivatePackage() received by ApexService, path " - << package_path; + << packagePath; - Result<void> res = ::android::apex::DeactivatePackage(package_path); + Result<void> res = ::android::apex::deactivatePackage(packagePath); if (res.ok()) { return BinderStatus::ok(); } - LOG(ERROR) << "Failed to deactivate " << package_path << ": " << res.error(); + // TODO: Get correct binder error status. + LOG(ERROR) << "Failed to deactivate " << packagePath << ": " << res.error(); return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, + BinderStatus::EX_ILLEGAL_ARGUMENT, String8(res.error().message().c_str())); } BinderStatus ApexService::getActivePackages( std::vector<ApexInfo>* aidl_return) { - auto packages = ::android::apex::GetActivePackages(); + auto packages = ::android::apex::getActivePackages(); for (const auto& package : packages) { - ApexInfo apex_info = GetApexInfo(package); - apex_info.isActive = true; - aidl_return->push_back(std::move(apex_info)); + ApexInfo apexInfo = getApexInfo(package); + apexInfo.isActive = true; + aidl_return->push_back(std::move(apexInfo)); } return BinderStatus::ok(); } -BinderStatus ApexService::getActivePackage(const std::string& package_name, +BinderStatus ApexService::getActivePackage(const std::string& packageName, ApexInfo* aidl_return) { - Result<ApexFile> apex = ::android::apex::GetActivePackage(package_name); + Result<ApexFile> apex = ::android::apex::getActivePackage(packageName); if (apex.ok()) { - *aidl_return = GetApexInfo(*apex); + *aidl_return = getApexInfo(*apex); aidl_return->isActive = true; } return BinderStatus::ok(); } BinderStatus ApexService::getAllPackages(std::vector<ApexInfo>* aidl_return) { - const auto& active = ::android::apex::GetActivePackages(); - const auto& factory = ::android::apex::GetFactoryPackages(); + const auto& active = ::android::apex::getActivePackages(); + const auto& factory = ::android::apex::getFactoryPackages(); for (const ApexFile& pkg : active) { - ApexInfo apex_info = GetApexInfo(pkg); + ApexInfo apex_info = getApexInfo(pkg); apex_info.isActive = true; aidl_return->push_back(std::move(apex_info)); } @@ -449,70 +401,55 @@ BinderStatus ApexService::getAllPackages(std::vector<ApexInfo>* aidl_return) { return o.GetPath() == pkg.GetPath(); }; if (std::find_if(active.begin(), active.end(), same_path) == active.end()) { - aidl_return->push_back(GetApexInfo(pkg)); + aidl_return->push_back(getApexInfo(pkg)); } } return BinderStatus::ok(); } -BinderStatus ApexService::installAndActivatePackage( - const std::string& package_path, ApexInfo* aidl_return) { - LOG(DEBUG) << "installAndActivatePackage() received by ApexService, path: " - << package_path; - auto res = InstallPackage(package_path); - if (!res.ok()) { - LOG(ERROR) << "Failed to install package " << package_path << " : " - << res.error(); - return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, - String8(res.error().message().c_str())); - } - *aidl_return = GetApexInfo(*res); - aidl_return->isActive = true; - return BinderStatus::ok(); -} - BinderStatus ApexService::preinstallPackages( const std::vector<std::string>& paths) { - BinderStatus debug_check = CheckDebuggable("preinstallPackages"); - if (!debug_check.isOk()) { - return debug_check; + BinderStatus debugCheck = CheckDebuggable("preinstallPackages"); + if (!debugCheck.isOk()) { + return debugCheck; } - Result<void> res = ::android::apex::PreinstallPackages(paths); + Result<void> res = ::android::apex::preinstallPackages(paths); if (res.ok()) { return BinderStatus::ok(); } + // TODO: Get correct binder error status. LOG(ERROR) << "Failed to preinstall packages " << android::base::Join(paths, ',') << ": " << res.error(); return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, + BinderStatus::EX_ILLEGAL_ARGUMENT, String8(res.error().message().c_str())); } BinderStatus ApexService::postinstallPackages( const std::vector<std::string>& paths) { - BinderStatus debug_check = CheckDebuggable("postinstallPackages"); - if (!debug_check.isOk()) { - return debug_check; + BinderStatus debugCheck = CheckDebuggable("postinstallPackages"); + if (!debugCheck.isOk()) { + return debugCheck; } - Result<void> res = ::android::apex::PostinstallPackages(paths); + Result<void> res = ::android::apex::postinstallPackages(paths); if (res.ok()) { return BinderStatus::ok(); } + // TODO: Get correct binder error status. LOG(ERROR) << "Failed to postinstall packages " << android::base::Join(paths, ',') << ": " << res.error(); return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, + BinderStatus::EX_ILLEGAL_ARGUMENT, String8(res.error().message().c_str())); } BinderStatus ApexService::abortStagedSession(int session_id) { LOG(DEBUG) << "abortStagedSession() received by ApexService."; - Result<void> res = ::android::apex::AbortStagedSession(session_id); + Result<void> res = ::android::apex::abortStagedSession(session_id); if (!res.ok()) { return BinderStatus::fromExceptionCode( BinderStatus::EX_ILLEGAL_ARGUMENT, @@ -523,7 +460,7 @@ BinderStatus ApexService::abortStagedSession(int session_id) { BinderStatus ApexService::revertActiveSessions() { LOG(DEBUG) << "revertActiveSessions() received by ApexService."; - Result<void> res = ::android::apex::RevertActiveSessions("", ""); + Result<void> res = ::android::apex::revertActiveSessions(""); if (!res.ok()) { return BinderStatus::fromExceptionCode( BinderStatus::EX_ILLEGAL_ARGUMENT, @@ -533,13 +470,13 @@ BinderStatus ApexService::revertActiveSessions() { } BinderStatus ApexService::resumeRevertIfNeeded() { - BinderStatus debug_check = CheckDebuggable("resumeRevertIfNeeded"); - if (!debug_check.isOk()) { - return debug_check; + BinderStatus debugCheck = CheckDebuggable("resumeRevertIfNeeded"); + if (!debugCheck.isOk()) { + return debugCheck; } LOG(DEBUG) << "resumeRevertIfNeeded() received by ApexService."; - Result<void> res = ::android::apex::ResumeRevertIfNeeded(); + Result<void> res = ::android::apex::resumeRevertIfNeeded(); if (!res.ok()) { return BinderStatus::fromExceptionCode( BinderStatus::EX_ILLEGAL_ARGUMENT, @@ -549,15 +486,17 @@ BinderStatus ApexService::resumeRevertIfNeeded() { } BinderStatus ApexService::snapshotCeData(int user_id, int rollback_id, - const std::string& apex_name) { + const std::string& apex_name, + int64_t* _aidl_return) { LOG(DEBUG) << "snapshotCeData() received by ApexService."; - Result<void> res = - ::android::apex::SnapshotCeData(user_id, rollback_id, apex_name); + Result<ino_t> res = + ::android::apex::snapshotCeData(user_id, rollback_id, apex_name); if (!res.ok()) { return BinderStatus::fromExceptionCode( BinderStatus::EX_SERVICE_SPECIFIC, String8(res.error().message().c_str())); } + *_aidl_return = static_cast<uint64_t>(*res); return BinderStatus::ok(); } @@ -565,7 +504,7 @@ BinderStatus ApexService::restoreCeData(int user_id, int rollback_id, const std::string& apex_name) { LOG(DEBUG) << "restoreCeData() received by ApexService."; Result<void> res = - ::android::apex::RestoreCeData(user_id, rollback_id, apex_name); + ::android::apex::restoreCeData(user_id, rollback_id, apex_name); if (!res.ok()) { return BinderStatus::fromExceptionCode( BinderStatus::EX_SERVICE_SPECIFIC, @@ -576,19 +515,8 @@ BinderStatus ApexService::restoreCeData(int user_id, int rollback_id, BinderStatus ApexService::destroyDeSnapshots(int rollback_id) { LOG(DEBUG) << "destroyDeSnapshots() received by ApexService."; - Result<void> res = ::android::apex::DestroyDeSnapshots(rollback_id); - if (!res.ok()) { - return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, - String8(res.error().message().c_str())); - } - return BinderStatus::ok(); -} - -BinderStatus ApexService::destroyCeSnapshots(int user_id, int rollback_id) { - LOG(DEBUG) << "destroyCeSnapshots() received by ApexService."; - Result<void> res = ::android::apex::DestroyCeSnapshots(user_id, rollback_id); - if (!res.ok()) { + Result<void> res = ::android::apex::destroyDeSnapshots(rollback_id); + if (!res) { return BinderStatus::fromExceptionCode( BinderStatus::EX_SERVICE_SPECIFIC, String8(res.error().message().c_str())); @@ -599,9 +527,9 @@ BinderStatus ApexService::destroyCeSnapshots(int user_id, int rollback_id) { BinderStatus ApexService::destroyCeSnapshotsNotSpecified( int user_id, const std::vector<int>& retain_rollback_ids) { LOG(DEBUG) << "destroyCeSnapshotsNotSpecified() received by ApexService."; - Result<void> res = ::android::apex::DestroyCeSnapshotsNotSpecified( + Result<void> res = ::android::apex::destroyCeSnapshotsNotSpecified( user_id, retain_rollback_ids); - if (!res.ok()) { + if (!res) { return BinderStatus::fromExceptionCode( BinderStatus::EX_SERVICE_SPECIFIC, String8(res.error().message().c_str())); @@ -617,47 +545,7 @@ BinderStatus ApexService::remountPackages() { if (auto root = CheckCallerIsRoot("remountPackages"); !root.isOk()) { return root; } - if (auto res = ::android::apex::RemountPackages(); !res.ok()) { - return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, - String8(res.error().message().c_str())); - } - return BinderStatus::ok(); -} - -BinderStatus ApexService::recollectPreinstalledData( - const std::vector<std::string>& paths) { - LOG(DEBUG) << "recollectPreinstalledData() received by ApexService, paths: " - << Join(paths, ','); - if (auto debug = CheckDebuggable("recollectPreinstalledData"); - !debug.isOk()) { - return debug; - } - if (auto root = CheckCallerIsRoot("recollectPreinstalledData"); - !root.isOk()) { - return root; - } - ApexFileRepository& instance = ApexFileRepository::GetInstance(); - if (auto res = instance.AddPreInstalledApex(paths); !res.ok()) { - return BinderStatus::fromExceptionCode( - BinderStatus::EX_SERVICE_SPECIFIC, - String8(res.error().message().c_str())); - } - return BinderStatus::ok(); -} - -BinderStatus ApexService::recollectDataApex( - const std::string& path, const std::string& decompression_dir) { - LOG(DEBUG) << "recollectDataApex() received by ApexService, paths " << path - << " and " << decompression_dir; - if (auto debug = CheckDebuggable("recollectDataApex"); !debug.isOk()) { - return debug; - } - if (auto root = CheckCallerIsRoot("recollectDataApex"); !root.isOk()) { - return root; - } - ApexFileRepository& instance = ApexFileRepository::GetInstance(); - if (auto res = instance.AddDataApex(path); !res.ok()) { + if (auto res = ::android::apex::remountPackages(); !res.ok()) { return BinderStatus::fromExceptionCode( BinderStatus::EX_SERVICE_SPECIFIC, String8(res.error().message().c_str())); @@ -677,18 +565,16 @@ status_t ApexService::onTransact(uint32_t _aidl_code, const Parcel& _aidl_data, for (int i = 0; i < argc && _aidl_data.dataAvail() > 0; i++) { args.add(_aidl_data.readString16()); } - sp<IBinder> unused_callback; - sp<IResultReceiver> result_receiver; + sp<IBinder> unusedCallback; + sp<IResultReceiver> resultReceiver; status_t status; - if ((status = _aidl_data.readNullableStrongBinder(&unused_callback)) != - OK) + if ((status = _aidl_data.readNullableStrongBinder(&unusedCallback)) != OK) return status; - if ((status = _aidl_data.readNullableStrongBinder(&result_receiver)) != - OK) + if ((status = _aidl_data.readNullableStrongBinder(&resultReceiver)) != OK) return status; status = shellCommand(in, out, err, args); - if (result_receiver != nullptr) { - result_receiver->send(status); + if (resultReceiver != nullptr) { + resultReceiver->send(status); } return OK; } @@ -707,7 +593,7 @@ status_t ApexService::dump(int fd, const Vector<String16>& /*args*/) { return BAD_VALUE; } else { for (const auto& item : list) { - std::string msg = ToString(item); + std::string msg = toString(item); dprintf(fd, "%s", msg.c_str()); } } @@ -725,19 +611,14 @@ status_t ApexService::dump(int fd, const Vector<String16>& /*args*/) { } } std::string revert_reason = ""; - const auto& crashing_native_process = session.GetCrashingNativeProcess(); + std::string crashing_native_process = session.GetCrashingNativeProcess(); if (!crashing_native_process.empty()) { revert_reason = " Revert Reason: " + crashing_native_process; } - std::string error_message_dump = ""; - const auto& error_message = session.GetErrorMessage(); - if (!error_message.empty()) { - error_message_dump = " Error Message: " + error_message; - } std::string msg = StringLog() << "Session ID: " << session.GetId() << child_ids_str << " State: " << SessionState_State_Name(session.GetState()) - << revert_reason << error_message_dump << std::endl; + << revert_reason << std::endl; dprintf(fd, "%s", msg.c_str()); } @@ -756,25 +637,25 @@ status_t ApexService::shellCommand(int in, int out, int err, } log << "ApexService:" << std::endl << " help - display this help" << std::endl - << " stagePackages [package_path1] ([package_path2]...) - stage " + << " stagePackages [packagePath1] ([packagePath2]...) - stage " "multiple packages from the given path" << std::endl - << " getActivePackage [package_name] - return info for active package " + << " getActivePackage [packageName] - return info for active package " "with given name, if present" << std::endl << " getAllPackages - return the list of all packages" << std::endl << " getActivePackages - return the list of active packages" << std::endl - << " activatePackage [package_path] - activate package from the " + << " activatePackage [packagePath] - activate package from the " "given path" << std::endl - << " deactivatePackage [package_path] - deactivate package from the " + << " deactivatePackage [packagePath] - deactivate package from the " "given path" << std::endl - << " preinstallPackages [package_path1] ([package_path2]...) - run " + << " preinstallPackages [packagePath1] ([packagePath2]...) - run " "pre-install hooks of the given packages" << std::endl - << " postinstallPackages [package_path1] ([package_path2]...) - run " + << " postinstallPackages [packagePath1] ([packagePath2]...) - run " "post-install hooks of the given packages" << std::endl << " getStagedSessionInfo [sessionId] - displays information about a " @@ -806,7 +687,7 @@ status_t ApexService::shellCommand(int in, int out, int err, if (cmd == String16("stagePackages")) { if (args.size() < 2) { - print_help(err, "stagePackages requires at least one package_path"); + print_help(err, "stagePackages requires at least one packagePath"); return BAD_VALUE; } std::vector<std::string> pkgs; @@ -832,7 +713,7 @@ status_t ApexService::shellCommand(int in, int out, int err, BinderStatus status = getAllPackages(&list); if (status.isOk()) { for (const auto& item : list) { - std::string msg = ToString(item); + std::string msg = toString(item); dprintf(out, "%s", msg.c_str()); } return OK; @@ -852,7 +733,7 @@ status_t ApexService::shellCommand(int in, int out, int err, BinderStatus status = getActivePackages(&list); if (status.isOk()) { for (const auto& item : list) { - std::string msg = ToString(item); + std::string msg = toString(item); dprintf(out, "%s", msg.c_str()); } return OK; @@ -872,7 +753,7 @@ status_t ApexService::shellCommand(int in, int out, int err, ApexInfo package; BinderStatus status = getActivePackage(String8(args[1]).string(), &package); if (status.isOk()) { - std::string msg = ToString(package); + std::string msg = toString(package); dprintf(out, "%s", msg.c_str()); return OK; } @@ -887,7 +768,7 @@ status_t ApexService::shellCommand(int in, int out, int err, if (cmd == String16("activatePackage")) { if (args.size() != 2) { - print_help(err, "activatePackage requires one package_path"); + print_help(err, "activatePackage requires one packagePath"); return BAD_VALUE; } BinderStatus status = activatePackage(String8(args[1]).string()); @@ -902,7 +783,7 @@ status_t ApexService::shellCommand(int in, int out, int err, if (cmd == String16("deactivatePackage")) { if (args.size() != 2) { - print_help(err, "deactivatePackage requires one package_path"); + print_help(err, "deactivatePackage requires one packagePath"); return BAD_VALUE; } BinderStatus status = deactivatePackage(String8(args[1]).string()); @@ -975,7 +856,7 @@ status_t ApexService::shellCommand(int in, int out, int err, BinderStatus status = submitStagedSession(params, &list); if (status.isOk()) { for (const auto& item : list.apexInfos) { - std::string msg = ToString(item); + std::string msg = toString(item); dprintf(out, "%s", msg.c_str()); } return OK; @@ -991,7 +872,7 @@ status_t ApexService::shellCommand(int in, int out, int err, if (args.size() < 2) { print_help(err, "preinstallPackages/postinstallPackages requires at least" - " one package_path"); + " one packagePath"); return BAD_VALUE; } std::vector<std::string> pkgs; @@ -1048,15 +929,15 @@ void CreateAndRegisterService() { sp<ProcessState> ps(ProcessState::self()); // Create binder service and register with LazyServiceRegistrar - sp<ApexService> apex_service = sp<ApexService>::make(); - auto lazy_registrar = LazyServiceRegistrar::getInstance(); - lazy_registrar.forcePersist(true); - lazy_registrar.registerService(apex_service, kApexServiceName); + sp<ApexService> apexService = new ApexService(); + auto lazyRegistrar = LazyServiceRegistrar::getInstance(); + lazyRegistrar.forcePersist(true); + lazyRegistrar.registerService(apexService, kApexServiceName); } void AllowServiceShutdown() { - auto lazy_registrar = LazyServiceRegistrar::getInstance(); - lazy_registrar.forcePersist(false); + auto lazyRegistrar = LazyServiceRegistrar::getInstance(); + lazyRegistrar.forcePersist(false); } void StartThreadPool() { diff --git a/apexd/apexservice_test.cpp b/apexd/apexservice_test.cpp index 545bb38d..7f78610c 100644 --- a/apexd/apexservice_test.cpp +++ b/apexd/apexservice_test.cpp @@ -59,8 +59,8 @@ #include "apexd_session.h" #include "apexd_test_utils.h" #include "apexd_utils.h" + #include "session_state.pb.h" -#include "string_log.h" using apex::proto::SessionState; @@ -73,8 +73,6 @@ using android::apex::testing::ApexInfoEq; using android::apex::testing::CreateSessionInfo; using android::apex::testing::IsOk; using android::apex::testing::SessionInfoEq; -using android::base::EndsWith; -using android::base::ErrnoError; using android::base::Join; using android::base::ReadFully; using android::base::StartsWith; @@ -84,7 +82,6 @@ using android::dm::DeviceMapper; using android::fs_mgr::Fstab; using android::fs_mgr::GetEntryForMountPoint; using android::fs_mgr::ReadFstabFromFile; -using ::apex::proto::ApexManifest; using ::testing::Contains; using ::testing::EndsWith; using ::testing::HasSubstr; @@ -126,7 +123,6 @@ class ApexServiceTest : public ::testing::Test { vold_service_->supportsCheckpoint(&supports_fs_checkpointing_); ASSERT_TRUE(IsOk(status)); CleanUp(); - service_->recollectPreinstalledData(kApexPackageBuiltinDirs); } void TearDown() override { CleanUp(); } @@ -300,8 +296,9 @@ class ApexServiceTest : public ::testing::Test { "-f", file, }; - auto res = ForkAndRun(args); - CHECK(res.ok()) << res.error(); + std::string error_msg; + int res = ForkAndRun(args, &error_msg); + CHECK_EQ(0, res) << error_msg; std::string data; CHECK(android::base::ReadFileToString(file, &data)); @@ -450,10 +447,21 @@ class ApexServiceTest : public ::testing::Test { private: void CleanUp() { - DeleteDirContent(kActiveApexPackagesDataDir); - DeleteDirContent(kApexBackupDir); - DeleteDirContent(kApexHashTreeDir); - DeleteDirContent(ApexSession::GetSessionsDir()); + auto status = WalkDir(kApexDataDir, [](const fs::directory_entry& p) { + std::error_code ec; + fs::file_status status = p.status(ec); + ASSERT_FALSE(ec) << "Failed to stat " << p.path() << " : " + << ec.message(); + if (fs::is_directory(status)) { + fs::remove_all(p.path(), ec); + } else { + fs::remove(p.path(), ec); + } + ASSERT_FALSE(ec) << "Failed to delete " << p.path() << " : " + << ec.message(); + }); + fs::remove_all(kApexSessionsDir); + ASSERT_TRUE(IsOk(status)); DeleteIfExists("/data/misc_ce/0/apexdata/apex.apexd_test"); DeleteIfExists("/data/misc_ce/0/apexrollback/123456"); @@ -518,8 +526,7 @@ Result<void> ReadDevice(const std::string& block_device) { static constexpr size_t kBufSize = 1024 * kBlockSize; std::vector<uint8_t> buffer(kBufSize); - unique_fd fd( - TEMP_FAILURE_RETRY(open(block_device.c_str(), O_RDONLY | O_CLOEXEC))); + unique_fd fd(TEMP_FAILURE_RETRY(open(block_device.c_str(), O_RDONLY))); if (fd.get() == -1) { return ErrnoError() << "Can't open " << block_device; } @@ -635,7 +642,7 @@ TEST_F(ApexServiceTest, StageFailKey) { // May contain one of two errors. std::string error = st.exceptionMessage().c_str(); - ASSERT_THAT(error, HasSubstr("No preinstalled apex found for package " + ASSERT_THAT(error, HasSubstr("No preinstalled data found for package " "com.android.apex.test_package.no_inst_key")); } @@ -711,7 +718,7 @@ TEST_F(ApexServiceTest, SubmitStagedSessionFailDoesNotLeakTempVerityDevices) { } } -TEST_F(ApexServiceTest, StageSuccessClearsPreviouslyActivePackage) { +TEST_F(ApexServiceTest, StageSuccess_ClearsPreviouslyActivePackage) { PrepareTestApexForInstall installer1(GetTestFile("apex.apexd_test_v2.apex")); PrepareTestApexForInstall installer2( GetTestFile("apex.apexd_test_different_app.apex")); @@ -775,6 +782,7 @@ TEST_F(ApexServiceTest, MultiStageSuccess) { } ASSERT_EQ(std::string("com.android.apex.test_package"), installer.package); + // TODO: Add second test. Right now, just use a separate version. PrepareTestApexForInstall installer2(GetTestFile("apex.apexd_test_v2.apex")); if (!installer2.Prepare()) { return; @@ -832,10 +840,18 @@ TEST_F(ApexServiceTest, SnapshotCeData) { ASSERT_TRUE( RegularFileExists("/data/misc_ce/0/apexdata/apex.apexd_test/hello.txt")); - service_->snapshotCeData(0, 123456, "apex.apexd_test"); + int64_t result; + service_->snapshotCeData(0, 123456, "apex.apexd_test", &result); ASSERT_TRUE(RegularFileExists( "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/hello.txt")); + + // Check that the return value is the inode of the snapshot directory. + struct stat buf; + memset(&buf, 0, sizeof(buf)); + ASSERT_EQ(0, + stat("/data/misc_ce/0/apexrollback/123456/apex.apexd_test", &buf)); + ASSERT_EQ(int64_t(buf.st_ino), result); } TEST_F(ApexServiceTest, RestoreCeData) { @@ -862,7 +878,7 @@ TEST_F(ApexServiceTest, RestoreCeData) { DirExists("/data/misc_ce/0/apexrollback/123456/apex.apexd_test")); } -TEST_F(ApexServiceTest, DestroyDeSnapshotsDeSys) { +TEST_F(ApexServiceTest, DestroyDeSnapshots_DeSys) { CreateDir("/data/misc/apexrollback/123456"); CreateDir("/data/misc/apexrollback/123456/my.apex"); CreateFile("/data/misc/apexrollback/123456/my.apex/hello.txt"); @@ -880,7 +896,7 @@ TEST_F(ApexServiceTest, DestroyDeSnapshotsDeSys) { ASSERT_FALSE(DirExists("/data/misc/apexrollback/123456")); } -TEST_F(ApexServiceTest, DestroyDeSnapshotsDeUser) { +TEST_F(ApexServiceTest, DestroyDeSnapshots_DeUser) { CreateDir("/data/misc_de/0/apexrollback/123456"); CreateDir("/data/misc_de/0/apexrollback/123456/my.apex"); CreateFile("/data/misc_de/0/apexrollback/123456/my.apex/hello.txt"); @@ -898,31 +914,6 @@ TEST_F(ApexServiceTest, DestroyDeSnapshotsDeUser) { ASSERT_FALSE(DirExists("/data/misc_de/0/apexrollback/123456")); } -TEST_F(ApexServiceTest, DestroyCeSnapshots) { - CreateDir("/data/misc_ce/0/apexrollback/123456"); - CreateDir("/data/misc_ce/0/apexrollback/123456/apex.apexd_test"); - CreateFile("/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt"); - - CreateDir("/data/misc_ce/0/apexrollback/77777"); - CreateDir("/data/misc_ce/0/apexrollback/77777/apex.apexd_test"); - CreateFile("/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt"); - - ASSERT_TRUE(RegularFileExists( - "/data/misc_ce/0/apexrollback/123456/apex.apexd_test/file.txt")); - ASSERT_TRUE(RegularFileExists( - "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt")); - - android::binder::Status st = service_->destroyCeSnapshots(0, 123456); - ASSERT_TRUE(IsOk(st)); - // Should be OK if the directory doesn't exist. - st = service_->destroyCeSnapshots(1, 123456); - ASSERT_TRUE(IsOk(st)); - - ASSERT_TRUE(RegularFileExists( - "/data/misc_ce/0/apexrollback/77777/apex.apexd_test/thing.txt")); - ASSERT_FALSE(DirExists("/data/misc_ce/0/apexrollback/123456")); -} - TEST_F(ApexServiceTest, DestroyCeSnapshotsNotSpecified) { CreateDir("/data/misc_ce/0/apexrollback/123456"); CreateDir("/data/misc_ce/0/apexrollback/123456/apex.apexd_test"); @@ -954,31 +945,6 @@ TEST_F(ApexServiceTest, DestroyCeSnapshotsNotSpecified) { ASSERT_FALSE(DirExists("/data/misc_ce/0/apexrollback/98765")); } -TEST_F(ApexServiceTest, SubmitStagedSessionCleanupsTempMountOnFailure) { - // Parent session id: 23 - // Children session ids: 37 73 - PrepareTestApexForInstall installer( - GetTestFile("apex.apexd_test_different_app.apex"), - "/data/app-staging/session_37", "staging_data_file"); - PrepareTestApexForInstall installer2( - GetTestFile("apex.apexd_test_manifest_mismatch.apex"), - "/data/app-staging/session_73", "staging_data_file"); - if (!installer.Prepare() || !installer2.Prepare()) { - FAIL() << GetDebugStr(&installer) << GetDebugStr(&installer2); - } - ApexInfoList list; - ApexSessionParams params; - params.sessionId = 23; - params.childSessionIds = {37, 73}; - ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list))) - << GetDebugStr(&installer); - - // Check that temp mounts were cleanded up. - for (const auto& mount : GetApexMounts()) { - EXPECT_FALSE(EndsWith(mount, ".tmp")) << "Found temp mount " << mount; - } -} - template <typename NameProvider> class ApexServiceActivationTest : public ApexServiceTest { public: @@ -1165,8 +1131,7 @@ TEST_F(ApexServiceActivationSuccessTest, ShowsUpInMountedApexDatabase) { << GetDebugStr(installer_.get()); MountedApexDatabase db; - db.PopulateFromMounts(kActiveApexPackagesDataDir, kApexDecompressedDir, - kApexHashTreeDir); + db.PopulateFromMounts(); std::optional<MountedApexData> mounted_apex; db.ForallMountedApexes(installer_->package, @@ -1292,8 +1257,7 @@ TEST_F(ApexServiceNoHashtreeApexActivationTest, ShowsUpInMountedApexDatabase) { << GetDebugStr(installer_.get()); MountedApexDatabase db; - db.PopulateFromMounts(kActiveApexPackagesDataDir, kApexDecompressedDir, - kApexHashTreeDir); + db.PopulateFromMounts(); std::optional<MountedApexData> mounted_apex; db.ForallMountedApexes(installer_->package, @@ -1363,7 +1327,7 @@ TEST_F(ApexServiceTest, NoHashtreeApexStagePackagesMovesHashtree) { auto read_fn = [](const std::string& path) -> std::vector<uint8_t> { static constexpr size_t kBufSize = 4096; std::vector<uint8_t> buffer(kBufSize); - unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); + unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY))); if (fd.get() == -1) { PLOG(ERROR) << "Failed to open " << path; ADD_FAILURE(); @@ -1415,78 +1379,53 @@ TEST_F(ApexServiceTest, NoHashtreeApexStagePackagesMovesHashtree) { } TEST_F(ApexServiceTest, GetFactoryPackages) { - Result<std::vector<ApexInfo>> factory_packages = GetFactoryPackages(); - ASSERT_TRUE(IsOk(factory_packages)); - ASSERT_TRUE(factory_packages->size() > 0); - - std::vector<std::string> builtin_dirs; - for (const auto& d : kApexPackageBuiltinDirs) { - std::string realpath; - if (android::base::Realpath(d, &realpath)) { - builtin_dirs.push_back(realpath); - } - // realpath might fail in case when dir is a non-existing path. We can - // ignore non-existing paths. - } - - // Decompressed APEX is also considred factory package - builtin_dirs.push_back(kApexDecompressedDir); + Result<std::vector<ApexInfo>> factoryPackages = GetFactoryPackages(); + ASSERT_TRUE(IsOk(factoryPackages)); + ASSERT_TRUE(factoryPackages->size() > 0); - for (const ApexInfo& package : *factory_packages) { - bool is_builtin = false; - for (const auto& dir : builtin_dirs) { - if (StartsWith(package.modulePath, dir)) { - is_builtin = true; - } - } - ASSERT_TRUE(is_builtin); + for (const ApexInfo& package : *factoryPackages) { + ASSERT_TRUE(isPathForBuiltinApexes(package.modulePath)); } } TEST_F(ApexServiceTest, NoPackagesAreBothActiveAndInactive) { - Result<std::vector<ApexInfo>> active_packages = GetActivePackages(); - ASSERT_TRUE(IsOk(active_packages)); - ASSERT_TRUE(active_packages->size() > 0); - Result<std::vector<ApexInfo>> inactive_packages = GetInactivePackages(); - ASSERT_TRUE(IsOk(inactive_packages)); - std::vector<std::string> active_packages_strings = - GetPackagesStrings(*active_packages); - std::vector<std::string> inactive_packages_strings = - GetPackagesStrings(*inactive_packages); - std::sort(active_packages_strings.begin(), active_packages_strings.end()); - std::sort(inactive_packages_strings.begin(), inactive_packages_strings.end()); + Result<std::vector<ApexInfo>> activePackages = GetActivePackages(); + ASSERT_TRUE(IsOk(activePackages)); + ASSERT_TRUE(activePackages->size() > 0); + Result<std::vector<ApexInfo>> inactivePackages = GetInactivePackages(); + ASSERT_TRUE(IsOk(inactivePackages)); + std::vector<std::string> activePackagesStrings = + GetPackagesStrings(*activePackages); + std::vector<std::string> inactivePackagesStrings = + GetPackagesStrings(*inactivePackages); + std::sort(activePackagesStrings.begin(), activePackagesStrings.end()); + std::sort(inactivePackagesStrings.begin(), inactivePackagesStrings.end()); std::vector<std::string> intersection; std::set_intersection( - active_packages_strings.begin(), active_packages_strings.end(), - inactive_packages_strings.begin(), inactive_packages_strings.end(), + activePackagesStrings.begin(), activePackagesStrings.end(), + inactivePackagesStrings.begin(), inactivePackagesStrings.end(), std::back_inserter(intersection)); ASSERT_THAT(intersection, SizeIs(0)); } TEST_F(ApexServiceTest, GetAllPackages) { - Result<std::vector<ApexInfo>> all_packages = GetAllPackages(); - ASSERT_TRUE(IsOk(all_packages)); - ASSERT_TRUE(all_packages->size() > 0); - Result<std::vector<ApexInfo>> active_packages = GetActivePackages(); - std::vector<std::string> active_strings = - GetPackagesStrings(*active_packages); - Result<std::vector<ApexInfo>> factory_packages = GetFactoryPackages(); - std::vector<std::string> factory_strings = - GetPackagesStrings(*factory_packages); - for (ApexInfo& apexInfo : *all_packages) { - std::string package_string = GetPackageString(apexInfo); - bool should_be_active = - std::find(active_strings.begin(), active_strings.end(), - package_string) != active_strings.end(); - bool should_be_factory = - std::find(factory_strings.begin(), factory_strings.end(), - package_string) != factory_strings.end(); - ASSERT_EQ(should_be_active, apexInfo.isActive) - << package_string << " should " << (should_be_active ? "" : "not ") - << "be active"; - ASSERT_EQ(should_be_factory, apexInfo.isFactory) - << package_string << " should " << (should_be_factory ? "" : "not ") - << "be factory"; + Result<std::vector<ApexInfo>> allPackages = GetAllPackages(); + ASSERT_TRUE(IsOk(allPackages)); + ASSERT_TRUE(allPackages->size() > 0); + Result<std::vector<ApexInfo>> activePackages = GetActivePackages(); + std::vector<std::string> activeStrings = GetPackagesStrings(*activePackages); + Result<std::vector<ApexInfo>> factoryPackages = GetFactoryPackages(); + std::vector<std::string> factoryStrings = + GetPackagesStrings(*factoryPackages); + for (ApexInfo& apexInfo : *allPackages) { + std::string packageString = GetPackageString(apexInfo); + bool shouldBeActive = std::find(activeStrings.begin(), activeStrings.end(), + packageString) != activeStrings.end(); + bool shouldBeFactory = + std::find(factoryStrings.begin(), factoryStrings.end(), + packageString) != factoryStrings.end(); + ASSERT_EQ(shouldBeActive, apexInfo.isActive); + ASSERT_EQ(shouldBeFactory, apexInfo.isFactory); } } @@ -1913,11 +1852,6 @@ TEST_F(ApexServiceTest, SubmitMultiSessionTestSuccess) { expected.isVerified = false; expected.isStaged = true; ASSERT_THAT(session, SessionInfoEq(expected)); - - // Check that temp mounts were cleanded up. - for (const auto& mount : GetApexMounts()) { - EXPECT_FALSE(EndsWith(mount, ".tmp")) << "Found temp mount " << mount; - } } TEST_F(ApexServiceTest, SubmitMultiSessionTestFail) { @@ -2049,42 +1983,6 @@ TEST_F(ApexServiceTest, AbortStagedSessionActivatedFail) { SessionInfoEq(expected2))); } -// Only finalized sessions should be deleted on DeleteFinalizedSessions() -TEST_F(ApexServiceTest, DeleteFinalizedSessions) { - // Fetch list of all session state - std::vector<SessionState::State> states; - for (int i = SessionState::State_MIN; i < SessionState::State_MAX; i++) { - if (!SessionState::State_IsValid(i)) { - continue; - } - states.push_back(SessionState::State(i)); - } - - // For every session state, create a new session. This is to verify we only - // delete sessions in final state. - auto nonFinalSessions = 0u; - for (auto i = 0u; i < states.size(); i++) { - auto session = ApexSession::CreateSession(230 + i); - SessionState::State state = states[i]; - ASSERT_TRUE(IsOk(session->UpdateStateAndCommit(state))); - if (!session->IsFinalized()) { - nonFinalSessions++; - } - } - std::vector<ApexSession> sessions = ApexSession::GetSessions(); - ASSERT_EQ(states.size(), sessions.size()); - - // Now try cleaning up all finalized sessions - ApexSession::DeleteFinalizedSessions(); - sessions = ApexSession::GetSessions(); - ASSERT_EQ(nonFinalSessions, sessions.size()); - - // Verify only finalized sessions have been deleted - for (auto& session : sessions) { - ASSERT_FALSE(session.IsFinalized()); - } -} - TEST_F(ApexServiceTest, BackupActivePackages) { if (supports_fs_checkpointing_) { GTEST_SKIP() << "Can't run if filesystem checkpointing is enabled"; @@ -2141,7 +2039,7 @@ TEST_F(ApexServiceTest, BackupActivePackagesClearsPreviousBackup) { } // Make sure /data/apex/backups exists. - ASSERT_TRUE(IsOk(CreateDirIfNeeded(std::string(kApexBackupDir), 0700))); + ASSERT_TRUE(IsOk(createDirIfNeeded(std::string(kApexBackupDir), 0700))); // Create some bogus files in /data/apex/backups. std::ofstream old_backup(StringPrintf("%s/file1", kApexBackupDir)); ASSERT_TRUE(old_backup.good()); @@ -2185,7 +2083,7 @@ TEST_F(ApexServiceTest, BackupActivePackagesZeroActivePackages) { // Make sure that /data/apex/active exists and is empty ASSERT_TRUE( - IsOk(CreateDirIfNeeded(std::string(kActiveApexPackagesDataDir), 0755))); + IsOk(createDirIfNeeded(std::string(kActiveApexPackagesDataDir), 0755))); auto active_pkgs = ReadEntireDir(kActiveApexPackagesDataDir); ASSERT_TRUE(IsOk(active_pkgs)); ASSERT_EQ(0u, active_pkgs->size()); @@ -2200,7 +2098,7 @@ TEST_F(ApexServiceTest, BackupActivePackagesZeroActivePackages) { ASSERT_EQ(0u, backups->size()); } -TEST_F(ApexServiceTest, ActivePackagesDirEmpty) { +TEST_F(ApexServiceTest, ActivePackagesFolderDoesNotExist) { PrepareTestApexForInstall installer(GetTestFile("apex.apexd_test_v2.apex"), "/data/app-staging/session_41", "staging_data_file"); @@ -2209,8 +2107,10 @@ TEST_F(ApexServiceTest, ActivePackagesDirEmpty) { return; } - // Make sure that /data/apex/active is empty - DeleteDirContent(kActiveApexPackagesDataDir); + // Make sure that /data/apex/active does not exist + std::error_code ec; + fs::remove_all(fs::path(kActiveApexPackagesDataDir), ec); + ASSERT_FALSE(ec) << "Failed to delete " << kActiveApexPackagesDataDir; ApexInfoList list; ApexSessionParams params; @@ -2267,24 +2167,12 @@ TEST_F(ApexServiceTest, UnstagePackagesFail) { UnorderedElementsAre(installer1.test_installed_file)); } -TEST_F(ApexServiceTest, UnstagePackagesFailPreInstalledApex) { - auto status = service_->unstagePackages( - {"/system/apex/com.android.apex.cts.shim.apex"}); - ASSERT_FALSE(IsOk(status)); - const std::string& error_message = - std::string(status.exceptionMessage().c_str()); - ASSERT_THAT(error_message, - HasSubstr("Can't uninstall pre-installed apex " - "/system/apex/com.android.apex.cts.shim.apex")); - ASSERT_TRUE(RegularFileExists("/system/apex/com.android.apex.cts.shim.apex")); -} - class ApexServiceRevertTest : public ApexServiceTest { protected: void SetUp() override { ApexServiceTest::SetUp(); } void PrepareBackup(const std::vector<std::string>& pkgs) { - ASSERT_TRUE(IsOk(CreateDirIfNeeded(std::string(kApexBackupDir), 0700))); + ASSERT_TRUE(IsOk(createDirIfNeeded(std::string(kApexBackupDir), 0700))); for (const auto& pkg : pkgs) { PrepareTestApexForInstall installer(pkg); ASSERT_TRUE(installer.Prepare()) << " failed to prepare " << pkg; @@ -2526,10 +2414,7 @@ TEST_F(ApexServiceRevertTest, RevertStoresCrashingNativeProcess) { // Make sure /data/apex/active is non-empty. ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file}))); std::string native_process = "test_process"; - // TODO(ioffe): this is calling into internals of apexd which makes test quite - // britle. With some refactoring we should be able to call binder api, or - // make this a unit test of apexd.cpp. - Result<void> res = ::android::apex::RevertActiveSessions(native_process, ""); + Result<void> res = ::android::apex::revertActiveSessions(native_process); session = ApexSession::GetSession(1543); ASSERT_EQ(session->GetCrashingNativeProcess(), native_process); } @@ -2717,6 +2602,21 @@ TEST_F(ApexShimUpdateTest, UpdateToV2Success) { ASSERT_TRUE(IsOk(service_->stagePackages({installer.test_file}))); } +TEST_F(ApexShimUpdateTest, UpdateToV2FailureWrongSHA512) { + PrepareTestApexForInstall installer( + GetTestFile("com.android.apex.cts.shim.v2_wrong_sha.apex")); + + if (!installer.Prepare()) { + FAIL() << GetDebugStr(&installer); + } + + const auto& status = service_->stagePackages({installer.test_file}); + ASSERT_FALSE(IsOk(status)); + const std::string& error_message = + std::string(status.exceptionMessage().c_str()); + ASSERT_THAT(error_message, HasSubstr("has unexpected SHA512 hash")); +} + TEST_F(ApexShimUpdateTest, SubmitStagedSessionFailureHasPreInstallHook) { PrepareTestApexForInstall installer( GetTestFile("com.android.apex.cts.shim.v2_with_pre_install_hook.apex"), @@ -2815,7 +2715,7 @@ TEST_F(ApexServiceTest, SubmitStagedSessionCorruptApexFails) { ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list))); } -TEST_F(ApexServiceTest, SubmitStagedSessionCorruptApexFailsB146895998) { +TEST_F(ApexServiceTest, SubmitStagedSessionCorruptApexFails_b146895998) { PrepareTestApexForInstall installer(GetTestFile("corrupted_b146895998.apex"), "/data/app-staging/session_71", "staging_data_file"); @@ -2830,7 +2730,7 @@ TEST_F(ApexServiceTest, SubmitStagedSessionCorruptApexFailsB146895998) { ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list))); } -TEST_F(ApexServiceTest, StageCorruptApexFailsB146895998) { +TEST_F(ApexServiceTest, StageCorruptApexFails_b146895998) { PrepareTestApexForInstall installer(GetTestFile("corrupted_b146895998.apex")); if (!installer.Prepare()) { @@ -2887,10 +2787,8 @@ TEST_F(ApexServiceTest, RemountPackagesPackageOnSystemChanged) { auto active_apex = GetActivePackage("com.android.apex.test_package"); ASSERT_RESULT_OK(active_apex); ASSERT_EQ(2u, active_apex->versionCode); - // Check that module path didn't change, modulo symlink. - std::string realSystemPath; - ASSERT_TRUE(android::base::Realpath(kSystemPath, &realSystemPath)); - ASSERT_EQ(realSystemPath, active_apex->modulePath); + // Sanity check that module path didn't change. + ASSERT_EQ(kSystemPath, active_apex->modulePath); } TEST_F(ApexServiceActivationSuccessTest, RemountPackagesPackageOnDataChanged) { @@ -2916,25 +2814,6 @@ TEST_F(ApexServiceActivationSuccessTest, RemountPackagesPackageOnDataChanged) { ASSERT_EQ(installer_->test_installed_file, active_apex->modulePath); } -TEST_F(ApexServiceTest, - SubmitStagedSessionFailsManifestMismatchCleansUpHashtree) { - PrepareTestApexForInstall installer( - GetTestFile("apex.apexd_test_no_hashtree_manifest_mismatch.apex"), - "/data/app-staging/session_83", "staging_data_file"); - if (!installer.Prepare()) { - return; - } - - ApexInfoList list; - ApexSessionParams params; - params.sessionId = 83; - ASSERT_FALSE(IsOk(service_->submitStagedSession(params, &list))); - std::string hashtree_file = std::string(kApexHashTreeDir) + "/" + - installer.package + "@" + - std::to_string(installer.version) + ".new"; - ASSERT_FALSE(RegularFileExists(hashtree_file)); -} - class LogTestToLogcat : public ::testing::EmptyTestEventListener { void OnTestStart(const ::testing::TestInfo& test_info) override { #ifdef __ANDROID__ @@ -2943,7 +2822,7 @@ class LogTestToLogcat : public ::testing::EmptyTestEventListener { using base::StringPrintf; base::LogdLogger l; std::string msg = - StringPrintf("=== %s::%s (%s:%d)", test_info.test_suite_name(), + StringPrintf("=== %s::%s (%s:%d)", test_info.test_case_name(), test_info.name(), test_info.file(), test_info.line()); l(LogId::MAIN, LogSeverity::INFO, "ApexTestCases", __FILE__, __LINE__, msg.c_str()); @@ -2990,164 +2869,11 @@ TEST_F(ApexServiceActivationNoCode, NoCodeApexIsNotExecutable) { EXPECT_TRUE(found_apex_mountpoint); } -struct BannedNameProvider { - static std::string GetTestName() { return "sharedlibs.apex"; } - static std::string GetPackageName() { return "sharedlibs"; } -}; - -class ApexServiceActivationBannedName - : public ApexServiceActivationTest<BannedNameProvider> { - public: - ApexServiceActivationBannedName() : ApexServiceActivationTest(false) {} -}; - -TEST_F(ApexServiceActivationBannedName, ApexWithBannedNameCannotBeActivated) { - ASSERT_FALSE( - IsOk(service_->activatePackage(installer_->test_installed_file))); -} - -namespace { -void PrepareCompressedTestApex(const std::string& input_apex, - const std::string& builtin_dir, - const std::string& decompressed_dir, - const std::string& active_apex_dir) { - const Result<ApexFile>& apex_file = ApexFile::Open(input_apex); - ASSERT_TRUE(apex_file.ok()); - ASSERT_TRUE(apex_file->IsCompressed()) << "Not a compressed APEX"; - - auto prebuilt_file_path = - builtin_dir + "/" + android::base::Basename(input_apex); - fs::copy(input_apex, prebuilt_file_path); - - const ApexManifest& manifest = apex_file->GetManifest(); - const std::string& package = manifest.name(); - const int64_t& version = manifest.version(); - - auto decompressed_file_path = decompressed_dir + "/" + package + "@" + - std::to_string(version) + ".apex"; - auto result = apex_file->Decompress(decompressed_file_path); - ASSERT_TRUE(result.ok()) << "Failed to decompress " << result.error(); - auto active_apex_file_path = - active_apex_dir + "/" + package + "@" + std::to_string(version) + ".apex"; - auto error = - link(decompressed_file_path.c_str(), active_apex_file_path.c_str()); - ASSERT_EQ(error, 0) << "Failed to hardlink decompressed APEX"; -} - -CompressedApexInfo CreateCompressedApex(const std::string& name, - const int version, const int size) { - CompressedApexInfo result; - result.moduleName = name; - result.versionCode = version; - result.decompressedSize = size; - return result; -} -} // namespace - -class ApexServiceTestForCompressedApex : public ApexServiceTest { - public: - static constexpr const char* kTempPrebuiltDir = "/data/apex/temp_prebuilt"; - - void SetUp() override { - ApexServiceTest::SetUp(); - ASSERT_NE(nullptr, service_.get()); - - TemporaryDir decompression_dir, active_apex_dir; - if (0 != mkdir(kTempPrebuiltDir, 0777)) { - int saved_errno = errno; - ASSERT_EQ(saved_errno, EEXIST) - << kTempPrebuiltDir << ":" << strerror(saved_errno); - } - PrepareCompressedTestApex( - GetTestFile("com.android.apex.compressed.v1.capex"), kTempPrebuiltDir, - kApexDecompressedDir, kActiveApexPackagesDataDir); - service_->recollectPreinstalledData({kTempPrebuiltDir}); - service_->recollectDataApex(kActiveApexPackagesDataDir, - kApexDecompressedDir); - } - - void TearDown() override { - ApexServiceTest::TearDown(); - DeleteDirContent(kTempPrebuiltDir); - rmdir(kTempPrebuiltDir); - DeleteDirContent(kApexDecompressedDir); - DeleteDirContent(kActiveApexPackagesDataDir); - } -}; - -TEST_F(ApexServiceTestForCompressedApex, CalculateSizeForCompressedApex) { - int64_t result; - // Empty list of compressed apex info - { - CompressedApexInfoList empty_list; - ASSERT_TRUE( - IsOk(service_->calculateSizeForCompressedApex(empty_list, &result))); - ASSERT_EQ(result, 0ll); - } - - // Multiple compressed APEX should get summed - { - CompressedApexInfoList non_empty_list; - CompressedApexInfo new_apex = CreateCompressedApex("new_apex", 1, 1); - CompressedApexInfo new_apex_2 = CreateCompressedApex("new_apex_2", 1, 2); - CompressedApexInfo compressed_apex_same_version = - CreateCompressedApex("com.android.apex.compressed", 1, 4); - CompressedApexInfo compressed_apex_higher_version = - CreateCompressedApex("com.android.apex.compressed", 2, 8); - non_empty_list.apexInfos.push_back(new_apex); - non_empty_list.apexInfos.push_back(new_apex_2); - non_empty_list.apexInfos.push_back(compressed_apex_same_version); - non_empty_list.apexInfos.push_back(compressed_apex_higher_version); - ASSERT_TRUE(IsOk( - service_->calculateSizeForCompressedApex(non_empty_list, &result))); - ASSERT_EQ(result, 11ll); // 1+2+8. compressed_apex_same_version is ignored - } -} - -TEST_F(ApexServiceTestForCompressedApex, ReserveSpaceForCompressedApex) { - // Multiple compressed APEX should reserve equal to - // CalculateSizeForCompressedApex - { - CompressedApexInfoList non_empty_list; - CompressedApexInfo new_apex = CreateCompressedApex("new_apex", 1, 1); - CompressedApexInfo new_apex_2 = CreateCompressedApex("new_apex_2", 1, 2); - CompressedApexInfo compressed_apex_same_version = - CreateCompressedApex("com.android.apex.compressed", 1, 4); - CompressedApexInfo compressed_apex_higher_version = - CreateCompressedApex("com.android.apex.compressed", 2, 8); - non_empty_list.apexInfos.push_back(new_apex); - non_empty_list.apexInfos.push_back(new_apex_2); - non_empty_list.apexInfos.push_back(compressed_apex_same_version); - non_empty_list.apexInfos.push_back(compressed_apex_higher_version); - int64_t required_size; - ASSERT_TRUE(IsOk(service_->calculateSizeForCompressedApex(non_empty_list, - &required_size))); - ASSERT_EQ(required_size, - 11ll); // 1+2+8. compressed_apex_same_version is ignored - - ASSERT_TRUE(IsOk(service_->reserveSpaceForCompressedApex(non_empty_list))); - auto files = ReadDir(kOtaReservedDir, [](auto _) { return true; }); - ASSERT_TRUE(IsOk(files)); - ASSERT_EQ(files->size(), 1u); - EXPECT_EQ((int64_t)fs::file_size((*files)[0]), required_size); - } - - // Sending empty list should delete reserved file - { - CompressedApexInfoList empty_list; - ASSERT_TRUE(IsOk(service_->reserveSpaceForCompressedApex(empty_list))); - auto files = ReadDir(kOtaReservedDir, [](auto _) { return true; }); - ASSERT_TRUE(IsOk(files)); - ASSERT_EQ(files->size(), 0u); - } -} - } // namespace apex } // namespace android int main(int argc, char** argv) { android::base::InitLogging(argv, &android::base::StderrLogger); - android::base::SetMinimumLogSeverity(android::base::VERBOSE); ::testing::InitGoogleTest(&argc, argv); ::testing::UnitTest::GetInstance()->listeners().Append( new android::apex::LogTestToLogcat()); diff --git a/apexd/sysprop/Android.bp b/apexd/sysprop/Android.bp index 0b81efd9..7e3ae611 100644 --- a/apexd/sysprop/Android.bp +++ b/apexd/sysprop/Android.bp @@ -1,7 +1,3 @@ -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - sysprop_library { name: "com.android.sysprop.apex", srcs: ["ApexProperties.sysprop"], diff --git a/apexd/sysprop/ApexProperties.sysprop b/apexd/sysprop/ApexProperties.sysprop index 8baf2730..bd768303 100644 --- a/apexd/sysprop/ApexProperties.sysprop +++ b/apexd/sysprop/ApexProperties.sysprop @@ -22,19 +22,3 @@ prop { access: Readonly prop_name: "ro.apex.updatable" } - -prop { - api_name: "dm_delete_timeout" - type: UInt - scope: Internal - access: Readonly - prop_name: "apexd.config.dm_delete.timeout" -} - -prop { - api_name: "dm_create_timeout" - type: UInt - scope: Internal - access: Readonly - prop_name: "apexd.config.dm_create.timeout" -} diff --git a/apexd/sysprop/api/com.android.sysprop.apex-current.txt b/apexd/sysprop/api/com.android.sysprop.apex-current.txt index e69de29b..17983127 100644 --- a/apexd/sysprop/api/com.android.sysprop.apex-current.txt +++ b/apexd/sysprop/api/com.android.sysprop.apex-current.txt @@ -0,0 +1,8 @@ +props { + module: "android.sysprop.ApexProperties" + prop { + api_name: "updatable" + scope: Internal + prop_name: "ro.apex.updatable" + } +} diff --git a/apexer/Android.bp b/apexer/Android.bp index 29be7d1c..a33aade4 100644 --- a/apexer/Android.bp +++ b/apexer/Android.bp @@ -12,32 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - apexer_tools = [ "apexer", "aapt2", "avbtool", "e2fsdroid", + "merge_zips", "mke2fs", "resize2fs", "sefcontext_compile", + "soong_zip", "zipalign", - "make_f2fs", - "sload_f2fs", // TODO(b/124476339) apex doesn't follow 'required' dependencies so we need to include this // manually for 'avbtool'. "fec", ] -// TODO(b/157110982): cannot specify "required" dependency on go binary -apexer_go_tools = [ - "merge_zips", - "soong_zip", -] - python_library_host { name: "apex_manifest", srcs: [ @@ -61,10 +51,6 @@ python_binary_host { srcs: [ "apexer.py", ], - // TODO(b/157625953) mke2fs.conf can't embedded directly. - data: [ - ":mke2fs_conf", - ], version: { py2: { enabled: true, @@ -117,8 +103,7 @@ apex_test { manifest: "etc/manifest.json", ignore_system_library_special_case: true, key: "com.android.support.apexer.key", - binaries: apexer_tools + apexer_go_tools, - updatable: false, + binaries: apexer_tools, } // TODO(b/148659029): this test can't run in TEST_MAPPING. @@ -133,7 +118,6 @@ python_test_host { ":com.android.example-legacy.apex", ":com.android.example-logging_parent.apex", ":com.android.example-overridden_package_name.apex", - ":apexer_test_host_tools", "testdata/com.android.example.apex.avbpubkey", "testdata/com.android.example.apex.pem", "testdata/com.android.example.apex.pk8", @@ -151,59 +135,8 @@ python_test_host { libs: [ "apex_manifest", ], -} - -apexer_deps_minus_go_tools = apexer_tools + [ - "deapexer", - "debugfs_static", -] - -apexer_deps_tools = apexer_deps_minus_go_tools + apexer_go_tools - -genrule_defaults { - name: "apexer_test_host_tools_list", - tools: apexer_deps_tools + [ - "signapk", - ], -} - -genrule { - name: "apexer_test_host_tools", - srcs: [ - ":current_android_jar", - ], - out: ["apexer_test_host_tools.zip"], - tools: apexer_deps_tools + [ - // To force signapk.jar generated in out/soong/host - "signapk", + required: [ + "apexer", + "deapexer" ], - cmd: "HOST_OUT_BIN=$$(dirname $(location apexer)) && " + - "HOST_SOONG_OUT=$$(dirname $$HOST_OUT_BIN) && " + - "SIGNAPK_JAR=$$(find $${HOST_SOONG_OUT}/framework -name \"signapk*\") && " + - "LIBCPLUSPLUS=$$(find $${HOST_SOONG_OUT}/lib64 -name \"libc++.*\") && " + - "LIBCONSCRYPT_OPENJDK_JNI=$$(find $${HOST_SOONG_OUT}/lib64 -name \"libconscrypt_openjdk_jni.*\") && " + - "BASE=$(genDir)/binary_files && " + - "BIN=$$BASE/bin && " + - "LIB=$$BASE/lib64 && " + - "mkdir -p $$BIN && " + - "mkdir -p $$LIB && " + - "cp $(in) $$BIN && " + - "cp $(location apexer) $$BIN && " + - "cp $(location deapexer) $$BIN && " + - "cp $(location avbtool) $$BIN && " + - "cp $(location aapt2) $$BIN && " + - "cp $(location e2fsdroid) $$BIN && " + - "cp $(location merge_zips) $$BIN && " + - "cp $(location mke2fs) $$BIN && " + - "cp $(location resize2fs) $$BIN && " + - "cp $(location sefcontext_compile) $$BIN && " + - "cp $(location soong_zip) $$BIN && " + - "cp $(location fec) $$BIN && " + - "cp $(location zipalign) $$BIN && " + - "cp $(location debugfs_static) $$BIN && " + - "cp $$SIGNAPK_JAR $$BIN && " + - "cp $$LIBCPLUSPLUS $$LIB && " + - "cp $$LIBCONSCRYPT_OPENJDK_JNI $$LIB && " + - "$(location soong_zip) -C $$BASE -D $$BASE -o $(out) && " + - "rm -rf $$BASE", } diff --git a/apexer/TEST_MAPPING b/apexer/TEST_MAPPING index 1ad30781..cbb6a858 100644 --- a/apexer/TEST_MAPPING +++ b/apexer/TEST_MAPPING @@ -1,9 +1,4 @@ { - "presubmit": [ - { - "name": "apexer_test" - } - ], "imports": [ { "path": "system/apex/tests" diff --git a/apexer/apexer.py b/apexer/apexer.py index f0c17f43..f698c8c8 100644 --- a/apexer/apexer.py +++ b/apexer/apexer.py @@ -23,7 +23,6 @@ import apex_build_info_pb2 import argparse import hashlib import os -import pkgutil import re import shlex import shutil @@ -102,13 +101,6 @@ def ParseArgs(argv): choices=['zip', 'image'], help='type of APEX payload being built "zip" or "image"') parser.add_argument( - '--payload_fs_type', - metavar='FS_TYPE', - required=False, - default='ext4', - choices=['ext4', 'f2fs'], - help='type of filesystem being used for payload image "ext4" or "f2fs"') - parser.add_argument( '--override_apk_package_name', required=False, help='package name of the APK container. Default is the apex name in --manifest.' @@ -170,7 +162,7 @@ def ParseArgs(argv): parser.add_argument( '--unsigned_payload_only', action='store_true', - help="""Outputs the unsigned payload image/zip only. Also, setting this flag implies + help="""Outputs the unsigned payload image/zip only. Also, setting this flag implies --payload_only is set too.""" ) parser.add_argument( @@ -190,7 +182,7 @@ def FindBinaryPath(binary): ':'.join(tool_path_list)) -def RunCommand(cmd, verbose=False, env=None, expected_return_values={0}): +def RunCommand(cmd, verbose=False, env=None): env = env or {} env.update(os.environ.copy()) @@ -202,10 +194,10 @@ def RunCommand(cmd, verbose=False, env=None, expected_return_values={0}): cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) output, _ = p.communicate() - if verbose or p.returncode not in expected_return_values: + if verbose or p.returncode is not 0: print(output.rstrip()) - assert p.returncode in expected_return_values, 'Failed to execute: ' + ' '.join(cmd) + assert p.returncode is 0, 'Failed to execute: ' + ' '.join(cmd) return (output, p.returncode) @@ -386,9 +378,6 @@ def GenerateBuildInfo(args): if args.logging_parent: build_info.logging_parent = args.logging_parent - if args.payload_type == 'image': - build_info.payload_fs_type = args.payload_fs_type - return build_info def AddLoggingParent(android_manifest, logging_parent_value): @@ -461,8 +450,9 @@ def CreateApex(args, work_dir): print("Cannot read manifest file: '" + args.manifest + "'") return False - # create an empty image that is sufficiently big - size_in_mb = (GetDirSize(args.input_dir) / (1024 * 1024)) + # create an empty ext4 image that is sufficiently big + # sufficiently big = size + 16MB margin + size_in_mb = (GetDirSize(args.input_dir) / (1024 * 1024)) + 16 content_dir = os.path.join(work_dir, 'content') os.mkdir(content_dir) @@ -483,112 +473,67 @@ def CreateApex(args, work_dir): else: key_name = os.path.basename(os.path.splitext(args.key)[0]) + if manifest_apex.name != key_name: + print("package name '" + manifest_apex.name + + "' does not match with key name '" + key_name + "'") + return False img_file = os.path.join(content_dir, 'apex_payload.img') - if args.payload_fs_type == 'ext4': - # sufficiently big = size + 16MB margin - size_in_mb += 16 - - # margin is for files that are not under args.input_dir. this consists of - # n inodes for apex_manifest files and 11 reserved inodes for ext4. - # TOBO(b/122991714) eliminate these details. use build_image.py which - # determines the optimal inode count by first building an image and then - # count the inodes actually used. - inode_num_margin = GetFilesAndDirsCount(manifests_dir) + 11 - inode_num = GetFilesAndDirsCount(args.input_dir) + inode_num_margin - - cmd = ['mke2fs'] - cmd.extend(['-O', '^has_journal']) # because image is read-only - cmd.extend(['-b', str(BLOCK_SIZE)]) - cmd.extend(['-m', '0']) # reserved block percentage - cmd.extend(['-t', 'ext4']) - cmd.extend(['-I', '256']) # inode size - cmd.extend(['-N', str(inode_num)]) - uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) - cmd.extend(['-U', uu]) - cmd.extend(['-E', 'hash_seed=' + uu]) - cmd.append(img_file) - cmd.append(str(size_in_mb) + 'M') - with tempfile.NamedTemporaryFile(dir=work_dir, suffix="mke2fs.conf") as conf_file: - conf_data = pkgutil.get_data('apexer', 'mke2fs.conf') - conf_file.write(conf_data) - conf_file.flush() - RunCommand(cmd, args.verbose, - {"MKE2FS_CONFIG": conf_file.name, 'E2FSPROGS_FAKE_TIME': '1'}) - - # Compile the file context into the binary form - compiled_file_contexts = os.path.join(work_dir, 'file_contexts.bin') - cmd = ['sefcontext_compile'] - cmd.extend(['-o', compiled_file_contexts]) - cmd.append(args.file_contexts) - RunCommand(cmd, args.verbose) - - # Add files to the image file - cmd = ['e2fsdroid'] - cmd.append('-e') # input is not android_sparse_file - cmd.extend(['-f', args.input_dir]) - cmd.extend(['-T', '0']) # time is set to epoch - cmd.extend(['-S', compiled_file_contexts]) - cmd.extend(['-C', args.canned_fs_config]) - cmd.append('-s') # share dup blocks - cmd.append(img_file) - RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) - - cmd = ['e2fsdroid'] - cmd.append('-e') # input is not android_sparse_file - cmd.extend(['-f', manifests_dir]) - cmd.extend(['-T', '0']) # time is set to epoch - cmd.extend(['-S', compiled_file_contexts]) - cmd.extend(['-C', args.canned_fs_config]) - cmd.append('-s') # share dup blocks - cmd.append(img_file) - RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) - - # Resize the image file to save space - cmd = ['resize2fs'] - cmd.append('-M') # shrink as small as possible - cmd.append(img_file) - RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) - - elif args.payload_fs_type == 'f2fs': - # F2FS requires a ~100M minimum size (necessary for ART, could be reduced a bit for other) - # TODO(b/158453869): relax these requirements for readonly devices - size_in_mb += 100 - - # Create an empty image - cmd = ['/usr/bin/fallocate'] - cmd.extend(['-l', str(size_in_mb)+'M']) - cmd.append(img_file) - RunCommand(cmd, args.verbose) - - # Format the image to F2FS - cmd = ['make_f2fs'] - cmd.extend(['-g', 'android']) - uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) - cmd.extend(['-U', uu]) - cmd.extend(['-T', '0']) - cmd.append('-r') # sets checkpointing seed to 0 to remove random bits - cmd.append(img_file) - RunCommand(cmd, args.verbose) + # margin is for files that are not under args.input_dir. this consists of + # n inodes for apex_manifest files and 11 reserved inodes for ext4. + # TOBO(b/122991714) eliminate these details. use build_image.py which + # determines the optimal inode count by first building an image and then + # count the inodes actually used. + inode_num_margin = GetFilesAndDirsCount(manifests_dir) + 11 + inode_num = GetFilesAndDirsCount(args.input_dir) + inode_num_margin + + cmd = ['mke2fs'] + cmd.extend(['-O', '^has_journal']) # because image is read-only + cmd.extend(['-b', str(BLOCK_SIZE)]) + cmd.extend(['-m', '0']) # reserved block percentage + cmd.extend(['-t', 'ext4']) + cmd.extend(['-I', '256']) # inode size + cmd.extend(['-N', str(inode_num)]) + uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) + cmd.extend(['-U', uu]) + cmd.extend(['-E', 'hash_seed=' + uu]) + cmd.append(img_file) + cmd.append(str(size_in_mb) + 'M') + RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) + + # Compile the file context into the binary form + compiled_file_contexts = os.path.join(work_dir, 'file_contexts.bin') + cmd = ['sefcontext_compile'] + cmd.extend(['-o', compiled_file_contexts]) + cmd.append(args.file_contexts) + RunCommand(cmd, args.verbose) - # Add files to the image - cmd = ['sload_f2fs'] - cmd.extend(['-C', args.canned_fs_config]) - cmd.extend(['-f', manifests_dir]) - cmd.extend(['-s', args.file_contexts]) - cmd.extend(['-T', '0']) - cmd.append(img_file) - RunCommand(cmd, args.verbose, expected_return_values={0,1}) - - cmd = ['sload_f2fs'] - cmd.extend(['-C', args.canned_fs_config]) - cmd.extend(['-f', args.input_dir]) - cmd.extend(['-s', args.file_contexts]) - cmd.extend(['-T', '0']) - cmd.append(img_file) - RunCommand(cmd, args.verbose, expected_return_values={0,1}) - - # TODO(b/158453869): resize the image file to save space + # Add files to the image file + cmd = ['e2fsdroid'] + cmd.append('-e') # input is not android_sparse_file + cmd.extend(['-f', args.input_dir]) + cmd.extend(['-T', '0']) # time is set to epoch + cmd.extend(['-S', compiled_file_contexts]) + cmd.extend(['-C', args.canned_fs_config]) + cmd.append('-s') # share dup blocks + cmd.append(img_file) + RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) + + cmd = ['e2fsdroid'] + cmd.append('-e') # input is not android_sparse_file + cmd.extend(['-f', manifests_dir]) + cmd.extend(['-T', '0']) # time is set to epoch + cmd.extend(['-S', compiled_file_contexts]) + cmd.extend(['-C', args.canned_fs_config]) + cmd.append('-s') # share dup blocks + cmd.append(img_file) + RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) + + # Resize the image file to save space + cmd = ['resize2fs'] + cmd.append('-M') # shrink as small as possible + cmd.append(img_file) + RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) if args.unsigned_payload_only: shutil.copyfile(img_file, args.output) diff --git a/apexer/apexer_test.py b/apexer/apexer_test.py index 7fec4ec5..176b994c 100644 --- a/apexer/apexer_test.py +++ b/apexer/apexer_test.py @@ -21,7 +21,6 @@ import logging import os import re import shutil -import stat import subprocess import tempfile import unittest @@ -162,7 +161,6 @@ DEBUG_TEST = False class ApexerRebuildTest(unittest.TestCase): def setUp(self): self._to_cleanup = [] - self._get_host_tools(os.path.join(get_current_dir(), "apexer_test_host_tools.zip")) def tearDown(self): if not DEBUG_TEST: @@ -175,37 +173,6 @@ class ApexerRebuildTest(unittest.TestCase): else: print(self._to_cleanup) - def _get_host_tools(self, host_tools_file_path): - dir_name = tempfile.mkdtemp(prefix=self._testMethodName+"_host_tools_") - self._to_cleanup.append(dir_name) - if os.path.isfile(host_tools_file_path): - with ZipFile(host_tools_file_path, 'r') as zip_obj: - zip_obj.extractall(path=dir_name) - - files = {} - for i in ["apexer", "deapexer", "avbtool", "mke2fs", "sefcontext_compile", "e2fsdroid", - "resize2fs", "soong_zip", "aapt2", "merge_zips", "zipalign", "debugfs_static", - "signapk.jar", "android.jar"]: - file_path = os.path.join(dir_name, "bin", i) - if os.path.exists(file_path): - os.chmod(file_path, stat.S_IRUSR | stat.S_IXUSR); - files[i] = file_path - else: - files[i] = i - self.host_tools = files - self.host_tools_path = os.path.join(dir_name, "bin") - - path = os.path.join(dir_name, "bin") - if "PATH" in os.environ: - path += ":" + os.environ["PATH"] - os.environ["PATH"] = path - - ld_library_path = os.path.join(dir_name, "lib64") - if "LD_LIBRARY_PATH" in os.environ: - ld_library_path += ":" + os.environ["LD_LIBRARY_PATH"] - if "ANDROID_HOST_OUT" in os.environ: - ld_library_path += ":" + os.path.join(os.environ["ANDROID_HOST_OUT"], "lib64") - os.environ["LD_LIBRARY_PATH"] = ld_library_path def _get_container_files(self, apex_file_path): dir_name = tempfile.mkdtemp(prefix=self._testMethodName+"_container_files_") @@ -249,8 +216,7 @@ class ApexerRebuildTest(unittest.TestCase): def _extract_payload(self, apex_file_path): dir_name = tempfile.mkdtemp(prefix=self._testMethodName+"_extracted_payload_") self._to_cleanup.append(dir_name) - cmd = ["deapexer", "--debugfs_path", self.host_tools["debugfs_static"], - "extract", apex_file_path, dir_name] + cmd = ["deapexer", "extract", apex_file_path, dir_name] run_host_command(cmd) # Remove payload files added by apexer and e2fs tools. @@ -269,13 +235,9 @@ class ApexerRebuildTest(unittest.TestCase): if unsigned_payload_only or "--payload_only" in args: payload_only = True - os.environ["APEXER_TOOL_PATH"] = (self.host_tools_path + - ":out/soong/host/linux-x86/bin:prebuilts/sdk/tools/linux/bin") + os.environ["APEXER_TOOL_PATH"] = ( + "out/soong/host/linux-x86/bin:prebuilts/sdk/tools/linux/bin") cmd = ["apexer", "--force", "--include_build_info", "--do_not_check_keyname"] - if DEBUG_TEST: - cmd.append('-v') - cmd.extend(["--apexer_tool_path", os.environ["APEXER_TOOL_PATH"]]) - cmd.extend(["--android_jar_path", self.host_tools["android.jar"]]) cmd.extend(["--manifest", container_files["apex_manifest.pb"]]) if "apex_manifest.json" in container_files: cmd.extend(["--manifest_json", container_files["apex_manifest.json"]]) @@ -299,35 +261,14 @@ class ApexerRebuildTest(unittest.TestCase): run_host_command(cmd) return fn - def _get_java_toolchain(self): - java_toolchain = "java" - if os.path.isfile("prebuilts/jdk/jdk11/linux-x86/bin/java"): - java_toolchain = "prebuilts/jdk/jdk11/linux-x86/bin/java" - elif "ANDROID_JAVA_TOOLCHAIN" in os.environ: - java_toolchain = os.path.join(os.environ["ANDROID_JAVA_TOOLCHAIN"], "java") - elif "ANDROID_JAVA_HOME" in os.environ: - java_toolchain = os.path.join(os.environ["ANDROID_JAVA_HOME"], "bin", "java") - elif "JAVA_HOME" in os.environ: - java_toolchain = os.path.join(os.environ["JAVA_HOME"], "bin", "java") - - java_dep_lib = os.environ["LD_LIBRARY_PATH"] - if "ANDROID_HOST_OUT" in os.environ: - java_dep_lib += ":" + os.path.join(os.environ["ANDROID_HOST_OUT"], "lib64") - if "ANDROID_BUILD_TOP" in os.environ: - java_dep_lib += ":" + os.path.join(os.environ["ANDROID_BUILD_TOP"], - "out/soong/host/linux-x86/lib64") - - return [java_toolchain, java_dep_lib] - def _sign_apk_container(self, unsigned_apex): fd, fn = tempfile.mkstemp(prefix=self._testMethodName+"_repacked_", suffix=".apex") os.close(fd) self._to_cleanup.append(fn) - java_toolchain, java_dep_lib = self._get_java_toolchain() cmd = [ - java_toolchain, - "-Djava.library.path=" + java_dep_lib, - "-jar", self.host_tools['signapk.jar'], + "prebuilts/jdk/jdk11/linux-x86/bin/java", + "-Djava.library.path=out/soong/host/linux-x86/lib64", + "-jar", "out/soong/host/linux-x86/framework/signapk.jar", "-a", "4096", os.path.join(get_current_dir(), TEST_X509_KEY), os.path.join(get_current_dir(), TEST_PK8_KEY), diff --git a/apexer/runtests.sh b/apexer/runtests.sh index e2eacc90..7a499c75 100755 --- a/apexer/runtests.sh +++ b/apexer/runtests.sh @@ -28,12 +28,10 @@ m -j apexer export APEXER_TOOL_PATH="${ANDROID_BUILD_TOP}/out/soong/host/linux-x86/bin:${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/linux/bin" PATH+=":${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/linux/bin" -for fs_type in ext4 f2fs -do input_dir=$(mktemp -d) output_dir=$(mktemp -d) -function cleanup { +function finish { sudo umount /dev/loop10 sudo losetup --detach /dev/loop10 @@ -41,7 +39,7 @@ function cleanup { rm -rf ${output_dir} } -trap cleanup ERR +trap finish EXIT ############################################# # prepare the inputs ############################################# @@ -57,7 +55,7 @@ ln -s file1 ${input_dir}/sym1 manifest_dir=$(mktemp -d) manifest_file=${manifest_dir}/apex_manifest.pb echo '{"name": "com.android.example.apex", "version": 1}' > ${manifest_dir}/apex_manifest.json -${ANDROID_BUILD_TOP}/out/soong/host/linux-x86/bin/conv_apex_manifest proto ${manifest_dir}/apex_manifest.json -o ${manifest_file} +${ANDROID_HOST_OUT}/bin/conv_apex_manifest proto ${manifest_dir}/apex_manifest.json -o ${manifest_file} # Create the file_contexts file file_contexts_file=$(mktemp) @@ -84,9 +82,7 @@ output_file=${output_dir}/test.apex ${ANDROID_HOST_OUT}/bin/apexer --verbose --manifest ${manifest_file} \ --file_contexts ${file_contexts_file} \ --canned_fs_config ${canned_fs_config_file} \ - --payload_fs_type ${fs_type} \ --key ${ANDROID_BUILD_TOP}/system/apex/apexer/testdata/com.android.example.apex.pem \ - --android_jar_path ${ANDROID_BUILD_TOP}/prebuilts/sdk/current/public/android.jar \ ${input_dir} ${output_file} ############################################# @@ -135,8 +131,4 @@ sudo diff ${input_dir}/sub/file3 ${output_dir}/mnt/sub/file3 # check the android manifest aapt dump xmltree ${output_file} AndroidManifest.xml -echo "Passed for ${fs_type}" -cleanup -done - -echo "Passed for all fs types" +echo Passed diff --git a/apexer/testdata/Android.bp b/apexer/testdata/Android.bp index 951f849e..5f72dc9b 100644 --- a/apexer/testdata/Android.bp +++ b/apexer/testdata/Android.bp @@ -12,10 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - android_app_certificate { name: "com.android.example.apex.certificate", // This will use com.android.my.apex.x509.pem (the cert) and @@ -37,8 +33,6 @@ apex { key: "com.android.example.apex.key", certificate: ":com.android.example.apex.certificate", installable: false, - updatable:false, - generate_hashtree: false, } apex { @@ -50,7 +44,6 @@ apex { certificate: ":com.android.example.apex.certificate", min_sdk_version: "29", installable: false, - generate_hashtree: false, } apex { @@ -62,7 +55,6 @@ apex { certificate: ":com.android.example.apex.certificate", installable: false, logging_parent: "foobar", - updatable: false, } apex { @@ -74,5 +66,4 @@ apex { certificate: ":com.android.example.apex.certificate", installable: false, package_name: "com.android.overridden.example.apex", - updatable: false, } diff --git a/docs/README.md b/docs/README.md index 64867a2b..5ba0ab9c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,19 +1,15 @@ # APEX File Format -Android Pony EXpress (APEX) is a container format introduced in Android Q that -is used in the install flow for lower-level system modules. This format -facilitates the updates of system components that don't fit into the standard -Android application model. Some example components are native services and -libraries, hardware abstraction layers +Android Pony EXpress (APEX) is a container format introduced in Android Q +that is used in the install flow for lower-level system +modules. This format facilitates the updates of system components that don't fit +into the standard Android application model. Some example components are native +services and libraries, hardware abstraction layers ([HALs](/https://source.android.com/devices/architecture/hal-types)), runtime ([ART](/https://source.android.com/devices/tech/dalvik)), and class libraries. The term "APEX" can also refer to an APEX file. -This document describes technical details of the APEX file format. If you are -looking at how to build an APEX package, kindly refer to [this how-to](howto.md) -document. - ## Background Although Android supports updates of modules that fit within the standard app @@ -33,7 +29,7 @@ has the following drawbacks: This section describes the high-level design of the APEX file format and the APEX manager, which is a service that manages APEX files. -### APEX format {#apex-format} +### APEX format This is the format of an APEX file. @@ -59,8 +55,8 @@ infrastructure such as ADB, PackageManager, and package installer apps (such as Play Store). For example, the APEX file can use an existing tool such as `aapt` to inspect basic metadata from the file. The file contains package name and version information. This information is generally also available in -`apex_manifest.json`. `AndroidManifest.xml` might contain additional targeting -information that can be used by the existing app publishing tools. +`apex_manifest.json`. `AndroidManifest.xml` might contain additional +targeting information that can be used by the existing app publishing tools. `apex_manifest.json` is recommended over `AndroidManifest.xml` for new code and systems that deal with APEX. @@ -72,16 +68,16 @@ metadata block are created using libavb. The file system payload isn't parsed inside the `apex_payload.img` file. `apex_pubkey` is the public key used to sign the file system image. At runtime, -this key ensures that the downloaded APEX is signed with the same entity that -signs the same APEX in the built-in partitions. +this key ensures that the downloaded APEX is signed with the same entity +that signs the same APEX in the built-in partitions. ### APEX manager -The APEX manager (or `apexd`) is a native daemon responsible for verifying, -installing, and uninstalling APEX files. This process is launched and is ready -early in the boot sequence. APEX files are normally pre-installed on the device -under `/system/apex`. The APEX manager defaults to using these packages if no -updates are available. +The APEX manager (or `apexd`) is a native daemon responsible for +verifying, installing, and uninstalling APEX files. This process is launched and +is ready early in the boot sequence. APEX files are normally pre-installed on +the device under `/system/apex`. The APEX manager defaults to using these +packages if no updates are available. The update sequence of an APEX uses the [PackageManager class](https://developer.android.com/reference/android/content/pm/PackageManager) @@ -136,17 +132,17 @@ The APEX format supports these file types: - Data files - Config files -The APEX format can only update some of these file types. Whether a file type -can be updated depends on the platform and how stable the interfaces for the -files types are defined. +The APEX format can only update some of these file types. Whether a file +type can be updated depends on the platform and how stable the interfaces for +the files types are defined. ### Signing APEX files are signed in two ways. First, the `apex_payload.img` (specifically, the vbmeta descriptor appended to `apex_payload.img`) file is signed with a key. Then, the entire APEX is signed using the -[APK signature scheme v3](/https://source.android.com/security/apksigning/v3). -Two different keys are used in this process. +[APK signature scheme v3](/https://source.android.com/security/apksigning/v3). Two different keys are used +in this process. On the device side, a public key corresponding to the private key used to sign the vbmeta descriptor is installed. The APEX manager uses the public key to @@ -156,8 +152,9 @@ different keys and is enforced both at build time and runtime. ### APEX in built-in partitions APEX files can be located in built-in partitions such as `/system`. The -partition is already over dm-verity, so the APEX files are mounted directly over -the loop device. +partition is +already over dm-verity, so the APEX files are mounted directly over the loop +device. If an APEX is present in a built-in partition, the APEX can be updated by providing an APEX package with the same package name and a higher version code. @@ -168,18 +165,18 @@ the newer version of the APEX is only activated after reboot. ## Kernel requirements To support APEX mainline modules on an Android device, the following Linux -kernel features are required: the loop driver and dm-verity. The loop driver -mounts the file system image in an APEX module and dm-verity verifies the APEX -module. +kernel features are required: the loop driver and dm-verity. The loop +driver mounts the file system image in an APEX module and dm-verity verifies the +APEX module. -The performance of the loop driver and dm-verity is important in achieving good -system performance when using APEX modules. +The performance of the loop driver and dm-verity is important in achieving +good system performance when using APEX modules. ### Supported kernel versions APEX mainline modules are supported on devices using kernel versions 4.4 or -higher. New devices launching with Android Q or higher must use kernel version -4.9 or higher to support APEX modules. +higher. New devices launching with Android Q or higher +must use kernel version 4.9 or higher to support APEX modules. ### Required kernel patches @@ -190,9 +187,10 @@ of the Android common tree. #### Kernel version 4.4 This version is only supported for devices that are upgraded from Android 9 to -Android Q and want to support APEX modules. To get the required patches, a -down-merge from the `android-4.4` branch is strongly recommended. The following -is a list of the required individual patches for kernel version 4.4. +Android Q and want to support APEX modules. To get the +required patches, a down-merge from the `android-4.4` branch is strongly +recommended. The following is a list of the required individual patches +for kernel version 4.4. - UPSTREAM: loop: add ioctl for changing logical block size ([4.4](https://android-review.googlesource.com/c/kernel/common/+/777013){: .external}) @@ -218,9 +216,9 @@ the `android-common` branch. ### Required kernel configuration options -The following list shows the base configuration requirements for supporting APEX -modules that were introduced in Android Q. The items with an asterisk (\*) are -existing requirements from Android 9 and lower. +The following list shows the base configuration requirements for supporting +APEX modules that were introduced in Android Q. The +items with an asterisk (\*) are existing requirements from Android 9 and lower. ``` (*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices) @@ -244,8 +242,8 @@ requirements. Note: Because the implementation details for APEX are still under development, the content in this section is subject to change. -This section describes how to build an APEX using the Android build system. The -following is an example of `Android.bp` for an APEX named `apex.test`. +This section describes how to build an APEX using the Android build system. +The following is an example of `Android.bp` for an APEX named `apex.test`. ``` apex { @@ -282,12 +280,12 @@ apex { #### File types and locations in APEX -File type | Location in APEX ----------------- | ---------------------------------------------------------- -Shared libraries | `/lib` and `/lib64` (`/lib/arm` for translated arm in x86) -Executables | `/bin` -Java libraries | `/javalib` -Prebuilts | `/etc` +|File type |Location in APEX | +|-------------------|--------------------------------------------------------------| +|Shared libraries |`/lib` and `/lib64` (`/lib/arm` for translated arm in x86) | +|Executables |`/bin` | +|Java libraries |`/javalib` | +|Prebuilts |`/etc` | ### Transitive dependencies @@ -378,7 +376,6 @@ $ avbtool extract_public_key --key foo.pem --output foo.avbpubkey ``` In Android.bp: - ``` apex_key { name: "apex.test.key", @@ -510,21 +507,21 @@ kernel to fully support APEX. For example, the kernel might have been built without `CONFIG_BLK_DEV_LOOP=Y`, which is crucial for mounting the file system image inside an APEX. -Flattened APEX is a specially built APEX that can be activated on devices with a -legacy kernel. Files in a flattened APEX are directly installed to a directory +Flattened APEX is a specially built APEX that can be activated on devices with +a legacy kernel. Files in a flattened APEX are directly installed to a directory under the built-in partition. For example, `lib/libFoo.so` in a flattend APEX `my.apex` is installed to `/system/apex/my.apex/lib/libFoo.so`. Activating a flattened APEX doesn't involve the loop device. The entire directory `/system/apex/my.apex` is directly bind-mounted to `/apex/name@ver`. -Flattened APEXs can't be updated by downloading updated versions of the APEXs -from network because the downloaded APEXs can't be flattened. Flattened APEXs -can be updated only via a regular OTA. +Flattened APEXs can't be updated by downloading updated versions +of the APEXs from network because the downloaded APEXs can't be flattened. +Flattened APEXs can be updated only via a regular OTA. Note that flattened APEX is the default configuration for now. This means all -APEXes are by default flattened unless you explicitly configure your device to -support updatable APEX (explained above). +APEXes are by default flattened unless you explicitly configure your device +to support updatable APEX (explained above). Also note that, mixing flattened and non-flattened APEXes in a device is NOT supported. It should be either all non-flattened or all flattened. This is @@ -533,168 +530,22 @@ like Mainline. APEXes that are not pre-signed (i.e. built from the source) should also be non-flattened and signed with proper keys in that case. The device should inherit from `updatable_apex.mk` as explained above. -## Compressed apexes {#compressed-apex} - -APEX compression is a new feature introduced in Android S. Its main purpose is -to reduce the storage impact of updatable APEX packages: after an update to an -APEX is installed, its pre-installed version is not used anymore, and space that -is taken by it effectively becomes a dead weight. - -APEX compression minimizes the storage impact by using a highly-compressed -set of APEX files on read-only partitions (e.g. `/system`). In Android S a -DEFLATE zip compression is used. - -Note: compression doesn't provide any optimization in the following scenarios: - -* Bootstrap apexes that are required to be mounted very early in the boot - sequence. List of bootstrap apexes is configured in `kBootstrapApexes` - constant in `system/apex/apexd/apexd.cpp`. -* Non-updatable apexes. Compression is only beneficial in case an updated - version of an apex is installed on `/data partition`. - Full list of updatable apexes is available at - https://source.android.com/devices/architecture/modular-system. -* Dynamic shared libs apexes. Since `apexd` will always activate both versions - of such apexes (pre-installed and upgraded), compressing them doesn't provide - any value. - -### Compressed APEX file format - -This is the format of a compressed APEX file. - -![Compressed APEX file format](compressed-apex-format.png) - -**Figure 2.** Compressed APEX file format - -At the top level, a compressed APEX file is a zip file containing the original apex in deflated -form with compression level of 9 and other files stored uncompressed. - -The four files in an APEX file are: - -* `original_apex`: deflated with compression level of 9 -* `apex_manifest.pb`: stored only -* `AndroidManifest.xml`: stored only -* `apex_pubkey`: stored only - - -`original_apex` is the original uncompressed [APEX file](#apex-format). - -`apex_manifest.pb` `AndroidManifest.xml` `apex_pubkey` are copies of the -corresponding files from `original_apex`. - - -### Building compressed apex - -Compressed apex can be built using `apex_compression_tool.py` located at -`system/apex/tools`. - -Note: the outer apk container of the produced compressed apex file won't be -automatically signed. You will need to manually sign it with using the correct -certificate. See [Signing Builds for Release]( -https://source.android.com/devices/tech/ota/sign_builds#apex-signing-key-replacement). - -There are a few different parameters related to APEX compression available in -the build system. - -In `Android.bp` whether an apex is compressible is controlled by `compressible` -property: - -``` -apex { - name: "apex.test", - manifest: "apex_manifest.json", - file_contexts: "file_contexts", - compressible: true, -} -``` - -Note: this only serves as a hint to build system that this apex can be -compressed. Such property is required due to the fact that not all apexes are -compressible as mentioned in the [section above](#compressed-apex). - -TODO(b/183208430): add docs on how this works for prebuilts. - -A `PRODUCT_COMPRESSED_APEX` product flag is used to control whether a system -image built from source should contain compressed apexes or not. - -For local experimentation you can force a build to compress apexes by setting -`OVERRIDE_PRODUCT_COMPRESSED_APEX=true`. - -Compressed APEX files generated by the build system will have `.capex` -extension. It makes it easier to distinguish between compressed and uncompressed -versions of an APEX. - -### Supported compression algorithms - -Android S only supports deflate zip compression. - -### Activating compressed apex during boot - -Before activating a compressed APEX, `original_apex` inside it will be -decompressed into `/data/apex/decompressed` directory. The resulting -decompressed APEX will be hard linked to the `/data/apex/active` directory. - -Note: because of the hard link step above, it's important that files under -`/data/apex/decompressed` have the same SELinux label as files under -`/data/apex/active`. - -Consider following example as an illustration of the process described above. - -Let's assume that `/system/apex/com.android.foo.capex` is a compressed APEX -being activated, and it's `versionCode` is `37`. - -1. First `original_apex` inside `/system/apex/com.android.foo.capex` is - decompressed into `/data/apex/decompressed/com.android.foo@37.apex`. -2. After that `restorecon /data/apex/decompressed/com.android.foo@37.apex` is - performed to make sure that it has a correct SELinux label. -3. Verification checks are performed on - `/data/apex/decompressed/com.android.foo@37.apex` to ensure it's validity: - * `apexd` checks that public key bundled in - `/data/apex/decompressed/com.android.foo@37.apex` is equal to the one - bundled in `/system/apex/com.android.foo.capex` -4. Next `/data/apex/decompressed/com.android.foo@37.apex` is hard linked to - `/data/apex/active/com.android.foo@37.apex`. -5. Finally, regular activation logic for uncompressed APEX files is performed - for `/data/apex/active/com.android.foo@37.apex`. - -For more information see implementation of `OnStart` function in -`system/apex/apexd/apexd.cpp`. - -### Interaction with OTA - -Compressed APEX files have some implications on the OTA delivery and -application. Since an OTA might contain a compressed APEX file with higher -version compared to what is currently active on the device, some free space must -be reserved before rebooting a device to apply an OTA. - -To help OTA system, two new binder APIs are exposed by apexd: - -* `calculateSizeForCompressedApex` - calculates size required for decompressing - APEX files in OTA package. It can be used to check if device has enough space - before downloading an OTA. -* `reserveSpaceForCompressedApex` - reserves space on the disk that in the - future will be used by apexd for decompression of compressed APEX files inside - the OTA package. - - -In case of A/B OTA, `apexd` will attempt decompression in the background as part -of the postinstall OTA routine. If decompression fails, `apexd` will fallback to -decompressing during the boot that applies the OTA. - ## Alternatives considered when developing APEX -Here are some options that we considered when designing the APEX file format, -and why we included or excluded them. +Here are some options that we considered when designing the APEX file +format, and why we included or excluded them. ### Regular package management systems -Linux distributions have package management systems like `dpkg` and `rpm`, which -are powerful, mature and robust. However, they weren't adopted for APEX because -they can't protect the packages after installation. Verification is done only -when packages are being installed. Attackers can break the integrity of the -installed packages unnoticed. This is a regression for Android where all system -components were stored in read-only file systems whose integrity is protected by -dm-verity for every I/O. Any tampering to system components must be prohibited, -or be detectable so that the device can refuse to boot if compromised. +Linux distributions have package management systems like `dpkg` and `rpm`, +which are powerful, mature and robust. However, they weren't +adopted for APEX because they can't protect the packages after +installation. Verification is done only when packages are being installed. +Attackers can break the integrity of the installed packages unnoticed. This is +a regression for Android where all system components were stored in read-only +file systems whose integrity is protected by dm-verity for every I/O. Any +tampering to system components must be prohibited, or be detectable so that +the device can refuse to boot if compromised. ### dm-crypt for integrity @@ -722,15 +573,15 @@ partition, they were accessible via paths such as `/system/lib/libfoo.so`. A client of an APEX file (other APEX files or the platform) should use the new paths. This change in paths might require updates to the existing code. -One way to avoid the path change is to overlay the file contents in an APEX file -over the `/system` partition. However, we decided not to overlay files over the -`/system` partition because we believed this would negatively affect performance -as the number of files being overlayed (possibly even stacked one after another) -increases. +One way to avoid the path change is to overlay the file contents in an APEX +file over the `/system` partition. However, we decided not to overlay files over +the `/system` partition because we believed this would negatively affect +performance as the number of files being overlayed (possibly even stacked one +after another) increases. Another option was to hijack file access functions such as `open`, `stat`, and `readlink`, so that paths that start with `/system` are redirected to their corresponding paths under `/apex`. We discarded this option because it's -practically infeasible to change all functions that accept paths. For example, -some apps statically link Bionic, which implements the functions. In that case, -the redirection won't happen for the app. +practically infeasible to change all functions that accept paths. For +example, some apps statically link Bionic, which implements the functions. In +that case, the redirection won't happen for the app. diff --git a/docs/compressed-apex-format.png b/docs/compressed-apex-format.png Binary files differdeleted file mode 100644 index d67efcbc..00000000 --- a/docs/compressed-apex-format.png +++ /dev/null diff --git a/docs/howto.md b/docs/howto.md deleted file mode 100644 index a7042493..00000000 --- a/docs/howto.md +++ /dev/null @@ -1,556 +0,0 @@ -# How To APEX - -[go/android-apex-howto](http://go/android-apex-howto) (internal link) - -This doc reflects the current implementation status, and thus is expected to -change regularly. - -## Reference - -To understand the design rationale, visit this -[public doc](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md#alternatives-considered-when-developing-apex) -and [go/android-apex](http://go/android-apex) (internal). - -## Building an APEX - -A cheat sheet: - -``` -apex { - name: "com.android.my.apex", - - manifest: "apex_manifest.json", - - // optional. if unspecified, a default one is auto-generated - androidManifest: "AndroidManifest.xml", - - // libc.so and libcutils.so are included in the apex - native_shared_libs: ["libc", "libcutils"], - binaries: ["vold"], - java_libs: ["core-all"], - apps: ["myapk"], - prebuilts: ["my_prebuilt"], - - compile_multilib: "both", - - key: "com.android.my.apex.key", - certificate: ":com.android.my.apex.certificate", -} -``` - -`apex_manifest.json` should look like: - -``` -{ - "name": "com.android.my.apex", - "version": 1 -} -``` - -The file contexts files should be created at -`/system/sepolicy/apex/com.android.my.apex-file_contexts`: - -``` -(/.*)? u:object_r:system_file:s0 -/sub(/.*)? u:object_r:sub_file:s0 -/sub/file3 u:object_r:file3_file:s0 -``` - -The file should describe the contents of your apex. Note that the file is -amended by the build system so that the `apexd` can access the root directory of -your apex and the `apex_manifest.pb` file. (Technically, they are labeled as -`system_file`.) So if you're -[building the apex without Soong](#building-apex-without-soong), please be sure -that `apexd` can access the root directory and the `apex_manifest.pb` file. (In -the example above, the first line does that.) - -#### A script to create a skeleton of APEX - -For convenience, you might want to use a -[script](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/tools/create_apex_skeleton.sh) -that creates a skeleton (`Android.bp`, keys, etc.) of an APEX for you. You only -need to adjust the `APEX_NAME` variable to be your actual APEX name. - -#### File types and places where they are installed in apex - -file type | place in apex --------------- | ---------------------------------------------------------- -shared libs | `/lib` and `/lib64` (`/lib/arm` for translated arm in x86) -executables | `/bin` -java libraries | `/javalib` -android apps | `/app` or `/priv-app` -prebuilts | `/etc` - -### Transitive dependencies - -Transitive dependencies of a native shared lib or an executable are -automatically included in the APEX. For example, if `libFoo` depends on -`libBar`, then the two libs are included even when only `libFoo` is listed in -`native_shared_libs` property. - -However, if a transitive dependency has a stable ABI, it is not included -transitively. It can be included in an APEX only by directly being referenced. -Currently (2019/08/05), the only module type that can provide stable ABI is -`cc_library`. To do so, add `stubs.*` property as shown below: - -``` -cc_library { - name: "foo", - srcs: [...], - stubs: { - symbol_file: "foo.map.txt", - versions: ["29", "30"], - }, -} -``` - -Use this when a lib has to be accessed across the APEX boundary, e.g. between -APEXes or between an APEX and the platform. - -### apex_available - -Any module that is “included” (not just referenced) in an APEX either via the -direct dependency or the transitive dependency has to correctly set the -`apex_available` property in its `Android.bp` file. The property can have one or -more of the following values: - -* `<name_of_an_apex>`: Like `com.android.adbd`. By specifying the APEX names - explicitly, the module is guaranteed to be included in those APEXes. This is - useful when a module has to be kept as an implementation detail of an APEX - and therefore shouldn’t be used from outside. -* `//apex_available:anyapex`: This means that the module can be included in - any APEX. This is useful for general-purpose utility libraries like - `libbase`, `libcutils`, etc. -* `//apex_available:platform`: The module can be installed to the platform, - outside of APEXes. This is the default value. However, `if apex_available` - is set to either of `<name_of_an_apex` or `//apex_available:anyapex`, the - default is removed. If a module has to be included in both APEX and the - platform, `//apex_available:platform` and`//apex_available:anyapex` should - be specified together. - -The act of adding an APEX name to the `apex_available` property of a module has -to be done or be reviewed by the author(s) of the module. Being included in an -APEX means that the module will be portable, i.e., running on multiple versions -of the current and previous platforms, whereas it usually was expected to run on -the current (the up-to-date) platform. Therefore, the module might have to be -prepared to not have version-specific dependencies to the platform, like the -existence of a dev node, a system call, etc. - -### Handling multiple ABIs - -`compile_multilib`: specifies the ABI(s) that this APEX will compile native -modules for. Can be either of `both`, `first`, `32`, `64`, `prefer32`. For most -of the cases, this should be `both`. - -`native_shared_libs`: installed for **_both_** primary and secondary ABIs of the -device. Of course, if the APEX is built for a target having single ABI (i.e. -32-bit only or 64-bit only), only libraries with the corresponding ABI are -installed. - -`binaries`: installed only for the **_primary_** ABI of the device. In other -words, - -* If the device is 32-bit only, only the 32-bit variant of the binary is - installed. -* If the device supports both 32/64 ABIs, but with - `TARGET_PREFER_32_BIT_EXECUTABLES=true`, then only the 32-bit variant of the - binary is installed. -* If the device is 64-bit only, then only the 64-bit variant of the binary is - installed. -* If the device supports both 32/64 ABIs, but without - `TARGET_PREFER_32_BIT_EXECUTABLES=true`, then only the 64-bit variant of the - binary is installed. - -In order to fine control the ABIs of the native libraries and binaries to be -installed, use -`multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]` -properties. - -* `first`: matches with the primary ABI of the device. This is the default for - `binaries`. -* `lib32`: matches with the 32-bit ABI of the device, if supported -* `lib64`: matches with the 64-bit ABI of the device, it supported -* `prefer32`: matches with the 32-bit ABI of the device, if support. If 32-bit - ABI is not supported, it is matched with the 64-bit ABI. -* `both`: matches with the both ABIs. This is the default for - `native_shared_libraries`. -* `java libraries` and `prebuilts`: ABI-agnostic - -Example: (let’s assume that the device supports 32/64 and does not prefer32) - -``` -apex { - // other properties are omitted - compile_multilib: "both", - native_shared_libs: ["libFoo"], // installed for 32 and 64 - binaries: ["exec1"], // installed for 64, but not for 32 - multilib: { - first: { - native_shared_libs: ["libBar"], // installed for 64, but not for 32 - binaries: ["exec2"], // same as binaries without multilib.first - }, - both: { - native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib - binaries: ["exec3"], // installed for 32 and 64 - }, - prefer32: { - native_shared_libs: ["libX"], // installed for 32, but not for 64 - }, - lib64: { - native_shared_libs: ["libY"], // installed for 64, but not for 32 - }, - }, -} -``` - -### APEX image signing - -**Note**: the APEX skeleton creation -[script](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/tools/create_apex_skeleton.sh) -automates this step. - -Each APEX must be signed with different keys. There is no concept of the -platform key. `apexd` in the future might reject if multiple APEXes are signed -with the same key. When a new key is needed, create a public-private key pair -and make an `apex_key` module. Use `key` property to sign an APEX using the key. -The public key is included in the zip container of the APEX as a file entry -`apex_pubkey`. - -How to generate the key pair: - -``` -# create an rsa key pair -$ openssl genrsa -out com.android.my.apex.pem 4096 - -# extract the public key from the key pair -$ avbtool extract_public_key --key com.android.my.apex.pem \ ---output com.android.my.apex.avbpubkey - -# in Android.bp -apex_key { - name: "com.android.my.apex.key", - public_key: "com.android.my.apex.avbpubkey", - private_key: "com.android.my.apex.pem", -} -``` - -Important: In the above example, the name of the public key (that is -`com.android.my.apex`) becomes the ID of the key. The ID of the key used to sign -an APEX is recorded in the APEX. At runtime, a public key with the same ID in -the device is used to verify the APEX. - -### APK (APEX container) signing - -**Note**: the APEX skeleton creation -[script](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/tools/create_apex_skeleton.sh) -automates this step. - -An APEX should also be signed just like APKs. So, an APEX is signed twice; once -for the mini file system (`apex_payload.img` file) and once for the entire file. - -Just like APK, the file-level signing is done via the `certificate` property. It -can be set in three ways. - -* not set: if unset, the APEX is signed with the certificate located at - `PRODUCT_DEFAULT_DEV_CERTIFICATE`. If the flag is also unset, it defaults to - `build/target/product/security/testkey` -* `<name>`: the APEX is signed with the certificate named `<name>` in the same - directory as `PRODUCT_DEFAULT_DEV_CERTIFICATE` -* `<name>`: the APEX signed with the certificate which is defined by a - Soong module named `<name>`. The certificate module can be defined as - follows. - -``` -android_app_certificate { - name: "com.android.my.apex.certificate", - // This will use com.android.my.apex.x509.pem (the cert) and - // com.android.my.apex.pk8 (the private key) - certificate: "com.android.my.apex", -} -``` - -How to generate the certificate/private key pair: - -``` -# Create certificate and private in PEM form -$ openssl req -x509 -newkey rsa:4096 -nodes -days 999999 -keyout key.pem -out com.android.my.apex.x509.pem - -# Enter following info via the interactive prompts -# Country Name: US -# State: California -# Locality Name: Mountain View -# Organization Name: Android -# Organization Unit Name: Android -# Common Name: <your-apk-name> -# Email address: android@android.com - -# Convert the private to pkcs8 format -$ openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -out com.android.my.apex.pk8 -nocrypt -``` - -### Signing APEXs with release keys - -The procedures described in the [APEX image signing](#apex-image-signing) and -[APK (APEX container) signing](#apk-apex-container_signing) sections require the -private keys to be present in the tree. This is not suitable for public release. -Please refer to the -[APEX signing key replacement](https://source.android.com/devices/tech/ota/sign_builds#apex-signing-key-replacement) -documentation to prepare the APEX packages for release. - -For the Google-specific procedure for release keys, the documentation is -available at -[go/android-apex-howto-internal](http://go/android-apex-howto-internal) -(internal only). - -### Linker namespaces for native libraries and binaries - -The linker needs to be set up with separate namespaces for each APEX, for -isolation. It is done through `ld.config.txt` files, which are autogenerated by -`linkerconfig`. Normally you only need to ensure that the APEX manifest -correctly lists the native libraries it requires (from platform or other APEXes) -and provides, which by default is taken from the build system. - -Refer to the [design doc](go/linker-config-apex) for more information about -linkerconfig and apex. - -## Installing an APEX - -Use - -``` -adb install --staged <path_to_apex> && adb reboot -``` - -The `adb install --staged` command triggers a verification for the staged APEX -which might fail when the APEX is signed incorrectly. - -Note that on Q devices when the `adb install --staged` command completes you -still will have to wait until the verification for the staged APEX is finished -before issuing `adb reboot`. - -On R devices we added the `--wait` option to `adb install` to wait until the -verification is completed before returning. On S devices the `--wait` option is -implicit. - -## Hot swapping an APEX (development only) - -Use - -``` -adb sync && adb shell cmd -w apexservice remountPackages -``` - -Note that for this command to remount your APEX, you must ensure that all -processes that have reference to your APEX are killed. E.g. if you are -developing an APEX that contributes to system\_server, you can use the -following: - -``` -adb root -adb remount -adb shell stop -adb sync -adb shell cmd -w apexservice remountPackages -adb shell start -``` - -## Using an APEX - -After the reboot, the apex will be mounted at `/apex/<apex_name>@<version>` -directory. Multiple versions of the same APEX can be mounted at the same time. A -mount point that always points to the latest version of an APEX is provided: -`/apex/<apex_name>`. - -Clients can use the latter path to read or execute something from APEX. - -So, typical usage of APEX is as follows. - -1. an APEX is pre-loaded under `/system/apex`when the device is shipped. -2. Files in it are accessed via the `/apex/<apex_name>/`path. -3. When an updated version of the APEX is installed in `/data/apex/active`, the - path will point to the new APEX after the reboot. - -## Updating service with APEX - -Using APEX, you can update a service. To do so, you need … - -1) Mark the service in system partition as updatable. Add the new option -‘updatable’ to the service definition. - -``` -/system/etc/init/myservice.rc: - -service myservice /system/bin/myservice - class core - user system - … - updatable -``` - -2) Create a new `.rc` file for the updated service. Use ‘`override`’ option to -redefine the existing service. - -``` -/apex/my.apex/etc/init.rc: - -service myservice /apex/my.apex/bin/myservice - class core - user system - … - override -``` - -Note that you can only have service definitions in the rc file in APEX. You -cannot have action triggers in APEXes. - -Also note that if a service marked as updatable is started before APEXes are -activated, the start is delayed until the activation of APEXes is finished. - -## Configuring system to support APEX updates - -Set the following system property to true to support APEX file updates. - -``` -<device.mk>: - -PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true - -BoardConfig.mk: -TARGET_FLATTEN_APEX := false - -or just -<device.mk>: - -$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk) -``` - -## Flattened APEX - -For legacy devices, it is sometimes impossible or infeasible to update the old -kernel to fully support APEX. For example, the kernel might have been built -without `CONFIG_BLK_DEV_LOOP=Y`, which is crucial for mounting the file system -image inside an APEX. - -Flattened APEX is a specially built APEX that can be activated on devices with a -legacy kernel. Files in a flattened APEX are directly installed to a directory -under the built-in partition. For example, `lib/libFoo.so` in a flattened APEX -my.apex is installed to `/system/apex/my.apex/lib/libFoo.so`. - -Activating a flattened APEX doesn't involve the loop device. The entire -directory `/system/apex/my.apex` is directly bind-mounted to `/apex/name@ver`. - -Flattened APEXs can‘t be updated by downloading updated versions of the APEXs -from network because the downloaded APEXs can’t be flattened. Flattened APEXs -can be updated only via a regular OTA. - -Note that flattened APEX is the default configuration for now (2019/Aug). This -means all APEXes are by default flattened unless you explicitly configure your -device to support updatable APEX (explained above). - -Also note that, mixing flattened and non-flattened APEXes in a device is NOT -supported. It should be either all non-flattened or all flattened. This is -especially important when shipping pre-signed APEX prebuilts for the projects -like Mainline. APEXes that are not pre-signed (i.e. built from the source) -should also be non-flattened and signed with proper keys in that case. The -device should inherit from `updatable_apex.mk` as explained above. - -## Building APEX without Soong - -An APEX can be built without relying on the build commands generated by Soong. - -1) Prepare following files: - -- APEX manifest file (in JSON) - -- AndroidManifest file (in XML, optional) - -- AVB private key - -- APK certificate (`*.x509.pem`) - -- APK private key (`*.pk8`) - -- `file_contexts` file - -- files to be packaged into the APEX - -2) Create `canned_fs_config` file - -It is a file that specifies access bits and uid/gid of each file in the APEX. - -``` -/ 1000 1000 0755 -/apex_manifest.json 1000 1000 0644 -/apex_manifest.pb 1000 1000 0644 -/file1 1000 1000 0644 -/file2 1000 1000 0644 -/dir 0 2000 0755 -/dir/file3 1000 1000 0644 -... -``` - -Note that ALL files AND directories must be specified. And don’t forget to have -a line for `/`and `/apex_manifest.pb`. (`/apex_manifest.json` line is for -Q-targeting modules) - -3) Invoke `apexer` - -``` -$ apexer \ - --manifest <apex_manifest_file> \ - --file_contexts <file_contexts_file> \ - --canned_fs_config <canned_fs_config_file> \ - --key <avb_private_key_file> \ - --payload_type image \ - --android_manifest <android_manifest_file> \ - --override_apk_package_name com.google.foo \ - <input_directory> \ - <output_apex_file> -``` - -`--android_manifest` and -`-override_apk_package` are optional arguments and -thus can be omitted if not needed. - -Note: The `<apex_manifest_file>` shouldn’t be under `<input_directory>`. - -4) Sign it - -`apexer` signs the `apex_payload.img` file only. The entire apex (which is a zip -file) has to be signed with `Signapk`. - -``` -$ java \ - -Djava.library.path=$(dirname out/soong/host/linux-x86/lib64/libconscrypt_openjdk_jni.so)\ - -jar out/soong/host/linux-x86/framework/signapk.jar \ - -a 4096 \ - <apk_certificate_file> \ - <apk_private_key_file> \ - <unsigned_input_file> \ - <signed_output_file> -``` - -This will sign the input file with the cert/privkey pairs to produce the output -file. - -## Re-packaging an existing APEX - -If an APEX has been build by passing `--include_build_info` to `apexer` (this is -the default when building via Soong), it will then include a file named -`apex_build_info.pb` which will store as much information as possible about how -the apex was built (see the `ApexBuildInfo` proto -[definition](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/proto/apex_build_info.proto) -for more info) with the exception of the signing keys. - -We also provide a tool named `deapexer` to extract the payload content of an -APEX in a local directory. - -By using these tools, you can then adapt the procedure described in the -[building the apex without Soong](#building-apex-without-soong) section and pass -the `--build_info apex_build_info.pb` file where `apex_build_info.pb` contains -all the build parameters that you would otherwise pass via flag to `apexer`. - -We do this programmatically in some unit test code to generate "unusual" APEX -files, see for example -[here](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/apexer/apexer_test.py) -and -[here](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/tests/testdata/sharedlibs/build/shared_libs_repack.py). diff --git a/library_linking_strategy.cc b/library_linking_strategy.cc deleted file mode 100644 index a56d92e2..00000000 --- a/library_linking_strategy.cc +++ /dev/null @@ -1,17 +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. - */ - -int main() { return 0; } diff --git a/libs/libapexutil/Android.bp b/libs/libapexutil/Android.bp index 5ba3181d..edf66f2f 100644 --- a/libs/libapexutil/Android.bp +++ b/libs/libapexutil/Android.bp @@ -14,10 +14,6 @@ * limitations under the License. */ -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - cc_defaults { name: "libapexutil-deps", static_libs: [ @@ -34,10 +30,6 @@ cc_library_static { export_include_dirs: ["."], srcs: ["apexutil.cpp"], host_supported: true, - apex_available: [ - "//apex_available:platform", - "com.android.runtime", - ], } cc_test { @@ -53,4 +45,4 @@ cc_test { "general-tests", ], host_supported: true, -} +}
\ No newline at end of file diff --git a/libs/libapexutil/apexutil.cpp b/libs/libapexutil/apexutil.cpp index 6203a3b4..e0b0787b 100644 --- a/libs/libapexutil/apexutil.cpp +++ b/libs/libapexutil/apexutil.cpp @@ -18,7 +18,6 @@ #include "apexutil.h" #include <dirent.h> -#include <string.h> #include <memory> @@ -68,8 +67,6 @@ GetActivePackages(const std::string &apex_root) { continue; if (strchr(entry->d_name, '@') != nullptr) continue; - if (strcmp(entry->d_name, "sharedlibs") == 0) - continue; std::string apex_path = apex_root + "/" + entry->d_name; auto manifest = ParseApexManifest(apex_path + "/apex_manifest.pb"); if (manifest.ok()) { diff --git a/proto/Android.bp b/proto/Android.bp index 30b24e33..be9966a4 100644 --- a/proto/Android.bp +++ b/proto/Android.bp @@ -14,10 +14,6 @@ * limitations under the License. */ -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - cc_library_static { name: "lib_apex_manifest_proto", host_supported: true, @@ -37,10 +33,6 @@ cc_library_static { type: "lite", }, srcs: ["apex_manifest.proto"], - apex_available: [ - "//apex_available:platform", - "com.android.runtime", - ], } python_library_host { @@ -98,19 +90,3 @@ cc_library_static { }, srcs: ["session_state.proto"], } - -genrule { - name: "apex-protos", - tools: ["soong_zip"], - dist: { - targets: ["apexer_tools"], - }, - srcs: [ - "apex_manifest.proto", - "apex_build_info.proto", - ], - out: ["apex-protos.zip"], - cmd: "mkdir $(genDir)/protos && " + - "cp $(in) $(genDir)/protos && " + - "$(location soong_zip) -o $(out) -C $(genDir)/protos -D $(genDir)/protos", -} diff --git a/proto/apex_build_info.proto b/proto/apex_build_info.proto index fd8a3499..e25baa20 100644 --- a/proto/apex_build_info.proto +++ b/proto/apex_build_info.proto @@ -48,7 +48,4 @@ message ApexBuildInfo { // Value of --logging_parent passed at build time. string logging_parent = 9; - - // Value of --payload_fs_type passed at build time. - string payload_fs_type = 10; } diff --git a/proto/apex_manifest.proto b/proto/apex_manifest.proto index 22fb7d79..dfbf4d23 100644 --- a/proto/apex_manifest.proto +++ b/proto/apex_manifest.proto @@ -48,35 +48,4 @@ message ApexManifest { // List of native libs which this apex uses from other apexes or system. repeated string requireNativeLibs = 8; - - // List of JNI libs. - // linkerconfig/libnativeloader use this field so that java libraries can - // load JNI libraries in the same apex. - // This is supposed to be filled by the build system with libraries which are - // marked as "is_jni: true" from the list of "native_shared_libs". - repeated string jniLibs = 9; - - // List of libs required that are located in a shared libraries APEX. - // Format of the content is 'library:hash'. - // Example) libc++.so:83d8f50... - repeated string requireSharedApexLibs = 10; - - // Whether this APEX provides libraries to be shared with other APEXs. This - // causes libraries contained in the APEX to be made available under - // /apex/sharedlibs . - bool provideSharedApexLibs = 11; - - message CompressedApexMetadata { - - // Valid only for compressed APEX. This field contains the root digest of - // the original_apex contained inside CAPEX. - string originalApexDigest = 1; - } - - // Exists only for compressed APEX - CompressedApexMetadata capexMetadata = 12; - - // Indicates that this APEX can be updated without rebooting device. - bool supportsRebootlessUpdate = 13; } - diff --git a/proto/session_state.proto b/proto/session_state.proto index cebf0adb..35319cf7 100644 --- a/proto/session_state.proto +++ b/proto/session_state.proto @@ -58,7 +58,4 @@ message SessionState { // The names of the apexes within this session. Only populated for sessions // that have been activated. repeated string apex_names = 9; - - // Populated with error details when session fails to activate - string error_message = 10; } diff --git a/pylintrc b/pylintrc deleted file mode 100644 index 88750048..00000000 --- a/pylintrc +++ /dev/null @@ -1,447 +0,0 @@ -# This Pylint rcfile contains a best-effort configuration to uphold the -# best-practices and style described in the Google Python style guide: -# https://google.github.io/styleguide/pyguide.html -# -# Its canonical open-source location is: -# https://google.github.io/styleguide/pylintrc - -[MASTER] - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=third_party - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Pickle collected data for later comparisons. -persistent=no - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Use multiple processes to speed up Pylint. -jobs=4 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable=abstract-method, - apply-builtin, - arguments-differ, - attribute-defined-outside-init, - backtick, - bad-option-value, - basestring-builtin, - buffer-builtin, - c-extension-no-member, - consider-using-enumerate, - cmp-builtin, - cmp-method, - coerce-builtin, - coerce-method, - delslice-method, - div-method, - duplicate-code, - eq-without-hash, - execfile-builtin, - file-builtin, - filter-builtin-not-iterating, - fixme, - getslice-method, - global-statement, - hex-method, - idiv-method, - implicit-str-concat-in-sequence, - import-error, - import-self, - import-star-module-level, - inconsistent-return-statements, - input-builtin, - intern-builtin, - invalid-str-codec, - locally-disabled, - long-builtin, - long-suffix, - map-builtin-not-iterating, - misplaced-comparison-constant, - missing-function-docstring, - metaclass-assignment, - next-method-called, - next-method-defined, - no-absolute-import, - no-else-break, - no-else-continue, - no-else-raise, - no-else-return, - no-init, # added - no-member, - no-name-in-module, - no-self-use, - nonzero-method, - oct-method, - old-division, - old-ne-operator, - old-octal-literal, - old-raise-syntax, - parameter-unpacking, - print-statement, - raising-string, - range-builtin-not-iterating, - raw_input-builtin, - rdiv-method, - reduce-builtin, - relative-import, - reload-builtin, - round-builtin, - setslice-method, - signature-differs, - standarderror-builtin, - suppressed-message, - sys-max-int, - too-few-public-methods, - too-many-ancestors, - too-many-arguments, - too-many-boolean-expressions, - too-many-branches, - too-many-instance-attributes, - too-many-locals, - too-many-nested-blocks, - too-many-public-methods, - too-many-return-statements, - too-many-statements, - trailing-newlines, - unichr-builtin, - unicode-builtin, - unnecessary-pass, - unpacking-in-except, - useless-else-on-loop, - useless-object-inheritance, - useless-suppression, - using-cmp-argument, - wrong-import-order, - xrange-builtin, - zip-builtin-not-iterating, - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". This option is deprecated -# and it will be removed in Pylint 2.0. -files-output=no - -# Tells whether to display a full report or only the messages -reports=no - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[BASIC] - -# Good variable names which should always be accepted, separated by a comma -good-names=main,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl - -# Regular expression matching correct function names -function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$ - -# Regular expression matching correct variable names -variable-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct constant names -const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Regular expression matching correct attribute names -attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ - -# Regular expression matching correct argument names -argument-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=^[a-z][a-z0-9_]*$ - -# Regular expression matching correct class names -class-rgx=^_?[A-Z][a-zA-Z0-9]*$ - -# Regular expression matching correct module names -module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$ - -# Regular expression matching correct method names -method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=10 - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=80 - -# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt -# lines made too long by directives to pytype. - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=(?x)( - ^\s*(\#\ )?<?https?://\S+>?$| - ^\s*(from\s+\S+\s+)?import\s+.+$) - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=yes - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check= - -# Maximum number of lines in a module -max-module-lines=99999 - -# String used as indentation unit. The internal Google style guide mandates 2 -# spaces. Google's externaly-published style guide says 4, consistent with -# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google -# projects (like TensorFlow). -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=TODO - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=yes - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging,absl.logging,tensorflow.io.logging - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub, - TERMIOS, - Bastion, - rexec, - sets - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant, absl - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls, - class_ - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=StandardError, - Exception, - BaseException diff --git a/shim/Android.bp b/shim/Android.bp index b9952664..351e754f 100644 --- a/shim/Android.bp +++ b/shim/Android.bp @@ -15,10 +15,6 @@ // TODO: consider removing _prebuilt suffix from module names and make use of // 'prefer: true' -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - prebuilt_apex { name: "com.android.apex.cts.shim.v1_prebuilt", overrides: ["CtsShimPrebuilt", "CtsShimPrivPrebuilt"], @@ -180,43 +176,3 @@ prebuilt_apex { filename: "com.android.apex.cts.shim.v3.apex", installable: false, } - -prebuilt_apex { - name: "com.android.apex.cts.shim.v2_different_certificate_prebuilt", - arch: { - arm: { - src: "prebuilts/arm/com.android.apex.cts.shim.v2_different_certificate.apex", - }, - arm64: { - src: "prebuilts/arm/com.android.apex.cts.shim.v2_different_certificate.apex", - }, - x86: { - src: "prebuilts/x86/com.android.apex.cts.shim.v2_different_certificate.apex", - }, - x86_64: { - src: "prebuilts/x86/com.android.apex.cts.shim.v2_different_certificate.apex", - }, - }, - filename: "com.android.apex.cts.shim.v2_different_certificate.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.cts.shim.v2_unsigned_apk_container_prebuilt", - arch: { - arm: { - src: "prebuilts/arm/com.android.apex.cts.shim.v2_unsigned_apk_container.apex", - }, - arm64: { - src: "prebuilts/arm/com.android.apex.cts.shim.v2_unsigned_apk_container.apex", - }, - x86: { - src: "prebuilts/x86/com.android.apex.cts.shim.v2_unsigned_apk_container.apex", - }, - x86_64: { - src: "prebuilts/x86/com.android.apex.cts.shim.v2_unsigned_apk_container.apex", - }, - }, - filename: "com.android.apex.cts.shim.v2_unsigned_apk_container.apex", - installable: false, -} diff --git a/shim/build/Android.bp b/shim/build/Android.bp index 2fa99bf5..2667984b 100644 --- a/shim/build/Android.bp +++ b/shim/build/Android.bp @@ -14,10 +14,6 @@ // Build rules to build shim apexes. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - genrule { name: "com.android.apex.cts.shim.pem", out: ["com.android.apex.cts.shim.pem"], @@ -40,27 +36,6 @@ apex_key { } genrule { - name: "com.android.apex.cts.shim.debug.pem", - out: ["com.android.apex.cts.shim.debug.pem"], - cmd: "openssl genrsa -out $(out) 4096", -} - -genrule { - name: "com.android.apex.cts.shim.debug.pubkey", - srcs: [":com.android.apex.cts.shim.debug.pem"], - out: ["com.android.apex.cts.shim.debug.pubkey"], - tools: ["avbtool"], - cmd: "$(location avbtool) extract_public_key --key $(in) --output $(out)", -} - -apex_key { - name: "com.android.apex.cts.shim.debug.key", - private_key: ":com.android.apex.cts.shim.debug.pem", - public_key: ":com.android.apex.cts.shim.debug.pubkey", - installable: false, -} - -genrule { name: "generate_hash_of_dev_null", out: ["hash.txt"], cmd: "sha512sum -b /dev/null | cut -d' ' -f1 | tee $(out)", @@ -83,7 +58,6 @@ apex { apps: ["CtsShim", "CtsShimPriv"], installable: false, allowed_files: "default_shim_allowed_list.txt", - updatable: false, } apex { @@ -96,20 +70,6 @@ apex { apps: ["CtsShim", "CtsShimPriv"], installable: false, allowed_files: "default_shim_allowed_list.txt", - updatable: false, -} - -apex { - name: "com.android.apex.cts.shim.v2_sign_payload_with_different_key", - // Use manifest_v2_rebootless to also re-use this APEX in the rebootless update test case. - manifest: "manifest_v2_rebootless.json", - androidManifest: "AndroidManifest.xml", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.cts.shim.debug.key", - prebuilts: ["hash_of_dev_null"], - installable: false, - allowed_files: "default_shim_allowed_list.txt", - updatable: false, } apex { @@ -121,7 +81,6 @@ apex { prebuilts: ["hash_of_dev_null"], installable: false, allowed_files: "default_shim_allowed_list.txt", - updatable: false, } apex { @@ -134,14 +93,12 @@ apex { apps: ["CtsShim", "CtsShimPriv"], installable: false, allowed_files: "default_shim_allowed_list.txt", - generate_hashtree: false, - updatable: false, + test_only_no_hashtree: true, } apex { name: "com.android.apex.cts.shim.v2_unsigned_payload", - // Use manifest_v2_rebootless to also re-use this APEX in the rebootless update test case. - manifest: "manifest_v2_rebootless.json", + manifest: "manifest_v2.json", androidManifest: "AndroidManifest.xml", file_contexts: ":apex.test-file_contexts", key: "com.android.apex.cts.shim.key", @@ -150,14 +107,12 @@ apex { installable: false, allowed_files: "default_shim_allowed_list.txt", test_only_unsigned_payload: true, - updatable: false, } override_apex { name: "com.android.apex.cts.shim.v2_different_package_name", package_name: "com.android.apex.cts.shim.different", - // Use rebootless APEX to re-use this APEX in the rebootless update test case. - base: "com.android.apex.cts.shim.v2_rebootless", + base: "com.android.apex.cts.shim.v2", } genrule { @@ -177,14 +132,12 @@ prebuilt_etc { // to stage it should fail. apex { name: "com.android.apex.cts.shim.v2_wrong_sha", - // Use manifest_v2_rebootless to also re-use this APEX in the rebootless update test case. - manifest: "manifest_v2_rebootless.json", + manifest: "manifest_v2.json", androidManifest: "AndroidManifest.xml", file_contexts: ":apex.test-file_contexts", key: "com.android.apex.cts.shim.key", prebuilts: ["empty_hash"], installable: false, - updatable: false, } prebuilt_etc { @@ -196,14 +149,12 @@ prebuilt_etc { apex { name: "com.android.apex.cts.shim.v2_additional_file", - // Use manifest_v2_rebootless to also re-use this APEX in the rebootless update test case. - manifest: "manifest_v2_rebootless.json", + manifest: "manifest_v2.json", androidManifest: "AndroidManifest.xml", file_contexts: ":apex.test-file_contexts", key: "com.android.apex.cts.shim.key", prebuilts: ["hash_of_dev_null", "apex_shim_additional_file"], installable: false, - updatable: false, } prebuilt_etc { @@ -216,14 +167,12 @@ prebuilt_etc { apex { name: "com.android.apex.cts.shim.v2_additional_folder", - // Use manifest_v2_rebootless to also re-use this APEX in the rebootless update test case. - manifest: "manifest_v2_rebootless.json", + manifest: "manifest_v2.json", androidManifest: "AndroidManifest.xml", file_contexts: ":apex.test-file_contexts", key: "com.android.apex.cts.shim.key", prebuilts: ["hash_of_dev_null", "apex_shim_additional_folder"], installable: false, - updatable: false, } apex { @@ -234,7 +183,6 @@ apex { key: "com.android.apex.cts.shim.key", prebuilts: ["hash_of_dev_null"], installable: false, - updatable: false, } apex { @@ -245,7 +193,6 @@ apex { key: "com.android.apex.cts.shim.key", prebuilts: ["hash_of_dev_null"], installable: false, - updatable: false, } genrule { @@ -265,9 +212,7 @@ genrule { ":com.android.apex.cts.shim.v2_with_post_install_hook", ":com.android.apex.cts.shim.v2_sdk_target_p", ":com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p", - ":com.android.apex.cts.shim.v2_rebootless", ":com.android.apex.cts.shim.v3", - ":com.android.apex.cts.shim.v3_rebootless", ":com.android.apex.cts.shim.v3_signed_bob", ":com.android.apex.cts.shim.v3_signed_bob_rot", ], @@ -291,7 +236,6 @@ apex { prebuilts: ["hash_v1"], apps: ["CtsShim", "CtsShimPriv"], allowed_files: "default_shim_allowed_list.txt", - updatable: false, } // This is to install the flattened version of com.android.apex.cts.shim. @@ -332,25 +276,21 @@ apex_key { apex { name: "com.android.apex.cts.shim_not_pre_installed", manifest: "manifest_not_pre_installed.json", - androidManifest: "AndroidManifestNotPreInstalled.xml", file_contexts: ":apex.test-file_contexts", key: "com.android.apex.cts.shim_not_pre_installed.key", prebuilts: ["hash_of_dev_null"], installable: false, - updatable: false, } apex { name: "com.android.apex.cts.shim.v2_different_certificate", - // Use manifest_v2_rebootless to also re-use this APEX in the rebootless update test case. - manifest: "manifest_v2_rebootless.json", + manifest: "manifest_v2.json", androidManifest: "AndroidManifest.xml", file_contexts: ":apex.test-file_contexts", key: "com.android.apex.cts.shim.key", prebuilts: ["hash_of_dev_null"], installable: false, certificate: ":com.android.apex.cts.shim.debug.cert", - updatable: false, } android_app_certificate { @@ -515,7 +455,6 @@ apex { apps: ["CtsShim", "CtsShimPriv"], installable: false, min_sdk_version: "29", - updatable: false, } genrule { @@ -529,15 +468,13 @@ genrule { // Apex shim that targets an old sdk (P) apex { name: "com.android.apex.cts.shim.v2_sdk_target_p", - // Use manifest_v2_rebootless to also re-use this APEX in the rebootless update test case. - manifest: "manifest_v2_rebootless.json", + manifest: "manifest_v2.json", androidManifest: "AndroidManifestSdkTargetP.xml", file_contexts: ":apex.test-file_contexts", key: "com.android.apex.cts.shim.key", prebuilts: ["hash_of_dev_null"], installable: false, apps: ["CtsShim", "CtsShimPriv"], - updatable: false, } // Apex shim with apk-in-apex that targets sdk P @@ -550,35 +487,15 @@ apex { prebuilts: ["hash_of_dev_null"], apps: ["CtsShimTargetPSdk"], installable: false, - updatable: false, } // Apex shim with unsigned apk genrule { name: "com.android.apex.cts.shim.v2_unsigned_apk_container", - // Use shim.v2_rebootless to re-use same APEX in the rebootless update test case. - srcs: [":com.android.apex.cts.shim.v2_rebootless"], + srcs: [":com.android.apex.cts.shim.v2"], out: ["com.android.apex.cts.shim.v2_unsigned_apk_container.apex"], cmd: "cp -v $(in) $(out) && zip -d $(out) META-INF*", -} - -// Apex shim for testing rebootless updates -apex { - name: "com.android.apex.cts.shim.v2_rebootless", - manifest: "manifest_v2_rebootless.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.cts.shim.key", - prebuilts: ["hash_of_dev_null"], - installable: false, - updatable: false, -} - -apex { - name: "com.android.apex.cts.shim.v3_rebootless", - manifest: "manifest_v3_rebootless.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.cts.shim.key", - prebuilts: ["hash_of_dev_null"], - installable: false, - updatable: false, + dist: { + targets: ["apps_only"], + } } diff --git a/shim/build/AndroidManifestNotPreInstalled.xml b/shim/build/AndroidManifestNotPreInstalled.xml deleted file mode 100644 index 82955f77..00000000 --- a/shim/build/AndroidManifestNotPreInstalled.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.apex.cts.shim_not_pre_installed"> - <!-- APEX does not have classes.dex --> - <application android:hasCode="false" /> - <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> -</manifest> - diff --git a/shim/build/manifest_not_pre_installed.json b/shim/build/manifest_not_pre_installed.json index 6653f8da..078e1aec 100644 --- a/shim/build/manifest_not_pre_installed.json +++ b/shim/build/manifest_not_pre_installed.json @@ -1,5 +1,4 @@ { "name": "com.android.apex.cts.shim_not_pre_installed", - "version": 1, - "supportsRebootlessUpdate": true + "version": 1 } diff --git a/shim/build/manifest_v2_rebootless.json b/shim/build/manifest_v2_rebootless.json deleted file mode 100644 index 5f6adfdb..00000000 --- a/shim/build/manifest_v2_rebootless.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "com.android.apex.cts.shim", - "version": 2, - "supportsRebootlessUpdate": true -} diff --git a/shim/build/manifest_v2_with_post_install_hook.json b/shim/build/manifest_v2_with_post_install_hook.json index a4e3f019..7faecd8c 100644 --- a/shim/build/manifest_v2_with_post_install_hook.json +++ b/shim/build/manifest_v2_with_post_install_hook.json @@ -1,6 +1,5 @@ { "name": "com.android.apex.cts.shim", "version": 2, - "postInstallHook": "/bin/false", - "supportsRebootlessUpdate": true + "postInstallHook": "/bin/false" } diff --git a/shim/build/manifest_v2_with_pre_install_hook.json b/shim/build/manifest_v2_with_pre_install_hook.json index e2a874c8..57bd6203 100644 --- a/shim/build/manifest_v2_with_pre_install_hook.json +++ b/shim/build/manifest_v2_with_pre_install_hook.json @@ -1,6 +1,5 @@ { "name": "com.android.apex.cts.shim", "version": 2, - "preInstallHook": "/bin/true", - "supportsRebootlessUpdate": true + "preInstallHook": "/bin/true" } diff --git a/shim/build/manifest_v3_rebootless.json b/shim/build/manifest_v3_rebootless.json deleted file mode 100644 index 61e63ff0..00000000 --- a/shim/build/manifest_v3_rebootless.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "com.android.apex.cts.shim", - "version": 3, - "supportsRebootlessUpdate": true -} diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v1.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v1.apex Binary files differindex 791d0dd9..7b5758dc 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v1.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v1.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2.apex Binary files differindex 0a26d236..dc2c8c9d 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_additional_file.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_additional_file.apex Binary files differindex 5ed2c7e0..3cf81757 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_additional_file.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_additional_file.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_additional_folder.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_additional_folder.apex Binary files differindex 133f7f72..53358189 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_additional_folder.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_additional_folder.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex Binary files differindex 6ce53975..4f0edff6 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_different_certificate.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_different_certificate.apex Binary files differindex 4228ffed..af89d200 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_different_certificate.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_different_certificate.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_different_package_name.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_different_package_name.apex Binary files differindex de090f19..12b85e43 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_different_package_name.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_different_package_name.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_no_hashtree.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_no_hashtree.apex Binary files differindex 162044cf..00c0c197 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_no_hashtree.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_no_hashtree.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_rebootless.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_rebootless.apex Binary files differdeleted file mode 100644 index 9d688876..00000000 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_rebootless.apex +++ /dev/null diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_sdk_target_p.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_sdk_target_p.apex Binary files differindex 44f7613c..50e77bdd 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_sdk_target_p.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_sdk_target_p.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex Binary files differdeleted file mode 100644 index 389871de..00000000 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex +++ /dev/null diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob.apex Binary files differindex f8fa44fb..3bde9ba5 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob_rot.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob_rot.apex Binary files differindex 96588183..26c3f27a 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob_rot.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob_rot.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex Binary files differindex 34400434..81aaecfc 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_unsigned_payload.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_unsigned_payload.apex Binary files differindex d6f7d8ff..d2be4a2a 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_unsigned_payload.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_unsigned_payload.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_with_post_install_hook.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_with_post_install_hook.apex Binary files differindex cc214f8c..e1f904e6 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_with_post_install_hook.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_with_post_install_hook.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_with_pre_install_hook.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_with_pre_install_hook.apex Binary files differindex dab82a02..c8aa9d98 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_with_pre_install_hook.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_with_pre_install_hook.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_without_apk_in_apex.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_without_apk_in_apex.apex Binary files differindex 9706f2f2..41c29bfd 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_without_apk_in_apex.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_without_apk_in_apex.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_wrong_sha.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_wrong_sha.apex Binary files differindex f305cff9..1447e7d2 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v2_wrong_sha.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v2_wrong_sha.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v3.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v3.apex Binary files differindex 3122c967..e538f831 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v3.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v3.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v3_rebootless.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v3_rebootless.apex Binary files differdeleted file mode 100644 index 0f5134da..00000000 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v3_rebootless.apex +++ /dev/null diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v3_signed_bob.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v3_signed_bob.apex Binary files differindex a83bf51f..86b418e0 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v3_signed_bob.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v3_signed_bob.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim.v3_signed_bob_rot.apex b/shim/prebuilts/arm/com.android.apex.cts.shim.v3_signed_bob_rot.apex Binary files differindex 703b6411..29a0877b 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim.v3_signed_bob_rot.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim.v3_signed_bob_rot.apex diff --git a/shim/prebuilts/arm/com.android.apex.cts.shim_not_pre_installed.apex b/shim/prebuilts/arm/com.android.apex.cts.shim_not_pre_installed.apex Binary files differindex 806ccc77..e8705c7f 100644 --- a/shim/prebuilts/arm/com.android.apex.cts.shim_not_pre_installed.apex +++ b/shim/prebuilts/arm/com.android.apex.cts.shim_not_pre_installed.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v1.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v1.apex Binary files differindex a94ae752..04ec92da 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v1.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v1.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2.apex Binary files differindex a9c27586..ff547892 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_additional_file.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_additional_file.apex Binary files differindex 5ed2c7e0..3cf81757 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_additional_file.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_additional_file.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_additional_folder.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_additional_folder.apex Binary files differindex 133f7f72..53358189 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_additional_folder.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_additional_folder.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex Binary files differindex 6ce53975..4f0edff6 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_different_certificate.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_different_certificate.apex Binary files differindex 4228ffed..af89d200 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_different_certificate.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_different_certificate.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_different_package_name.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_different_package_name.apex Binary files differindex de090f19..eca1c375 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_different_package_name.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_different_package_name.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_no_hashtree.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_no_hashtree.apex Binary files differindex 19ec142a..19da8646 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_no_hashtree.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_no_hashtree.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_rebootless.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_rebootless.apex Binary files differdeleted file mode 100644 index 9d688876..00000000 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_rebootless.apex +++ /dev/null diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_sdk_target_p.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_sdk_target_p.apex Binary files differindex 9929c3dc..6b7436b4 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_sdk_target_p.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_sdk_target_p.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex Binary files differdeleted file mode 100644 index 389871de..00000000 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex +++ /dev/null diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob.apex Binary files differindex 3f2fc505..659f4625 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob_rot.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob_rot.apex Binary files differindex cf628abb..c5e22108 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob_rot.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob_rot.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex Binary files differindex ba5af3b5..3ff88a91 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_unsigned_payload.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_unsigned_payload.apex Binary files differindex 38c073f8..cd702268 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_unsigned_payload.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_unsigned_payload.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_with_post_install_hook.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_with_post_install_hook.apex Binary files differindex cc214f8c..e1f904e6 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_with_post_install_hook.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_with_post_install_hook.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_with_pre_install_hook.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_with_pre_install_hook.apex Binary files differindex dab82a02..c8aa9d98 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_with_pre_install_hook.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_with_pre_install_hook.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_without_apk_in_apex.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_without_apk_in_apex.apex Binary files differindex 9706f2f2..41c29bfd 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_without_apk_in_apex.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_without_apk_in_apex.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_wrong_sha.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_wrong_sha.apex Binary files differindex f305cff9..1447e7d2 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v2_wrong_sha.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v2_wrong_sha.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v3.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v3.apex Binary files differindex 7f3e9ecb..b3ecef76 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v3.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v3.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v3_rebootless.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v3_rebootless.apex Binary files differdeleted file mode 100644 index 0f5134da..00000000 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v3_rebootless.apex +++ /dev/null diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v3_signed_bob.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v3_signed_bob.apex Binary files differindex 1aeb0b0c..9281ee39 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v3_signed_bob.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v3_signed_bob.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim.v3_signed_bob_rot.apex b/shim/prebuilts/x86/com.android.apex.cts.shim.v3_signed_bob_rot.apex Binary files differindex 20f1c850..23b23789 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim.v3_signed_bob_rot.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim.v3_signed_bob_rot.apex diff --git a/shim/prebuilts/x86/com.android.apex.cts.shim_not_pre_installed.apex b/shim/prebuilts/x86/com.android.apex.cts.shim_not_pre_installed.apex Binary files differindex 806ccc77..e8705c7f 100644 --- a/shim/prebuilts/x86/com.android.apex.cts.shim_not_pre_installed.apex +++ b/shim/prebuilts/x86/com.android.apex.cts.shim_not_pre_installed.apex diff --git a/tests/Android.bp b/tests/Android.bp index 425f2bc2..96fd30b4 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -12,10 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - java_defaults { name: "apex_e2e_test_defaults", libs: [ @@ -28,6 +24,17 @@ java_defaults { } java_test_host { + name: "adbd_e2e_tests", + srcs: ["src/**/AdbdHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.adbd", + ], + test_config: "adbd-e2e-tests.xml", + test_suites: ["device-tests"], +} + +java_test_host { name: "timezone_data_e2e_tests", srcs: ["src/**/TimezoneDataHostTest.java"], defaults: ["apex_e2e_test_defaults"], @@ -42,6 +49,17 @@ java_test_host { } java_test_host { + name: "statsd_e2e_tests", + srcs: ["src/**/StatsdHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.os.statsd", + ], + test_config: "statsd-e2e-tests.xml", + test_suites: ["device-tests"], +} + +java_test_host { name: "media_e2e_tests", srcs: ["src/**/MediaHostTest.java"], defaults: ["apex_e2e_test_defaults"], @@ -64,6 +82,95 @@ java_test_host { } java_test_host { + name: "mediaprovider_e2e_tests", + srcs: ["src/**/MediaProviderHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.mediaprovider", + ], + test_config: "mediaprovider-e2e-tests.xml", + test_suites: ["device-tests"], +} + +java_test_host { + name: "conscrypt_e2e_tests", + srcs: ["src/**/ConscryptHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.conscrypt", + ], + test_config: "conscrypt-e2e-tests.xml", + test_suites: ["device-tests"], +} + +java_test_host { + name: "neuralnetworks_e2e_tests", + srcs: ["src/**/NeuralNetworksHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.neuralnetworks", + ], + test_config: "neuralnetworks-e2e-tests.xml", + test_suites: ["device-tests"], +} + + +java_test_host { + name: "cellbroadcast_e2e_tests", + srcs: ["src/**/CellbroadcastHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.cellbroadcast", + ], + test_config: "cellbroadcast-e2e-tests.xml", + test_suites: ["device-tests"], +} + +java_test_host { + name: "ipsec_e2e_tests", + srcs: ["src/**/IpSecHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.ipsec", + ], + test_config: "ipsec-e2e-tests.xml", + test_suites: ["device-tests"], +} + +java_test_host { + name: "permission_e2e_tests", + srcs: ["src/**/PermissionHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.permission", + ], + test_config: "permission-e2e-tests.xml", + test_suites: ["device-tests"], +} + +java_test_host { + name: "wifi_e2e_tests", + srcs: ["src/**/WifiHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.wifi", + ], + test_config: "wifi-e2e-tests.xml", + test_suites: ["device-tests"], +} + +java_test_host { + name: "extservices_e2e_tests", + srcs: ["src/**/ExtServicesHostTest.java"], + defaults: ["apex_e2e_test_defaults"], + data: [ + ":test_com.android.extservices", + ], + test_config: "extservices-e2e-tests.xml", + test_suites: ["device-tests"], +} + +java_test_host { name: "apex_targetprep_tests", libs: ["tradefed"], srcs: ["src/**/ApexTargetPrepTest.java"], @@ -76,14 +183,19 @@ java_library_host { name: "apex_e2e_base_test", srcs: ["src/**/ApexE2EBaseHostTest.java"], static_libs: [ - "frameworks-base-hostutils", - "cts-install-lib-host", + "module_test_util", ], libs: [ "tradefed", ], } +java_library_host { + name: "module_test_util", + srcs: ["util/**/ModuleTestUtils.java"], + libs: ["tradefed", "truth-prebuilt"], +} + apex { name: "apex.test", manifest: "testdata/apex_manifest.json", @@ -91,7 +203,6 @@ apex { key: "apex.test.key", certificate: ":apex.test.certificate", installable: false, - updatable: false, } apex_key { @@ -126,11 +237,21 @@ prebuilt_etc { installable: false, } +cc_binary { + name: "sample_prefer32_binary", + srcs: ["sample_prefer32_binary.cc"], + target: { + android: { + compile_multilib: "prefer32", + }, + }, +} + java_test_host { name: "apex_rollback_tests", - srcs: ["src/**/ApexRollbackTests.java"], + srcs: ["src/**/ApexRollbackTests.java", "src/**/ApexTestUtils.java"], libs: ["tradefed", "truth-prebuilt"], - static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"], + static_libs: ["module_test_util"], test_config: "apex-rollback-tests.xml", test_suites: ["general-tests"], @@ -144,17 +265,23 @@ java_test_host { } java_test_host { + name: "module_test_utils_tests", + srcs: ["src/**/ModuleTestUtilsTest.java"], + libs: ["tradefed", "truth-prebuilt"], + static_libs: ["module_test_util"], + test_config: "module-test-utils-tests.xml", + test_suites: ["general-tests"], + data: [":com.android.apex.cts.shim.v2_prebuilt"], +} + +java_test_host { name: "apexd_host_tests", - srcs: [ - "src/**/ApexdHostTest.java", - ":apex-info-list", - ], + srcs: ["src/**/ApexdHostTest.java"], libs: ["tradefed"], static_libs: [ + "module_test_util", "truth-prebuilt", "apex_manifest_proto_java", - "frameworks-base-hostutils", - "cts-install-lib-host" ], test_config: "apexd-host-tests.xml", test_suites: ["general-tests"], @@ -165,67 +292,5 @@ java_test_host { ":apex.apexd_test_v3", ":com.android.apex.cts.shim.v2_prebuilt", ":com.android.apex.cts.shim.v2_no_pb", - ":com.android.apex.cts.shim.v2_additional_file_prebuilt", - ], -} - -java_test_host { - name: "sharedlibs_host_tests", - srcs: [ - "src/**/SharedLibsApexTest.java" - ], - libs: ["tradefed"], - java_resources: [ - ":com.android.apex.test.bar_stripped.v1.libvX_prebuilt", - ":com.android.apex.test.bar_stripped.v2.libvY_prebuilt", - ":com.android.apex.test.bar.v1.libvX_prebuilt", - ":com.android.apex.test.bar.v2.libvY_prebuilt", - ":com.android.apex.test.baz_stripped.v1.libvX_prebuilt", - ":com.android.apex.test.foo_stripped.v1.libvX_prebuilt", - ":com.android.apex.test.foo_stripped.v2.libvY_prebuilt", - ":com.android.apex.test.foo.v1.libvX_prebuilt", - ":com.android.apex.test.foo.v2.libvY_prebuilt", - ":com.android.apex.test.pony_stripped.v1.libvZ_prebuilt", - ":com.android.apex.test.pony.v1.libvZ_prebuilt", - ":com.android.apex.test.sharedlibs_generated.v1.libvX_prebuilt", - ":com.android.apex.test.sharedlibs_generated.v2.libvY_prebuilt", - ":com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ_prebuilt", ], - static_libs: [ - "compatibility-host-util", - "cts-install-lib-host", - "frameworks-base-hostutils", - "truth-prebuilt" - ], - test_config: "shared-libs-apex-tests.xml", - test_suites: ["general-tests"], -} - -java_test_host { - name: "apex_compression_platform_tests", - srcs: ["src/**/ApexCompressionTests.java"], - libs: ["tradefed", "truth-prebuilt"], - static_libs: ["cts-install-lib-host", "testng"], - test_config: "apex_compression_platform_tests.xml", - test_suites: ["general-tests"], - data: [ - ":com.android.apex.compressed.v1", - ":com.android.apex.compressed.v1_original", - ":com.android.apex.compressed.v1_different_digest", - ":com.android.apex.compressed.v2", - ":gen_manifest_mismatch_compressed_apex_v2", - ":apex_compression_tests_app", - ], -} - -android_test_helper_app { - name: "apex_compression_tests_app", - manifest: "app/AndroidManifest.xml", - srcs: ["app/src/**/*.java"], - static_libs: ["androidx.test.rules", "cts-install-lib", "cts-rollback-lib", "testng"], - test_suites: ["general-tests"], - java_resources: [ - ":com.android.apex.compressed.v1_original", - ":com.android.apex.compressed.v2_original", - ] } diff --git a/tests/TEST_MAPPING b/tests/TEST_MAPPING index d3177a80..cd488fb2 100644 --- a/tests/TEST_MAPPING +++ b/tests/TEST_MAPPING @@ -1,25 +1,49 @@ { "presubmit": [ { + "name": "apex_rollback_tests" + }, + { "name": "apex_targetprep_tests" }, { - "name": "timezone_data_e2e_tests" + "name": "adbd_e2e_tests" }, { - "name": "CtsApexSharedLibrariesTestCases" - } - ], - "presubmit-large": [ - // TODO(b/190710217): uncomment this. - //{ - // "name": "apex_rollback_tests" - //}, + "name": "apexd_host_tests" + }, + { + "name": "conscrypt_e2e_tests" + }, + { + "name": "extservices_e2e_tests" + }, + { + "name": "neuralnetworks_e2e_tests" + }, + { + "name": "permission_e2e_tests" + }, { "name": "sdkextensions_e2e_tests" }, { - "name": "sharedlibs_host_tests" + "name": "statsd_e2e_tests" + }, + { + "name": "timezone_data_e2e_tests" + }, + { + "name": "ipsec_e2e_tests" + }, + { + "name": "wifi_e2e_tests" + }, + { + "name": "mediaprovider_e2e_tests" + }, + { + "name": "cellbroadcast_e2e_tests" } ], "postsubmit": [ @@ -34,16 +58,57 @@ "name": "media_swcodec_e2e_tests", "keywords": ["primary-device"] }, + // The following changes are in post-submit to restrict to physical + // devices (currently userspace reboot fails on cuttlefish). + // TODO(b/147726967): Remove when Userspace reboot works on cuttlefish { - "name": "apex_compression_platform_tests" + "name": "adbd_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "conscrypt_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "extservices_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "neuralnetworks_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "permission_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "sdkextensions_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "timezone_data_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "ipsec_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "wifi_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "mediaprovider_e2e_tests", + "keywords": ["primary-device"] + }, + { + "name": "cellbroadcast_e2e_tests", + "keywords": ["primary-device"] } ], "imports": [ { "path": "cts/hostsidetests/stagedinstall" - }, - { - "path": "frameworks/base/tests/StagedInstallTest" } ] } diff --git a/tests/adbd-e2e-tests.xml b/tests/adbd-e2e-tests.xml new file mode 100644 index 00000000..a144d834 --- /dev/null +++ b/tests/adbd-e2e-tests.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Configuration for adbd module e2e tests"> + <option name="test-suite-tag" value="adbd_e2e_tests" /> + <option name="test-suite-tag" value="apct" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="adbd_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.adbd.apex" /> + </test> +</configuration> diff --git a/tests/apex_compression_platform_tests.xml b/tests/apex_compression_platform_tests.xml deleted file mode 100644 index a837fbad..00000000 --- a/tests/apex_compression_platform_tests.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Runs the apex compression test cases"> - <option name="test-suite-tag" value="apex_compression_platform_tests" /> - <option name="test-suite-tag" value="apct" /> - <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="apex_compression_tests_app.apk" /> - </target_preparer> - <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> - <option name="run-command" value="setprop persist.rollback.is_test 1" /> - <option name="teardown-command" value="setprop persist.rollback.is_test 0" /> - </target_preparer> - <test class="com.android.tradefed.testtype.HostTest" > - <option name="class" - value="com.android.tests.apex.host.ApexCompressionTests" /> - </test> -</configuration> diff --git a/tests/app/AndroidManifest.xml b/tests/app/AndroidManifest.xml deleted file mode 100644 index 0644ba7e..00000000 --- a/tests/app/AndroidManifest.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.tests.apex.app" > - - <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> - <application> - <receiver android:name="com.android.cts.install.lib.LocalIntentSender" - android:exported="true" /> - <uses-library android:name="android.test.runner" /> - </application> - - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.tests.apex.app" - android:label="ApexCompression Test"/> - -</manifest> diff --git a/tests/app/src/com/android/tests/apex/app/ApexCompressionTests.java b/tests/app/src/com/android/tests/apex/app/ApexCompressionTests.java deleted file mode 100644 index a97293ce..00000000 --- a/tests/app/src/com/android/tests/apex/app/ApexCompressionTests.java +++ /dev/null @@ -1,164 +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 com.android.tests.apex.app; - -import static com.google.common.truth.Truth.assertThat; - -import android.Manifest; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.rollback.RollbackInfo; - -import androidx.test.InstrumentationRegistry; - -import com.android.cts.install.lib.Install; -import com.android.cts.install.lib.InstallUtils; -import com.android.cts.install.lib.TestApp; -import com.android.cts.rollback.lib.RollbackUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -@RunWith(JUnit4.class) -public class ApexCompressionTests { - private static final String COMPRESSED_APEX_PACKAGE_NAME = "com.android.apex.compressed"; - private final Context mContext = InstrumentationRegistry.getContext(); - private final PackageManager mPm = mContext.getPackageManager(); - - private static final TestApp UNCOMPRESSED_APEX_V1 = new TestApp( - "TestAppUncompressedApexV1", COMPRESSED_APEX_PACKAGE_NAME, 2, /*isApex*/ true, - "com.android.apex.compressed.v1_original.apex"); - private static final TestApp UNCOMPRESSED_APEX_V2 = new TestApp( - "TestAppUncompressedApexV2", COMPRESSED_APEX_PACKAGE_NAME, 2, /*isApex*/ true, - "com.android.apex.compressed.v2_original.apex"); - - @Before - public void adoptShellPermissions() { - androidx.test.platform.app.InstrumentationRegistry - .getInstrumentation() - .getUiAutomation() - .adoptShellPermissionIdentity( - Manifest.permission.INSTALL_PACKAGES, - Manifest.permission.DELETE_PACKAGES, - Manifest.permission.TEST_MANAGE_ROLLBACKS); - } - - @After - public void dropShellPermissions() { - androidx.test.platform.app.InstrumentationRegistry - .getInstrumentation() - .getUiAutomation() - .dropShellPermissionIdentity(); - } - - @Test - public void testDecompressedApexIsConsideredFactory() throws Exception { - // Only retrieve active apex package - PackageInfo pi = mPm.getPackageInfo( - COMPRESSED_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); - assertThat(pi).isNotNull(); - assertThat(pi.isApex).isTrue(); - assertThat(pi.packageName).isEqualTo(COMPRESSED_APEX_PACKAGE_NAME); - assertThat(pi.getLongVersionCode()).isEqualTo(1); - boolean isFactoryPackage = (pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - assertThat(isFactoryPackage).isTrue(); - } - - @Test - public void testUnusedDecompressedApexIsCleanedUp_HigherVersion() throws Exception { - Install.single(UNCOMPRESSED_APEX_V2).setStaged().commit(); - } - - @Test - public void testUnusedDecompressedApexIsCleanedUp_SameVersion() throws Exception { - Install.single(UNCOMPRESSED_APEX_V1).setStaged().commit(); - } - - - @Test - public void testCapexToApexSwitch() throws Exception { - // Only retrieve active apex package - PackageInfo pi = mPm.getPackageInfo( - COMPRESSED_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); - assertThat(pi).isNotNull(); - assertThat(pi.isApex).isTrue(); - assertThat(pi.packageName).isEqualTo(COMPRESSED_APEX_PACKAGE_NAME); - assertThat(pi.getLongVersionCode()).isEqualTo(1); - boolean isFactoryPackage = (pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - assertThat(isFactoryPackage).isTrue(); - assertThat(pi.applicationInfo.sourceDir).startsWith("/system/apex"); - } - - @Test - public void testDecompressedApexVersionAlwaysHasSameVersionAsCapex() throws Exception { - // Only retrieve active apex package - PackageInfo pi = mPm.getPackageInfo( - COMPRESSED_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); - assertThat(pi).isNotNull(); - assertThat(pi.isApex).isTrue(); - assertThat(pi.packageName).isEqualTo(COMPRESSED_APEX_PACKAGE_NAME); - assertThat(pi.getLongVersionCode()).isEqualTo(1); - boolean isFactoryPackage = (pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - assertThat(isFactoryPackage).isTrue(); - } - - @Test - public void testCompressedApexCanBeRolledBack_Commit() throws Exception { - // Verify before updating - PackageInfo pi = mPm.getPackageInfo( - COMPRESSED_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); - assertThat(pi.getLongVersionCode()).isEqualTo(1); - boolean isFactoryPackage = (pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - assertThat(isFactoryPackage).isTrue(); - assertThat(pi.applicationInfo.sourceDir).startsWith("/data/apex/decompressed/"); - - Install.single(UNCOMPRESSED_APEX_V2).setStaged().setEnableRollback().commit(); - } - - @Test - public void testCompressedApexCanBeRolledBack_Rollback() throws Exception { - // Verify before rollback - PackageInfo pi = mPm.getPackageInfo( - COMPRESSED_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); - assertThat(pi.getLongVersionCode()).isEqualTo(2); - boolean isFactoryPackage = (pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - assertThat(isFactoryPackage).isFalse(); - assertThat(pi.applicationInfo.sourceDir).startsWith("/data/apex/active/"); - - // Trigger rollback - RollbackInfo available = RollbackUtils.getAvailableRollback(COMPRESSED_APEX_PACKAGE_NAME); - RollbackUtils.rollback(available.getRollbackId(), UNCOMPRESSED_APEX_V2); - RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId()); - InstallUtils.waitForSessionReady(committed.getCommittedSessionId()); - } - - @Test - public void testCompressedApexCanBeRolledBack_Verify() throws Exception { - // Verify rollback worked - PackageInfo pi = mPm.getPackageInfo( - COMPRESSED_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); - assertThat(pi.getLongVersionCode()).isEqualTo(1); - boolean isFactoryPackage = (pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - assertThat(isFactoryPackage).isFalse(); - assertThat(pi.applicationInfo.sourceDir).startsWith("/data/apex/active/"); - } -} diff --git a/tests/cellbroadcast-e2e-tests.xml b/tests/cellbroadcast-e2e-tests.xml new file mode 100644 index 00000000..368a88fa --- /dev/null +++ b/tests/cellbroadcast-e2e-tests.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Config for Cellbroadcast module e2e test cases"> + <option name="test-suite-tag" value="cellbroadcast_e2e_tests" /> + <option name="test-suite-tag" value="apct" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="cellbroadcast_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.cellbroadcast.apex" /> + </test> +</configuration>
\ No newline at end of file diff --git a/tests/conscrypt-e2e-tests.xml b/tests/conscrypt-e2e-tests.xml new file mode 100644 index 00000000..77f781a2 --- /dev/null +++ b/tests/conscrypt-e2e-tests.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Config for Conscrypt module e2e test cases"> + <option name="test-suite-tag" value="conscrypt_e2e_tests" /> + <option name="test-suite-tag" value="apct" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="conscrypt_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.conscrypt.apex" /> + </test> +</configuration> + diff --git a/tests/extservices-e2e-tests.xml b/tests/extservices-e2e-tests.xml new file mode 100644 index 00000000..c45392e6 --- /dev/null +++ b/tests/extservices-e2e-tests.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for extservices apex e2e testing"> + <option name="test-suite-tag" value="extservices_e2e_tests" /> + <option name="test-suite-tag" value="apct" /> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="extservices_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.extservices.apex" /> + </test> +</configuration> diff --git a/tests/ipsec-e2e-tests.xml b/tests/ipsec-e2e-tests.xml new file mode 100644 index 00000000..0030bd34 --- /dev/null +++ b/tests/ipsec-e2e-tests.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Config for ipsec module e2e testing"> + <option name="test-suite-tag" value="ipsec_e2e_test" /> + <option name="test-suite-tag" value="apct" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="ipsec_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.ipsec.apex" /> + </test> +</configuration> + diff --git a/tests/mediaprovider-e2e-tests.xml b/tests/mediaprovider-e2e-tests.xml new file mode 100644 index 00000000..e1212622 --- /dev/null +++ b/tests/mediaprovider-e2e-tests.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Config for MediaProvider module e2e test cases"> + <option name="test-suite-tag" value="mediaprovider_e2e_tests" /> + <option name="test-suite-tag" value="apct" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="mediaprovider_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.mediaprovider.apex" /> + </test> +</configuration> diff --git a/tests/module-test-utils-tests.xml b/tests/module-test-utils-tests.xml new file mode 100644 index 00000000..6337ab92 --- /dev/null +++ b/tests/module-test-utils-tests.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<configuration description="Unit tests for ModuleTestUtils"> + <option name="test-suite-tag" value="module_test_utils_tests" /> + <option name="test-suite-tag" value="apct" /> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="module_test_utils_tests.jar" /> + </test> +</configuration> diff --git a/tests/native/.clang-format b/tests/native/.clang-format deleted file mode 100644 index f6cb8ad9..00000000 --- a/tests/native/.clang-format +++ /dev/null @@ -1 +0,0 @@ -BasedOnStyle: Google diff --git a/tests/native/Android.bp b/tests/native/Android.bp deleted file mode 100644 index 79152223..00000000 --- a/tests/native/Android.bp +++ /dev/null @@ -1,55 +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 { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -cc_test { - name: "CtsApexSharedLibrariesTestCases", - test_suites: [ - "cts", - "device-tests", - "mts", - ], - compile_multilib: "both", - multilib: { - lib32: { - suffix: "32", - }, - lib64: { - suffix: "64", - }, - }, - - shared_libs: [ - "libbase", - "liblog", - ], - - static_libs: [ - "libc++fs", - "libfs_mgr", - ], - - srcs: [ - "apex_shared_libraries_test.cpp", - ], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - ], -} diff --git a/tests/native/AndroidTest.xml b/tests/native/AndroidTest.xml deleted file mode 100644 index fa189d5e..00000000 --- a/tests/native/AndroidTest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2021 The Android Open Source Project - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Config for CTS apex_shared_libraries test cases"> - <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="systems" /> - <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> - <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> - <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> - <option name="cleanup" value="true" /> - <option name="push" value="CtsApexSharedLibrariesTestCases->/data/local/tmp/CtsApexSharedLibrariesTestCases" /> - <option name="append-bitness" value="true" /> - </target_preparer> - <test class="com.android.tradefed.testtype.GTest" > - <option name="native-test-device-path" value="/data/local/tmp" /> - <option name="module-name" value="CtsApexSharedLibrariesTestCases" /> - <option name="runtime-hint" value="65s" /> - </test> -</configuration> diff --git a/tests/native/apex_shared_libraries_test.cpp b/tests/native/apex_shared_libraries_test.cpp deleted file mode 100644 index 9f536dc6..00000000 --- a/tests/native/apex_shared_libraries_test.cpp +++ /dev/null @@ -1,142 +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. - */ -#define LOG_TAG "apex_shared_libraries_test" - -#include <android-base/logging.h> -#include <android-base/properties.h> -#include <android-base/scopeguard.h> -#include <android-base/strings.h> -#include <dlfcn.h> -#include <fstab/fstab.h> -#include <gtest/gtest.h> -#include <link.h> - -#include <filesystem> -#include <fstream> -#include <string> - -using android::base::GetBoolProperty; -using android::base::Split; -using android::base::StartsWith; -using android::fs_mgr::Fstab; -using android::fs_mgr::ReadFstabFromFile; - -namespace fs = std::filesystem; - -static constexpr const char* kApexRoot = "/apex"; -static constexpr const char* kApexSharedLibsRoot = "/apex/sharedlibs"; - -TEST(apex_shared_libraries, symlink_libraries_loadable) { - if (!GetBoolProperty("ro.apex.updatable", false)) { - GTEST_SKIP() << "Skipping test because device doesn't support APEX"; - } - - Fstab fstab; - ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &fstab)); - - // Traverse mount points to identify apexs. - for (auto& entry : fstab) { - if (fs::path(entry.mount_point).parent_path() != kApexRoot) { - continue; - } - - // Focus on "active" apexs. - if (entry.mount_point.find('@') != std::string::npos) { - continue; - } - std::string dev_file = fs::path(entry.blk_device).filename(); - - // Filter out any mount irrelevant (e.g. tmpfs) - if (!StartsWith(dev_file, "loop") && !StartsWith(dev_file, "dm-")) { - continue; - } - -#if !defined(__LP64__) - auto lib = fs::path(entry.mount_point) / "lib"; -#else // !__LP64__ - auto lib = fs::path(entry.mount_point) / "lib64"; -#endif // !__LP64__ - if (!fs::is_directory(lib)) { - continue; - } - for (auto& p : fs::directory_iterator(lib)) { - std::error_code ec; - if (!fs::is_symlink(p, ec)) { - continue; - } - - // We are only checking libraries pointing at a location inside - // /apex/sharedlibs. - auto target = fs::read_symlink(p.path(), ec); - if (ec || !StartsWith(target.string(), kApexSharedLibsRoot)) { - continue; - } - - // Symlink validity check. - auto dest = fs::canonical(p.path(), ec); - EXPECT_FALSE(ec) << "Failed to resolve " << p.path() << " (symlink to " - << target << "): " << ec; - if (ec) { - continue; - } - - // Library loading validity check. - dlerror(); // Clear any pending errors. - void* handle = dlopen(p.path().c_str(), RTLD_NOW); - EXPECT_TRUE(handle != nullptr) << dlerror(); - if (handle == nullptr) { - continue; - } - auto guard = android::base::make_scope_guard([&]() { dlclose(handle); }); - - // Check that library is loaded and pointing to the realpath of the - // library. - auto dl_callback = [](dl_phdr_info* info, size_t /* size */, void* data) { - auto dest = *static_cast<fs::path*>(data); - if (info->dlpi_name == nullptr) { - // This is linker imposing as libdl.so - skip it - return 0; - } - int j; - for (j = 0; j < info->dlpi_phnum; j++) { - void* addr = (void*)(info->dlpi_addr + info->dlpi_phdr[j].p_vaddr); - Dl_info dl_info; - int rc = dladdr(addr, &dl_info); - if (rc == 0) { - continue; - } - if (dl_info.dli_fname) { - auto libpath = fs::path(dl_info.dli_fname); - if (libpath == dest) { - // Library found! - return 1; - } - } - } - - return 0; - }; - bool found = (dl_iterate_phdr(dl_callback, &dest) == 1); - EXPECT_TRUE(found) << "Error verifying library symlink " << p.path() - << " which points to " << target - << " which resolves to file " << dest; - if (found) { - LOG(INFO) << "Verified that " << p.path() - << " correctly loads as library " << dest; - } - } - } -} diff --git a/tests/neuralnetworks-e2e-tests.xml b/tests/neuralnetworks-e2e-tests.xml new file mode 100644 index 00000000..63c38a68 --- /dev/null +++ b/tests/neuralnetworks-e2e-tests.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Config for neuralnetworks module e2e test cases"> + <option name="test-suite-tag" value="neuralnetworks_e2e_tests" /> + <option name="test-suite-tag" value="apct" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="neuralnetworks_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.neuralnetworks.apex" /> + </test> +</configuration> + diff --git a/tests/permission-e2e-tests.xml b/tests/permission-e2e-tests.xml new file mode 100644 index 00000000..e1605abe --- /dev/null +++ b/tests/permission-e2e-tests.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<configuration description="Configuration for permission module e2e test cases"> + <option name="test-suite-tag" value="permission_e2e_tests" /> + <option name="test-suite-tag" value="apct" /> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="permission_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.permission.apex" /> + </test> +</configuration> diff --git a/tests/sample_prefer32_binary.cc b/tests/sample_prefer32_binary.cc new file mode 100644 index 00000000..76e81970 --- /dev/null +++ b/tests/sample_prefer32_binary.cc @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tests/shared-libs-apex-tests.xml b/tests/shared-libs-apex-tests.xml deleted file mode 100644 index d41b8916..00000000 --- a/tests/shared-libs-apex-tests.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Runs the shared libs apex host side test cases"> - <option name="test-suite-tag" value="sharedlibs_host_tests" /> - <option name="test-suite-tag" value="apct" /> - <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> - <test class="com.android.tradefed.testtype.HostTest" > - <option name="jar" value="sharedlibs_host_tests.jar" /> - </test> -</configuration> diff --git a/tests/testdata/sharedlibs/build/include/sharedlibstest.h b/tests/src/com/android/tests/apex/AdbdHostTest.java index a2f0cdc9..445ca88b 100644 --- a/tests/testdata/sharedlibs/build/include/sharedlibstest.h +++ b/tests/src/com/android/tests/apex/AdbdHostTest.java @@ -14,15 +14,17 @@ * limitations under the License. */ -#ifndef SHAREDLIBSTEST_H_ -#define SHAREDLIBSTEST_H_ +package com.android.tests.apex; -#include <string> +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -namespace sharedlibstest { +import org.junit.Ignore; +import org.junit.runner.RunWith; -std::string getSharedLibsTestFingerprint(); - -} // namespace sharedlibstest - -#endif // SHAREDLIBSTEST_H_ +/** + * Test to check if Apex can be staged, activated and uninstalled successfully. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +@Ignore // TODO(b/148759193): remove when test is fixed +public class AdbdHostTest extends ApexE2EBaseHostTest { +} diff --git a/tests/src/com/android/tests/apex/ApexE2EBaseHostTest.java b/tests/src/com/android/tests/apex/ApexE2EBaseHostTest.java index e0871df5..0812c6ac 100644 --- a/tests/src/com/android/tests/apex/ApexE2EBaseHostTest.java +++ b/tests/src/com/android/tests/apex/ApexE2EBaseHostTest.java @@ -21,10 +21,9 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assume.assumeTrue; -import android.cts.install.lib.host.InstallUtilsHost; import android.platform.test.annotations.RequiresDevice; -import com.android.tests.rollback.host.AbandonSessionsRule; +import com.android.tests.util.ModuleTestUtils; import com.android.tradefed.config.Option; import com.android.tradefed.config.Option.Importance; import com.android.tradefed.device.ITestDevice.ApexInfo; @@ -34,7 +33,6 @@ import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import org.junit.After; import org.junit.Assert; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import java.io.File; @@ -54,11 +52,8 @@ public abstract class ApexE2EBaseHostTest extends BaseHostJUnit4Test { private static final String USERSPACE_REBOOT_SUPPORTED_PROP = "init.userspace_reboot.is_supported"; - // Protected so that derived tests can have access to test utils automatically - protected final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); - - @Rule - public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this); + /* protected so that derived tests can have access to test utils automatically */ + protected final ModuleTestUtils mUtils = new ModuleTestUtils(this); @Option(name = OPTION_APEX_FILE_NAME, description = "The file name of the apex module.", @@ -69,13 +64,15 @@ public abstract class ApexE2EBaseHostTest extends BaseHostJUnit4Test { @Before public void setUp() throws Exception { - assumeTrue("Updating APEX is not supported", mHostUtils.isApexUpdateSupported()); + assumeTrue("Updating APEX is not supported", mUtils.isApexUpdateSupported()); + mUtils.abandonActiveStagedSession(); uninstallAllApexes(); } @After public void tearDown() throws Exception { - assumeTrue("Updating APEX is not supported", mHostUtils.isApexUpdateSupported()); + assumeTrue("Updating APEX is not supported", mUtils.isApexUpdateSupported()); + mUtils.abandonActiveStagedSession(); uninstallAllApexes(); } @@ -108,23 +105,35 @@ public abstract class ApexE2EBaseHostTest extends BaseHostJUnit4Test { private void uninstallAllApexes() throws Exception { for (String filename : getAllApexFilenames()) { - ApexInfo apex = mHostUtils.getApexInfo(mHostUtils.getTestFile(filename)); + ApexInfo apex = mUtils.getApexInfo(mUtils.getTestFile(filename)); uninstallApex(apex.name); } } protected final ApexInfo installApex(String filename) throws Exception { - File testAppFile = mHostUtils.getTestFile(filename); + File testAppFile = mUtils.getTestFile(filename); - String installResult = mHostUtils.installStagedPackage(testAppFile); + String installResult = getDevice().installPackage(testAppFile, false, "--wait"); assertWithMessage("failed to install test app %s. Reason: %s", filename, installResult) .that(installResult).isNull(); - ApexInfo testApexInfo = mHostUtils.getApexInfo(testAppFile); + ApexInfo testApexInfo = mUtils.getApexInfo(testAppFile); Assert.assertNotNull(testApexInfo); return testApexInfo; } + protected final void installApexes(String... filenames) throws Exception { + // We don't use the installApex method from the super class here, because that won't install + // the two apexes into the same session. + String[] args = new String[filenames.length + 1]; + args[0] = "install-multi-package"; + for (int i = 0; i < filenames.length; i++) { + args[i + 1] = mUtils.getTestFile(filenames[i]).getAbsolutePath(); + } + String stdout = getDevice().executeAdbCommand(args); + assertThat(stdout).isNotNull(); + } + protected final void reboot(boolean userspaceReboot) throws Exception { if (userspaceReboot) { assertThat(getDevice().setProperty("test.userspace_reboot.requested", "1")).isTrue(); diff --git a/tests/src/com/android/tests/apex/ApexRollbackTests.java b/tests/src/com/android/tests/apex/ApexRollbackTests.java index 68f82ca6..6cf54e1f 100644 --- a/tests/src/com/android/tests/apex/ApexRollbackTests.java +++ b/tests/src/com/android/tests/apex/ApexRollbackTests.java @@ -22,18 +22,16 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; -import android.cts.install.lib.host.InstallUtilsHost; - -import com.android.tests.rollback.host.AbandonSessionsRule; -import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tests.util.ModuleTestUtils; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.device.ITestDevice.ApexInfo; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.util.CommandResult; +import com.android.tradefed.util.CommandStatus; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,20 +44,13 @@ import java.util.Set; */ @RunWith(DeviceJUnit4ClassRunner.class) public class ApexRollbackTests extends BaseHostJUnit4Test { - private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); - @Rule - public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this); - - private boolean mWasAdbRoot = false; + private final ModuleTestUtils mUtils = new ModuleTestUtils(this); @Before public void setUp() throws Exception { - mHostUtils.uninstallShimApexIfNecessary(); + mUtils.abandonActiveStagedSession(); + mUtils.uninstallShimApexIfNecessary(); resetProperties(); - mWasAdbRoot = getDevice().isAdbRoot(); - if (!mWasAdbRoot) { - assumeTrue("Requires root", getDevice().enableAdbRoot()); - } } /** @@ -68,11 +59,9 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @After public void tearDown() throws Exception { - mHostUtils.uninstallShimApexIfNecessary(); + mUtils.abandonActiveStagedSession(); + mUtils.uninstallShimApexIfNecessary(); resetProperties(); - if (!mWasAdbRoot) { - getDevice().disableAdbRoot(); - } } private void resetProperties() throws Exception { @@ -92,15 +81,9 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @Test public void testAutomaticBootLoopRecovery() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - ITestDevice device = getDevice(); - // Skip this test if there is already crashing process on device - boolean hasCrashingProcess = - device.getBooleanProperty("sys.init.updatable_crashing", false); - String crashingProcess = device.getProperty("sys.init.updatable_crashing_process_name"); - assumeFalse( - "Device already has a crashing process: " + crashingProcess, hasCrashingProcess); - File apexFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); + assumeTrue("Device does not support updating APEX", mUtils.isApexUpdateSupported()); + + File apexFile = mUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); // To simulate an apex update that causes a boot loop, we install a // trigger_watchdog.rc file that arranges for a trigger_watchdog.sh @@ -109,9 +92,10 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { // persist.debug.trigger_watchdog.apex is installed. If so, // trigger_watchdog.sh repeatedly kills the system server causing a // boot loop. + ITestDevice device = getDevice(); assertThat(device.setProperty("persist.debug.trigger_watchdog.apex", "com.android.apex.cts.shim@2")).isTrue(); - String error = mHostUtils.installStagedPackage(apexFile); + String error = device.installPackage(apexFile, false, "--wait"); assertThat(error).isNull(); String sessionIdToCheck = device.executeShellCommand("pm get-stagedsessions --only-ready " @@ -145,10 +129,10 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @Test public void testSessionNotRevertedWithCheckpointingDisabled() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeFalse("Fs checkpointing is enabled", mHostUtils.isCheckpointSupported()); + assumeTrue("Device does not support updating APEX", mUtils.isApexUpdateSupported()); + assumeFalse("Fs checkpointing is enabled", supportsFsCheckpointing()); - File apexFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); + File apexFile = mUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); ITestDevice device = getDevice(); assertThat(device.setProperty("persist.debug.trigger_reboot_after_activation", @@ -156,7 +140,7 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { assertThat(device.setProperty("debug.trigger_reboot_once_after_activation", "1")).isTrue(); - String error = mHostUtils.installStagedPackage(apexFile); + String error = device.installPackage(apexFile, false, "--wait"); assertThat(error).isNull(); String sessionIdToCheck = device.executeShellCommand("pm get-stagedsessions --only-ready " @@ -169,8 +153,12 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { ApexInfo ctsShimV1 = new ApexInfo("com.android.apex.cts.shim", 1L); ApexInfo ctsShimV2 = new ApexInfo("com.android.apex.cts.shim", 2L); - String stagedSessionInfo = getStagedSession(sessionIdToCheck); - assertThat(stagedSessionInfo).contains("isApplied = true"); + String stagedSessionsInfo = device.executeShellCommand("pm get-stagedsessions"); + for (String line: stagedSessionsInfo.split("[\\r\\n]+")) { + if (line.contains(sessionIdToCheck)) { + assertThat(line).contains("isApplied = true"); + } + } Set<ApexInfo> activatedApexes = device.getActiveApexes(); assertThat(activatedApexes).contains(ctsShimV2); @@ -183,17 +171,17 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @Test public void testCheckpointingRevertsSession() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Device doesn't support fs checkpointing", mHostUtils.isCheckpointSupported()); + assumeTrue("Device does not support updating APEX", mUtils.isApexUpdateSupported()); + assumeTrue("Device doesn't support fs checkpointing", supportsFsCheckpointing()); - File apexFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); + File apexFile = mUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); ITestDevice device = getDevice(); assertThat(device.setProperty("persist.debug.trigger_reboot_after_activation", "com.android.apex.cts.shim@2.apex")).isTrue(); assertThat(device.setProperty("persist.debug.trigger_reboot_twice_after_activation", "1")).isTrue(); - String error = mHostUtils.installStagedPackage(apexFile); + String error = device.installPackage(apexFile, false, "--wait"); assertThat(error).isNull(); String sessionIdToCheck = device.executeShellCommand("pm get-stagedsessions --only-ready " @@ -207,8 +195,12 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { ApexInfo ctsShimV1 = new ApexInfo("com.android.apex.cts.shim", 1L); ApexInfo ctsShimV2 = new ApexInfo("com.android.apex.cts.shim", 2L); - String stagedSessionInfo = getStagedSession(sessionIdToCheck); - assertThat(stagedSessionInfo).contains("isFailed = true"); + String stagedSessionsInfo = device.executeShellCommand("pm get-stagedsessions"); + for (String line: stagedSessionsInfo.split("[\\r\\n]+")) { + if (line.contains(sessionIdToCheck)) { + assertThat(line).contains("isFailed = true"); + } + } Set<ApexInfo> activatedApexes = device.getActiveApexes(); assertThat(activatedApexes).contains(ctsShimV1); @@ -221,17 +213,17 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @Test public void testRebootingOnceDoesNotRevertSession() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Device doesn't support fs checkpointing", mHostUtils.isCheckpointSupported()); + assumeTrue("Device does not support updating APEX", mUtils.isApexUpdateSupported()); + assumeTrue("Device doesn't support fs checkpointing", supportsFsCheckpointing()); - File apexFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); + File apexFile = mUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); ITestDevice device = getDevice(); assertThat(device.setProperty("persist.debug.trigger_reboot_after_activation", "com.android.apex.cts.shim@2.apex")).isTrue(); assertThat(device.setProperty("debug.trigger_reboot_once_after_activation", "1")).isTrue(); - String error = mHostUtils.installStagedPackage(apexFile); + String error = device.installPackage(apexFile, false, "--wait"); assertThat(error).isNull(); String sessionIdToCheck = device.executeShellCommand("pm get-stagedsessions --only-ready " @@ -245,8 +237,12 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { ApexInfo ctsShimV1 = new ApexInfo("com.android.apex.cts.shim", 1L); ApexInfo ctsShimV2 = new ApexInfo("com.android.apex.cts.shim", 2L); - String stagedSessionInfo = getStagedSession(sessionIdToCheck); - assertThat(stagedSessionInfo).contains("isApplied = true"); + String stagedSessionsInfo = device.executeShellCommand("pm get-stagedsessions"); + for (String line: stagedSessionsInfo.split("[\\r\\n]+")) { + if (line.contains(sessionIdToCheck)) { + assertThat(line).contains("isApplied = true"); + } + } Set<ApexInfo> activatedApexes = device.getActiveApexes(); assertThat(activatedApexes).contains(ctsShimV2); @@ -261,7 +257,7 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @Test public void testApexdDoesNotBootLoopDeviceIfThereIsNothingToRevert() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mUtils.isApexUpdateSupported()); // On next boot trigger setprop sys.init.updatable_crashing 1, which will trigger a // revert mechanism in apexd. Since there is nothing to revert, this should be a no-op // and device will boot successfully. @@ -280,16 +276,16 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @Test public void testFailingUserspaceReboot_doesNotRevertUpdate() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mUtils.isApexUpdateSupported()); assumeTrue("Device doesn't support userspace reboot", getDevice().getBooleanProperty("init.userspace_reboot.is_supported", false)); - assumeTrue("Device doesn't support fs checkpointing", mHostUtils.isCheckpointSupported()); + assumeTrue("Device doesn't support fs checkpointing", supportsFsCheckpointing()); - File apexFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); + File apexFile = mUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); // Simulate failure in userspace reboot by triggering a full reboot in the middle of the // boot sequence. assertThat(getDevice().setProperty("test.apex_revert_test_force_reboot", "1")).isTrue(); - String error = mHostUtils.installStagedPackage(apexFile); + String error = getDevice().installPackage(apexFile, false, "--wait"); assertWithMessage("Failed to stage com.android.apex.cts.shim.v2.apex : %s", error).that( error).isNull(); // After we reboot the device, apexd will apply the update @@ -309,17 +305,17 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @Test public void testUserspaceRebootFailedShutdownSequence_doesNotRevertUpdate() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mUtils.isApexUpdateSupported()); assumeTrue("Device doesn't support userspace reboot", getDevice().getBooleanProperty("init.userspace_reboot.is_supported", false)); - assumeTrue("Device doesn't support fs checkpointing", mHostUtils.isCheckpointSupported()); + assumeTrue("Device doesn't support fs checkpointing", supportsFsCheckpointing()); - File apexFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); + File apexFile = mUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); // Simulate failure in userspace reboot by triggering a full reboot in the middle of the // boot sequence. assertThat(getDevice().setProperty("test.apex_userspace_reboot_simulate_shutdown_failed", "1")).isTrue(); - String error = mHostUtils.installStagedPackage(apexFile); + String error = getDevice().installPackage(apexFile, false, "--wait"); assertWithMessage("Failed to stage com.android.apex.cts.shim.v2.apex : %s", error).that( error).isNull(); // After the userspace reboot started, we simulate it's failure by rebooting device during @@ -342,17 +338,17 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @Test public void testUserspaceRebootFailedRemount_revertsUpdate() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mUtils.isApexUpdateSupported()); assumeTrue("Device doesn't support userspace reboot", getDevice().getBooleanProperty("init.userspace_reboot.is_supported", false)); - assumeTrue("Device doesn't support fs checkpointing", mHostUtils.isCheckpointSupported()); + assumeTrue("Device doesn't support fs checkpointing", supportsFsCheckpointing()); - File apexFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); + File apexFile = mUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); // Simulate failure in userspace reboot by triggering a full reboot in the middle of the // boot sequence. assertThat(getDevice().setProperty("test.apex_userspace_reboot_simulate_remount_failed", "1")).isTrue(); - String error = mHostUtils.installStagedPackage(apexFile); + String error = getDevice().installPackage(apexFile, false, "--wait"); assertWithMessage("Failed to stage com.android.apex.cts.shim.v2.apex : %s", error).that( error).isNull(); // After we reboot the device, apexd will apply the update @@ -371,14 +367,14 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { */ @Test public void testBootCompletedCleanupHappensEvenWhenThereIsCrashingProcess() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mUtils.isApexUpdateSupported()); assumeTrue("Device requires root", getDevice().isAdbRoot()); try { // On next boot trigger setprop sys.init.updatable_crashing 1, which will trigger a // revert mechanism in apexd. Since there is nothing to revert, this should be a no-op // and device will boot successfully. getDevice().setProperty("persist.debug.trigger_updatable_crashing_for_testing", "1"); - assertThat(getDevice().pushFile(mHostUtils.getTestFile("apex.apexd_test_v2.apex"), + assertThat(getDevice().pushFile(mUtils.getTestFile("apex.apexd_test_v2.apex"), "/data/apex/active/apexd_test_v2.apex")).isTrue(); getDevice().reboot(); assertWithMessage("Timed out waiting for device to boot").that( @@ -390,84 +386,18 @@ public class ApexRollbackTests extends BaseHostJUnit4Test { ITestDevice.ApexInfo testApex = new ITestDevice.ApexInfo( "com.android.apex.cts.shim", 2L); assertThat(activeApexes).doesNotContain(testApex); - mHostUtils.waitForFileDeleted("/data/apex/active/apexd_test_v2.apex", + mUtils.waitForFileDeleted("/data/apex/active/apexd_test_v2.apex", Duration.ofMinutes(3)); } finally { getDevice().executeShellV2Command("rm /data/apex/active/apexd_test_v2.apex"); } } - /** - * Test reason for revert is properly logged during boot loops - */ - @Test - public void testReasonForRevertIsLoggedDuringBootloop() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Fs checkpointing is enabled", mHostUtils.isCheckpointSupported()); - - ITestDevice device = getDevice(); - // Skip this test if there is already crashing process on device - final boolean hasCrashingProcess = - device.getBooleanProperty("sys.init.updatable_crashing", false); - final String crashingProcess = - device.getProperty("sys.init.updatable_crashing_process_name"); - assumeFalse( - "Device already has a crashing process: " + crashingProcess, hasCrashingProcess); - final File apexFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); - - // To simulate an apex update that causes a boot loop, we install a - // trigger_watchdog.rc file that arranges for a trigger_watchdog.sh - // script to be run at boot. The trigger_watchdog.sh script checks if - // the apex version specified in the property - // persist.debug.trigger_watchdog.apex is installed. If so, - // trigger_watchdog.sh repeatedly kills the system server causing a - // boot loop. - assertThat(device.setProperty("persist.debug.trigger_watchdog.apex", - "com.android.apex.cts.shim@2")).isTrue(); - final String error = mHostUtils.installStagedPackage(apexFile); - assertThat(error).isNull(); - - final String sessionIdToCheck = device.executeShellCommand("pm get-stagedsessions " - + "--only-ready --only-parent --only-sessionid").trim(); - assertThat(sessionIdToCheck).isNotEmpty(); - - // After we reboot the device, we expect the device to go into boot - // loop from trigger_watchdog.sh. Native watchdog should detect and - // report the boot loop, causing apexd to roll back to the previous - // version of the apex and force reboot. When the device comes up - // after the forced reboot, trigger_watchdog.sh will see the different - // version of the apex and refrain from forcing a boot loop, so the - // device will be recovered. - device.reboot(); - - final ApexInfo ctsShimV1 = new ApexInfo("com.android.apex.cts.shim", 1L); - final ApexInfo ctsShimV2 = new ApexInfo("com.android.apex.cts.shim", 2L); - final Set<ApexInfo> activatedApexes = device.getActiveApexes(); - assertThat(activatedApexes).contains(ctsShimV1); - assertThat(activatedApexes).doesNotContain(ctsShimV2); - - // Assert that a session has failed with the expected reason - final String stagedSessionString = getStagedSession(sessionIdToCheck); - assertThat(stagedSessionString).contains("Session reverted due to crashing native process"); - } - String getStagedSession(String sessionId) throws DeviceNotAvailableException { - final String[] lines = getDevice().executeShellCommand( - "pm get-stagedsessions").split("\n"); - for (int i = 0; i < lines.length; i++) { - if (lines[i].startsWith("sessionId = " + sessionId + ";")) { - // Join all lines realted to this session - final StringBuilder result = new StringBuilder(lines[i]); - for (int j = i + 1; j < lines.length; j++) { - if (lines[j].startsWith("sessionId = ")) { - // A new session block has started - break; - } - result.append(lines[j]); - } - return result.toString(); - } - } - return ""; + private boolean supportsFsCheckpointing() throws Exception { + CommandResult result = getDevice().executeShellV2Command("sm supports-checkpoint"); + assertWithMessage("Failed to check if fs checkpointing is supported : %s", + result.getStderr()).that(result.getStatus()).isEqualTo(CommandStatus.SUCCESS); + return "true".equals(result.getStdout().trim()); } } diff --git a/tests/src/com/android/tests/apex/ApexdHostTest.java b/tests/src/com/android/tests/apex/ApexdHostTest.java index 2d72e9fd..fc1fa4be 100644 --- a/tests/src/com/android/tests/apex/ApexdHostTest.java +++ b/tests/src/com/android/tests/apex/ApexdHostTest.java @@ -21,27 +21,17 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assume.assumeTrue; -import android.cts.install.lib.host.InstallUtilsHost; - -import com.android.apex.ApexInfo; -import com.android.apex.XmlParser; -import com.android.tests.rollback.host.AbandonSessionsRule; +import com.android.tests.util.ModuleTestUtils; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; -import java.io.FileInputStream; import java.time.Duration; -import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; /** * Host side integration tests for apexd. @@ -51,36 +41,14 @@ public class ApexdHostTest extends BaseHostJUnit4Test { private static final String SHIM_APEX_PATH = "/system/apex/com.android.apex.cts.shim.apex"; - private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); - - @Rule - public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this); - - private boolean mWasAdbRoot = false; - - @Before - public void setUp() throws Exception { - mHostUtils.uninstallShimApexIfNecessary(); - mWasAdbRoot = getDevice().isAdbRoot(); - if (!mWasAdbRoot) { - assumeTrue("Device requires root", getDevice().enableAdbRoot()); - } - } - - @After - public void tearDown() throws Exception { - mHostUtils.uninstallShimApexIfNecessary(); - if (!mWasAdbRoot) { - getDevice().disableAdbRoot(); - } - } + private final ModuleTestUtils mTestUtils = new ModuleTestUtils(this); @Test public void testOrphanedApexIsNotActivated() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mTestUtils.isApexUpdateSupported()); assumeTrue("Device requires root", getDevice().isAdbRoot()); try { - assertThat(getDevice().pushFile(mHostUtils.getTestFile("apex.apexd_test_v2.apex"), + assertThat(getDevice().pushFile(mTestUtils.getTestFile("apex.apexd_test_v2.apex"), "/data/apex/active/apexd_test_v2.apex")).isTrue(); getDevice().reboot(); assertWithMessage("Timed out waiting for device to boot").that( @@ -89,7 +57,7 @@ public class ApexdHostTest extends BaseHostJUnit4Test { ITestDevice.ApexInfo testApex = new ITestDevice.ApexInfo( "com.android.apex.test_package", 2L); assertThat(activeApexes).doesNotContain(testApex); - mHostUtils.waitForFileDeleted("/data/apex/active/apexd_test_v2.apex", + mTestUtils.waitForFileDeleted("/data/apex/active/apexd_test_v2.apex", Duration.ofMinutes(3)); } finally { getDevice().executeShellV2Command("rm /data/apex/active/apexd_test_v2.apex"); @@ -97,11 +65,11 @@ public class ApexdHostTest extends BaseHostJUnit4Test { } @Test public void testApexWithoutPbIsNotActivated() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mTestUtils.isApexUpdateSupported()); assumeTrue("Device requires root", getDevice().isAdbRoot()); final String testApexFile = "com.android.apex.cts.shim.v2_no_pb.apex"; try { - assertThat(getDevice().pushFile(mHostUtils.getTestFile(testApexFile), + assertThat(getDevice().pushFile(mTestUtils.getTestFile(testApexFile), "/data/apex/active/" + testApexFile)).isTrue(); getDevice().reboot(); assertWithMessage("Timed out waiting for device to boot").that( @@ -110,7 +78,7 @@ public class ApexdHostTest extends BaseHostJUnit4Test { ITestDevice.ApexInfo testApex = new ITestDevice.ApexInfo( "com.android.apex.cts.shim", 2L); assertThat(activeApexes).doesNotContain(testApex); - mHostUtils.waitForFileDeleted("/data/apex/active/" + testApexFile, + mTestUtils.waitForFileDeleted("/data/apex/active/" + testApexFile, Duration.ofMinutes(3)); } finally { getDevice().executeShellV2Command("rm /data/apex/active/" + testApexFile); @@ -119,14 +87,14 @@ public class ApexdHostTest extends BaseHostJUnit4Test { @Test public void testRemountApex() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mTestUtils.isApexUpdateSupported()); assumeTrue("Device requires root", getDevice().isAdbRoot()); final File oldFile = getDevice().pullFile(SHIM_APEX_PATH); try { getDevice().remountSystemWritable(); // In case remount requires a reboot, wait for boot to complete. getDevice().waitForBootComplete(Duration.ofMinutes(3).toMillis()); - final File newFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); + final File newFile = mTestUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); // Stop framework getDevice().executeShellV2Command("stop"); // Push new shim APEX. This simulates adb sync. @@ -150,7 +118,7 @@ public class ApexdHostTest extends BaseHostJUnit4Test { @Test public void testApexWithoutPbIsNotActivated_ProductPartitionHasOlderVersion() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mTestUtils.isApexUpdateSupported()); assumeTrue("Device requires root", getDevice().isAdbRoot()); try { @@ -159,10 +127,10 @@ public class ApexdHostTest extends BaseHostJUnit4Test { assertWithMessage("Timed out waiting for device to boot").that( getDevice().waitForBootComplete(Duration.ofMinutes(2).toMillis())).isTrue(); - final File v1 = mHostUtils.getTestFile("apex.apexd_test.apex"); + final File v1 = mTestUtils.getTestFile("apex.apexd_test.apex"); getDevice().pushFile(v1, "/product/apex/apex.apexd_test.apex"); - final File v2_no_pb = mHostUtils.getTestFile("apex.apexd_test_v2_no_pb.apex"); + final File v2_no_pb = mTestUtils.getTestFile("apex.apexd_test_v2_no_pb.apex"); getDevice().pushFile(v2_no_pb, "/data/apex/active/apex.apexd_test_v2_no_pb.apex"); getDevice().reboot(); @@ -176,7 +144,7 @@ public class ApexdHostTest extends BaseHostJUnit4Test { "com.android.apex.test_package", 2L)); // v2_no_pb should be deleted - mHostUtils.waitForFileDeleted("/data/apex/active/apex.apexd_test_v2_no_pb.apex", + mTestUtils.waitForFileDeleted("/data/apex/active/apex.apexd_test_v2_no_pb.apex", Duration.ofMinutes(3)); } finally { getDevice().remountSystemWritable(); @@ -191,7 +159,7 @@ public class ApexdHostTest extends BaseHostJUnit4Test { @Test public void testApexWithoutPbIsNotActivated_ProductPartitionHasNewerVersion() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support updating APEX", mTestUtils.isApexUpdateSupported()); assumeTrue("Device requires root", getDevice().isAdbRoot()); try { @@ -200,10 +168,10 @@ public class ApexdHostTest extends BaseHostJUnit4Test { assertWithMessage("Timed out waiting for device to boot").that( getDevice().waitForBootComplete(Duration.ofMinutes(2).toMillis())).isTrue(); - final File v3 = mHostUtils.getTestFile("apex.apexd_test_v3.apex"); + final File v3 = mTestUtils.getTestFile("apex.apexd_test_v3.apex"); getDevice().pushFile(v3, "/product/apex/apex.apexd_test_v3.apex"); - final File v2_no_pb = mHostUtils.getTestFile("apex.apexd_test_v2_no_pb.apex"); + final File v2_no_pb = mTestUtils.getTestFile("apex.apexd_test_v2_no_pb.apex"); getDevice().pushFile(v2_no_pb, "/data/apex/active/apex.apexd_test_v2_no_pb.apex"); getDevice().reboot(); @@ -217,7 +185,7 @@ public class ApexdHostTest extends BaseHostJUnit4Test { "com.android.apex.test_package", 2L)); // v2_no_pb should be deleted - mHostUtils.waitForFileDeleted("/data/apex/active/apex.apexd_test_v2_no_pb.apex", + mTestUtils.waitForFileDeleted("/data/apex/active/apex.apexd_test_v2_no_pb.apex", Duration.ofMinutes(3)); } finally { getDevice().remountSystemWritable(); @@ -228,115 +196,4 @@ public class ApexdHostTest extends BaseHostJUnit4Test { getDevice().executeShellV2Command("rm /data/apex/active/apex.apexd_test_v2_no_pb.apex"); } } - - @Test - public void testApexInfoListIsValid() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Device requires root", getDevice().isAdbRoot()); - - try (FileInputStream fis = new FileInputStream( - getDevice().pullFile("/apex/apex-info-list.xml"))) { - // #1 Data got from apexd via binder - Set<ITestDevice.ApexInfo> fromApexd = getDevice().getActiveApexes(); - // #2 Data got from the xml file (key is the path) - Map<String, ApexInfo> fromXml = XmlParser.readApexInfoList(fis).getApexInfo().stream() - .collect(Collectors.toMap(ApexInfo::getModulePath, ai -> ai)); - - // Make sure that all items in #1 are also in #2 and they are identical - for (ITestDevice.ApexInfo ai : fromApexd) { - ApexInfo apexFromXml = fromXml.get(ai.sourceDir); - assertWithMessage("APEX (" + ai.toString() + ") is not found in the list") - .that(apexFromXml).isNotNull(); - assertWithMessage("Version mismatch for APEX (" + ai.toString() + ")") - .that(ai.versionCode).isEqualTo(apexFromXml.getVersionCode()); - assertWithMessage("APEX (" + ai.toString() + ") is not active") - .that(apexFromXml.getIsActive()).isTrue(); - } - } - } - - /** - * Test to verify that the state of a staged session does not change if apexd is stopped and - * restarted while a session is staged. - */ - @Test - public void testApexSessionStateUnchangedBeforeReboot() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Device requires root", getDevice().isAdbRoot()); - - File apexFile = mHostUtils.getTestFile("com.android.apex.cts.shim.v2.apex"); - String error = mHostUtils.installStagedPackage(apexFile); - assertThat(error).isNull(); - String sessionId = getDevice().executeShellCommand( - "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); - assertThat(sessionId).isNotEmpty(); - String sessionStateCmd = "cmd -w apexservice getStagedSessionInfo " + sessionId; - String initialState = getDevice().executeShellV2Command(sessionStateCmd).getStdout(); - assertThat(initialState).isNotEmpty(); - - // Kill apexd. This means apexd will perform its start logic when the second install - // is staged. - getDevice().executeShellV2Command("kill `pidof apexd`"); - - // Verify that the session state remains consistent after apexd has restarted. - String updatedState = getDevice().executeShellV2Command(sessionStateCmd).getStdout(); - assertThat(updatedState).isEqualTo(initialState); - } - - /** - * Verifies that content of {@code /data/apex/sessions/} is migrated to the {@code - * /metadata/apex/sessions}. - */ - @Test - public void testSessionsDirMigrationToMetadata() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Device requires root", getDevice().isAdbRoot()); - - try { - getDevice().executeShellV2Command("mkdir -p /data/apex/sessions/1543"); - File file = File.createTempFile("foo", "bar"); - getDevice().pushFile(file, "/data/apex/sessions/1543/file"); - - // During boot sequence apexd will move /data/apex/sessions/1543/file to - // /metadata/apex/sessions/1543/file. - getDevice().reboot(); - assertWithMessage("Timed out waiting for device to boot").that( - getDevice().waitForBootComplete(Duration.ofMinutes(2).toMillis())).isTrue(); - - assertThat(getDevice().doesFileExist("/metadata/apex/sessions/1543/file")).isTrue(); - assertThat(getDevice().doesFileExist("/data/apex/sessions/1543/file")).isFalse(); - } finally { - getDevice().executeShellV2Command("rm -R /data/apex/sessions/1543"); - getDevice().executeShellV2Command("rm -R /metadata/apex/sessions/1543"); - } - } - - @Test - public void testFailsToActivateApexOnDataFallbacksToPreInstalled() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Device requires root", getDevice().isAdbRoot()); - - try { - final File file = - mHostUtils.getTestFile("com.android.apex.cts.shim.v2_additional_file.apex"); - getDevice().pushFile(file, "/data/apex/active/com.android.apex.cts.shim@2.apex"); - - getDevice().reboot(); - assertWithMessage("Timed out waiting for device to boot").that( - getDevice().waitForBootComplete(Duration.ofMinutes(2).toMillis())).isTrue(); - - // After reboot pre-installed version of shim apex should be activated, and corrupted - // version on /data should be deleted. - final Set<ITestDevice.ApexInfo> activeApexes = getDevice().getActiveApexes(); - ITestDevice.ApexInfo testApex = new ITestDevice.ApexInfo( - "com.android.apex.cts.shim", 1L); - assertThat(activeApexes).contains(testApex); - assertThat( - getDevice() - .doesFileExist("/data/apex/active/com.android.apex.cts.shim@2.apex")) - .isFalse(); - } finally { - getDevice().deleteFile("/data/apex/active/com.android.apex.cts.shim@2.apex"); - } - } } diff --git a/tests/src/com/android/tests/apex/CellbroadcastHostTest.java b/tests/src/com/android/tests/apex/CellbroadcastHostTest.java new file mode 100644 index 00000000..97230422 --- /dev/null +++ b/tests/src/com/android/tests/apex/CellbroadcastHostTest.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.apex; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.runner.RunWith; + +/** + * Test to check if Apex can be staged, activated and uninstalled successfully. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +public class CellbroadcastHostTest extends ApexE2EBaseHostTest { +}
\ No newline at end of file diff --git a/apexd/aidl/android/apex/CompressedApexInfo.aidl b/tests/src/com/android/tests/apex/ConscryptHostTest.java index 48015bd0..01cb840e 100644 --- a/apexd/aidl/android/apex/CompressedApexInfo.aidl +++ b/tests/src/com/android/tests/apex/ConscryptHostTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,15 @@ * limitations under the License. */ -package android.apex; +package com.android.tests.apex; -parcelable CompressedApexInfo { - @utf8InCpp String moduleName; - long versionCode; - long decompressedSize; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.runner.RunWith; + +/** + * Test to check if Apex can be staged, activated and uninstalled successfully. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +public class ConscryptHostTest extends ApexE2EBaseHostTest { } diff --git a/tests/testdata/sharedlibs/build/sharedlibstest.cpp b/tests/src/com/android/tests/apex/ExtServicesHostTest.java index bac580ef..ff5d6942 100644 --- a/tests/testdata/sharedlibs/build/sharedlibstest.cpp +++ b/tests/src/com/android/tests/apex/ExtServicesHostTest.java @@ -14,17 +14,15 @@ * limitations under the License. */ -#include "sharedlibstest.h" +package com.android.tests.apex; -#include <string> +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -namespace sharedlibstest { +import org.junit.runner.RunWith; -// This parameter gets modified by the build_artifacts.sh script. -#define FINGERPRINT "VERSION_XXX" - -std::string getSharedLibsTestFingerprint() { - return std::string("SHARED_LIB_") + FINGERPRINT; +/** + * Test to check if Apex can be staged, activated and uninstalled successfully. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +public class ExtServicesHostTest extends ApexE2EBaseHostTest { } - -} // namespace sharedlibstest diff --git a/apexd/aidl/android/apex/CompressedApexInfoList.aidl b/tests/src/com/android/tests/apex/IpSecHostTest.java index e1620bd1..765f4355 100644 --- a/apexd/aidl/android/apex/CompressedApexInfoList.aidl +++ b/tests/src/com/android/tests/apex/IpSecHostTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,13 @@ * limitations under the License. */ -package android.apex; +package com.android.tests.apex; -import android.apex.CompressedApexInfo; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -parcelable CompressedApexInfoList { - CompressedApexInfo[] apexInfos; +import org.junit.runner.RunWith; + +/** Test to check if Apex can be staged, activated and uninstalled successfully. */ +@RunWith(DeviceJUnit4ClassRunner.class) +public class IpSecHostTest extends ApexE2EBaseHostTest { } diff --git a/tests/src/com/android/tests/apex/MediaProviderHostTest.java b/tests/src/com/android/tests/apex/MediaProviderHostTest.java new file mode 100644 index 00000000..4fc72251 --- /dev/null +++ b/tests/src/com/android/tests/apex/MediaProviderHostTest.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.apex; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.runner.RunWith; + +/** + * Test to check if Apex can be staged, activated and uninstalled successfully. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +public class MediaProviderHostTest extends ApexE2EBaseHostTest { +} diff --git a/tests/src/com/android/tests/apex/NeuralNetworksHostTest.java b/tests/src/com/android/tests/apex/NeuralNetworksHostTest.java new file mode 100644 index 00000000..1effbe2a --- /dev/null +++ b/tests/src/com/android/tests/apex/NeuralNetworksHostTest.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.apex; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.runner.RunWith; + +/** + * Test to check if Apex can be staged, activated and uninstalled successfully. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +public class NeuralNetworksHostTest extends ApexE2EBaseHostTest { +} diff --git a/tests/src/com/android/tests/apex/PermissionHostTest.java b/tests/src/com/android/tests/apex/PermissionHostTest.java new file mode 100644 index 00000000..c673ade9 --- /dev/null +++ b/tests/src/com/android/tests/apex/PermissionHostTest.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.apex; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Ignore; +import org.junit.runner.RunWith; + +/** + * Test to check if Apex can be staged, activated and uninstalled successfully. + */ +@Ignore("b/152717947") +@RunWith(DeviceJUnit4ClassRunner.class) +public class PermissionHostTest extends ApexE2EBaseHostTest { +} diff --git a/tests/src/com/android/tests/apex/SharedLibsApexTest.java b/tests/src/com/android/tests/apex/SharedLibsApexTest.java deleted file mode 100644 index fdeac2c3..00000000 --- a/tests/src/com/android/tests/apex/SharedLibsApexTest.java +++ /dev/null @@ -1,534 +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 com.android.tests.apex; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assume.assumeTrue; - -import android.cts.install.lib.host.InstallUtilsHost; - -import com.android.compatibility.common.util.CpuFeatures; -import com.android.internal.util.test.SystemPreparer; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.RuleChain; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; - - -@RunWith(DeviceJUnit4ClassRunner.class) -public class SharedLibsApexTest extends BaseHostJUnit4Test { - - private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); - private final TemporaryFolder mTemporaryFolder = new TemporaryFolder(); - private final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, - this::getDevice); - - @Rule - public final RuleChain ruleChain = RuleChain.outerRule(mTemporaryFolder).around(mPreparer); - - enum ApexName { - FOO, - BAR, - BAZ, - PONY, - SHAREDLIBS, - SHAREDLIBS_SECONDARY - } - - enum ApexVersion { - ONE, - TWO - } - - enum ApexType { - DEFAULT, - STRIPPED - } - - enum SharedLibsVersion { - X, - Y, - Z - } - - /** - * Utility function to generate test apex names in the form e.g.: - * "com.android.apex.test.bar.v1.libvX.apex" - */ - private String getTestApex(ApexName apexName, ApexType apexType, ApexVersion apexVersion, - SharedLibsVersion sharedLibsVersion) { - StringBuilder ret = new StringBuilder(); - ret.append("com.android.apex.test."); - switch(apexName) { - case FOO: - ret.append("foo"); - break; - case BAR: - ret.append("bar"); - break; - case BAZ: - ret.append("baz"); - break; - case PONY: - ret.append("pony"); - break; - case SHAREDLIBS: - ret.append("sharedlibs_generated"); - break; - case SHAREDLIBS_SECONDARY: - ret.append("sharedlibs_secondary_generated"); - break; - } - - switch(apexType) { - case STRIPPED: - ret.append("_stripped"); - break; - case DEFAULT: - break; - } - - switch(apexVersion) { - case ONE: - ret.append(".v1"); - break; - case TWO: - ret.append(".v2"); - break; - } - - switch(sharedLibsVersion) { - case X: - ret.append(".libvX.apex"); - break; - case Y: - ret.append(".libvY.apex"); - break; - case Z: - ret.append(".libvZ.apex"); - break; - } - - return ret.toString(); - } - - /** - * Utility function to generate the file name of an installed package as per - * apexd convention e.g.: "com.android.apex.test.bar@1.apex" - */ - private String getInstalledApexFileName(ApexName apexName, ApexVersion apexVersion) { - StringBuilder ret = new StringBuilder(); - ret.append("com.android.apex.test."); - switch(apexName) { - case FOO: - ret.append("foo"); - break; - case BAR: - ret.append("bar"); - break; - case BAZ: - ret.append("baz"); - break; - case PONY: - ret.append("pony"); - break; - case SHAREDLIBS: - ret.append("sharedlibs"); - break; - case SHAREDLIBS_SECONDARY: - ret.append("sharedlibs_secondary"); - break; - } - ret.append("@"); - switch(apexVersion) { - case ONE: - ret.append("1"); - break; - case TWO: - ret.append("2"); - break; - } - ret.append(".apex"); - return ret.toString(); - } - - /** - * Tests basic functionality of two apex packages being force-installed and the C++ binaries - * contained in them being executed correctly. - */ - @Test - public void testInstallAndRunDefaultApexs() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Device requires root", getDevice().isAdbRoot()); - - for (String apex : new String[]{ - getTestApex(ApexName.BAR, ApexType.DEFAULT, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.FOO, ApexType.DEFAULT, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.PONY, ApexType.DEFAULT, ApexVersion.ONE, SharedLibsVersion.Z), - }) { - mPreparer.pushResourceFile(apex, - "/system/apex/" + apex); - } - mPreparer.reboot(); - - getDevice().disableAdbRoot(); - String runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.foo/bin/foo_test"); - assertThat(runAsResult).isEqualTo("FOO_VERSION_1 SHARED_LIB_VERSION_X"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test32"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) { - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test64"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - } - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.pony/bin/pony_test"); - assertThat(runAsResult).isEqualTo("PONY_VERSION_1 SHARED_LIB_VERSION_Z"); - - mPreparer.stageMultiplePackages( - new String[]{ - getTestApex(ApexName.BAR, ApexType.DEFAULT, ApexVersion.TWO, SharedLibsVersion.Y), - getTestApex(ApexName.FOO, ApexType.DEFAULT, ApexVersion.TWO, SharedLibsVersion.Y), - }, - new String[] { - "com.android.apex.test.bar", - "com.android.apex.test.foo", - }).reboot(); - - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.foo/bin/foo_test"); - assertThat(runAsResult).isEqualTo("FOO_VERSION_2 SHARED_LIB_VERSION_Y"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test32"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y"); - if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) { - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test64"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y"); - } - } - - /** - * Tests functionality of shared libraries apex: installs two apexs "stripped" of libc++.so and - * one apex containing it and verifies that C++ binaries can run. - */ - @Test - public void testInstallAndRunOptimizedApexs() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Device requires root", getDevice().isAdbRoot()); - - // Base case: - // - // Pre-installed on /system: - // package bar version 1 using library version X - // package foo version 1 using library version X - // package sharedlibs version 1 exporting library version X - // - // package pony version 1 using library version Z - // package sharedlibs_secondary version 1 exporting library version Z - - for (String apex : new String[]{ - getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.PONY, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.Z), - getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.ONE, - SharedLibsVersion.X), - getTestApex(ApexName.SHAREDLIBS_SECONDARY, ApexType.DEFAULT, ApexVersion.ONE, - SharedLibsVersion.Z), - }) { - mPreparer.pushResourceFile(apex, - "/system/apex/" + apex); - } - mPreparer.reboot(); - - getDevice().disableAdbRoot(); - String runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.foo/bin/foo_test"); - assertThat(runAsResult).isEqualTo("FOO_VERSION_1 SHARED_LIB_VERSION_X"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test32"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) { - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test64"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - } - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.pony/bin/pony_test"); - assertThat(runAsResult).isEqualTo("PONY_VERSION_1 SHARED_LIB_VERSION_Z"); - - // Edge case: sharedlibs updated with a same version apex. - // - // Updated packages (installed on /data/apex/active): - // package sharedlibs version 1 exporting library version X <-- new - // package sharedlibs_secondary version 1 exporting library version Z <-- new - // - // Pre-installed: - // package bar version 1 using library version X - // package foo version 1 using library version X - // (inactive) package sharedlibs version 1 exporting library version X - // - // package pony version 1 using library version Z - // (inactive) package sharedlibs_secondary version 1 exporting library version Z - - mPreparer.stageMultiplePackages( - new String[]{ - getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.ONE, - SharedLibsVersion.X), - getTestApex(ApexName.SHAREDLIBS_SECONDARY, ApexType.DEFAULT, ApexVersion.ONE, - SharedLibsVersion.Z), - }, - new String[]{ - "com.android.apex.test.sharedlibs", - "com.android.apex.test.sharedlibs_secondary", - }).reboot(); - - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.foo/bin/foo_test"); - assertThat(runAsResult).isEqualTo("FOO_VERSION_1 SHARED_LIB_VERSION_X"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test32"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) { - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test64"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - } - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.pony/bin/pony_test"); - assertThat(runAsResult).isEqualTo("PONY_VERSION_1 SHARED_LIB_VERSION_Z"); - - // Updated packages (installed on /data/apex/active): - // package bar version 2 using library version Y <-- new - // package foo version 2 using library version Y <-- new - // package sharedlibs version 2 exporting library version Y <-- new - // package sharedlibs_secondary version 1 exporting library version Z - // - // Pre-installed: - // (inactive) package bar version 1 using library version X - // (inactive) package foo version 1 using library version X - // package sharedlibs version 1 exporting library version X - // - // package pony version 1 using library version Z - // (inactive) package sharedlibs_secondary version 1 exporting library version Z - - mPreparer.stageMultiplePackages( - new String[]{ - getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.TWO, SharedLibsVersion.Y), - getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.TWO, SharedLibsVersion.Y), - getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.TWO, - SharedLibsVersion.Y), - }, - new String[]{ - "com.android.apex.test.bar", - "com.android.apex.test.foo", - "com.android.apex.test.sharedlibs", - }).reboot(); - - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.foo/bin/foo_test"); - assertThat(runAsResult).isEqualTo("FOO_VERSION_2 SHARED_LIB_VERSION_Y"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test32"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y"); - if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) { - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test64"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y"); - } - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.pony/bin/pony_test"); - assertThat(runAsResult).isEqualTo("PONY_VERSION_1 SHARED_LIB_VERSION_Z"); - - // Assume that an OTA now adds a package baz on /system needing libraries installed on - // /system: - // - // Updated packages (installed on /data/apex/active): - // package bar version 2 using library version Y - // package foo version 2 using library version Y - // package sharedlibs version 2 exporting library version Y - // - // Pre-installed: - // (inactive) package bar version 1 using library version X - // package baz version 1 using library version X <-- new - // (inactive) package foo version 1 using library version X - // package sharedlibs version 1 exporting library version X - // package pony version 1 using library version Z - // package sharedlibs_secondary version 1 exporting library version Z - - String baz_apex = - getTestApex(ApexName.BAZ, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X); - mPreparer.pushResourceFile(baz_apex, "/system/apex/" + baz_apex); - mPreparer.reboot(); - - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.foo/bin/foo_test"); - assertThat(runAsResult).isEqualTo("FOO_VERSION_2 SHARED_LIB_VERSION_Y"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test32"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y"); - if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) { - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test64"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y"); - } - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.baz/bin/baz_test"); - assertThat(runAsResult).isEqualTo("BAZ_VERSION_1 SHARED_LIB_VERSION_X"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.pony/bin/pony_test"); - assertThat(runAsResult).isEqualTo("PONY_VERSION_1 SHARED_LIB_VERSION_Z"); - } - - /** - * Tests that when a shared library apex is updated via OTA the previously - * downloaded version is remoted. - */ - @Test - public void testHigherVersionOnSystemDeletesDataVersion() throws Exception { - assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); - assumeTrue("Device requires root", getDevice().isAdbRoot()); - - // Base case: - // - // Pre-installed on /system: - // package bar version 1 using library version X - // package foo version 1 using library version X - // package sharedlibs version 1 exporting library version X - for (String apex : new String[]{ - getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.ONE, - SharedLibsVersion.X), - }) { - mPreparer.pushResourceFile(apex, - "/system/apex/" + apex); - } - mPreparer.reboot(); - String runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.foo/bin/foo_test"); - assertThat(runAsResult).isEqualTo("FOO_VERSION_1 SHARED_LIB_VERSION_X"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test32"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) { - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test64"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - } - - // Same-grade case: - // - // Pre-installed on /system: - // package bar version 1 using library version X - // package foo version 1 using library version X - // package sharedlibs version 1 exporting library version X - // Updated packages (installed on /data/apex/active): - // package bar version 1 using library version X - // package foo version 1 using library version X - // package sharedlibs version 1 exporting library version X - mPreparer.stageMultiplePackages( - new String[]{ - getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.ONE, - SharedLibsVersion.X), - }, - new String[]{ - "com.android.apex.test.bar", - "com.android.apex.test.foo", - "com.android.apex.test.sharedlibs", - }).reboot(); - - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.foo/bin/foo_test"); - assertThat(runAsResult).isEqualTo("FOO_VERSION_1 SHARED_LIB_VERSION_X"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test32"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) { - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test64"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_1 SHARED_LIB_VERSION_X"); - } - - // Simulate OTA upgrading pre-installed modules: - // - // Pre-installed on /system: - // package bar version 2 using library version Y - // package foo version 2 using library version Y - // package sharedlibs version 2 exporting library version Y - // - // Updated packages (installed on /data/apex/active): - // package bar version 1 using library version X (deleted) - // package foo version 1 using library version X (deleted) - // package sharedlibs version 1 exporting library version X (deleted) - // - for (String apex : new String[]{ - getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.ONE, SharedLibsVersion.X), - getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.ONE, - SharedLibsVersion.X), - }) { - mPreparer.deleteFile("/system/apex/" + apex); - } - for (String apex : new String[]{ - getTestApex(ApexName.BAR, ApexType.STRIPPED, ApexVersion.TWO, SharedLibsVersion.Y), - getTestApex(ApexName.FOO, ApexType.STRIPPED, ApexVersion.TWO, SharedLibsVersion.Y), - getTestApex(ApexName.SHAREDLIBS, ApexType.DEFAULT, ApexVersion.TWO, - SharedLibsVersion.Y), - }) { - mPreparer.pushResourceFile(apex, - "/system/apex/" + apex); - } - - // Check that files in /data are deleted on first boot. - assertThat(getDevice().doesFileExist("/data/apex/active/" - + getInstalledApexFileName(ApexName.BAR, ApexVersion.ONE))).isTrue(); - assertThat(getDevice().doesFileExist("/data/apex/active/" - + getInstalledApexFileName(ApexName.FOO, ApexVersion.ONE))).isTrue(); - assertThat(getDevice().doesFileExist("/data/apex/active/" - + getInstalledApexFileName(ApexName.SHAREDLIBS, ApexVersion.ONE))).isTrue(); - mPreparer.reboot(); - assertThat(getDevice().doesFileExist("/data/apex/active/" - + getInstalledApexFileName(ApexName.BAR, ApexVersion.ONE))).isFalse(); - assertThat(getDevice().doesFileExist("/data/apex/active/" - + getInstalledApexFileName(ApexName.FOO, ApexVersion.ONE))).isFalse(); - assertThat(getDevice().doesFileExist("/data/apex/active/" - + getInstalledApexFileName(ApexName.SHAREDLIBS, ApexVersion.ONE))).isFalse(); - - getDevice().disableAdbRoot(); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.foo/bin/foo_test"); - assertThat(runAsResult).isEqualTo("FOO_VERSION_2 SHARED_LIB_VERSION_Y"); - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test32"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y"); - if (CpuFeatures.isX86_64(getDevice()) || CpuFeatures.isArm64(getDevice())) { - runAsResult = getDevice().executeShellCommand( - "/apex/com.android.apex.test.bar/bin/bar_test64"); - assertThat(runAsResult).isEqualTo("BAR_VERSION_2 SHARED_LIB_VERSION_Y"); - } - } -} diff --git a/tests/src/com/android/tests/apex/StatsdHostTest.java b/tests/src/com/android/tests/apex/StatsdHostTest.java new file mode 100644 index 00000000..4ad3bb8d --- /dev/null +++ b/tests/src/com/android/tests/apex/StatsdHostTest.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.apex; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Ignore; +import org.junit.runner.RunWith; + +/** + * Test to check if Apex can be staged, activated and uninstalled successfully. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +@Ignore // TODO(b/150452752): unignore when test is fixed. +public class StatsdHostTest extends ApexE2EBaseHostTest { +} diff --git a/tests/src/com/android/tests/apex/WifiHostTest.java b/tests/src/com/android/tests/apex/WifiHostTest.java new file mode 100644 index 00000000..7fd85289 --- /dev/null +++ b/tests/src/com/android/tests/apex/WifiHostTest.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.apex; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.runner.RunWith; + +/** + * Test to check if Apex can be staged, activated and uninstalled successfully. + */ +@RunWith(DeviceJUnit4ClassRunner.class) +public class WifiHostTest extends ApexE2EBaseHostTest { + // TODO(b/146163587): Add more checks to verify apex installation. +} diff --git a/tests/src/com/android/tests/apex/host/ApexCompressionTests.java b/tests/src/com/android/tests/apex/host/ApexCompressionTests.java deleted file mode 100644 index a09ca60d..00000000 --- a/tests/src/com/android/tests/apex/host/ApexCompressionTests.java +++ /dev/null @@ -1,438 +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 com.android.tests.apex.host; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; - -import android.cts.install.lib.host.InstallUtilsHost; -import android.platform.test.annotations.LargeTest; - -import com.android.tradefed.device.DeviceNotAvailableException; -import com.android.tradefed.device.ITestDevice; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; -import com.android.tradefed.util.CommandResult; -import com.android.tradefed.util.CommandStatus; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.time.Duration; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * Test for platform support for Apex Compression feature - */ -@RunWith(DeviceJUnit4ClassRunner.class) -public class ApexCompressionTests extends BaseHostJUnit4Test { - private static final String COMPRESSED_APEX_PACKAGE_NAME = "com.android.apex.compressed"; - private static final String ORIGINAL_APEX_FILE_NAME = - COMPRESSED_APEX_PACKAGE_NAME + ".v1_original.apex"; - private static final String DECOMPRESSED_DIR_PATH = "/data/apex/decompressed/"; - private static final String APEX_ACTIVE_DIR = "/data/apex/active/"; - private static final String OTA_RESERVED_DIR = "/data/apex/ota_reserved/"; - private static final String DECOMPRESSED_APEX_SUFFIX = ".decompressed.apex"; - - private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); - private boolean mWasAdbRoot = false; - - @Before - public void setUp() throws Exception { - mWasAdbRoot = getDevice().isAdbRoot(); - if (!mWasAdbRoot) { - assumeTrue("Requires root", getDevice().enableAdbRoot()); - } - deleteFiles("/system/apex/" + COMPRESSED_APEX_PACKAGE_NAME + "*apex", - APEX_ACTIVE_DIR + COMPRESSED_APEX_PACKAGE_NAME + "*apex", - DECOMPRESSED_DIR_PATH + COMPRESSED_APEX_PACKAGE_NAME + "*apex", - OTA_RESERVED_DIR + "*"); - } - - @After - public void tearDown() throws Exception { - if (!mWasAdbRoot) { - getDevice().disableAdbRoot(); - } - deleteFiles("/system/apex/" + COMPRESSED_APEX_PACKAGE_NAME + "*apex", - APEX_ACTIVE_DIR + COMPRESSED_APEX_PACKAGE_NAME + "*apex", - DECOMPRESSED_DIR_PATH + COMPRESSED_APEX_PACKAGE_NAME + "*apex", - OTA_RESERVED_DIR + "*"); - } - - /** - * Runs the given phase of a test by calling into the device. - * Throws an exception if the test phase fails. - * <p> - * For example, <code>runPhase("testApkOnlyEnableRollback");</code> - */ - private void runPhase(String phase) throws Exception { - assertTrue(runDeviceTests("com.android.tests.apex.app", - "com.android.tests.apex.app.ApexCompressionTests", - phase)); - } - - /** - * Deletes files and reboots the device if necessary. - * @param files the paths of files which might contain wildcards - */ - private void deleteFiles(String... files) throws Exception { - boolean found = false; - for (String file : files) { - CommandResult result = getDevice().executeShellV2Command("ls " + file); - if (result.getStatus() == CommandStatus.SUCCESS) { - found = true; - break; - } - } - - if (found) { - getDevice().remountSystemWritable(); - for (String file : files) { - getDevice().executeShellCommand("rm -rf " + file); - } - getDevice().reboot(); - } - } - - private void pushTestApex(final String fileName) throws Exception { - final File apex = mHostUtils.getTestFile(fileName); - getDevice().remountSystemWritable(); - assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName)); - getDevice().reboot(); - } - - private List<String> getFilesInDir(String baseDir) throws DeviceNotAvailableException { - return getDevice().getFileEntry(baseDir).getChildren(false) - .stream().map(entry -> entry.getName()) - .collect(Collectors.toList()); - } - - /** - * Returns the active apex info as optional. - */ - private Optional<ITestDevice.ApexInfo> getActiveApexInfo(String packageName) - throws DeviceNotAvailableException { - return getDevice().getActiveApexes().stream().filter( - apex -> apex.name.equals(packageName)).findAny(); - } - - @Test - @LargeTest - public void testDecompressedApexIsConsideredFactory() throws Exception { - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - runPhase("testDecompressedApexIsConsideredFactory"); - } - - @Test - @LargeTest - public void testCompressedApexIsDecompressedAndActivated() throws Exception { - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - - // Ensure that compressed APEX was decompressed in DECOMPRESSED_DIR_PATH - List<String> files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).contains(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - - // Match the decompressed apex with original byte for byte - final File originalApex = mHostUtils.getTestFile(ORIGINAL_APEX_FILE_NAME); - final byte[] originalApexFileBytes = Files.readAllBytes(Paths.get(originalApex.toURI())); - final File decompressedFile = getDevice().pullFile( - DECOMPRESSED_DIR_PATH + COMPRESSED_APEX_PACKAGE_NAME + "@1" - + DECOMPRESSED_APEX_SUFFIX); - final byte[] decompressedFileBytes = - Files.readAllBytes(Paths.get(decompressedFile.toURI())); - assertThat(decompressedFileBytes).isEqualTo(originalApexFileBytes); - - // The decompressed APEX should note be hard linked to APEX_ACTIVE_DIR - files = getFilesInDir(APEX_ACTIVE_DIR); - assertThat(files).doesNotContain( - COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - } - - @Test - @LargeTest - public void testDecompressedApexSurvivesReboot() throws Exception { - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - - // Ensure that compressed APEX was activated from DECOMPRESSED_DIR_PATH - List<String> files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).contains(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - final File decompressedFile = getDevice().pullFile( - DECOMPRESSED_DIR_PATH + COMPRESSED_APEX_PACKAGE_NAME + "@1" - + DECOMPRESSED_APEX_SUFFIX); - final byte[] decompressedFileBytes = - Files.readAllBytes(Paths.get(decompressedFile.toURI())); - - getDevice().reboot(); - - // Ensure it gets activated again on reboot - files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).contains(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - final File decompressedFileAfterReboot = getDevice().pullFile( - DECOMPRESSED_DIR_PATH + COMPRESSED_APEX_PACKAGE_NAME + "@1" - + DECOMPRESSED_APEX_SUFFIX); - final byte[] decompressedFileBytesAfterReboot = - Files.readAllBytes(Paths.get(decompressedFileAfterReboot.toURI())); - assertThat(decompressedFileBytes).isEqualTo(decompressedFileBytesAfterReboot); - } - - @Test - @LargeTest - public void testDecompressionDoesNotHappenOnEveryReboot() throws Exception { - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - - final String decompressedApexFilePath = DECOMPRESSED_DIR_PATH - + COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX; - String lastModifiedTime1 = - getDevice().executeShellCommand("stat -c %Y " + decompressedApexFilePath); - - getDevice().reboot(); - getDevice().waitForDeviceAvailable(); - - String lastModifiedTime2 = - getDevice().executeShellCommand("stat -c %Y " + decompressedApexFilePath); - assertThat(lastModifiedTime1).isEqualTo(lastModifiedTime2); - } - - @Test - @LargeTest - public void testHigherVersionOnSystemTriggerDecompression() throws Exception { - // Install v1 on /system partition - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - // On boot, /data partition will have decompressed v1 APEX in it - List<String> files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).contains(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - - // Now replace /system APEX with v2 - getDevice().remountSystemWritable(); - getDevice().executeShellCommand("rm -rf /system/apex/" - + COMPRESSED_APEX_PACKAGE_NAME + "*apex"); - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v2.capex"); - - // Ensure that v2 was decompressed - files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).contains(COMPRESSED_APEX_PACKAGE_NAME + "@2" + DECOMPRESSED_APEX_SUFFIX); - } - - - @Test - @LargeTest - public void testDifferentRootDigestTriggersDecompression() throws Exception { - // Install v1 on /system partition - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - // On boot, /data partition will have decompressed v1 APEX in it - List<String> files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).contains(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - final File decompressedFile = getDevice().pullFile( - DECOMPRESSED_DIR_PATH + COMPRESSED_APEX_PACKAGE_NAME + "@1" - + DECOMPRESSED_APEX_SUFFIX); - final byte[] decompressedFileBytes = - Files.readAllBytes(Paths.get(decompressedFile.toURI())); - - // Now replace /system APEX with same version but different root digest - getDevice().remountSystemWritable(); - getDevice().executeShellCommand("rm -rf /system/apex/" - + COMPRESSED_APEX_PACKAGE_NAME + "*apex"); - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1_different_digest.capex"); - - // Ensure that decompressed APEX is different than before - files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).contains(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - final File decompressedFileAfterReboot = getDevice().pullFile( - DECOMPRESSED_DIR_PATH + COMPRESSED_APEX_PACKAGE_NAME + "@1" - + DECOMPRESSED_APEX_SUFFIX); - final byte[] decompressedFileBytesAfterReboot = - Files.readAllBytes(Paths.get(decompressedFileAfterReboot.toURI())); - assertThat(decompressedFileBytes).isNotEqualTo(decompressedFileBytesAfterReboot); - } - - @Test - @LargeTest - public void testUnusedDecompressedApexIsCleanedUp_HigherVersion() throws Exception { - // Install v1 on /system partition - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - // Ensure that compressed APEX was decompressed in DECOMPRESSED_DIR_PATH - List<String> files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).contains(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - - // Now install an update for that APEX so that decompressed APEX becomes redundant - runPhase("testUnusedDecompressedApexIsCleanedUp_HigherVersion"); - getDevice().reboot(); - - // Verify that DECOMPRESSED_DIR_PATH does not contain the decompressed APEX - files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).doesNotContain( - COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - } - - @Test - @LargeTest - public void testUnusedDecompressedApexIsCleanedUp_SameVersion() throws Exception { - // Install v1 on /system partition - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - // Ensure that compressed APEX was decompressed in DECOMPRESSED_DIR_PATH - List<String> files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).contains(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - - // Now install an update for that APEX so that decompressed APEX becomes redundant - runPhase("testUnusedDecompressedApexIsCleanedUp_SameVersion"); - getDevice().reboot(); - - // Verify that DECOMPRESSED_DIR_PATH does not contain the decompressed APEX - files = getFilesInDir(DECOMPRESSED_DIR_PATH); - assertThat(files).doesNotContain( - COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - } - - @Test - @LargeTest - public void testReservedSpaceIsNotCleanedOnReboot() throws Exception { - getDevice().executeShellCommand("touch " + OTA_RESERVED_DIR + "random"); - - getDevice().reboot(); - - List<String> files = getFilesInDir(OTA_RESERVED_DIR); - assertThat(files).hasSize(1); - assertThat(files).contains("random"); - } - - @Test - @LargeTest - public void testReservedSpaceIsCleanedUpOnDecompression() throws Exception { - getDevice().executeShellCommand("touch " + OTA_RESERVED_DIR + "random1"); - getDevice().executeShellCommand("touch " + OTA_RESERVED_DIR + "random2"); - - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - - assertThat(getFilesInDir(OTA_RESERVED_DIR)).isEmpty(); - } - - @Test - @LargeTest - public void testFailsToActivateApexOnDataFallbacksToPreInstalled() throws Exception { - // Push a data apex that will fail to activate - final File file = - mHostUtils.getTestFile("com.android.apex.compressed.v2_manifest_mismatch.apex"); - getDevice().pushFile(file, APEX_ACTIVE_DIR + COMPRESSED_APEX_PACKAGE_NAME + "@2.apex"); - // Push a CAPEX which should act as the fallback - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v2.capex"); - assertWithMessage("Timed out waiting for device to boot").that( - getDevice().waitForBootComplete(Duration.ofMinutes(2).toMillis())).isTrue(); - - // After reboot pre-installed version of shim apex should be activated, and corrupted - // version on /data should be deleted. - final ITestDevice.ApexInfo activeApex = - getActiveApexInfo(COMPRESSED_APEX_PACKAGE_NAME).get(); - assertThat(activeApex.versionCode).isEqualTo(2); - assertThat(getDevice().doesFileExist( - DECOMPRESSED_DIR_PATH + COMPRESSED_APEX_PACKAGE_NAME + "@2" - + DECOMPRESSED_APEX_SUFFIX)).isTrue(); - assertThat(getDevice().doesFileExist( - APEX_ACTIVE_DIR + COMPRESSED_APEX_PACKAGE_NAME + "@2" - + DECOMPRESSED_APEX_SUFFIX)).isFalse(); - assertThat(getDevice().doesFileExist( - APEX_ACTIVE_DIR + COMPRESSED_APEX_PACKAGE_NAME + "@2.apex")).isFalse(); - } - - @Test - @LargeTest - public void testCapexToApexSwitch() throws Exception { - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - assertThat(getFilesInDir(DECOMPRESSED_DIR_PATH)) - .contains(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - - // Now replace the CAPEX with an uncompressed APEX - getDevice().remountSystemWritable(); - getDevice().executeShellCommand("rm -rf /system/apex/" - + COMPRESSED_APEX_PACKAGE_NAME + "*apex"); - pushTestApex(ORIGINAL_APEX_FILE_NAME); - runPhase("testCapexToApexSwitch"); - - // Ensure active apex is running from /system - final ITestDevice.ApexInfo activeApex = getActiveApexInfo(COMPRESSED_APEX_PACKAGE_NAME) - .orElseThrow(() -> new AssertionError( - "Can't find " + COMPRESSED_APEX_PACKAGE_NAME)); - assertThat(activeApex.sourceDir).startsWith("/system"); - // Ensure previous decompressed APEX has been cleaned up - assertThat(getFilesInDir(DECOMPRESSED_DIR_PATH)) - .doesNotContain(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - } - - @Test - @LargeTest - public void testDecompressedApexVersionAlwaysHasSameVersionAsCapex() throws Exception { - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v2.capex"); - // Now replace /system APEX with v1 - getDevice().remountSystemWritable(); - getDevice().executeShellCommand("rm -rf /system/apex/" - + COMPRESSED_APEX_PACKAGE_NAME + "*apex"); - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - runPhase("testDecompressedApexVersionAlwaysHasSameVersionAsCapex"); - } - - @Test - @LargeTest - public void testCompressedApexCanBeRolledBack() throws Exception { - pushTestApex(COMPRESSED_APEX_PACKAGE_NAME + ".v1.capex"); - - // Now install update with rollback - runPhase("testCompressedApexCanBeRolledBack_Commit"); - getDevice().reboot(); - - // Rollback the apex - runPhase("testCompressedApexCanBeRolledBack_Rollback"); - getDevice().reboot(); - - runPhase("testCompressedApexCanBeRolledBack_Verify"); - } - - @Test - @LargeTest - public void testOrphanedDecompressedApexInActiveDirIsIgnored() throws Exception { - final File apex = mHostUtils.getTestFile( - COMPRESSED_APEX_PACKAGE_NAME + ".v1_original.apex"); - // Prepare an APEX in active directory with .decompressed.apex suffix. - // Place the same apex in system too. When booting, system APEX should - // be mounted while the decomrpessed APEX in active direcotyr should - // be ignored. - getDevice().remountSystemWritable(); - assertTrue(getDevice().pushFile(apex, - APEX_ACTIVE_DIR + COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX)); - assertTrue(getDevice().pushFile(apex, - "/system/apex/" + COMPRESSED_APEX_PACKAGE_NAME + ".v1.apex")); - getDevice().reboot(); - // Ensure active apex is running from /system - final ITestDevice.ApexInfo activeApex = getActiveApexInfo(COMPRESSED_APEX_PACKAGE_NAME) - .orElseThrow(() -> new AssertionError( - "Can't find " + COMPRESSED_APEX_PACKAGE_NAME)); - assertThat(activeApex.sourceDir).startsWith("/system"); - // Ensure orphaned decompressed APEX has been cleaned up - assertThat(getFilesInDir(APEX_ACTIVE_DIR)) - .doesNotContain(COMPRESSED_APEX_PACKAGE_NAME + "@1" + DECOMPRESSED_APEX_SUFFIX); - } -} - diff --git a/tests/src/com/android/tests/util/ModuleTestUtilsTest.java b/tests/src/com/android/tests/util/ModuleTestUtilsTest.java new file mode 100644 index 00000000..833974f8 --- /dev/null +++ b/tests/src/com/android/tests/util/ModuleTestUtilsTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.util; + +import static com.google.common.truth.Truth.assertWithMessage; + +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class ModuleTestUtilsTest extends BaseHostJUnit4Test { + private static final String SHIM_V2 = "com.android.apex.cts.shim.v2.apex"; + private final ModuleTestUtils mUtils = new ModuleTestUtils(this); + + @Before + public void setUp() throws Exception { + mUtils.abandonActiveStagedSession(); + mUtils.uninstallShimApexIfNecessary(); + } + /** + * Uninstalls any version greater than 1 of shim apex and reboots the device if necessary + * to complete the uninstall. + */ + @After + public void tearDown() throws Exception { + mUtils.abandonActiveStagedSession(); + mUtils.uninstallShimApexIfNecessary(); + } + + /** + * Unit test for {@link ModuleTestUtils#abandonActiveStagedSession()} + */ + @Test + public void testAbandonActiveStagedSession() throws Exception { + File apexFile = mUtils.getTestFile(SHIM_V2); + + // Install apex package + String installResult = getDevice().installPackage(apexFile, false, "--wait"); + assertWithMessage(String.format("failed to install apex first time %s. Reason: %s", + SHIM_V2, installResult)).that(installResult).isNull(); + + // Abandon ready session + mUtils.abandonActiveStagedSession(); + + // Install apex again + installResult = getDevice().installPackage(apexFile, false, "--wait"); + assertWithMessage(String.format("failed to install apex again %s. Reason: %s", + SHIM_V2, installResult)).that(installResult).isNull(); + } +} diff --git a/tests/statsd-e2e-tests.xml b/tests/statsd-e2e-tests.xml new file mode 100644 index 00000000..b3a270ce --- /dev/null +++ b/tests/statsd-e2e-tests.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Configuration for statsd module e2e test cases"> + <option name="test-suite-tag" value="statsd_e2e_tests" /> + <option name="test-suite-tag" value="apct" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="statsd_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.os.statsd.apex" /> + </test> +</configuration> diff --git a/tests/testdata/init.rc b/tests/testdata/init.rc index 4e89f444..1c90c830 100644 --- a/tests/testdata/init.rc +++ b/tests/testdata/init.rc @@ -6,7 +6,7 @@ service surfaceflinger /apex/com.android.apex.test/bin/surfaceflinger user system group graphics drmrpc readproc onrestart restart zygote - task_profiles HighPerformance + writepid /dev/stune/foreground/tasks socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0 socket pdx/system/vr/display/manager stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0 socket pdx/system/vr/display/vsync stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0 diff --git a/tests/testdata/sharedlibs/README.md b/tests/testdata/sharedlibs/README.md deleted file mode 100644 index a59686da..00000000 --- a/tests/testdata/sharedlibs/README.md +++ /dev/null @@ -1,13 +0,0 @@ -### Test artifacts for shared libraries APEX support - -This directory contains APEX packages used for testing the platform support for -moving shared libraries used by binaries within an APEX package into another -APEX package. - -Due to the peculiarity of the build needs, this directory contains prebuilt -artifacts used by tests. In order to regenerate these artifacts, run from the -root of the tree: - -```shell script -./system/apex/tests/testdata/sharedlibs/build/build_artifacts.sh -```
\ No newline at end of file diff --git a/tests/testdata/sharedlibs/build/Android.bp b/tests/testdata/sharedlibs/build/Android.bp deleted file mode 100644 index 74b08a25..00000000 --- a/tests/testdata/sharedlibs/build/Android.bp +++ /dev/null @@ -1,86 +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_binary { - name: "noop", - srcs: ["noop.cc"], - shared_libs: [ - "libsharedlibtest", - ], - multilib: { - lib32: { - suffix: "32", - }, - lib64: { - suffix: "64", - }, - }, - - compile_multilib: "both", - - apex_available: [ - "com.android.apex.test.sharedlibs_stub", - "com.android.apex.test.sharedlibs_secondary_stub", - ], -} - -python_binary_host { - name: "shared_libs_repack", - srcs: [ - "shared_libs_repack.py", - ], - version: { - py2: { - enabled: false, - }, - py3: { - enabled: true, - embedded_launcher: true, - }, - }, - libs: [ - "apex_build_info_proto", - "apex_manifest_proto", - ], - required: [ - "apexer", - "signapk", - ], -} - -cc_library_shared { - name: "libsharedlibtest", - srcs: [ "sharedlibstest.cpp", ], - - local_include_dirs: [ - "include", - ], - - export_include_dirs: [ - "include", - ], - apex_available: [ - "com.android.apex.test.bar", - "com.android.apex.test.baz", - "com.android.apex.test.foo", - "com.android.apex.test.pony", - "com.android.apex.test.sharedlibs_stub", - "com.android.apex.test.sharedlibs_secondary_stub", - ], -} diff --git a/tests/testdata/sharedlibs/build/build_artifacts.sh b/tests/testdata/sharedlibs/build/build_artifacts.sh deleted file mode 100755 index 8dbc64f5..00000000 --- a/tests/testdata/sharedlibs/build/build_artifacts.sh +++ /dev/null @@ -1,206 +0,0 @@ -#!/bin/bash -e - -# List of files required in output. Every other file generated will be skipped. -OUTFILES=( - com.android.apex.test.bar_stripped.v1.libvX.apex - com.android.apex.test.bar_stripped.v2.libvY.apex - com.android.apex.test.bar.v1.libvX.apex - com.android.apex.test.bar.v2.libvY.apex - com.android.apex.test.baz_stripped.v1.libvX.apex - com.android.apex.test.foo_stripped.v1.libvX.apex - com.android.apex.test.foo_stripped.v2.libvY.apex - com.android.apex.test.foo.v1.libvX.apex - com.android.apex.test.foo.v2.libvY.apex - com.android.apex.test.pony_stripped.v1.libvZ.apex - com.android.apex.test.pony.v1.libvZ.apex - com.android.apex.test.sharedlibs_generated.v1.libvX.apex - com.android.apex.test.sharedlibs_generated.v2.libvY.apex - com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex -) - -# "apex" type build targets to build. -APEX_TARGETS=( - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.bar:com.android.apex.test.bar - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.foo:com.android.apex.test.foo - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.pony:com.android.apex.test.pony -) - -# "genrule" type build targets to build, and directory they are built from. -GENRULE_TARGETS=( - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.bar:com.android.apex.test.bar_stripped - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.baz:com.android.apex.test.baz_stripped - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.foo:com.android.apex.test.foo_stripped - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.pony:com.android.apex.test.pony_stripped - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs:com.android.apex.test.sharedlibs_generated - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary:com.android.apex.test.sharedlibs_secondary_generated -) - -if [ ! -e "build/make/core/Makefile" ]; then - echo "$0 must be run from the top of the tree" - exit 1 -fi - -OUT_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var OUT_DIR) -DIST_DIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var DIST_DIR) -TMPDIR=$(source build/envsetup.sh > /dev/null; TARGET_PRODUCT= get_build_var TMPDIR) - -manifestdirs=() - -for t in "${APEX_TARGETS[@]}" "${GENRULE_TARGETS[@]}"; do - IFS=: read -a ar <<< "${t}" - manifestdirs+=( ${ar[0]}) -done - -manifestdirs=($(printf "%s\n" "${manifestdirs[@]}" | sort -u)) - -generated_artifacts=() - -archs=( - arm - arm64 - x86 - x86_64 -) - -apexversions=( - 1 - 2 -) - -libversions=( - X - Y - Z -) - -for arch in "${archs[@]}"; do - for apexversion in "${apexversions[@]}"; do - apexfingerprint="VERSION_${apexversion}" - sed -i "s/#define FINGERPRINT .*/#define FINGERPRINT \"${apexfingerprint}\"/g" \ - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.bar/bar_test.cc \ - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.baz/baz_test.cc \ - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.foo/foo_test.cc \ - system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.pony/pony_test.cc - - for d in "${manifestdirs[@]}"; do - sed -i "s/ \"version\": .*/ \"version\": ${apexversion}/g" \ - ${d}/manifest.json - done - for libversion in "${libversions[@]}"; do - # Check if we need to build this combination of versions. - found=n - for t in "${APEX_TARGETS[@]}" "${GENRULE_TARGETS[@]}"; do - IFS=: read -a ar <<< "${t}" - outfile=${ar[1]}.v${apexversion}.libv${libversion}.apex - if printf '%s\n' "${OUTFILES[@]}" | grep -q -F "${outfile}"; then - found=y - break - fi - done - if [ "${found}" != "y" ]; then - # Skipping this combination. - continue - fi - - echo "Building combination arch: ${arch}, apexversion: ${apexversion}, libversion: ${libversion}" - libfingerprint="VERSION_${libversion}" - sed -i "s/#define FINGERPRINT .*/#define FINGERPRINT \"${libfingerprint}\"/g" \ - system/apex/tests/testdata/sharedlibs/build/sharedlibstest.cpp - - build/soong/soong_ui.bash \ - --make-mode \ - TARGET_PRODUCT=aosp_${arch} \ - dist sharedlibs_test - - for t in "${APEX_TARGETS[@]}" "${GENRULE_TARGETS[@]}"; do - IFS=: read -a ar <<< "${t}" - outfile=${ar[1]}.v${apexversion}.libv${libversion}.apex - if printf '%s\n' "${OUTFILES[@]}" | grep -q -P "^${outfile}\$"; then - cp -v \ - "${DIST_DIR}"/"${ar[1]}".apex \ - system/apex/tests/testdata/sharedlibs/prebuilts/${arch}/${outfile} - generated_artifacts+=(system/apex/tests/testdata/sharedlibs/prebuilts/${arch}/${outfile}) - fi - done - done - done -done - -# Generate the Android.bp file for the prebuilts. -tmpfile=$(mktemp) - -cat > "${tmpfile}" << EOF -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file is auto-generated by -// ./system/apex/tests/testdata/sharedlibs/build/build_artifacts.sh -// Do NOT edit manually. - -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} -EOF - -artifacts_filenames=() -for artifact in "${generated_artifacts[@]}"; do - artifacts_filenames+=($(basename ${artifact})) -done - -artifacts_filenames=($(printf '%s\n' "${artifacts_filenames[@]}" | sort -u)) - -for artifact in "${artifacts_filenames[@]}"; do - outfile=$(basename "${artifact}") - # remove .apex suffix - rulename=${outfile%.apex} - - cat >> "${tmpfile}" << EOF - -prebuilt_apex { - name: "${rulename}_prebuilt", - arch: { -EOF - - for arch in "${archs[@]}"; do - cat >> "${tmpfile}" << EOF - ${arch}: { - src: "${arch}/${outfile}", - }, -EOF - done - - cat >> "${tmpfile}" << EOF - }, - filename: "${outfile}", - installable: false, -} -EOF -done - -mv "${tmpfile}" system/apex/tests/testdata/sharedlibs/prebuilts/Android.bp - -# Restore the default version string to avoid bogus diffs. -sed -i "s/#define FINGERPRINT .*/#define FINGERPRINT \"VERSION_XXX\"/g" \ -system/apex/tests/testdata/sharedlibs/build/sharedlibstest.cpp \ -system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.bar/bar_test.cc \ -system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.baz/baz_test.cc \ -system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.foo/foo_test.cc \ -system/apex/tests/testdata/sharedlibs/build/com.android.apex.test.pony/pony_test.cc - -for d in "${manifestdirs[@]}"; do - sed -i "s/ \"version\": .*/ \"version\": 1/g" \ - ${d}/manifest.json -done - -ls -l "${generated_artifacts[@]}" diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/Android.bp b/tests/testdata/sharedlibs/build/com.android.apex.test.bar/Android.bp deleted file mode 100644 index 0c62e645..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/Android.bp +++ /dev/null @@ -1,96 +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"], -} - -apex_key { - name: "com.android.apex.test.bar.key", - public_key: "com.android.apex.test.bar.avbpubkey", - private_key: "com.android.apex.test.bar.pem", -} - -android_app_certificate { - name: "com.android.apex.test.bar.certificate", - certificate: "com.android.apex.test.bar", -} - -apex { - name: "com.android.apex.test.bar", - manifest: "manifest.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test.bar.key", - installable: false, - binaries: [ "bar_test" ], - dist: { - targets: ["sharedlibs_test"], - }, - updatable: false, - compile_multilib: "both", - multilib: { - both: { - binaries: [ - "bar_test", - ], - }, - }, -} - -cc_binary { - name: "bar_test", - srcs: ["bar_test.cc"], - shared_libs: [ - "libsharedlibtest", - ], - multilib: { - lib32: { - suffix: "32", - }, - lib64: { - suffix: "64", - }, - }, - - compile_multilib: "both", - - apex_available: [ "com.android.apex.test.bar" ], -} - -genrule { - name: "com.android.apex.test.bar_stripped", - out: ["com.android.apex.test.bar_stripped.apex"], - defaults: ["apexer_test_host_tools_list"], - dist: { - targets: ["sharedlibs_test"], - }, - srcs: [ - ":com.android.apex.test.bar", - "com.android.apex.test.bar.avbpubkey", - "com.android.apex.test.bar.pem", - "com.android.apex.test.bar.pk8", - "com.android.apex.test.bar.x509.pem", - ], - tools: [ - "shared_libs_repack", - ], - cmd: "$(location shared_libs_repack) " + - " --mode strip" + - " --key $(location com.android.apex.test.bar.pem)" + - " --input $(location :com.android.apex.test.bar)" + - " --output $(genDir)/com.android.apex.test.bar_stripped.apex" + - " --pk8key $(location com.android.apex.test.bar.pk8)" + - " --pubkey $(location com.android.apex.test.bar.avbpubkey)" + - " --x509key $(location com.android.apex.test.bar.x509.pem)" + - " --tmpdir $(genDir)", -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/bar_test.cc b/tests/testdata/sharedlibs/build/com.android.apex.test.bar/bar_test.cc deleted file mode 100644 index 9b397de2..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/bar_test.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include <iostream> -#include <string> - -#include "sharedlibstest.h" - -// This parameter gets modified by the build_artifacts.sh script. -#define FINGERPRINT "VERSION_XXX" - -int main() { - std::cout << "BAR_" << FINGERPRINT << " " - << sharedlibstest::getSharedLibsTestFingerprint(); - return 0; -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.avbpubkey b/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.avbpubkey Binary files differdeleted file mode 100644 index 931a477a..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.avbpubkey +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.pem deleted file mode 100644 index 9363cda9..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAp/bgVatvywj6wEKD0ldJ+RxfFOv/67norL43z55gd/K/u1W4 -GoFHm0e5T1PV0j5zMi7EraIWy5g9eTmtU9DTwJoIKHw/agrVimxBsd5Los/zHAGJ -JoRy5Wo7cenpp2QpNzfY3HdN88PMZ3FiRooa/GepoOI7IBs3IEmGJTC3N5CZYsh7 -b3yzZn4cuWuQZ+oD+G/z1AjK9Q1po3auz5lqSu1VD8GoswwfrC7G/MvH/5Jytu/o -XKESeGIri8YDMqsVX7hHTGbQ8MBpy5RtFfIGND+XulZvTl87bXAhJVbJjD3GlZyM -i/eZL4pUdxKjv/ZOT5bI1/NTJ6Qtr6yMgJuVbL7VHYFPISxdwXYY/fkr0AvZw+hL -KopDrb3Jn+79elZqOuZ6l1muCpkXRs4QS4fRSh4Lnk2782p+y070WhdA+zgzDLhX -4ytyhAGNi9wWNCjGDT3C8AOoRzn8PhG8sHsAHL6sFYpJx3LRyv/nO1jFGHDd3cgj -Vql0NTyVPlj8JiSQ1aQvXMtf1M7pwp6igjKPf37tVrZ4gDpjQm26VSy8lcgKgR8w -Ej4Ti4NbkcZuoJNjIfJZJAUDtr+aXJA2bWRO0ggTnuvC3sHCMH4o6i2FdNzsahYn -z2QY7h7hiuCdXJdoFWBhSOQ9cUcBroymuSNWhliSxj7XObql2hK+u3Bzp3MCAwEA -AQKCAgAdQrPYGNKT40+TmMLQLOa1IA0sXuSpkyyGk2izoZqaqs5d+1PkQitQUNFm -kWtJghmdX2ph+T/RXgcvjC220UViYzMSonqFpbeHss5LBzfT+DgY4+eZry846iXK -9X3/7EIF3ZPI7HvHAJAmYSlGsp565DA319GHCVa0KDrXVcJFSsp93AEs7eNu8n9c -ifGROMJSUGaAxLter2R81psjjU1oGipcYVdbQbxuyYNe3L1Nt5yGZArtwB2wnSGK -6wb5l7ZUg4zgMXUqy8pibcwHK6+LAJ0VGCOx2oNG0Gbl01WvOb/Tpn8RjyO/lXCb -gcLHGUiRMupwPHJ7EG3pEb00VmZUUCd5bBv76A8WyS9hrGZbcHy26nS/9HkadT5Q -IZVjWSbjZA13Tm0OmSKgwV3UC5yRZnRj4vNoku9bhWhZ+n7GCAYbSBiAVBrlIgM6 -yxw66Sbxq7jLEZEUDWLTGl00W07hvF44FHYIATsOMzHyLPbKAeozimP/sZVw+dFN -KcNVKJtvxf/xoZIvM32BevrwqlfgQRr6G+sILzP60NlLMqQTsXlqsqZlyZ6ROrWp -0q1T1NQBqiyNjk5Iq+nfQl0wVK1F8Xj/IrZ4udddKKunWtDIADcdx3HshkmiVVwp -3lTadEEEA64Jwu9GyoYK78+xINkIm8/GK4LPgo4a5eRO5JaAqQKCAQEA1/DzERsj -FqmrF3SVKt15ZqAV71YkVmlVTcE62Gr93kE0YEhkK9HrxcddkiPgkaKP/VUXpzwL -zVsvdIlPaCdLQVoZt/c2weDvNO+8DKtb3v4AYgc8fltSCvPMzdc3VezL1Y0mZmMV -sZndsw2ZpZnRrtHllqPQR8mC3mLgvhUyQw2yIcWPuI1L3VBTjd/9z6MDgDTZ3gKq -h5odK5gu+KIT5hUsnxCb1Z7D3WAw/uprwpW4IFIFWbMeXPwVGuwKQ0/ueqSWQPoC -mO5sVLd43auO9pAtp0BT8shdPkxuJiI7FNckdh8QJJOu2JkvN9O9/GZEDK2NuPZN -ONo0hAkczxUkjQKCAQEAxx+BeFsY7H2ai55QR8TkEaK4jVuqiJZFjMipQWkM8vyM -kYXUG/sV61gex5TZEdagxGdGyGsm58kq9cySY75CGPjCm9bPJ2qsKnKEkw4PK6BM -eqHres2x7XjE64tF4AIYLPNVmMQKc/5Ke7uTsA2W4YYHSwDKODRFGKhgc4oI5G+w -j5TtnqeOQ+WxseFEQEp9RgUIssO6m0K4lE9lrwTFsu95USrpJQXs6J/nZct/xXAO -fRuv/nB7GMSj+Ay7TEfFbAw0D8NKEYzpF1/oUjPW8aihXh3Eq6HySBjRbAH8YCjC -WZYSm7f7TLunIonRL+dqCHB3WFDsvta2nwdPdUb7/wKCAQAZquxZhi4/jV9m5Fau -x7CcgD7bOhQLqW2YVnWWL/GJL5r4LuKpSsSJt87phhY1eWtAI5MyL7L/b+1OHtwv -dyw80mboNRxvIzuLwUtK/jtnYC3PeSi5pEU2RBB+Dyzmq8T211ZPKUv01mNB20X+ -JzCDZTOzGjmxrsQ9hudL8N0Ol1wrI36X40O3RMsJvCxBOBE8dgvHle2LPMhm3CoJ -J8rRuIabSbAcTkjd0YdBZb/1WzKNtPIp3V6oktY3YwM9SQ0ByvqJMq6IWx7JWx2k -y7WsnSqwDLdtzl82/oLBSaRYL9KHr92NW3iXCm5QZnzYuZcxIpgL+krnjRhc8XBZ -NRwpAoIBAQCntCU61J6dLvwmcuNyTqU3JTEB/R4Xg1h4RdgnOu6pB4LsXSZTmpjP -aZwiw34+w+ELCWBYE8bkmE0ST4VLdEX++iQNVFGMBQ+TgHef0st8FrnS3uSQvQUJ -2BkhuF7VV249DYQd8Z5MKvNYWpb8Q7W7o0IpLTUjOQKozcbOCIeMvXSauPeYE86B -6MZL5kmxTAtOGZdF2AsmEH+ciXI+gWpwVbh7YASUJfVtxp8A4O9vvfy16cfEJ7/F -EHh4xWBJ0ni3k1+Vlwie12rJQQFNmlOBnGCr/65QT0ja5+wZZ2LDKhDlmrt5Yu7H -pZQSRrhj/CcVjIM3YpDB+dw8+880GuDJAoIBAD/3oFK1uG++uU/Mo+3YtZj5YWuE -Z5FNE7tJGeyKUvKXTLUO1NTG+HEmSGkOyeiXrQNJssFOjgVzNx1kke5Pi4qv4+QL -087gFnhDnrMHBSwcWIpkEe5zQEYrS/yRJlD63WM72ivLDNTXe/BQfuCIdX9qBHbG -qQQzjaLdj0xtKQsidL2Cy/PlqfqGzp90I6uXbRiOrC4rsNjr9mX82atNswua0tcD -mMGi0eJXtKOS2aaJPU9yofd4sBW+n94Ff5ue1cwfjdW/lKaAPDBGNRCgvvcyM39Q -DWK6NSHzz7pVO5+tb54vMZK4TySPrS1qlin4AALo4tHIBuIDkaIHTmDsF8c= ------END RSA PRIVATE KEY----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.pk8 b/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.pk8 Binary files differdeleted file mode 100644 index 6119c7f2..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.pk8 +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.x509.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.x509.pem deleted file mode 100644 index e687fc56..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/com.android.apex.test.bar.x509.pem +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF1zCCA78CFEnLOBRbjwndwo8AjZVTyorFhju2MA0GCSqGSIb3DQEBCwUAMIGm -MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91 -bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi -MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEiMCAGA1UEAwwZY29t -LmFuZHJvaWQuYXBleC50ZXN0LmJhcjAgFw0yMDEwMDUxNjMzMDVaGA80NzU4MDkw -MTE2MzMwNVowgaYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYw -FAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQL -DAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMSIw -IAYDVQQDDBljb20uYW5kcm9pZC5hcGV4LnRlc3QuYmFyMIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEA42Gi4Y3vNA1zQh+IDY/mQbRfvy5ikmWFTP4PwKcO -XSg21BiTx7o7k+cKbuHjCWp3eh2O1fRXB9WXLQ3//8BNLZLUuaGWM7+fcsN5V+aK -yPxzt/6fYBOzb4BkYnMgJjmOHBo9TL1g35IPM6O5Y3oSvCBhQuMdvZYbBDBAZ4Zn -+0PoP7fWPPUne4PGL5KF48ttpKbXeFhUjWcVnh440Amfs4+dHrblf4/f1iKUIrT8 -hNWgOBhUrHAKyKAyE/FYydVm6smLZN39m/5ac7LDwYzRBP5IDVKbLxfmwSOfj0n9 -1YvEyl/BSwY0V29AbCbsl9eVQkE2Jy1UqS7we8rK+V+Z3b/dTOh7skdAwMHGDi6v -qIxjNr9tAX1Pxhj1PbqYM7nIa7hLmNfJk6N/k72VjJdiWyp2WcQFIJQduSbtgMS3 -CA+LzCJqEbqz5SyaFHh89KsqHPGOxcsVHI5mIRWhBioCKlCbsqvcZg+ChzdY7Hbk -ViBhqmZr7cAXyZEqUXApSmX3E1yKCXry1WF9hl8homsG0rO1C+seS+R3OiWRNdrK -o9Aik1gtwpoxbJH+Hxc0usS4yW3b3YyFN2tPUN2NoTFrkKNSRfDwqlWCJH8wd1Og -4dTxyYesVLCKW0/YfPKBCPFlAqx+yDBOe7lxP/1kuWdKeIHxdhlGpjqUoaeqi+46 -+xMCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAlGQyBIzKvdstBN/6uASKOHcWuqfD -COyBXnUmMHEWawtS0C4aL0rJmvILQaVrFp3dPkjI3RpN8TZqVnQfawOewOBe/2Vk -cpzGxsIJQpjmFmMJr730LX/RDELnTnz3VKwIbCoZBZ3qktiAI5+rGmmP4FrU8MVl -m+VnShyxFSmLakhcyezOos3+ibJDgFsoNFDQ9b8aTUFKy4Xsa5OMSYeAJc4L2IWY -dowDHu5wnRTfy0uCXn095GdgSiYAAvSp58M/bXuVaHXm/Qg0upWdEaouSMecAYwz -PTF2sEpmoAUVIHOusm1Chqopa4kQtQCcVB3b/1YDZTeodjKHI7w9WLqjEeq7+fYl -msvxlYtyL3r6JpQXyYhy1643zmI8/P1TYTi1AbsVNwz/87xmHyrldLKH+jmWMspx -hiynKOTZahtO48WhkRGLa+iJk14ztUdD3MxAJwZiMUMFhLth/zYLGSyBwqR1smrs -J5k7BL67b07JApjoPL1OHn9ypBrV5L2CKwSfEGEzp65BtfGIGDYyoNxEv8zky8KY -XHxZaSWor6RQRp1QX6VRcXYk7XQHWuIevlW5W2APRXCAC05+rlVvi98swJQUcR3/ -oZCgxdr9mSV01fRZMkXv5exkhu/KR8c1ZstqRcSswRv4vrFFTFRahrTWGk0DQXU3 -EH5MwrfZ9sUq7Iw= ------END CERTIFICATE----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/manifest.json b/tests/testdata/sharedlibs/build/com.android.apex.test.bar/manifest.json deleted file mode 100644 index da13d6d8..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.bar/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.apex.test.bar", - "version": 1 -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/Android.bp b/tests/testdata/sharedlibs/build/com.android.apex.test.baz/Android.bp deleted file mode 100644 index 6076d71d..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/Android.bp +++ /dev/null @@ -1,77 +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"], -} - -apex_key { - name: "com.android.apex.test.baz.key", - public_key: "com.android.apex.test.baz.avbpubkey", - private_key: "com.android.apex.test.baz.pem", -} - -android_app_certificate { - name: "com.android.apex.test.baz.certificate", - certificate: "com.android.apex.test.baz", -} - -apex { - name: "com.android.apex.test.baz", - manifest: "manifest.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test.baz.key", - installable: false, - binaries: [ "baz_test" ], - dist: { - targets: ["sharedlibs_test"], - }, - updatable: false, -} - -cc_binary { - name: "baz_test", - srcs: ["baz_test.cc"], - shared_libs: [ - "libsharedlibtest", - ], - apex_available: [ "com.android.apex.test.baz" ], -} - -genrule { - name: "com.android.apex.test.baz_stripped", - out: ["com.android.apex.test.baz_stripped.apex"], - defaults: ["apexer_test_host_tools_list"], - dist: { - targets: ["sharedlibs_test"], - }, - srcs: [ - ":com.android.apex.test.baz", - "com.android.apex.test.baz.avbpubkey", - "com.android.apex.test.baz.pem", - "com.android.apex.test.baz.pk8", - "com.android.apex.test.baz.x509.pem", - ], - tools: [ - "shared_libs_repack", - ], - cmd: "$(location shared_libs_repack) " + - " --mode strip" + - " --key $(location com.android.apex.test.baz.pem)" + - " --input $(location :com.android.apex.test.baz)" + - " --output $(genDir)/com.android.apex.test.baz_stripped.apex" + - " --pk8key $(location com.android.apex.test.baz.pk8)" + - " --pubkey $(location com.android.apex.test.baz.avbpubkey)" + - " --x509key $(location com.android.apex.test.baz.x509.pem)" + - " --tmpdir $(genDir)", -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/baz_test.cc b/tests/testdata/sharedlibs/build/com.android.apex.test.baz/baz_test.cc deleted file mode 100644 index ea5b341a..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/baz_test.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include <iostream> -#include <string> - -#include "sharedlibstest.h" - -// This parameter gets modified by the build_artifacts.sh script. -#define FINGERPRINT "VERSION_XXX" - -int main() { - std::cout << "BAZ_" << FINGERPRINT << " " - << sharedlibstest::getSharedLibsTestFingerprint(); - return 0; -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.avbpubkey b/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.avbpubkey Binary files differdeleted file mode 100644 index ef865d7a..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.avbpubkey +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.pem deleted file mode 100644 index ee8b8a66..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKQIBAAKCAgEAu48tcVfIiZ9syLm4CqkRE5/MOhfzro4epTGV0VrkOc8JYQic -YjRAN02/7QfGk/aei3xRuVKqAqDxnZ5IE5ii6/HT9TOInMK08/vr7jc6N7dfQCSW -nyvG3WBC2HWEmuVCGJrY1j/lmIzKuUcMgpF0ZaqB1Qm/viI/ppXZiEm234SFAg0y -RZbiiGRy5K388xkTA0nbIAi3iRq8RSfDN4kFzIH/Y7AsNF/tzI2K+4PziGmReH/e -ekgPhC7XUUySNrjctl7naDgvuihu18QwsC+WNCop7aHLe/ByOrOFq5R/mGsPCP6q -JyUJ0P9YotI28y+h7rd88W6IsTQMz9jw1JENx2YGJHkjRYIy97vDDbeeyBm5qI3I -Suy8IgzG/JW0w4pvUB6pWzfDuw+Hr4sqU7Bjb/CFeFUfzSGTg7efDN8W3L/O/wtH -+hR1t0B0BZBtW9TOFCo4z30C1u2OltT5Vr8GgsODMdR6tQCNpu4WmWfImriETuKQ -OzPYo2eKCbsoLEQxda+EUu4h5FDH703JYWvrxCCmHeW624iXy1LavEc9YYOe0oRT -DslIvYSHimH9jvgXRU7HLz0obljwwYRM+7aHWmTClrIC+aSEq1x0COHt8fWtbJ3f -gBCdNSBcd0GEX55V6Ez1lytGWLzwPqedYbQnoKdIJPu5Ge7GMm75de0lgR8CAwEA -AQKCAgBoV2e1dVt3zHwtUrxjGdkJLM3lx6tmAWRlDCfHlyP+UQJru+mb7GuJGLTb -/YZojDt5Z8jjK2yvF7AyunpohHKmhhsffvLSGrOmRBDlrk2x706LFY/Brw3r3AB0 -ATSrIz1ZCNP2pQdqjXC+EBuSi67QXEHsLYdBFDaKyzSAUFnvEP8ZvBOqiR0vOYp9 -U5mz99AO9Uh1EsRf/sKcSlmdDJpwQiW85KZC4NcfA+M8txSFYA1wltpC9tHC/HgG -n218Ce2nezaLUS6kBphbaqaXbXHHRWmb7HWSVpqFs5d6c5tkRLLRkzM/oahLX7KE -qiOtuGMCtYtJmO9sfYNfIdYguy2JOylBQU1HLT2eTp/be29aUfblGpvG+4WM49+e -da33R65viP6uvwcTuiQIZdfouJLgADutfmLAX4XGk110k/AOzSFCeIvX0oeoc/Nv -IZDgGQjELBuyKdCEnheo3UkngXZg2hbVSoI+EQE900wm8s3vi8GoKq32VuZAiNGi -mW6ebs1A3fOLCPrK9GdbLu6A2uXyeeQH7NMiyK9o5jWPIU7NJ3KUgGQmbNz0/aqi -jN93DdRjeoYXUdD2oYC8S9XMqtdiUubVEMqDeGg2jhLxYDk9EfKD9KZnmFSBM7a0 -WlZlixTXi77ktJS7+1YlRWluPIhD0MXdEkHeRmgGZoc0w9ayoQKCAQEA6IrCf8U6 -SUG2QaLrIjAVcR76UETGR4gE5s5E1qvZB3/yGzou9QeBOGFz5lRQbrGD9nrNFuqq -Z/VJmsL2aia4dPDjjJLPW3budcq8GjR/ZdGCkE+VWc3D/CUf8ja4Lx3MxDjym0cw -i+pMysym/6QNo/sAltzQYehtQlGNUisII+p1FbHKQAZwLgczvglZObgynegSUyKT -xrXcUyqOozU933YPkPE1X3cKyF2JFoK/YzxMCfDlYIs4SfwlFnFELbz5ug4Zx2WU -Ouc9JWhAELYzHnbHP0OkP6mUTjSJC52XCdHg8b2Qtrof/PEuJ3Xoyz8PFBQIeRjD -HzTJ4i0Pzwd/rwKCAQEAznrE11R6IbsAznmE5d2nmrph8n0qnpXi9Ryd5c4W6VNm -+hnelTnhAkGtNHJCloL24zPlAlb9CAGKJM7U1I21PqgZpIJ7hHTU1FLiwc83dkal -IQ5yVbAPoa4O/qn0Omo+MQ+tYCygDHiAJFQFah+pX1Jj+6djn7kKNCzpVeN6aVFu -hblHAOgwhDlt+l461wjYsI39osPfgInO/z/oY0WuIFEYCA5kWXEuNImqZi7GTcs3 -EQVR+OxTxwt9IUJVeTAusv8V5DFqlojF9jZVBB60VcfMAEyjsAF0/ZsweuBU3jnD -mAln3pIS4AON0zGM/eZFKKkHZT+ZVqIUOVCFoyGBkQKCAQEAwmX10SCM6F7hwR80 -WCFAW4/dDCtiYrwn9NctHxUMWsOwHujWBosekIaPgFat4svNmMjyGJ1WlY+t143y -t6zk+QXEBGlapYjYMmqoM3P9qJ2r+348SZXFqE1U1oS+Fs1fuA4vanXp9J2LUuIh -HYcEzDfyNywjnCXU6OMKNE27AWNoPBmkDUAUmbX1oIFqMOF2lyFB6HP4e97ecDwc -f/3rWpr0ymOLDeKThgsDpmjpHEl0+76B0uKvzNHYI1nO+DmJvus4y8N0VoWnTVVI -cXAPbgE38gBXF81pKLOseaRldpUY6p5hkxAn26m3vs9ILFjr/wn8R1fXDohv2P94 -vsbzCwKCAQAkXsu9gkvhFSeXNyCJvPmA78PBCvsu5AgOVPQbPqoaf25sL5Jdhsxz -sU3pJxdDm94RN1rnhpsbhenngedLaYq7drDNoY5QTqQOomr+6JlEZD1CDWFmZpTa -TeamRRmYEI7T5YcMoc+vYqpvu70YbGtRNxoVge6ye82oUyDm2CL/2jA1reUr67pg -EB2nNGH47r38m4ZJ3WbJJX0oyQEOO3/ogWBSSvayKpWQ+47gYOzdVyZkASPnTPmU -3hk0epLDvhD7xqL8hxfXXFBChl+DUkVBtufgRZ+vqRIKegOYIVvRqSsi5MU/F0vr -2bRptxi2wJD+EIgU9Zb1A6e8UMq5aXWBAoIBAQDS8w0TG4R8SLHXOs9JagC53zAx -qVV8mrL9BEYzeuM6vNpyZIO75O94q2ifGvzsISxLf2xom99BxpcWo9UAyHFgubL3 -+P0spdJSeP0OWJgldEqwWGMrzQbYZomzi/QUlFpXNZfHgHZLaCK7qKu9RDQLVtug -5i+yVjSKl6RcaCCG9E2u68yxKlI246RNK1HZXQgxsnz6BnP/cqAn9G+yzT9p6nmK -tt2d2s35MS1zV9YzACi2idsZBeio7bghC1maj/TJvq9gRwIRN4UpbPRsflpdejFY -nEEW+tEbNzFrepkw08+9dRGnPU1G1NYvyQqMhc98lVBiMoDiH8X18gVWafhR ------END RSA PRIVATE KEY----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.pk8 b/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.pk8 Binary files differdeleted file mode 100644 index 37948c1a..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.pk8 +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.x509.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.x509.pem deleted file mode 100644 index d41d86c7..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/com.android.apex.test.baz.x509.pem +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF1zCCA78CFAgqiNJewYXPYz+41LOIDLe9lXnzMA0GCSqGSIb3DQEBCwUAMIGm -MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91 -bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi -MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEiMCAGA1UEAwwZY29t -LmFuZHJvaWQuYXBleC50ZXN0LmJhejAgFw0yMDExMTAwOTIzNTJaGA80NzU4MTAw -NzA5MjM1MlowgaYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYw -FAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQL -DAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMSIw -IAYDVQQDDBljb20uYW5kcm9pZC5hcGV4LnRlc3QuYmF6MIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAtm4cczWBEzR+kzRPHsBh61ihAQNtWN2mKLhZZKRS -YrNZ59+M53menjsc1Bs5fYvKTMGj2u2a/yvq0QiSAlgVMY+1aZ40FN22tBTmy4yr -6iTMi9Q+uUbQJlHLWSy8n3oFuxEHAP96ZPDJyqPHSJpKeSPKIgwyw3I+nXb/fmB4 -HvBmjHbFnwvKqHFyoEm5p/wGZn2vMtxZqlignNtUZr2zYTJW9YCuL80L/o1jdAFk -5sHH1tBEcp1ZTXRgKm2XFfp7fqfOWkS9XL3VUF0LVWMgyv0u2fr7n8TPZb8iz5PA -naCUN9hCoPrj4vg29eVYy2IoeSrLOAUiK986mZDToKbhxuLSHVV+IRNnqIQV0+mu -rR8OCqDlWbD+fU29cacAjGFt72unLAIEE49GHZFS2pTZ0cDXX1bBUrBVTMlcnCjn -YiU7XtPaJGelBkJEu/ErjHE9TrsQGKkzMwd2ySTOsk3K4OtWl+E9i50TW8Vu7gR+ -Qr4lZvY90OmXw+k02pkCo5g1GU8uXoTPjd0JQsmiEk01dMIPyWxXBYiuiRUxu3mc -vYJJGKZSMX3VKKYmtFN9lTuf/OyjztHjRQPit9sWVcYgKuy0kw1LQSJxtyNQIeFm -ipZVzh8Wo2TMm8argWMPHxyIfjEiVbNhXirC4My0Z02agyyB4Edxg2jkiKQBss3y -/IECAwEAATANBgkqhkiG9w0BAQsFAAOCAgEArOMz9Hn03yy2ano628v0wXMFixVA -/XzSpb8GWi4GzJxV96c9t6QQPVdo/XS0uBuEa2Uc0/W5icU9+iKzBvQHM3MI1jI6 -/oj8/mGAzvyvIA0pdKP1XOvugsgi2UDNr5QNgZ1UPIpVkzeBQpLgTiWL70Pl/znE -b7Q1nKEFeNWxBaMk6u6n6gNh6sMLv1doSCi0FM1cnWHv/0qxPizGjTHKmGs4TbMZ -zWnBoH8XSMxruAEbucl1E7vYXgYthOW0I+SCFpyP53e9VdoNNYWMeNKCm3EoIY86 -XWLArBBQcCCrsLas45670ouQ/H9Yn498MwFinuWcsfROghXQwhn7fPaDQ2oVlftj -4LQS2vD20mJV15wa/1n/VDzRAxIZwPdtbJX6JZO8E7Oc0+WSehwhaU+x7k9GYyBv -tiEGS297qZC0/WvoE3VtRHZjOzphxt6PLelCEoqhdZy+q0uGxj8TmPRXo9xtmBLH -6GPgZe3dR2SJ4uMqBjt6/6/Rki3du3btAn2O4b0Zf6h3wftqK6INbsJ7fRpIgLaU -GvCZiCUZaxkNcCO94RnZJrTTVO1rJ1ZBkcqyMHvrSRhPCAyH2zVtHcNDCiR06GNA -+7P5SuyntvmnZKteKc2HdTnm42ewIXyDIwTs1crc8/luevw81s+SJqWumSGTxpfU -IAM423xlNZfBXZA= ------END CERTIFICATE----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/manifest.json b/tests/testdata/sharedlibs/build/com.android.apex.test.baz/manifest.json deleted file mode 100644 index 1c57f3af..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.baz/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.apex.test.baz", - "version": 1 -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/Android.bp b/tests/testdata/sharedlibs/build/com.android.apex.test.foo/Android.bp deleted file mode 100644 index 451f8a01..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/Android.bp +++ /dev/null @@ -1,77 +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"], -} - -apex_key { - name: "com.android.apex.test.foo.key", - public_key: "com.android.apex.test.foo.avbpubkey", - private_key: "com.android.apex.test.foo.pem", -} - -android_app_certificate { - name: "com.android.apex.test.foo.certificate", - certificate: "com.android.apex.test.foo", -} - -apex { - name: "com.android.apex.test.foo", - manifest: "manifest.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test.foo.key", - installable: false, - binaries: [ "foo_test" ], - dist: { - targets: ["sharedlibs_test"], - }, - updatable: false, -} - -cc_binary { - name: "foo_test", - srcs: ["foo_test.cc"], - shared_libs: [ - "libsharedlibtest", - ], - apex_available: [ "com.android.apex.test.foo" ], -} - -genrule { - name: "com.android.apex.test.foo_stripped", - out: ["com.android.apex.test.foo_stripped.apex"], - defaults: ["apexer_test_host_tools_list"], - dist: { - targets: ["sharedlibs_test"], - }, - srcs: [ - ":com.android.apex.test.foo", - "com.android.apex.test.foo.avbpubkey", - "com.android.apex.test.foo.pem", - "com.android.apex.test.foo.pk8", - "com.android.apex.test.foo.x509.pem", - ], - tools: [ - "shared_libs_repack", - ], - cmd: "$(location shared_libs_repack) " + - " --mode strip" + - " --key $(location com.android.apex.test.foo.pem)" + - " --input $(location :com.android.apex.test.foo)" + - " --output $(genDir)/com.android.apex.test.foo_stripped.apex" + - " --pk8key $(location com.android.apex.test.foo.pk8)" + - " --pubkey $(location com.android.apex.test.foo.avbpubkey)" + - " --x509key $(location com.android.apex.test.foo.x509.pem)" + - " --tmpdir $(genDir)", -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.avbpubkey b/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.avbpubkey Binary files differdeleted file mode 100644 index 575ba511..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.avbpubkey +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.pem deleted file mode 100644 index 1d651b58..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAz2ykMX5GHx1z+1BpFcNwnZv/8AladKxjG962kBuZrxSpu3pb -8JKTqajY1TNmM7Hs0P8RjDY0M2ryNWodNu7GZKSOz7L+LztHeJFuZByCHjh4em79 -sYivhJKqlX0EVCnxW+IPQPQqxJKDoFBGWo/HfbhXiZJR5OT/XrNhl9KfpFL1NHfU -dmZHIqFJHCrcQ4jQNxAh9XIvw0bwEwWe/q3Hdxs7UC4mH4m0Hz3cmKe73H0al3FW -2L3xgy5vmc93SgzO0Csrmcrsp84B3qtNd6B0Xna52AEmBMuhWMRiFlIpV/Pf9utS -PYoxU3IRjUJ0FacMbhgQOZdtXZGW+G3FLnH2XHX9rUNnrlMmgTmoCGH/JD4ZwXvF -KoEUZ8wlGVZksNz8i79yPMxR/QyEppf56YSD72FrfCBMVdBDi0vYk7/nhqJU97ws -VFwhFDTxp0//rca8UOnn0pP9HKylxT6sff/w2U+zzzn1MLsu/Tx6JEbDj+464RnK -zxBL0wyzUUkwOodgctW8UFPSrttbQD5+7G+kQUl3q1854bX1EdW5jGF3l/OxE9P6 -mbsZlHPOjd4nFOtju7cgKpDbFtVwyDZRBtoPBIbBqS9bf7Y3oaesVeFkDfeC5SkC -bUIDlri8Sysuse9uoNtaGzvYu5Wnuv3FE1V4X9Ak92S8VyUT2a2miW6Hj9sCAwEA -AQKCAgAefqdpC1p9ypO5l+nLJE+TLFMlVAqzaoCroUOPzi76+Xu2r1eC99mzsLoo -JgVZhkf9tfI7feCQyqFPTwl6gQIz26mPSY5rHTj1tdPX7gUHMmAsB9NOXX0IbZOc -pKOVSBFO495AO2VqPuwRDpw5Rjga+JYOCK/3id8tagvoCTQlMXkRPKjEu2ar5bBc -7sQxPZT28206q43wFKbI9SOZ56ySizNeJ1q9ej479ZlP7CEHWnElYKlW9h3inloT -79dm0Jk7K42eb6H5TaUiumaKNtHE7YmHAyw2ukU/SqftBilD3/vGTnRpzb5QuU1x -ShrM8CE4slr4TJXskrHyVhkOKf0A+RNKcZ6YICrEK1GGOTItNMcAGbHSTQBqVr5I -MApXfiEQfZetgSv0e9s+DGecB016QG/uJis6OyABV0hYBcnrNPNgpUhmDfy+IN6A -dpdjeSD/7iOXTEnCe9jrY5+0ot4rKZbl00GoZpb+3pYoBcpDiIVJvPxrWUZeC6lc -aZjgmjkg2IDXE7swevsStLqSRY3cusMTnRS1Ay37/gobE+f2rJZArY4GAobckT3R -im6q8cfzTH46wAZBuO6wHlmNfbDVZlvXx6zFhkqpefxRQ1V6Fa9LvkICcGmuhn1O -9p3DBphflcbHtMGwJpmmr0A3T/aakw2aMT9Qzda91SItlqxy+QKCAQEA8pE4vt3R -a9PEABO3rF5oWT8UaHfGBTDhoZnG1KDofDvoKrw2R09mz5nhROIDnVaQMtkWChhi -YhChOHZANp1K2xaCXqDRzbFVq1e/HB9Ag6mTZkT5R9Z088/vOWRIA9H3rk74GB+y -9iaCusk8NiIqlubvc7XaoTO4ChQ9X+p9QDAmvZVaDfsJGSXYvqfVfMy179mIlzCN -DSTePQsn1qEiPKltRNBysuluDJqa/PhcsGd7Oeo8SJuLFVOHk9rTuemvIbkxI8v8 -PZ6LD4ORCDsEKst5giqvVBl3yZ2ntuLlouxQGAg0F+VGxSvDYz81zwUHUmsSXDFo -gw22tWGCQpeFvQKCAQEA2uk2jzEnMvhklfh+3mAUNpPRUyAC7KHIB0vSgvkRTK1w -aqWF5jCx0onz9sW7lGYcM6RRXBJNi7z8NxouY2Bdw7ijGd7ZuL2ybpbsIetfMV16 -xGZCEL1JEHOnglEochGzspwKmADqWpV/suXKcOlrAZeH1y28TNOgaprTG8zr02DJ -CQrV8uJnE3HArEHtQdtjsPv/OhDEjEA3Gl2k3hRuhsf0rfeMby0QvvqyVb0Y4SEM -iIq8MP/dZPgNrDi4r3s0aps10ciAHyHtyuLv2PRkc5ddsBh5V2uEc+S0TTFRRW5N -uuM6E7uiuCbNrdd/1dfl55ojIYgI4TgR6lH5kmPJdwKCAQBPAK9rstE/fkRLBiD/ -WexAjQP3lnL/Q9FpEa2pmRK/S7+tE4nWJe1FVkgBaF9nAkeK2BuOhCye5e2sdw8o -+ofj3WvuqBBNHyHY4YZUAXXArB1e5L4QALAsrJ+soJW38M3rjrrNGJ3v/9D6Rwp+ -Uxht958rn6IqeK7LUZY/xB6xJj2n55niDc4Dy8jRJ9anhAEJsl8DZwO5sTVUympa -RDbjbQcyr3V8Af0ey8gI9lcx+TIwRbMGrupYstDofhARcCPjJu7zSr/HzfhawC4f -cSFFUuorU/2wtW7HUrrKHRJPwwm/GgTld35aP4uuqmq7F1cwJ8FeF5WDgZbtcmm7 -iKA9AoIBAHaOWSsBns4e8jK6atM6S5gnQ/V137+R+ofhC3g9NZ5GTByl2jeJZbS1 -W7fo7Kb5Cgr50cpAa1jjl+CrwDW3yfAmvcZUB6viqJD2EZppI5vTmZpmGx9/s+NC -D5UnKPVmGuD/W0lpLYKzdn5HrvSppXcuPrZNoa4l6rnxcaWbvJg00YuhH6+z58kD -ESr5ZWoGTB5cy6QB0sB2QqF318MiY52BC0VwTNElIe2cThrbF29Ne8EzCaqr15ZI -NPdxnKwE2KVnu6UKpkC2GleHwgfIi+KCNo4ZIxYyN4CgevlXXUFx9IzjZN+s/fon -obqlfCkvDOb6dk5BozV+LU2u6a/bdQ8CggEBAK/n4ELmvMRbkB4lr9BEN5GyWwvq -Zkd3e1EcefMpR/dXv+2YD95BlUa2kOhvGVNb2TtnTGcodtYgmWgkSCNwYGO15YX+ -f1OdqWJdJs/FEgMWyAElLHcrwFUJIDuBBLCZrYYKSgNoUM/Ddk/CaAGWT4lsq1U7 -X+GC8SVGkxyqV4GzI4uQ9/vxgbxP9zIbHQqa4BUUM4kzaWxTtJuaF+fHsafgTsDb -BDmOR/vVAhbM02Bw6xCqWNB337dIC2LJNmvmbTz/4lxe9mZIvN56UzcXAHSB8Xkj -fbn4ipVY7l/NC4LBLzRvEItTdNr7zAC4QpsU2/upw5PPhv5R4Q2FKV96zM8= ------END RSA PRIVATE KEY----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.pk8 b/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.pk8 Binary files differdeleted file mode 100644 index fa38e322..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.pk8 +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.x509.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.x509.pem deleted file mode 100644 index 145a6560..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/com.android.apex.test.foo.x509.pem +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF1zCCA78CFGuWfjBtDvTQFqx0afFbqDDoozbTMA0GCSqGSIb3DQEBCwUAMIGm -MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91 -bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi -MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEiMCAGA1UEAwwZY29t -LmFuZHJvaWQuYXBleC50ZXN0LmZvbzAgFw0yMDEwMDUxNDU2NTJaGA80NzU4MDkw -MTE0NTY1MlowgaYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYw -FAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQL -DAdBbmRyb2lkMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMSIw -IAYDVQQDDBljb20uYW5kcm9pZC5hcGV4LnRlc3QuZm9vMIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAuB+RzoO8K96g3xYQng3cToQGYfCFVA47S3qUBlpj -Qr4IIw/R/Mo/0/LoK3OMBbjrpwYm0mBRrBttoX0jqlANDOrsk0T6jrWsIiz0iDar -5PEJjZpnfQILN76shJ/YgW4GQdLvjrZMtksBxKXLbID1uxBBD7KZzIpb7euiV06w -gaNJZyZg+2J0Isj8qI4H34x2GtjQxd2rN8KTLYOgatekkQCauHH/LLCxVA0K62v5 -+NPmrhBTQNO8TSMLmqci13jYcvs9Oj2qkwQKD9i4SMn1VoHrswjulXMibWEyu5xB -72vjwd9+xpLDcLxHDCQW8uFS7z/omhIE/DTC4QnmGmfz3gyi8O3sekLAdrP5YMz6 -+GrrNN8dwKr470g02oMtdpvTIC+4CcIMUuNBPvzsLUnCAxYTWo2QM8hcHx7Bs3XA -gRaeC2pEIcz9oWoTr/G/5ipdmLSDUVtBQAiNa3KouY9OAO7RtqmweLiXaBfPQSBM -lBWnyNhfEnhp++4Ef3LFDRpFfyo12XUysdmdreVjlZJ8MykJD5AU0EZ3gQbjcmWX -vnIWJTc3045dMCDn/1pOss8//q8doIhwVoTCRK0UAofZEajbtfDQTtggWyjMTPtR -pDtAJA1kmBLnCpjcrnj995pkz6rbMU76zDE7SzoVHE0zRnjDIiLCvNNScJ6oxY/R -/wcCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAp5kVSkIcuFWMl2X7yTyC3AuOfsOS -nqnEGs5ns1ij1SjSsogwt4apnjfgXdc3sye2eX3s1SWnxkIBwBQVOIrvSWgN1Wda -9UQ5uKcrbiz2yT1QMBD1VYsv/zzRPWNP7rzcR7szfaNQOje3BoCaQOkWutgDKi3O -kN8mz7VtfQniKvw/bbrSQMyVkQpy40XQTyJckfizomTVXlI79AoOVayER/Osgjp9 -8qwjtKVp/o3f3Nd5g2yS9GwsGBbXST0KhSB7bmsLHxPGyF1Zw8i6kKMuS9PX0E8r -lBGGMZceKDp0eGDXeEUdVIn7labsS2UpMKRuDurpsvol8s3lVBFhAD8yT3yLRNhM -c573H1ttrE/tlDOj6pdE/uz24WG+M11iKNuqW+/XnUiUjZpK+2Bt6ev4Yg9cDknn -ih9dr+/YEvCopBCgwaURhTOxDKNNDQCQbu10NnT+apnomWVgWIAmu9SJRE0dFHj/ -46TIUnmmnjg1tTq90yJYoZyXLib6r9PUIXtUEGtBbbjg0axhUqP1jr6FKYnnK3TP -NkxrkUVDUuI76qDhPIfqECymJz8fx97AJcPGT4qwOJKcCgft86RHOJIFr1AGVGSw -b+Vw2IWKNwyAqixjN/hhmNH2nlUdhuPPDk1GuTwlODuAvC/GTfOms7yacSeEO0wk -Sw6cwZWCm3nMiiE= ------END CERTIFICATE----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/foo_test.cc b/tests/testdata/sharedlibs/build/com.android.apex.test.foo/foo_test.cc deleted file mode 100644 index 34d44d4f..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/foo_test.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include <iostream> -#include <string> - -#include "sharedlibstest.h" - -// This parameter gets modified by the build_artifacts.sh script. -#define FINGERPRINT "VERSION_XXX" - -int main() { - std::cout << "FOO_" << FINGERPRINT << " " - << sharedlibstest::getSharedLibsTestFingerprint(); - return 0; -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/manifest.json b/tests/testdata/sharedlibs/build/com.android.apex.test.foo/manifest.json deleted file mode 100644 index c986f711..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.foo/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.apex.test.foo", - "version": 1 -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/Android.bp b/tests/testdata/sharedlibs/build/com.android.apex.test.pony/Android.bp deleted file mode 100644 index 3dd0349e..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/Android.bp +++ /dev/null @@ -1,77 +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"], -} - -apex_key { - name: "com.android.apex.test.pony.key", - public_key: "com.android.apex.test.pony.avbpubkey", - private_key: "com.android.apex.test.pony.pem", -} - -android_app_certificate { - name: "com.android.apex.test.pony.certificate", - certificate: "com.android.apex.test.pony", -} - -apex { - name: "com.android.apex.test.pony", - manifest: "manifest.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test.pony.key", - installable: false, - binaries: [ "pony_test" ], - dist: { - targets: ["sharedlibs_test"], - }, - updatable: false, -} - -cc_binary { - name: "pony_test", - srcs: ["pony_test.cc"], - shared_libs: [ - "libsharedlibtest", - ], - apex_available: [ "com.android.apex.test.pony" ], -} - -genrule { - name: "com.android.apex.test.pony_stripped", - out: ["com.android.apex.test.pony_stripped.apex"], - defaults: ["apexer_test_host_tools_list"], - dist: { - targets: ["sharedlibs_test"], - }, - srcs: [ - ":com.android.apex.test.pony", - "com.android.apex.test.pony.avbpubkey", - "com.android.apex.test.pony.pem", - "com.android.apex.test.pony.pk8", - "com.android.apex.test.pony.x509.pem", - ], - tools: [ - "shared_libs_repack", - ], - cmd: "$(location shared_libs_repack) " + - " --mode strip" + - " --key $(location com.android.apex.test.pony.pem)" + - " --input $(location :com.android.apex.test.pony)" + - " --output $(genDir)/com.android.apex.test.pony_stripped.apex" + - " --pk8key $(location com.android.apex.test.pony.pk8)" + - " --pubkey $(location com.android.apex.test.pony.avbpubkey)" + - " --x509key $(location com.android.apex.test.pony.x509.pem)" + - " --tmpdir $(genDir)", -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.avbpubkey b/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.avbpubkey Binary files differdeleted file mode 100644 index f7af6e90..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.avbpubkey +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.pem deleted file mode 100644 index c0e2965d..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKQIBAAKCAgEAu5za6xUAaREb/RXJGh0QOi+tPLjtmDkYL3COBcZ6dTsBYZuE -TkBwNgsvfk713Ddk6iOirjcakRLdj1K9lJdyBHMocNz8TjizO/3uRhLA34Fgog6Y -h0xwpDfb5gPyb18OvCVFuRLIremGa68zPnXl+p0kxzqafKICGjAp3x3ugNcSMXlN -0nKm4MSDiqZbTlAYutW+jTFr+e2VABbbCY6ijgQJMFDHNkA4QgW2rwEK63OaTImT -OKT44JnQ5VJpl8VN7HRaAyg5MUjVfuo/HSIkFpYZ5rxgWOZioqvJw+lFV6/TSrxk -P2JWQuu9mBDmuAoaEHE/yyLPLDKtQUquc6IwGBFG2/dn1WKoTB1X0TsCaIwTBGVV -n23foz/+uE2FJuh0mNaGWMHN4cOekbYhti1xIvOnHFlOIWdpmRRkIvUwjZYVkCu9 -UnPDHmhbA+3cr8scqc66JTESc1A/uoIGBtK10aY5jUyg4Tvdjc+PlwapeIAseYzK -x4eW1ujaTP+DEMBaamQLOwhvewxXubTyhjIN4epQma11pVfLwfuM7GAGqlyzTMXE -BQLkm17TIPEkb+wpU9zP4Xn5FshE8mb6k71fVXwWeBaHtF+tx4Ml5Jv2XUe23EDJ -5pnIB0sXyJcEjDlqbNCzfZn/LMJQHSd5h+wfwLoeL3U5TigpFSQp9MypSX0CAwEA -AQKCAgB7LdhaYrabRT2AJI6eE5j06xqt9KkiudHUS+0jg5YhZDVa9bWffxVtllh/ -cK5iAQjD5dPI2Ksbtyw7DtMkPW8B1u4ldCI/5WBgsi+AWI3D8XkVzcl9g8WtPHOn -iM3jK6FMDJjDk76o2NuF1kkp6FSv//8Gw8ZssB37PcYwFMHkW9E5JHDhDJ/ekYfg -P6tRNquV+AKdR2aieMfMgDUeCEVYQvQZgd/aEb4eMwwnyOJ3hrY3LFi55y70oGkU -N9DWchfgeOAklINAhZaPNpNruF/DaJfm86W6mMEIFwxpEb6SfQGYXyrept0GISuh -LO+exBsq0oBVCizF0xwH81Wo3EMAWnjAGMNxzKOdtJhP0VCL5asgMb6/QbKecko3 -2kZNAeW0aC8wFajCOarQQ5JgMcPdC2gGqjUakqHj+1QlbvG/KI1Xav0ET/OIFOuY -RAneqIaWVkU76tldm3mnp6b4vS6lsT/v/56sHtCUWGDmVF8zNkZ94LF6XcbEt6ZJ -p0Ssq0e35Z60ikAPuxNXNA0kgJBpq/V6j0Iqf9otGuEFLCy9AEtCKSYHVQvwRmMC -wLGr0L47p7RSKyMqWwJFZmMPfbAUz+IlYtxqzQ316L2NSCRnTHPV+thh9W/HIXvu -pciYkbjk2m4cpjiYEZRS6wLadfPGqMosNJX42G1INM2wEzfeQQKCAQEA35iBH9P+ -hWGYl1rmZBQfv2uYJPo6TDyM7kOrIctmPek8XPWNI9jnayE0Mmpf/eA1sTZpOzg2 -hVVc2QK8Baqxaq6JwNz/WVIvvD8kuCnsBLVYfAxEXgS5aF6NYbSg2oZf0CaMAFtM -xf0SIyhol9Xl9d48CrmumNZnkvpa7AzOvb9PRn8TXFJFkmkyDmDWvQVaBZbbkKMC -Ak3UA2nB8ypXfujLc2aBAeoSSw2d/EdoZW4TI/v5sFI+FM9Eg8KG3MP7rhaemptl -6DNh5PpFYl6smqE87Cb+0jmR3ek9jkjxXJxE4VrkBrDT4BYogb4TnNP7wdux5GJ7 -NqBAGQ8Mjj4ezQKCAQEA1s1dt/VxbOBPCRWlmIlWnmiY4pf3dkl1HOClNOwtviG7 -gCRmeIwtt4O2uwZOMd/JyYLBg4yieT8cCDbXSPXVop765bftBz/Ce5xU9sadLzI5 -sUdHRJkBtHsC12vXZrlrcnVauKAoArDcz5AgndqZ6i7lw5wsB2fQhITGJ1fYMW0L -RRugKU+wuAgur5GOoTPmryrf8WAbKYh9eeyoBHvK2u8JxsiUXUuJIPvOPjArmHLe -fxsoN8G3o4Rud9ugPEjsfT3RU8oA+9+LUMVbNl6q8RF9qherxKqmXwukh4C4iuVn -MyJ3FBAq4Z4BSbyirl4APC4Q41z//ZTYrAuFx9J1cQKCAQBY99ihLnw+3GeYCe5U -cgFz7D78r6hUv18gS0Kjzsge6FhBcN85HUxvvyWCzfrmDLmwisLyclqXUTEBlGn2 -I0Y2+b4MRKNCCka+M63Lrbqg4PuVWFg3xM91bPH6p6G9cexb6YqZdbqlqR33aVO8 -3rqCy2u+pMWJQP6zZ/SXqjz1GVNU7Klqeb3/FOZ6/CNV0PRR9wXklkftXMR4mzM2 -K2nnMIALqgS5G0cuH/v17v/mJBdvoQpoE0FqjFJpzxRUcZMKYSu4vw6chx1zu/Wx -v5QUbwXLvXR1d7zHvM/mdrW7MN7jgIPs+Z1Es+xoO5aYN20cZOtywZDfWoJGtks7 -qhIdAoIBAQCqntwXmH2tRwtgovIzlLvZ/imaq61kJvtAoex4ejXndfHy2ncOwAI8 -aAJI0rxf/2vQhe1iqd4QwyFoIO+mw6cbkn6m5A8CGBJKj6YpkyAd8h5Dg+PHSGZD -Twa1yLKDpTsE4tTaHFVLteLfeJN/77kcfH4Df9S1WTAXY0Pm0m8m63/tOAFjbypn -NBCpYsxRneFaOItDttw8hG9u3p2jWhWLDB7O6Fp5NNvK+FkdqrOmV3AGtLKgf154 -I2SADlNcL2yyGt1gWe+oIiwOT4WhTVcpP4R7DGxjPk4C50OcYpGzun7b7j96D1GQ -fyp0wMLUEFTNeKXvg9rPOWFWX5y3WaPxAoIBAQCiWoixjIhHK0f2yMvcYAzqb3A3 -AZs8IjU7phUxAa8LB4R2V5NOYsUl8xIZqZgoPjyJvlI0S82IjEuLqnsmh8GgtJxa -vOL8JLUX27kr/eOTkz6EnzYVtViFM+D2re7QvakGSh+D43sC29S+dUSMrB6XvFdP -pBbD6paLmefvgRyP8kxMTujsqVS9TSAqZLirxGSC28wgrRx9CRTy93MxUfw9spCO -V4/TO384iLZBlj6bDJc4g7YL1KYvZ6ZCCLWoq2TXyBHAOjfThttOoGFj1B4KqdZP -rO7xAyJCS3t++Nm1AWqmnA7wmNEhMruEZchqGOP7A5f/xcxdsjY0m5LTth50 ------END RSA PRIVATE KEY----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.pk8 b/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.pk8 Binary files differdeleted file mode 100644 index 104a5830..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.pk8 +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.x509.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.x509.pem deleted file mode 100644 index 2f96a293..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/com.android.apex.test.pony.x509.pem +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF2TCCA8ECFBuIrFu5kQxwNJM09/WyY4JaskQLMA0GCSqGSIb3DQEBCwUAMIGn -MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91 -bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi -MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEjMCEGA1UEAwwaY29t -LmFuZHJvaWQuYXBleC50ZXN0LnBvbnkwIBcNMjAxMTI3MTA1MTI1WhgPNDc1ODEw -MjQxMDUxMjVaMIGnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEW -MBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UE -CwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEj -MCEGA1UEAwwaY29tLmFuZHJvaWQuYXBleC50ZXN0LnBvbnkwggIiMA0GCSqGSIb3 -DQEBAQUAA4ICDwAwggIKAoICAQCrNP9VtH73dqoDIKN9GQ/XTizM2y79KgiUah8J -4w2FLn3Q/TKxH/NRJVhunAbNLbu8bqfOhETFFWWr8FD2CNq7j5ZSN/xZHHgo40F+ -U1+SisPErTVa9oma4fn62qrzwOZ/uDqwCwzv2Qx7SAYE52M4JAwMI16sV7KZGXBI -3MZApfRWcuk9FcH0SPCQReY+P4nRqpNqSdsDw0u6BFnPmc2oymkUy7VV7KLjpNo1 -si5M43U3QyLhx6abVdeeIdcC1ycNYEyYKWigBqWOWHIxj2FWUA/vF1OLJm6AR5Z/ -VYh9/7EPvPW+QsMwaM/YRgoCXAnbWA1T7XDJx6aksy6CHt53FjufNwqKC9npthYl -rdy7rv8XBfrp6/ZXzRhsEWXMHliGrDQF0vyHPgs9CsfdPQtR3H+7tV5nXfa4vj2S -WbkmZyOyDLTQku7luh2dDcn4Jw9yOoAc2cD7ql6H/Hd4MKrPX/SKahpK7ypZebgY -RRovxgwxrgFkP9J8/xW1GCY767a9E/sjVaFdQb1rAUWkNG2NBUDtFKdu7mg1KFyE -c1dTY4cpLiahWLyD845bLnunWaYqutRm3ufu10lBstqF1RkBHcqvoxfoa2IeNB23 -MPqKGaovQuFj+cRHsetC5W3VmROcvAvIJzhcvy3CK5JhvXdvGTkFaoA+6hifY2WU -Chli5wIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCKEncgnGCwsHkC9UGg/HDwU97Q -ZH2kqNXdFRYURp8BmQbtNYuYSs9CYDpGugOjW7RILmr954KO+KNCo1OX6c4RWwQ0 -5LAh5+/FmBHiTJlWHRFGhL9WR8Mn1iwEsrANMyhoeoLwlKDVORM2/rS7APBM1pQl -gQfQHLt0cWoFpoL+pHElyuoVVKGB8sf06atcB/U8HND/xSY8fa7YjBOPYIMoUEvQ -aeN4JbxYNStqGDEz7+FoguXudQAvq8JFECsFwjWZd0/I5AjqcHiAG9ZaOrUiVRl2 -MdZSf2fjeSHXV3TH7i9f5vmcPUNERZxo/dutOTTVxlS42qrUaVwuSJzDG/MfXZWB -A6IB3P2qe5lzGl8mmvGKbX3ZXakf0OJaGb/Wn7GpsTynS3P6Zqa02oz89kquERey -PoHrR3tzNbUbDnjc/px2h2esG7E7E4bRc1PE6ndpvhs2vKypTpYO2v0cXWSk+xiR -eUZGPIXS7yMHSu4yHxoWUnWoEfzhKJ87gpk87wkSieL73zC/uMkjjyksmngN59VU -ODkUALrflWdHHynYiaA09zsDCjDuWDBXl17DNPV9lb4L0wLjJSklhuGsHo+wtW9k -HJx+H972Qk79C4/ZuNiRx83xt8hruqlky2wkZKB9cxYTiizoD9CXwpBl+KGo5oFX -ZjsDT4tnAHMa69yUoA== ------END CERTIFICATE----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/manifest.json b/tests/testdata/sharedlibs/build/com.android.apex.test.pony/manifest.json deleted file mode 100644 index d1b5a60d..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.apex.test.pony", - "version": 1 -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/pony_test.cc b/tests/testdata/sharedlibs/build/com.android.apex.test.pony/pony_test.cc deleted file mode 100644 index 761d2410..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.pony/pony_test.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include <iostream> -#include <string> - -#include "sharedlibstest.h" - -// This parameter gets modified by the build_artifacts.sh script. -#define FINGERPRINT "VERSION_XXX" - -int main() { - std::cout << "PONY_" << FINGERPRINT << " " - << sharedlibstest::getSharedLibsTestFingerprint(); - return 0; -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/Android.bp b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/Android.bp deleted file mode 100644 index 78816364..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/Android.bp +++ /dev/null @@ -1,74 +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"], -} - -apex_key { - name: "com.android.apex.test.sharedlibs.key", - public_key: "com.android.apex.test.sharedlibs.avbpubkey", - private_key: "com.android.apex.test.sharedlibs.pem", -} - -android_app_certificate { - name: "com.android.apex.test.sharedlibs.certificate", - certificate: "com.android.apex.test.sharedlibs", -} - -apex { - name: "com.android.apex.test.sharedlibs_stub", - manifest: "manifest.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test.sharedlibs.key", - installable: false, - // We want to force libc++.so to be available in this stub APEX, so put an empty binary. - binaries: [ "noop" ], - updatable: false, - compile_multilib: "both", - multilib: { - both: { - binaries: [ - "noop", - ], - }, - }, -} - -genrule { - name: "com.android.apex.test.sharedlibs_generated", - out: ["com.android.apex.test.sharedlibs_generated.apex"], - defaults: ["apexer_test_host_tools_list"], - dist: { - targets: ["sharedlibs_test"], - }, - srcs: [ - ":com.android.apex.test.sharedlibs_stub", - "com.android.apex.test.sharedlibs.avbpubkey", - "com.android.apex.test.sharedlibs.pem", - "com.android.apex.test.sharedlibs.pk8", - "com.android.apex.test.sharedlibs.x509.pem", - ], - tools: [ - "shared_libs_repack", - ], - cmd: "$(location shared_libs_repack) " + - " --mode sharedlibs" + - " --key $(location com.android.apex.test.sharedlibs.pem)" + - " --input $(location :com.android.apex.test.sharedlibs_stub)" + - " --output $(genDir)/com.android.apex.test.sharedlibs_generated.apex" + - " --pk8key $(location com.android.apex.test.sharedlibs.pk8)" + - " --pubkey $(location com.android.apex.test.sharedlibs.avbpubkey)" + - " --x509key $(location com.android.apex.test.sharedlibs.x509.pem)" + - " --tmpdir $(genDir)", -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.avbpubkey b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.avbpubkey Binary files differdeleted file mode 100644 index b9a268d9..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.avbpubkey +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.pem deleted file mode 100644 index ce939dcc..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKgIBAAKCAgEA2EG+ZcurX/nUUNhpbARwgQGgLTQkXU3yNYRk6t5WoXI6t3D2 -/bePE27RRzUhplvs5SZthRl3pgkzhZtajObm31VaUX5cXGveux1bQdtfTeMmEfPl -TZ5bjM2aWV8QOpPQxUbL3tjCr2NB6sRX864nxl6RvFSgbVACBDVDBbdiHfq4UTGM -oL0APyO9PCfPueFXwOJyW+gtMc50m+s46E/7Je0hmCfx5xlTjnue0CzF9oDPvat6 -72hX5JFOxefHPIVW2/IFnjzMsyCwjF4WyB2eZjmH3Mtx44Xn5hH/a+1wMvvcJmjx -W4rexJVdDSr+RZTQC0Wr2eObk/6ib4gjXcjCr8cAdTB5g9IbKmGdOlHM3VavlkL9 -CMzNy+/w5UnqWrLJanC9nJba6PmwxG7n5doFODwB1ZypX3crrxjyT5saKN0KnSF3 -Ux3yt8SPnAr5QZ01ta4H26I65YwQi+Bqf/MOy+DgEDv3GEUTGLvK5YK2sDSWdIr4 -s3NRpkkUpAo/RZsqlxYCkYXtrdngP5r1ej0+uuzXt8cqXytcTqMpJzwDtNvlJiRG -iqYzQZE+p3JrLgUTnT+clezOU4SbbaK17z+gLFoy9rs15ChF+VM0GFeuWh0pTOv8 -UfpsOZrI4Cwgr1q8oUGhcXwj6cgqdxzlrRFqb7no6L0rajk2dQLBoCFIV4ECAwEA -AQKCAgAgHQMtEqWMRwkkSD6/b5lVTux+SfPsdxq0n8hsqD+tEc1uWDQVUSDJ/fbN -4DHzBkuTa7VvwmxmF4+zE3LK4a7/EymqWF1WzB3zI1Td3rm0UzrgB5vRfuaRbiax -htBeIn0qDm1P1lhyuwaa2jVFVmNJrdluYhLAqNTj0xT00FqdoRGl3PnJFMfomGIN -gMv0CmaBmh7pTv0HHGVske2NcfMVmrUWZzgg3T3vNqRKvZtYE6DFxaUn0BLdOka8 -VMLdVd+kIbh72wN6xivxbDdt2BghjgGC5CMxaj0ZiSqo2EWFDKmQepz8vw59msCK -qAvCQWrzgZEXdhkwTOvKLCk0UA+4zJoZ1hF3tZWStftqHB+zl1nsz3H8vBHzUaGJ -1ufJqBZrmKqJvMEMlxoV+A8ftV/SZTmdXrEb/CNLGAfNdf8mhjc5hU58HLQPHQxA -3IAj1Jyllc5hZHYXKAvu89ift0ZxZel8Nim+STmzpXiTvhia8+NEJlbOIp7JJf9L -OOhj33PUiIR9e2iPOrhPOkV4o3HVW/Dpwu2P1DSqOIoGxB0zFE+eZGGf9HVNOyy9 -xUUGRpzdD8M09gnI3Yszabo2HjKdKYQdmoSc7jzMMwvQGd9Zt08KvpsWj8mry7l9 -VVy8h4rWlxmJtC8aXOHI5thV/4jmwfRYbDEzh4Qt2XxVQCyvUQKCAQEA9uviNj0Q -FWtpeYULYMksz63JryaPeI3wAJtz6efYTcJ025qobTiHhoIER3TInsBBUQ3/YRYy -ZBtCeKpLxYA81IabIX9lO757RMslEb5KeD3Da2HqNnrh3hyRXgXXcUMbed8JunlC -5FRCRafikkj5ABvLHB9AYgAhS/vnTGQ7+XgpFsYi1kUGbJw+t4ly/ydWLjs5XoY7 -JdAuwnI/T8z31yYGG8T3oMSQV+BXT1doGNalKGupGMrUyvvqJQvScvxsVSWS8cRS -A1Xu91R0oMYhwnUkMZc7qYEVdGqcXFagSbnXufT4J7Bw+H7wbY17fYOC3cGkh3AK -FCa1d74Zd/3aswKCAQEA4DU7vukSOSDAf1hChkYXPzUJdrk31AVfeISYZmKqYah6 -65I2kzOnaszDQRg11wHXHSRhU0oVQxA77VTQBNQzOy+F7oB3o0cuQ+n6X5U8QQy4 -2dmgiYp3rkrqoH9KVn5VsYdwyYQHmE1IVMazo2YByv2N7aq4KN0MEgYUzizF38jX -hf7ubQA7mGr9I0Wm1keQJ1m9VZ06mPKLfG70LViJAAvCYzSLY/31FBGVNq4JlreP -EI4hgrYv7tzU6BqzrIdAmFOYOOsdRnUUW9+OhVzreU2EBAAmjK1jWa6FFAXGJEhi -/qyO2suvNdWsIGxfqt5yhPMevMqjyEPmvwpLF+5u+wKCAQEAj9ShZVS2bLOvsdB0 -60DkMGkcBUGh6uhK+B+VKpgZYFo4Nb9mApEeKJTNp034mriEk5FixAvo+HUEiEMy -de4YAPgTnzSVJHL1XQI0Kpy8xkO79G4JvwhfT0E20Bz4/QnJFHl+Mjf2ZghKvkZn -7SxClvSZoFz35N4MhzVJ6y6r3MpIrPJnUobMkjGFOuX+rXAdfDqVVWE9TO5yfmOM -S5CqgZGtlzlpwSUeq4GLejUA9w75D41+52knAMIzBrdXNBGjjQmhCeGAoF7LHxj8 -ArbG7X3MwnJEl50QgUqkoAj5v1hYuAJhFsVpWOagaEA0wcz8Su5ER3xU8p4FsKV0 -MngVjwKCAQEAqVuMpcioWz7CKW8h0Qtgw/3sCCIgaaclVoPSGoSs7te1AfyP/OEn -tSS22JTRFnftZbX1TlTHesDog31tJDil+i8Lm/yuYkeCSwqSdWDlAr35Y5VgDoTp -ol40nMeJ/4uub0s/hviURBcca+0sBGEpOYwNiVlLgpJ2a6bsUFDBpyiupCjNMMjc -O2WVkO8r9vBXk2HWArWhbabIdlXZW+dklQRM8WLfZ8iNN3uQmp0b4R0GlBrIdVPp -ISTuLeT9k3UW9fkvIs92baJCnqNfpJ1rwVUsQ1lZxSmzwipxm45A/WcwX+84eU0i -LCgavOMf4JHnL0X2EeV/kea4hdXgo1MXwQKCAQEApKpBnJ2DPxN/I0cHbOFLH1rS -A3lZvx0iOz7AFVK9laJ/794s5RdugXQ0MO4D0u+QrYN/Q3GnMpuNmHM8FRRNRIte -jdpPmFLyPOrqOCaeISmMnwZl1GZtOjZwOWRWP/7pK8cv6bI4H8tDyYq9T3b0K+TK -mqhqoHyN4aNrLCxggmhFS8wlK5UmuwJRfbAx4KGbfYk7fy4THkGjn5ZL3q9PO9Fa -1jWsiBFdnmRQvD4svnTswPEts/rJ9o1P5+AnWtVKK7Npq9eOFhPRJ3R3hruHJath -Cw999aol+hd093kcd7RzRmRjxUZ6oKxs1yRF7o9QQxnsmHRUT79Yy9LBOprvVA== ------END RSA PRIVATE KEY----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.pk8 b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.pk8 Binary files differdeleted file mode 100644 index 933eb474..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.pk8 +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.x509.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.x509.pem deleted file mode 100644 index 95f96af8..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/com.android.apex.test.sharedlibs.x509.pem +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF5TCCA80CFBhOpuxTEQRD+MtXkeI8yP5rnPrwMA0GCSqGSIb3DQEBCwUAMIGt -MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91 -bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi -MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEpMCcGA1UEAwwgY29t -LmFuZHJvaWQuYXBleC50ZXN0LnNoYXJlZGxpYnMwIBcNMjAxMDA1MTYzMzIwWhgP -NDc1ODA5MDExNjMzMjBaMIGtMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZv -cm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQ -MA4GA1UECwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lk -LmNvbTEpMCcGA1UEAwwgY29tLmFuZHJvaWQuYXBleC50ZXN0LnNoYXJlZGxpYnMw -ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDjKxHP9SI0nMgAqYsSueNU -042arnj7pjdzhqHhRWr8Ub0mwMXx0ulkgZcnhCbdg52670T2wPhl/xi4/HL7PybU -sjt1QsjOaYcDdSrdxnsPe4RXZG4aE/+z06pm7TJ2jMGjJzS9x4AXbFNBxLsLp+gk -fuxhpGQklgymRUojtUFMWhnXURQz5wyYJGUrV32FpOo5JKtO4A42pNhcHNBzeQp0 -LVAwcQalmxra1maf0PXTg6L4yukyNzfLYcukjkQilzrkWGPqGexCzLF+WXiuznNj -BqPguKXvdsmSUIqAKGw6QHnuD6cAm3d2CuMzd4fjdOwzxFtfDEXS8f8ai/K+UEvx -eqf8eXV0cpKROrbf0CafcUli/3CKIc3UXoQr5kx4LSp0eJHOyaoGEF3Dex/k0dgJ -O80nNGTydhwvfMoZ4MOm+4yzDTqKu/Hw2ebM9po3vsfF3oy/hjEX0CaS4DnKDugg -WUwAbTHG5k/lbzy6mkjRDxwSzn6sDrhHM1rs45thiQF9hfZuDwUbaGmxFAq8+Gtu -xcr6bhryxaiO10MdMHI5KP1ZfZT49c+K8oVlTOsHxxxSK3eiQOs4k8A0jef7gfTP -rnDKbdq7JHr73bHXv495UtgZMCKtS3p4kvHs5PYblWvJmYImMrLjqdtuckYOeEIO -2N+79Rm8YAItX+SigohnZwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQAiJdApc590 -zoYl2w6NOj1xMljzta5Ld92lbJ3O4r1IoFQ6p+bSuV5RlPEC/kzR1G2WrZf66IrW -UOSxtDjY6Bt1GKO949BKTp4/gdrnanst7ai5VnPeGWstUIEJ5SmF7C9QBhWnGnFT -9zGdtbKnkcreZ81yabbNwAAwZWgX5hfkLSuMu7SzgLnzVDQOvbg96esWCbbBNPAl -KhZb5Bzc42TDlUxWfqIC0Of3GjcLu1Ukn4fwFphMD4wGoHIgpGD6975BVWESpmnV -tPDwiI+02Nha1aySZr/TiTId0AUucb6fqySqjCbOowv3DimKt+anwZjk1k/12TLb -Uro5nOPbWwQkQws7tnfNb8VBWoGNc+SJbh260rhv7gpwsvXOdbKbyR7mSqXylreh -DBUd/UL2eR1IrMuixK4bvmfVd7y+lxYKutEk+ifwSEuAubcbZ/dKH4PzjRnu1evv -4M/1sLH4LRd+qHoR4ylopX8dWn3xh2Xq9KFrnTXRk1nd1YeGqcyTz+H7bBhYZKLO -vPYTUb8ZYc/h0VGaABVywnb8wWZH4Op7ytKDybV2PGjhdLWaA3Y758ySkEab80ye -XSuSPevgTDc7ZV3/Ijs7cW6+XVKnWWl4H7DQxG4B9vWrKl0YejNJBUNaXoE5t1hN -TG2GM5D9sA/8HiW1JygwH0CBXMT5drYYjA== ------END CERTIFICATE----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/manifest.json b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/manifest.json deleted file mode 100644 index abdb7c42..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.apex.test.sharedlibs", - "version": 1 -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/Android.bp b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/Android.bp deleted file mode 100644 index 9e803e32..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/Android.bp +++ /dev/null @@ -1,66 +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"], -} - -apex_key { - name: "com.android.apex.test.sharedlibs_secondary.key", - public_key: "com.android.apex.test.sharedlibs_secondary.avbpubkey", - private_key: "com.android.apex.test.sharedlibs_secondary.pem", -} - -android_app_certificate { - name: "com.android.apex.test.sharedlibs_secondary.certificate", - certificate: "com.android.apex.test.sharedlibs_secondary", -} - -apex { - name: "com.android.apex.test.sharedlibs_secondary_stub", - manifest: "manifest.json", - file_contexts: ":apex.test-file_contexts", - key: "com.android.apex.test.sharedlibs_secondary.key", - installable: false, - // We want to force libc++.so to be available in this stub APEX, so put an empty binary. - binaries: [ "noop" ], - updatable: false, -} - -genrule { - name: "com.android.apex.test.sharedlibs_secondary_generated", - out: ["com.android.apex.test.sharedlibs_secondary_generated.apex"], - defaults: ["apexer_test_host_tools_list"], - dist: { - targets: ["sharedlibs_test"], - }, - srcs: [ - ":com.android.apex.test.sharedlibs_secondary_stub", - "com.android.apex.test.sharedlibs_secondary.avbpubkey", - "com.android.apex.test.sharedlibs_secondary.pem", - "com.android.apex.test.sharedlibs_secondary.pk8", - "com.android.apex.test.sharedlibs_secondary.x509.pem", - ], - tools: [ - "shared_libs_repack", - ], - cmd: "$(location shared_libs_repack) " + - " --mode sharedlibs" + - " --key $(location com.android.apex.test.sharedlibs_secondary.pem)" + - " --input $(location :com.android.apex.test.sharedlibs_secondary_stub)" + - " --output $(genDir)/com.android.apex.test.sharedlibs_secondary_generated.apex" + - " --pk8key $(location com.android.apex.test.sharedlibs_secondary.pk8)" + - " --pubkey $(location com.android.apex.test.sharedlibs_secondary.avbpubkey)" + - " --x509key $(location com.android.apex.test.sharedlibs_secondary.x509.pem)" + - " --tmpdir $(genDir)", -} diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.avbpubkey b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.avbpubkey Binary files differdeleted file mode 100644 index 3ccad0fa..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.avbpubkey +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.pem deleted file mode 100644 index 5d4e1e1d..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKgIBAAKCAgEA2U7XN6RmrgLci4ZK2Osai9MqNIfx0gBX43DeIt6/1mQ5T590 -016wrGN6Oux0+LcdPU4PmpcyHw8+JVgdZjxG5c4icfWEuuc3Y+Pb6/FhvNwNrN5U -wx7/DFGyEWaRZEe3pk6oDETSYvY5CzQjrFz+mDDwNnho0zxsRD74GWumQOkdAg0W -7ihUq4/gCnjx+SeijTJAZcN0+oSldRa8+yN5zYdtO9gt9PGZLr87pvIFbcKp8QIT -sReN9eFoLHYYktq5y4YXIhhV7W0imsPzAq6gtzHC6+qgDPCvwD6/GTvGHgF+DEUl -/NuQgqgeVrFOEnHZ5qsawRwVeVq6sRd2ZQUdj+ix5KTS9/iKpg/cyLHCaXzEWWcZ -DtKnZKxdFTyXa4w+UshQkKDHPYOKfOXLaTTywz9oAZNDBQsLjUUIDZhyxRq7a5Zk -gUi5zG+Wx6r46pdSmMb76Qa2vasMQUYFoRINRfrsU6EJp55fjWCDzFXMIdm8nSkz -4DY6T+XLu76zAMrZZPHrQle2D5zi/mu2yvbHi0sbujUCFBFrPpLkaD8TE1gqxqfh -p5+xXD8tal6Mj7exLvRCadNaQ1R8CtcZhVxtUPmGVwlUZ6aF1QpARQtfZuVBEiut -kyfTb9ns8r3xmxsUOSwJA6SgRuN0gvXIfDnNumDaP9dkuGE98uQpTu8kZbsCAwEA -AQKCAgEAg0oR1yk5bAqIirdxAwtP94h16FT18eWJM/2eB71Cc9oLkiKJp6Z+4Tgc -wfrYVOf0/3PpE4IjowZHirJo2Lq0LuVShD1MmstU+MHSvgMRBNSCYp3U4ioY961o -AwFP+CEoQI8nEnqGDYorPqyanOl2XCa9CnvHAVBxLO5KYLlcMb1lbDbSUsMFHL4J -IuqdbuXWXK4uoAzt4OlBObOqK6TsUxNuGIjsgx9waADbnmp8gyroF5ckpIrRlus/ -UBVtlVQWinMSCORhDdgw3wZiDI3KxcOHu5b+abMEzAZc2Hb3pGtMZ0djwxg4f+fo -pIHs5FHqz2Uy/dbk1nPNdW7ydegYShuQmsvbOT6R8Qi25c9SiirXKOtdCM50JTW7 -SRaReqhiy828m9DOrwSXXiHrmyNevpwGQtRELGi14rgPqnpuUNaOmMEGAstQ+xcU -Hl0DVuxWXN37iEKa4zv/LKwRzBVxlrSC1M3ufiOi2tVl5btMrdz5jZwDLYQxI7TX -Nu0l/tEWD98/sJyOk4bmGUFrD5+oBpPGw/MrK6k7DmJjp8W1NRiiIACI/flzRWcA -la7lPk23kG0/lmrWY87NQhEaPR9RMqmizlnF3VzWyDxXgZcn4ucjRmH7qhQLsZCC -VIEI3oPoQSd7w+pHghLTA4R0KqW2ur/d05mkOmLfGnL27wPqaJECggEBAPX2tgHr -DLIapja0HtxYNx0I5H/lHi3bzBI426h7317oIQpASKsqaIu/jOSDt5gLC0g+qSJi -gT/l454bkdN4lTAMvY+3vJg3cN4U8J9GAejZi2X23Hed1QVuBSckz3CrHanqVBMN -Nj9FsfQpqLJpewVqqdKR9rK+1ZNyMWfEO9M3pxlpZyWhPHg7KtbqBo0hS5Stlqpy -xOo4nF4WC+OGNUo02R12ETJM4AmMs28Z5oNYZZ6K4cqKReNcDqt891ryXrow6aq9 -JBYz8sCeBQSTnPNnhbLJH/YOVMlhdAJhBcxEDbNKM6vmFQOuBEp12tjQoXsiv54C -uBou7aRft8uGeHMCggEBAOIszCN1ZvD+xbaLDtKgPrAuDVRaZuZ9vb2TVB7Gcpto -hCXU5GoapqQgjGplsXOQ1FMRozeRL4lQWfPfiueLAV3AeIHm0qjD2aanhJPvPrnb -UCOdWRID99q9/s6bP98XiDseZiTHNlWitwpbWPAvmW2/08otmf4TgeLrA8p75CtK -qJXU7o7fWTBFHF238rusK3N9t0Vikb0Cx1xDui6b5PeWtqAREV1/swP0J0av1fwK -OuvZ8VdUjb5KMUMtGgMEWA277kYuryalu+oQH+bfe3XXUJS7fAI7GlokVqhJ8CLZ -eSSZDtdqHib968cA4wr55nt1rEiDWzG7f2GeZra6s5kCggEAN53QrABdP4ydFvOF -oudjlvIi0PSa7V2s+FXY/XD9IjW0+t9sTx/owejPUACkrAGbTHu2vOqvNSajYGX1 -hG7YtSO8XVn7kCPBJsZvXmRzHBbM2YKHeZi7yV2GVsKREXXv4DL3TdOH96inw4ED -/0uwoJnsyots0CAspQmGOGN775e+9hUKWMzrongmiLAkSRdFQto5nlMTSa8BVJkB -mTIIrL3kdi/zVX9ijWY+UJn3sK11VPMseSLpCK8RNh+swujZGJrky1G3bjnS41EX -62ABdlxrM/EchAPbkimyFLOhnv2oZ2kY4/7Ds7BOkhOyJ6KNUQ2bbHxK6si/vZJT -OfcvFwKCAQEA0nnVzvmmPocY/vMRbDjrnZB9nw4xzDUfqZe9JJaQeMcekwY3OfZr -NTmE8k6IgH8618MGHOPjVOmNjEFvRmI5d0Fx45EmYR9BILGr0u9FdDf/r+Txyq4e -rVU6FpKrMbT4deuoKnmourCdnem8LmhdY6CsOu2M7MDCkqUZ9gitIQxtLmHlTtfS -a/UknKJeJP/nv6YyM0OzVC2N0PLGBDHXNgDvGq5HdrcrpHZFRqbDf7UVd/5tdVOe -RINOrLEAD+au+rj02CMBo/l/kiZHSdaXUeZ5eq+ui3Ts5Q4EBsAn1IaFEeXNxfFe -9fI+xAazQrekISg0l5aF+xX9SJ7b/xhnoQKCAQEA7vvi5zUalTNRreeExX0wiKRj -sCvUhQoykimKpWzSUg6DpdF+DhK59/ERVSkA4C2WwHEYqZdrgEKnmqOZQkqgtq1B -O5xxioRtn6VsZRYjaRw5Cq/Ej1AeDgQr+SH29kBtym81mMBbkPnFoBxKCjuuVn9u -4Kjg910wxErzipjDB2alfkuFCqNz9IZd2XSmpmGhpE/hBlZP4slJXTX77nIxRTHB -Vq5gehJoEhLNsFAu34BgrXRIhzSBALdalbepNrHh2br87rJpjxeRASbuyyNdgZyl -wk+aJxHo6RpMD7xHxsxv5kmRcLITPICk+ysLZ5ZHMaEuGIVFxDdOoFGylRV4wQ== ------END RSA PRIVATE KEY----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.pk8 b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.pk8 Binary files differdeleted file mode 100644 index 2eee60f0..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.pk8 +++ /dev/null diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.x509.pem b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.x509.pem deleted file mode 100644 index 74dd144f..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/com.android.apex.test.sharedlibs_secondary.x509.pem +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF+TCCA+ECFGnrAltwoToH4tPw7aN36oFBb8zxMA0GCSqGSIb3DQEBCwUAMIG3 -MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91 -bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEi -MCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTEzMDEGA1UEAwwqY29t -LmFuZHJvaWQuYXBleC50ZXN0LnNoYXJlZGxpYnNfc2Vjb25kYXJ5MCAXDTIwMTEy -NzEwNTE0N1oYDzQ3NTgxMDI0MTA1MTQ3WjCBtzELMAkGA1UEBhMCVVMxEzARBgNV -BAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoM -B0FuZHJvaWQxEDAOBgNVBAsMB0FuZHJvaWQxIjAgBgkqhkiG9w0BCQEWE2FuZHJv -aWRAYW5kcm9pZC5jb20xMzAxBgNVBAMMKmNvbS5hbmRyb2lkLmFwZXgudGVzdC5z -aGFyZWRsaWJzX3NlY29uZGFyeTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC -ggIBANLZWZWEtzYdHL+yio1j16r4aDCtRMdTFT8dWhXi/khEodZ9PZHV73eAWl7o -5AHO2h/4To6hqxKlzl5lbNWT/Xu7OFvhaqy1gD2XiOyZxqNITba3SV4qDnWYAMHc -JdcYKv/F5UMeiXQueBJ2HaWcWoZPOmHwfJbMxSvr+LOwtI1vvR38G68uIFyR0Rlb -jQggv/WsudLlB++0c+jOtPLmD65h7dXXXlqGvu+7Rt5GWYxtN/sIZ0KPqtLk1JQD -nIa5E05UDcqfGnyo5mz7t9Tj+4dyH/B+fY9JMXJMNMcy47CfdhR4NfnsUTmLqXRc -fOwSRfuodMbCd4WAgA6e6KHjlBO89NOA3QamQDyhT3XURrMCMeRQvb155dEoHCs5 -FDBu138hku5aylLfiHkEHfwEDu9wsWAd3CWsBfqOMAEQG7AB+4lCaGVhYKMxKWjM -Pdqzfy47ODHrMEprfrYlOkdJ+9Fu9HJZvEMQXh7tleoOSIuvqmL1ksRSUXsE2JHJ -KC/hoBCcVsMsujxn7tQilB0e+TLDml08dIrrGLV1UiqU/80q0MC2Bwinne21KAtY -UMJd2pImZr3H4z7J6wQmuevvcZksaEwWXtQzIoXenmaSW9bwqVy6k+DushOghGy/ -CAeeJAc35sUFsa8ysp0aPARZIqCYiesGdiO/wW+UVibo2JyLAgMBAAEwDQYJKoZI -hvcNAQELBQADggIBAFE7g7ZTGqSDpJ/PyLKLp+oItJ+JNJVd5UbLvLMiA4t7QTtE -0aPCxMGybJGeYbs16OB3ZqWZlVyjirEXAmSH2HZxO1uIeDcCjndSCfz+oxmBs27j -C8t5BzExaelpP/J9nyKObzaZ1EJ+KdTqPVmrhgN8uUU82mRt5oJZuxGbHNdbzxGh -zXHi+q5oecWaiBFDSF9pvKFp7nvbW4MIaGcm9Flx9JdYhgzRfXeJV7EsKE2Kxflj -nojgyUzjvkNTa/wRASgwr/hgpPL0mpD9gSejxOA5wRGLknDlIfsfEh5cJTFiBO5b -MZiSHe7Ds+8GWyKk8y8G2YtB0Z5HxD91rzPA2W/JEDHhoI8jT47I3JuOyJMjC9DV -kAu4nhReBilOmU+oS1Iv+TheslPGLEZ7JOHsmyQh5X5H0D+YH7H9L8BOWEBlNBhy -zPlMKNWsvMZmem/fhuXK1xUUWZuqoj2tF2fMmiYf6fSNRb7dQI10uBslxX7myNyj -pqZNSk/E9V650UC5JixpOtPxrSsPXifOLwB7OEkT5v7v/MoBqlSfqve9/62Ktnuc -NC9Rt3fqU+VOs6tVUPGnSJRDc2uhvU8rJZN86e1xX8d7lhHZwO8sJtaxZ2KYduM1 -ncEODTAxK7wQ041CS7H/kULG6CEPnIg0a1ZjASWhQRbQ1Rj/Y40CmHxThnyx ------END CERTIFICATE----- diff --git a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/manifest.json b/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/manifest.json deleted file mode 100644 index 80da6f14..00000000 --- a/tests/testdata/sharedlibs/build/com.android.apex.test.sharedlibs_secondary/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.apex.test.sharedlibs_secondary", - "version": 1 -} diff --git a/tests/testdata/sharedlibs/build/noop.cc b/tests/testdata/sharedlibs/build/noop.cc deleted file mode 100644 index cfd2ef57..00000000 --- a/tests/testdata/sharedlibs/build/noop.cc +++ /dev/null @@ -1,9 +0,0 @@ -#include <iostream> - -#include "sharedlibstest.h" - -int main() { - std::cout << "This binary should never be executed"; - std::cout << sharedlibstest::getSharedLibsTestFingerprint(); - return 1; -}
\ No newline at end of file diff --git a/tests/testdata/sharedlibs/build/shared_libs_repack.py b/tests/testdata/sharedlibs/build/shared_libs_repack.py deleted file mode 100644 index 31b9a6bc..00000000 --- a/tests/testdata/sharedlibs/build/shared_libs_repack.py +++ /dev/null @@ -1,421 +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. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Repacking tool for Shared Libs APEX testing.""" - -import argparse -import hashlib -import logging -import os -import shutil -import subprocess -import sys -import tempfile -from zipfile import ZipFile - -import apex_build_info_pb2 -import apex_manifest_pb2 - -logger = logging.getLogger(__name__) - -def comma_separated_list(arg): - return arg.split(',') - - -def parse_args(argv): - parser = argparse.ArgumentParser( - description='Repacking tool for Shared Libs APEX testing') - - parser.add_argument('--input', required=True, help='Input file') - parser.add_argument('--output', required=True, help='Output file') - parser.add_argument( - '--key', required=True, help='Path to the private avb key file') - parser.add_argument( - '--pk8key', - required=True, - help='Path to the private apk key file in pk8 format') - parser.add_argument( - '--pubkey', required=True, help='Path to the public avb key file') - parser.add_argument( - '--tmpdir', required=True, help='Temporary directory to use') - parser.add_argument( - '--x509key', - required=True, - help='Path to the public apk key file in x509 format') - parser.add_argument( - '--mode', default='strip', choices=['strip', 'sharedlibs']) - parser.add_argument( - '--libs', - default='libc++.so,libsharedlibtest.so', - type=comma_separated_list, - help='Libraries to strip/repack. Expects comma separated values.') - return parser.parse_args(argv) - - -def run(args, verbose=None, **kwargs): - """Creates and returns a subprocess.Popen object. - - Args: - args: The command represented as a list of strings. - verbose: Whether the commands should be shown. Default to the global - verbosity if unspecified. - kwargs: Any additional args to be passed to subprocess.Popen(), such as env, - stdin, etc. stdout and stderr will default to subprocess.PIPE and - subprocess.STDOUT respectively unless caller specifies any of them. - universal_newlines will default to True, as most of the users in - releasetools expect string output. - - Returns: - A subprocess.Popen object. - """ - if 'stdout' not in kwargs and 'stderr' not in kwargs: - kwargs['stdout'] = subprocess.PIPE - kwargs['stderr'] = subprocess.STDOUT - if 'universal_newlines' not in kwargs: - kwargs['universal_newlines'] = True - if verbose: - logger.info(' Running: \"%s\"', ' '.join(args)) - return subprocess.Popen(args, **kwargs) - - -def run_and_check_output(args, verbose=None, **kwargs): - """Runs the given command and returns the output. - - Args: - args: The command represented as a list of strings. - verbose: Whether the commands should be shown. Default to the global - verbosity if unspecified. - kwargs: Any additional args to be passed to subprocess.Popen(), such as env, - stdin, etc. stdout and stderr will default to subprocess.PIPE and - subprocess.STDOUT respectively unless caller specifies any of them. - - Returns: - The output string. - - Raises: - ExternalError: On non-zero exit from the command. - """ - proc = run(args, verbose=verbose, **kwargs) - output, _ = proc.communicate() - if output is None: - output = '' - # Don't log any if caller explicitly says so. - if verbose: - logger.info('%s', output.rstrip()) - if proc.returncode != 0: - raise RuntimeError( - 'Failed to run command \'{}\' (exit code {}):\n{}'.format( - args, proc.returncode, output)) - return output - - -def get_container_files(apex_file_path, tmpdir): - dir_name = tempfile.mkdtemp(prefix='container_files_', dir=tmpdir) - with ZipFile(apex_file_path, 'r') as zip_obj: - zip_obj.extractall(path=dir_name) - files = {} - for i in [ - 'apex_manifest.json', 'apex_manifest.pb', 'apex_build_info.pb', 'assets', - 'apex_payload.img', 'apex_payload.zip' - ]: - file_path = os.path.join(dir_name, i) - if os.path.exists(file_path): - files[i] = file_path - - image_file = files.get('apex_payload.img') - if image_file is None: - image_file = files.get('apex_payload.zip') - - files['apex_payload'] = image_file - - return files - - -def extract_payload_from_img(img_file_path, tmpdir): - dir_name = tempfile.mkdtemp(prefix='extracted_payload_', dir=tmpdir) - cmd = [ - _get_host_tools_path('debugfs_static'), '-R', - 'rdump ./ %s' % dir_name, img_file_path - ] - run_and_check_output(cmd) - - # Remove payload files added by apexer and e2fs tools. - for i in ['apex_manifest.json', 'apex_manifest.pb']: - if os.path.exists(os.path.join(dir_name, i)): - os.remove(os.path.join(dir_name, i)) - if os.path.isdir(os.path.join(dir_name, 'lost+found')): - shutil.rmtree(os.path.join(dir_name, 'lost+found')) - return dir_name - - -def run_apexer(container_files, payload_dir, key_path, pubkey_path, tmpdir): - apexer_cmd = _get_host_tools_path('apexer') - cmd = [ - apexer_cmd, '--force', '--include_build_info', '--do_not_check_keyname' - ] - cmd.extend([ - '--apexer_tool_path', - os.path.dirname(apexer_cmd) + ':prebuilts/sdk/tools/linux/bin' - ]) - cmd.extend(['--manifest', container_files['apex_manifest.pb']]) - if 'apex_manifest.json' in container_files: - cmd.extend(['--manifest_json', container_files['apex_manifest.json']]) - cmd.extend(['--build_info', container_files['apex_build_info.pb']]) - if 'assets' in container_files: - cmd.extend(['--assets_dir', container_files['assets']]) - cmd.extend(['--key', key_path]) - cmd.extend(['--pubkey', pubkey_path]) - - # Decide on output file name - apex_suffix = '.apex.unsigned' - fd, fn = tempfile.mkstemp(prefix='repacked_', suffix=apex_suffix, dir=tmpdir) - os.close(fd) - cmd.extend([payload_dir, fn]) - - run_and_check_output(cmd) - return fn - - -def _get_java_toolchain(): - java_toolchain = 'java' - if os.path.isfile('prebuilts/jdk/jdk11/linux-x86/bin/java'): - java_toolchain = 'prebuilts/jdk/jdk11/linux-x86/bin/java' - - java_dep_lib = ( - os.path.join(os.path.dirname(_get_host_tools_path()), 'lib64') + ':' + - os.path.join(os.path.dirname(_get_host_tools_path()), 'lib')) - - return [java_toolchain, java_dep_lib] - - -def _get_host_tools_path(tool_name=None): - # This script is located at e.g. - # out/soong/host/linux-x86/bin/shared_libs_repack/shared_libs_repack.py. - # Find the host tools dir by going up two directories. - dirname = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - if tool_name: - return os.path.join(dirname, tool_name) - return dirname - - -def sign_apk_container(unsigned_apex, x509key_path, pk8key_path, tmpdir): - fd, fn = tempfile.mkstemp(prefix='repacked_', suffix='.apex', dir=tmpdir) - os.close(fd) - java_toolchain, java_dep_lib = _get_java_toolchain() - - cmd = [ - java_toolchain, '-Djava.library.path=' + java_dep_lib, '-jar', - os.path.join( - os.path.dirname(_get_host_tools_path()), 'framework', 'signapk.jar'), - '-a', '4096', x509key_path, pk8key_path, unsigned_apex, fn - ] - run_and_check_output(cmd) - return fn - - -def compute_sha512(file_path): - block_size = 65536 - hashbuf = hashlib.sha512() - with open(file_path, 'rb') as f: - fb = f.read(block_size) - while len(fb) > 0: - hashbuf.update(fb) - fb = f.read(block_size) - return hashbuf.hexdigest() - - -def parse_fs_config(fs_config): - configs = fs_config.splitlines() - # Result is set of configurations. - # Each configuration is set of items as [file path, uid, gid, mode]. - # All items are stored as string. - result = [] - for config in configs: - result.append(config.split()) - return result - - -def config_to_str(configs): - result = '' - for config in configs: - result += ' '.join(config) + '\n' - return result - - -def _extract_lib_or_lib64(payload_dir, lib_full_path): - # Figure out if this is lib or lib64: - # Strip out the payload_dir and split by / - libpath = lib_full_path[len(payload_dir):].lstrip('/').split('/') - return libpath[0] - - -def main(argv): - args = parse_args(argv) - apex_file_path = args.input - - container_files = get_container_files(apex_file_path, args.tmpdir) - payload_dir = extract_payload_from_img(container_files['apex_payload.img'], - args.tmpdir) - libs = args.libs - assert len(libs)> 0 - - lib_paths = [os.path.join(payload_dir, lib_dir, lib) - for lib_dir in ['lib', 'lib64'] - for lib in libs - if os.path.exists(os.path.join(payload_dir, lib_dir, lib))] - - assert len(lib_paths) > 0 - - lib_paths_hashes = [(lib, compute_sha512(lib)) for lib in lib_paths] - - if args.mode == 'strip': - # Stripping mode. Add a reference to the version of libc++.so to the - # requireSharedApexLibs entry in the manifest, and remove lib64/libc++.so - # from the payload. - pb = apex_manifest_pb2.ApexManifest() - with open(container_files['apex_manifest.pb'], 'rb') as f: - pb.ParseFromString(f.read()) - for lib_path_hash in lib_paths_hashes: - basename = os.path.basename(lib_path_hash[0]) - libpath = _extract_lib_or_lib64(payload_dir, lib_path_hash[0]) - assert libpath in ('lib', 'lib64') - pb.requireSharedApexLibs.append(os.path.join(libpath, basename) + ':' - + lib_path_hash[1]) - # Replace existing library with symlink - symlink_dst = os.path.join('/', 'apex', 'sharedlibs', - libpath, basename, lib_path_hash[1], - basename) - os.remove(lib_path_hash[0]) - os.system('ln -s {0} {1}'.format(symlink_dst, lib_path_hash[0])) - # - # Example of resulting manifest: - # --- - # name: "com.android.apex.test.foo" - # version: 1 - # requireNativeLibs: "libc.so" - # requireNativeLibs: "libdl.so" - # requireNativeLibs: "libm.so" - # requireSharedApexLibs: "lib/libc++.so:23c5dd..." - # requireSharedApexLibs: "lib/libsharedlibtest.so:870f38..." - # requireSharedApexLibs: "lib64/libc++.so:72a584..." - # requireSharedApexLibs: "lib64/libsharedlibtest.so:109015..." - # -- - # To print uncomment the following: - # from google.protobuf import text_format - # print(text_format.MessageToString(pb)) - with open(container_files['apex_manifest.pb'], 'wb') as f: - f.write(pb.SerializeToString()) - - if args.mode == 'sharedlibs': - # Sharedlibs mode. Mark in the APEX manifest that this package contains - # shared libraries. - pb = apex_manifest_pb2.ApexManifest() - with open(container_files['apex_manifest.pb'], 'rb') as f: - pb.ParseFromString(f.read()) - del pb.requireNativeLibs[:] - pb.provideSharedApexLibs = True - with open(container_files['apex_manifest.pb'], 'wb') as f: - f.write(pb.SerializeToString()) - - pb = apex_build_info_pb2.ApexBuildInfo() - with open(container_files['apex_build_info.pb'], 'rb') as f: - pb.ParseFromString(f.read()) - - canned_fs_config = parse_fs_config(pb.canned_fs_config.decode('utf-8')) - - # Remove the bin directory from payload dir and from the canned_fs_config. - shutil.rmtree(os.path.join(payload_dir, 'bin')) - canned_fs_config = [config for config in canned_fs_config - if not config[0].startswith('/bin')] - - # Remove from the canned_fs_config the entries we are about to relocate in - # different dirs. - source_lib_paths = [os.path.join('/', libpath, lib) - for libpath in ['lib', 'lib64'] - for lib in libs] - # We backup the fs config lines for the libraries we are going to relocate, - # so we can set the same permissions later. - canned_fs_config_original_lib = {config[0] : config - for config in canned_fs_config - if config[0] in source_lib_paths} - - canned_fs_config = [config for config in canned_fs_config - if config[0] not in source_lib_paths] - - # We move any targeted library in lib64/ or lib/ to a directory named - # /lib64/libNAME.so/${SHA512_OF_LIBCPP}/ or - # /lib/libNAME.so/${SHA512_OF_LIBCPP}/ - # - for lib_path_hash in lib_paths_hashes: - basename = os.path.basename(lib_path_hash[0]) - libpath = _extract_lib_or_lib64(payload_dir, lib_path_hash[0]) - tmp_lib = os.path.join(payload_dir, libpath, basename + '.bak') - shutil.move(lib_path_hash[0], tmp_lib) - destdir = os.path.join(payload_dir, libpath, basename, lib_path_hash[1]) - os.makedirs(destdir) - shutil.move(tmp_lib, os.path.join(destdir, basename)) - - canned_fs_config.append( - ['/' + libpath + '/' + basename, '0', '2000', '0755']) - canned_fs_config.append( - ['/' + libpath + '/' + basename + '/' + lib_path_hash[1], - '0', '2000', '0755']) - - if os.path.join('/', libpath, basename) in canned_fs_config_original_lib: - config = canned_fs_config_original_lib[os.path.join( - '/', - libpath, - basename)] - canned_fs_config.append([os.path.join('/', libpath, basename, - lib_path_hash[1], basename), - config[1], config[2], config[3]]) - else: - canned_fs_config.append([os.path.join('/', libpath, basename, - lib_path_hash[1], basename), - '1000', '1000', '0644']) - - pb.canned_fs_config = config_to_str(canned_fs_config).encode('utf-8') - with open(container_files['apex_build_info.pb'], 'wb') as f: - f.write(pb.SerializeToString()) - - try: - for lib in lib_paths: - os.rmdir(os.path.dirname(lib)) - except OSError: - # Directory not empty, that's OK. - pass - - repack_apex_file_path = run_apexer(container_files, payload_dir, args.key, - args.pubkey, args.tmpdir) - - resigned_apex_file_path = sign_apk_container(repack_apex_file_path, - args.x509key, args.pk8key, - args.tmpdir) - - shutil.copyfile(resigned_apex_file_path, args.output) - - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/tests/testdata/sharedlibs/prebuilts/Android.bp b/tests/testdata/sharedlibs/prebuilts/Android.bp deleted file mode 100644 index 1ef2977e..00000000 --- a/tests/testdata/sharedlibs/prebuilts/Android.bp +++ /dev/null @@ -1,301 +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. - -// This file is auto-generated by -// ./system/apex/tests/testdata/sharedlibs/build/build_artifacts.sh -// Do NOT edit manually. - -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - -prebuilt_apex { - name: "com.android.apex.test.bar_stripped.v1.libvX_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.bar_stripped.v1.libvX.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.bar_stripped.v1.libvX.apex", - }, - x86: { - src: "x86/com.android.apex.test.bar_stripped.v1.libvX.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.bar_stripped.v1.libvX.apex", - }, - }, - filename: "com.android.apex.test.bar_stripped.v1.libvX.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.bar_stripped.v2.libvY_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.bar_stripped.v2.libvY.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.bar_stripped.v2.libvY.apex", - }, - x86: { - src: "x86/com.android.apex.test.bar_stripped.v2.libvY.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.bar_stripped.v2.libvY.apex", - }, - }, - filename: "com.android.apex.test.bar_stripped.v2.libvY.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.bar.v1.libvX_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.bar.v1.libvX.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.bar.v1.libvX.apex", - }, - x86: { - src: "x86/com.android.apex.test.bar.v1.libvX.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.bar.v1.libvX.apex", - }, - }, - filename: "com.android.apex.test.bar.v1.libvX.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.bar.v2.libvY_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.bar.v2.libvY.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.bar.v2.libvY.apex", - }, - x86: { - src: "x86/com.android.apex.test.bar.v2.libvY.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.bar.v2.libvY.apex", - }, - }, - filename: "com.android.apex.test.bar.v2.libvY.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.baz_stripped.v1.libvX_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.baz_stripped.v1.libvX.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.baz_stripped.v1.libvX.apex", - }, - x86: { - src: "x86/com.android.apex.test.baz_stripped.v1.libvX.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.baz_stripped.v1.libvX.apex", - }, - }, - filename: "com.android.apex.test.baz_stripped.v1.libvX.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.foo_stripped.v1.libvX_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.foo_stripped.v1.libvX.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.foo_stripped.v1.libvX.apex", - }, - x86: { - src: "x86/com.android.apex.test.foo_stripped.v1.libvX.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.foo_stripped.v1.libvX.apex", - }, - }, - filename: "com.android.apex.test.foo_stripped.v1.libvX.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.foo_stripped.v2.libvY_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.foo_stripped.v2.libvY.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.foo_stripped.v2.libvY.apex", - }, - x86: { - src: "x86/com.android.apex.test.foo_stripped.v2.libvY.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.foo_stripped.v2.libvY.apex", - }, - }, - filename: "com.android.apex.test.foo_stripped.v2.libvY.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.foo.v1.libvX_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.foo.v1.libvX.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.foo.v1.libvX.apex", - }, - x86: { - src: "x86/com.android.apex.test.foo.v1.libvX.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.foo.v1.libvX.apex", - }, - }, - filename: "com.android.apex.test.foo.v1.libvX.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.foo.v2.libvY_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.foo.v2.libvY.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.foo.v2.libvY.apex", - }, - x86: { - src: "x86/com.android.apex.test.foo.v2.libvY.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.foo.v2.libvY.apex", - }, - }, - filename: "com.android.apex.test.foo.v2.libvY.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.pony_stripped.v1.libvZ_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.pony_stripped.v1.libvZ.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.pony_stripped.v1.libvZ.apex", - }, - x86: { - src: "x86/com.android.apex.test.pony_stripped.v1.libvZ.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.pony_stripped.v1.libvZ.apex", - }, - }, - filename: "com.android.apex.test.pony_stripped.v1.libvZ.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.pony.v1.libvZ_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.pony.v1.libvZ.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.pony.v1.libvZ.apex", - }, - x86: { - src: "x86/com.android.apex.test.pony.v1.libvZ.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.pony.v1.libvZ.apex", - }, - }, - filename: "com.android.apex.test.pony.v1.libvZ.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.sharedlibs_generated.v1.libvX_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.sharedlibs_generated.v1.libvX.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.sharedlibs_generated.v1.libvX.apex", - }, - x86: { - src: "x86/com.android.apex.test.sharedlibs_generated.v1.libvX.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.sharedlibs_generated.v1.libvX.apex", - }, - }, - filename: "com.android.apex.test.sharedlibs_generated.v1.libvX.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.sharedlibs_generated.v2.libvY_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.sharedlibs_generated.v2.libvY.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.sharedlibs_generated.v2.libvY.apex", - }, - x86: { - src: "x86/com.android.apex.test.sharedlibs_generated.v2.libvY.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.sharedlibs_generated.v2.libvY.apex", - }, - }, - filename: "com.android.apex.test.sharedlibs_generated.v2.libvY.apex", - installable: false, -} - -prebuilt_apex { - name: "com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ_prebuilt", - arch: { - arm: { - src: "arm/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex", - }, - arm64: { - src: "arm64/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex", - }, - x86: { - src: "x86/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex", - }, - x86_64: { - src: "x86_64/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex", - }, - }, - filename: "com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex", - installable: false, -} diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar.v1.libvX.apex Binary files differdeleted file mode 100644 index 07fcff94..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar.v2.libvY.apex Binary files differdeleted file mode 100644 index 2361fd60..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index 20bdd345..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar_stripped.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar_stripped.v2.libvY.apex Binary files differdeleted file mode 100644 index 67915351..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.bar_stripped.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.baz_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.baz_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index e6405b42..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.baz_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo.v1.libvX.apex Binary files differdeleted file mode 100644 index 799890ab..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo.v2.libvY.apex Binary files differdeleted file mode 100644 index 906ec226..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index 5ee73835..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo_stripped.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo_stripped.v2.libvY.apex Binary files differdeleted file mode 100644 index c2f9f687..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.foo_stripped.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.pony.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.pony.v1.libvZ.apex Binary files differdeleted file mode 100644 index 323b81ff..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.pony.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.pony_stripped.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.pony_stripped.v1.libvZ.apex Binary files differdeleted file mode 100644 index 8a578ba0..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.pony_stripped.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.sharedlibs_generated.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.sharedlibs_generated.v1.libvX.apex Binary files differdeleted file mode 100644 index 58e7f956..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.sharedlibs_generated.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.sharedlibs_generated.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.sharedlibs_generated.v2.libvY.apex Binary files differdeleted file mode 100644 index b0385526..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.sharedlibs_generated.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex Binary files differdeleted file mode 100644 index 3ab73afa..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar.v1.libvX.apex Binary files differdeleted file mode 100644 index c04a1691..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar.v2.libvY.apex Binary files differdeleted file mode 100644 index 9bedfa25..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index a8459790..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar_stripped.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar_stripped.v2.libvY.apex Binary files differdeleted file mode 100644 index c8f89611..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.bar_stripped.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.baz_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.baz_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index e2e5b708..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.baz_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo.v1.libvX.apex Binary files differdeleted file mode 100644 index d6795a59..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo.v2.libvY.apex Binary files differdeleted file mode 100644 index 65dcd649..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index 894afa22..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo_stripped.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo_stripped.v2.libvY.apex Binary files differdeleted file mode 100644 index c1ce6fad..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.foo_stripped.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.pony.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.pony.v1.libvZ.apex Binary files differdeleted file mode 100644 index 80c64239..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.pony.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.pony_stripped.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.pony_stripped.v1.libvZ.apex Binary files differdeleted file mode 100644 index 4d380da7..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.pony_stripped.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.sharedlibs_generated.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.sharedlibs_generated.v1.libvX.apex Binary files differdeleted file mode 100644 index e0cf9e7c..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.sharedlibs_generated.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.sharedlibs_generated.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.sharedlibs_generated.v2.libvY.apex Binary files differdeleted file mode 100644 index 93f24f25..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.sharedlibs_generated.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex Binary files differdeleted file mode 100644 index 21a6dc3c..00000000 --- a/tests/testdata/sharedlibs/prebuilts/arm64/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar.v1.libvX.apex Binary files differdeleted file mode 100644 index 4767f4a3..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar.v2.libvY.apex Binary files differdeleted file mode 100644 index cff076ac..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index bca5c496..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar_stripped.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar_stripped.v2.libvY.apex Binary files differdeleted file mode 100644 index 01f822e9..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.bar_stripped.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.baz_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.baz_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index 96248526..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.baz_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo.v1.libvX.apex Binary files differdeleted file mode 100644 index 8b352bd0..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo.v2.libvY.apex Binary files differdeleted file mode 100644 index 71cdc202..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index eccb3d4e..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo_stripped.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo_stripped.v2.libvY.apex Binary files differdeleted file mode 100644 index 0265ac95..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.foo_stripped.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.pony.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.pony.v1.libvZ.apex Binary files differdeleted file mode 100644 index ef1d1d90..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.pony.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.pony_stripped.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.pony_stripped.v1.libvZ.apex Binary files differdeleted file mode 100644 index 8439fe3c..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.pony_stripped.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.sharedlibs_generated.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.sharedlibs_generated.v1.libvX.apex Binary files differdeleted file mode 100644 index 8ba0e5b4..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.sharedlibs_generated.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.sharedlibs_generated.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.sharedlibs_generated.v2.libvY.apex Binary files differdeleted file mode 100644 index 2928193e..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.sharedlibs_generated.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex Binary files differdeleted file mode 100644 index e849bcd0..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar.v1.libvX.apex Binary files differdeleted file mode 100644 index e733e1d2..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar.v2.libvY.apex Binary files differdeleted file mode 100644 index 522b9c8e..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index eec0184b..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar_stripped.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar_stripped.v2.libvY.apex Binary files differdeleted file mode 100644 index cbd96978..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.bar_stripped.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.baz_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.baz_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index b2ca4130..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.baz_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo.v1.libvX.apex Binary files differdeleted file mode 100644 index 2f5a2c27..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo.v2.libvY.apex Binary files differdeleted file mode 100644 index 1bc4774e..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo_stripped.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo_stripped.v1.libvX.apex Binary files differdeleted file mode 100644 index 77db6dff..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo_stripped.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo_stripped.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo_stripped.v2.libvY.apex Binary files differdeleted file mode 100644 index 042d4bc4..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.foo_stripped.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.pony.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.pony.v1.libvZ.apex Binary files differdeleted file mode 100644 index 5a7c5fb3..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.pony.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.pony_stripped.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.pony_stripped.v1.libvZ.apex Binary files differdeleted file mode 100644 index bb99f7ef..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.pony_stripped.v1.libvZ.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.sharedlibs_generated.v1.libvX.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.sharedlibs_generated.v1.libvX.apex Binary files differdeleted file mode 100644 index 2ff31ca4..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.sharedlibs_generated.v1.libvX.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.sharedlibs_generated.v2.libvY.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.sharedlibs_generated.v2.libvY.apex Binary files differdeleted file mode 100644 index 013f2754..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.sharedlibs_generated.v2.libvY.apex +++ /dev/null diff --git a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex b/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex Binary files differdeleted file mode 100644 index 3c6cf339..00000000 --- a/tests/testdata/sharedlibs/prebuilts/x86_64/com.android.apex.test.sharedlibs_secondary_generated.v1.libvZ.apex +++ /dev/null diff --git a/tests/util/com/android/tests/util/ModuleTestUtils.java b/tests/util/com/android/tests/util/ModuleTestUtils.java new file mode 100644 index 00000000..8f62bc33 --- /dev/null +++ b/tests/util/com/android/tests/util/ModuleTestUtils.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.util; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.tradefed.build.BuildInfoKey.BuildInfoFileKey; +import com.android.tradefed.build.IBuildInfo; +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.device.ITestDevice.ApexInfo; +import com.android.tradefed.log.LogUtil.CLog; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.util.CommandResult; +import com.android.tradefed.util.CommandStatus; +import com.android.tradefed.util.FileUtil; +import com.android.tradefed.util.IRunUtil; +import com.android.tradefed.util.RunUtil; +import com.android.tradefed.util.SystemUtil.EnvVariable; + +import com.google.common.base.Stopwatch; + +import org.junit.Assert; + +import java.io.File; +import java.io.IOException; +import java.time.Duration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +public class ModuleTestUtils { + private static final String SHIM = "com.android.apex.cts.shim"; + private static final String APEX_INFO_EXTRACT_REGEX = + ".*package:\\sname='(\\S+)\\'\\sversionCode='(\\d+)'\\s.*"; + + private static final Duration WAIT_FOR_SESSION_READY_TTL = Duration.ofSeconds(10); + private static final Duration SLEEP_FOR = Duration.ofMillis(200); + + protected final Pattern mIsSessionReadyPattern = + Pattern.compile("(isReady = true)|(isStagedSessionReady = true)"); + protected final Pattern mIsSessionAppliedPattern = + Pattern.compile("(isApplied = true)|(isStagedSessionApplied = true)"); + + private IRunUtil mRunUtil = new RunUtil(); + private BaseHostJUnit4Test mTest; + + private IBuildInfo getBuild() { + return mTest.getBuild(); + } + + public ModuleTestUtils(BaseHostJUnit4Test test) { + mTest = test; + } + + /** + * Retrieve package name and version code from test apex file. + * + * @param apex input apex file to retrieve the info from + */ + public ApexInfo getApexInfo(File apex) { + String aaptOutput = runCmd(String.format( + "aapt dump badging %s", apex.getAbsolutePath())); + String[] lines = aaptOutput.split("\n"); + Pattern p = Pattern.compile(APEX_INFO_EXTRACT_REGEX); + for (String l : lines) { + Matcher m = p.matcher(l); + if (m.matches()) { + ApexInfo apexInfo = new ApexInfo(m.group(1), Long.parseLong(m.group(2))); + return apexInfo; + } + } + return null; + } + + /** + * Get the test file. + * + * @param testFileName name of the file + */ + public File getTestFile(String testFileName) throws IOException { + File testFile = null; + + String testcasesPath = System.getenv(EnvVariable.ANDROID_HOST_OUT_TESTCASES.toString()); + if (testcasesPath != null) { + testFile = searchTestFile(new File(testcasesPath), testFileName); + } + if (testFile != null) { + return testFile; + } + + File hostLinkedDir = getBuild().getFile(BuildInfoFileKey.HOST_LINKED_DIR); + if (hostLinkedDir != null) { + testFile = searchTestFile(hostLinkedDir, testFileName); + } + if (testFile != null) { + return testFile; + } + + // Find the file in the buildinfo. + File buildInfoFile = getBuild().getFile(testFileName); + if (buildInfoFile != null) { + return buildInfoFile; + } + + throw new IOException("Cannot find " + testFileName); + } + + private String runCmd(String cmd) { + CLog.d("About to run command: %s", cmd); + CommandResult result = mRunUtil.runTimedCmd(1000 * 60 * 5, cmd.split("\\s+")); + Assert.assertNotNull(result); + Assert.assertTrue( + String.format("Command %s failed", cmd), + result.getStatus().equals(CommandStatus.SUCCESS)); + CLog.v("output:\n%s", result.getStdout()); + return result.getStdout(); + } + + /** + * Searches the file with the given name under the given directory, returns null if not found. + */ + private File searchTestFile(File baseSearchFile, String testFileName) { + if (baseSearchFile != null && baseSearchFile.isDirectory()) { + File testFile = FileUtil.findFile(baseSearchFile, testFileName); + if (testFile != null && testFile.isFile()) { + return testFile; + } + } + return null; + } + + public void waitForStagedSessionReady() throws DeviceNotAvailableException { + // TODO: implement wait for session ready logic inside PackageManagerShellCommand instead. + boolean sessionReady = false; + Duration spentWaiting = Duration.ZERO; + while (spentWaiting.compareTo(WAIT_FOR_SESSION_READY_TTL) < 0) { + CommandResult res = mTest.getDevice().executeShellV2Command("pm get-stagedsessions"); + Assert.assertEquals("", res.getStderr()); + sessionReady = Stream.of(res.getStdout().split("\n")).anyMatch(this::isReadyNotApplied); + if (sessionReady) { + CLog.i("Done waiting after " + spentWaiting); + break; + } + try { + Thread.sleep(SLEEP_FOR.toMillis()); + spentWaiting = spentWaiting.plus(SLEEP_FOR); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + Assert.assertTrue("Staged session wasn't ready in " + WAIT_FOR_SESSION_READY_TTL, + sessionReady); + } + + /** + * Abandons any staged session that is marked {@code ready} + */ + public void abandonActiveStagedSession() throws DeviceNotAvailableException { + CommandResult res = mTest.getDevice().executeShellV2Command("pm list staged-sessions " + + "--only-ready --only-parent --only-sessionid"); + assertThat(res.getStderr()).isEqualTo(""); + String activeSessionId = res.getStdout(); + if (activeSessionId != null && !activeSessionId.equals("")) { + res = mTest.getDevice().executeShellV2Command("pm install-abandon " + + activeSessionId); + if (!res.getStderr().equals("") || res.getStatus() != CommandStatus.SUCCESS) { + CLog.d("Failed to abandon session " + activeSessionId + + " Error: " + res.getStderr()); + } + } + } + + /** + * Uninstalls a shim apex only if its latest version is installed on /data partition + * + * <p>This is purely to optimize tests run time, since uninstalling an apex requires a reboot. + */ + public void uninstallShimApexIfNecessary() throws Exception { + if (!isApexUpdateSupported()) { + return; + } + + final String errorMessage = mTest.getDevice().uninstallPackage(SHIM); + if (errorMessage == null) { + CLog.i("Uninstalling shim apex"); + mTest.getDevice().reboot(); + } else { + // Most likely we tried to uninstall system version and failed. It should be fine to + // continue tests. + // TODO(b/140813980): use ApexInfo.sourceDir to decide whenever to issue an uninstall. + CLog.w("Failed to uninstall shim APEX: " + errorMessage); + } + assertThat(getShimApex().versionCode).isEqualTo(1L); + } + + private ITestDevice.ApexInfo getShimApex() throws DeviceNotAvailableException { + return mTest.getDevice().getActiveApexes().stream().filter( + apex -> apex.name.equals(SHIM)).findAny().orElseThrow( + () -> new AssertionError("Can't find " + SHIM)); + } + + /** + * Return {@code true} if and only if device supports updating apex. + */ + public boolean isApexUpdateSupported() throws Exception { + return mTest.getDevice().getBooleanProperty("ro.apex.updatable", false); + } + + private boolean isReadyNotApplied(String sessionInfo) { + boolean isReady = mIsSessionReadyPattern.matcher(sessionInfo).find(); + boolean isApplied = mIsSessionAppliedPattern.matcher(sessionInfo).find(); + return isReady && !isApplied; + } + + /** + * Waits for given {@code timeout} for {@code filePath} to be deleted. + */ + public void waitForFileDeleted(String filePath, Duration timeout) throws Exception { + Stopwatch stopwatch = Stopwatch.createStarted(); + while (true) { + if (!mTest.getDevice().doesFileExist(filePath)) { + return; + } + if (stopwatch.elapsed().compareTo(timeout) > 0) { + break; + } + Thread.sleep(500); + } + throw new AssertionError("Timed out waiting for " + filePath + " to be deleted"); + } +} diff --git a/tests/wifi-e2e-tests.xml b/tests/wifi-e2e-tests.xml new file mode 100644 index 00000000..1a130962 --- /dev/null +++ b/tests/wifi-e2e-tests.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Config for wifi apex e2e testing"> + <option name="test-suite-tag" value="wifi_e2e_tests" /> + <option name="test-suite-tag" value="apct" /> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <test class="com.android.tradefed.testtype.HostTest" > + <option name="jar" value="wifi_e2e_tests.jar" /> + <option name="set-option" value="apex_file_name:test_com.android.wifi.apex" /> + </test> +</configuration> + diff --git a/tools/Android.bp b/tools/Android.bp index 6b4427b1..1f119e52 100644 --- a/tools/Android.bp +++ b/tools/Android.bp @@ -12,10 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -package { - default_applicable_licenses: ["Android-Apache-2.0"], -} - python_binary_host { name: "deapexer", srcs: [ @@ -38,56 +34,3 @@ python_binary_host { ], } -python_binary_host { - name: "apex_compression_tool", - srcs: [ - "apex_compression_tool.py", - ], - version: { - py2: { - enabled: false, - }, - py3: { - enabled: true, - embedded_launcher: true, - }, - }, - libs: [ - "apex_manifest_proto", - ], - required: [ - "avbtool", - "conv_apex_manifest", - ], -} - -python_test_host { - name: "apex_compression_test", - main: "apex_compression_test.py", - srcs: [ - "apex_compression_test.py", - ], - data: [ - ":avbtool", - ":com.android.example.apex", - ":conv_apex_manifest", - ":apex_compression_tool", - ":deapexer", - ":soong_zip", - ], - version: { - py2: { - enabled: false, - }, - py3: { - enabled: true, - }, - }, - libs: [ - "apex_manifest_proto", - ], - test_suites: ["general-tests"], - test_options: { - unit_test: true, - }, -} diff --git a/tools/apex_compression_test.py b/tools/apex_compression_test.py deleted file mode 100644 index 018fc563..00000000 --- a/tools/apex_compression_test.py +++ /dev/null @@ -1,323 +0,0 @@ -#!/usr/bin/env python -# -# 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. -# -"""Unit tests for apex_compression_tool.""" -import hashlib -import logging -import os -import shutil -import subprocess -import tempfile -import unittest -from zipfile import ZipFile, ZIP_STORED, ZIP_DEFLATED - -import apex_manifest_pb2 - -logger = logging.getLogger(__name__) - -TEST_APEX = 'com.android.example.apex' - -# In order to debug test failures, set DEBUG_TEST to True and run the test from -# local workstation bypassing atest, e.g.: -# $ m apex_compression_tool_test && \ -# out/host/linux-x86/nativetest64/apex_compression_tool_test/\ -# apex_compression_tool_test -# -# the test will print out the command used, and the temporary files used by the -# test. -DEBUG_TEST = False - - -def run(args, verbose=None, **kwargs): - """Creates and returns a subprocess.Popen object. - - Args: - args: The command represented as a list of strings. - verbose: Whether the commands should be shown. Default to the global - verbosity if unspecified. - kwargs: Any additional args to be passed to subprocess.Popen(), such as env, - stdin, etc. stdout and stderr will default to subprocess.PIPE and - subprocess.STDOUT respectively unless caller specifies any of them. - universal_newlines will default to True, as most of the users in - releasetools expect string output. - - Returns: - A subprocess.Popen object. - """ - if 'stdout' not in kwargs and 'stderr' not in kwargs: - kwargs['stdout'] = subprocess.PIPE - kwargs['stderr'] = subprocess.STDOUT - if 'universal_newlines' not in kwargs: - kwargs['universal_newlines'] = True - if DEBUG_TEST: - print('\nRunning: \n%s\n' % ' '.join(args)) - # Don't log any if caller explicitly says so. - if verbose: - logger.info(' Running: \'%s\'', ' '.join(args)) - return subprocess.Popen(args, **kwargs) - - -def run_host_command(args, verbose=None, **kwargs): - host_build_top = os.environ.get('ANDROID_BUILD_TOP') - if host_build_top: - host_command_dir = os.path.join(host_build_top, - 'out/soong/host/linux-x86/bin') - args[0] = os.path.join(host_command_dir, args[0]) - return run_and_check_output(args, verbose, **kwargs) - - -def run_and_check_output(args, verbose=None, **kwargs): - """Runs the given command and returns the output. - - Args: - args: The command represented as a list of strings. - verbose: Whether the commands should be shown. Default to the global - verbosity if unspecified. - kwargs: Any additional args to be passed to subprocess.Popen(), such as env, - stdin, etc. stdout and stderr will default to subprocess.PIPE and - subprocess.STDOUT respectively unless caller specifies any of them. - - Returns: - The output string. - - Raises: - ExternalError: On non-zero exit from the command. - """ - - proc = run(args, verbose=verbose, **kwargs) - output, _ = proc.communicate() - if output is None: - output = '' - # Don't log any if caller explicitly says so. - if verbose: - logger.info('%s', output.rstrip()) - if proc.returncode != 0: - raise RuntimeError( - "Failed to run command '{}' (exit code {}):\n{}".format( - args, proc.returncode, output)) - return output - - -def get_current_dir(): - """Returns the current dir, relative to the script dir.""" - # The script dir is the one we want, which could be different from pwd. - current_dir = os.path.dirname(os.path.realpath(__file__)) - return current_dir - - -def get_sha1sum(file_path): - h = hashlib.sha256() - - with open(file_path, 'rb') as file: - while True: - # Reading is buffered, so we can read smaller chunks. - chunk = file.read(h.block_size) - if not chunk: - break - h.update(chunk) - - return h.hexdigest() - - -class ApexCompressionTest(unittest.TestCase): - def setUp(self): - self._to_cleanup = [] - - def tearDown(self): - if not DEBUG_TEST: - for i in self._to_cleanup: - if os.path.isdir(i): - shutil.rmtree(i, ignore_errors=True) - else: - os.remove(i) - del self._to_cleanup[:] - else: - print('Cleanup: ' + str(self._to_cleanup)) - - def _run_apex_compression_tool(self, args): - cmd = ['apex_compression_tool'] - host_build_top = os.environ.get('ANDROID_BUILD_TOP') - if host_build_top: - os.environ['APEX_COMPRESSION_TOOL_PATH'] = ( - os.path.join(host_build_top, 'out/soong/host/linux-x86/bin') - + ':' + os.path.join(host_build_top, 'prebuilts/sdk/tools/linux/bin')) - else: - os.environ['APEX_COMPRESSION_TOOL_PATH'] = os.path.dirname( - shutil.which('apex_compression_tool')) - cmd.extend(args) - run_host_command(cmd, True) - - def _get_container_files(self, apex_file_path): - dir_name = tempfile.mkdtemp( - prefix=self._testMethodName + '_container_files_') - self._to_cleanup.append(dir_name) - with ZipFile(apex_file_path, 'r') as zip_obj: - zip_obj.extractall(path=dir_name) - files = {} - for i in ['apex_manifest.json', 'apex_manifest.pb', 'apex_pubkey', - 'apex_build_info.pb', 'apex_payload.img', 'apex_payload.zip', - 'AndroidManifest.xml', 'original_apex']: - file_path = os.path.join(dir_name, i) - if os.path.exists(file_path): - files[i] = file_path - - image_file = files.get('apex_payload.img', None) - if image_file is None: - image_file = files.get('apex_payload.zip', None) - else: - files['apex_payload'] = image_file - # Also retrieve the root digest of the image - avbtool_cmd = ['avbtool', - 'print_partition_digests', '--image', files['apex_payload']] - # avbtool_cmd output has format "<name>: <value>" - files['digest'] = run_host_command( - avbtool_cmd, True).split(': ')[1].strip() - - return files - - def _get_manifest_string(self, manifest_path): - cmd = ['conv_apex_manifest'] - cmd.extend([ - 'print', - manifest_path - ]) - return run_host_command(cmd, 'True') - - # Mutates the manifest located at |manifest_path| - def _unset_original_apex_digest(self, manifest_path): - # Open the protobuf - with open(manifest_path, 'rb') as f: - pb = apex_manifest_pb2.ApexManifest() - pb.ParseFromString(f.read()) - pb.ClearField('capexMetadata') - with open(manifest_path, 'wb') as f: - f.write(pb.SerializeToString()) - - def _compress_apex(self, uncompressed_apex_fp): - """Returns file path to compressed APEX""" - fd, compressed_apex_fp = tempfile.mkstemp( - prefix=self._testMethodName + '_compressed_', - suffix='.capex') - os.close(fd) - self._to_cleanup.append(compressed_apex_fp) - self._run_apex_compression_tool([ - 'compress', - '--input', uncompressed_apex_fp, - '--output', compressed_apex_fp - ]) - return compressed_apex_fp - - def _decompress_apex(self, compressed_apex_fp): - """Returns file path to decompressed APEX""" - decompressed_apex_fp = tempfile. \ - NamedTemporaryFile(prefix=self._testMethodName + '_decompressed_', - suffix='.apex').name - # Use deapexer to decompress - cmd = ['deapexer'] - cmd.extend([ - 'decompress', - '--input', compressed_apex_fp, - '--output', decompressed_apex_fp - ]) - run_host_command(cmd, True) - - self.assertTrue(os.path.exists(decompressed_apex_fp), - 'Decompressed APEX does not exist') - self._to_cleanup.append(decompressed_apex_fp) - return decompressed_apex_fp - - def _get_type(self, apex_file_path): - cmd = ['deapexer', 'info', '--print-type', apex_file_path] - return run_host_command(cmd, True).strip() - - def test_compression(self): - uncompressed_apex_fp = os.path.join(get_current_dir(), TEST_APEX + '.apex') - # TODO(samiul): try compressing a compressed APEX - compressed_apex_fp = self._compress_apex(uncompressed_apex_fp) - - # Verify output file has been created and is smaller than input file - uncompressed_file_size = os.path.getsize(uncompressed_apex_fp) - compressed_file_size = os.path.getsize(compressed_apex_fp) - self.assertGreater(compressed_file_size, 0, 'Compressed APEX is empty') - self.assertLess(compressed_file_size, uncompressed_file_size, - 'Compressed APEX is not smaller than uncompressed APEX') - - # Verify type of the apex is 'COMPRESSED' - self.assertEqual(self._get_type(compressed_apex_fp), 'COMPRESSED') - - # Verify the contents of the compressed apex files - content_in_compressed_apex = self._get_container_files(compressed_apex_fp) - self.assertIsNotNone(content_in_compressed_apex['original_apex']) - content_in_uncompressed_apex = self._get_container_files( - uncompressed_apex_fp) - self.assertIsNotNone(content_in_uncompressed_apex['apex_payload']) - self.assertIsNotNone(content_in_uncompressed_apex['digest']) - - # Verify that CAPEX manifest contains digest of original_apex - manifest_string = self._get_manifest_string( - content_in_compressed_apex['apex_manifest.pb']) - self.assertIn('originalApexDigest: "' - + content_in_uncompressed_apex['digest'] + '"', manifest_string) - - for i in ['apex_manifest.json', 'apex_manifest.pb', 'apex_pubkey', - 'apex_build_info.pb', 'AndroidManifest.xml']: - if i in content_in_uncompressed_apex: - if i == 'apex_manifest.pb': - # Get rid of originalApexDigest field, which should be the - # only difference - self._unset_original_apex_digest(content_in_compressed_apex[i]) - self.assertEqual(get_sha1sum(content_in_compressed_apex[i]), - get_sha1sum(content_in_uncompressed_apex[i])) - - def test_decompression(self): - # setup: create compressed APEX - uncompressed_apex_fp = os.path.join(get_current_dir(), TEST_APEX + '.apex') - compressed_apex_fp = self._compress_apex(uncompressed_apex_fp) - - # Decompress it - decompressed_apex_fp = self._decompress_apex(compressed_apex_fp) - - # Verify type of the apex is 'UNCOMPRESSED' - self.assertEqual(self._get_type(decompressed_apex_fp), 'UNCOMPRESSED') - - # Verify decompressed APEX is same as uncompressed APEX - self.assertEqual(get_sha1sum(uncompressed_apex_fp), - get_sha1sum(decompressed_apex_fp), - 'Decompressed APEX is not same as uncompressed APEX') - - # Try decompressing uncompressed APEX. It should not work. - with self.assertRaises(RuntimeError) as error: - self._decompress_apex(uncompressed_apex_fp) - - self.assertIn(uncompressed_apex_fp - + ' is not a compressed APEX', str(error.exception)) - - def test_only_original_apex_is_compressed(self): - uncompressed_apex_fp = os.path.join(get_current_dir(), TEST_APEX + '.apex') - compressed_apex_fp = self._compress_apex(uncompressed_apex_fp) - - with ZipFile(compressed_apex_fp, 'r') as zip_obj: - self.assertEqual(zip_obj.getinfo('original_apex').compress_type, - ZIP_DEFLATED) - content_in_uncompressed_apex = self._get_container_files( - uncompressed_apex_fp) - for i in ['apex_manifest.json', 'apex_manifest.pb', 'apex_pubkey', - 'apex_build_info.pb', 'AndroidManifest.xml']: - if i in content_in_uncompressed_apex: - self.assertEqual(zip_obj.getinfo(i).compress_type, ZIP_STORED) - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/tools/apex_compression_tool.py b/tools/apex_compression_tool.py deleted file mode 100644 index 3c0f16d8..00000000 --- a/tools/apex_compression_tool.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python -# -# 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. - -"""apex_compression_tool is a tool that can compress/decompress APEX. - -Example: - apex_compression_tool compress --input /apex/to/compress --output output/path - apex_compression_tool decompress --input /apex/to/decompress --output dir/ - apex_compression_tool verify-compressed --input /file/to/check -""" -from __future__ import print_function - -import argparse -import os -import shutil -import subprocess -import sys -import tempfile -from zipfile import ZipFile - -import apex_manifest_pb2 - -tool_path_list = None - - -def FindBinaryPath(binary): - for path in tool_path_list: - binary_path = os.path.join(path, binary) - if os.path.exists(binary_path): - return binary_path - raise Exception('Failed to find binary ' + binary + ' in path ' + - ':'.join(tool_path_list)) - - -def RunCommand(cmd, verbose=False, env=None, expected_return_values=None): - expected_return_values = expected_return_values or {0} - env = env or {} - env.update(os.environ.copy()) - - cmd[0] = FindBinaryPath(cmd[0]) - - if verbose: - print('Running: ' + ' '.join(cmd)) - p = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) - output, _ = p.communicate() - - if verbose or p.returncode not in expected_return_values: - print(output.rstrip()) - - assert p.returncode in expected_return_values, 'Failed to execute: ' \ - + ' '.join(cmd) - - return output, p.returncode - - -def RunCompress(args, work_dir): - """RunCompress takes an uncompressed APEX and compresses into compressed APEX - - Compressed apex will contain the following items: - - original_apex: The original uncompressed APEX - - Duplicates of various meta files inside the input APEX, e.g - AndroidManifest.xml, public_key - - Args: - args.input: file path to uncompressed APEX - args.output: file path to where compressed APEX will be placed - work_dir: file path to a temporary folder - Returns: - True if compression was executed successfully, otherwise False - """ - global tool_path_list - tool_path_list = args.apex_compression_tool_path - - cmd = ['soong_zip'] - cmd.extend(['-o', args.output]) - - # We want to put the input apex inside the compressed APEX with name - # "original_apex". So we create a hard link and put the renamed file inside - # the zip - original_apex = os.path.join(work_dir, 'original_apex') - os.link(args.input, original_apex) - cmd.extend(['-C', work_dir]) - cmd.extend(['-f', original_apex]) - - # We also need to extract some files from inside of original_apex and zip - # together with compressed apex - with ZipFile(original_apex, 'r') as zip_obj: - extract_dir = os.path.join(work_dir, 'extract') - for meta_file in ['apex_manifest.json', 'apex_manifest.pb', - 'apex_pubkey', 'apex_build_info.pb', - 'AndroidManifest.xml']: - if meta_file in zip_obj.namelist(): - zip_obj.extract(meta_file, path=extract_dir) - file_path = os.path.join(extract_dir, meta_file) - cmd.extend(['-C', extract_dir]) - cmd.extend(['-f', file_path]) - cmd.extend(['-s', meta_file]) - # Extract the image for retrieving root digest - zip_obj.extract('apex_payload.img', path= work_dir) - image_path = os.path.join(work_dir, 'apex_payload.img') - - # Set digest of original_apex to apex_manifest.pb - apex_manifest_path = os.path.join(extract_dir, 'apex_manifest.pb') - assert AddOriginalApexDigestToManifest(apex_manifest_path, image_path) - - # Don't forget to compress - cmd.extend(['-L', '9']) - - RunCommand(cmd, verbose=True) - - return True - - -def AddOriginalApexDigestToManifest(capex_manifest_path, apex_image_path): - # Retrieve the root digest of the image - avbtool_cmd = [ - 'avbtool', - 'print_partition_digests', '--image', - apex_image_path] - # avbtool_cmd output has format "<name>: <value>" - root_digest = RunCommand(avbtool_cmd, True)[0].decode().split(': ')[1].strip() - # Update the manifest proto file - with open(capex_manifest_path, 'rb') as f: - pb = apex_manifest_pb2.ApexManifest() - pb.ParseFromString(f.read()) - # Populate CompressedApexMetadata - capex_metadata = apex_manifest_pb2.ApexManifest().CompressedApexMetadata() - capex_metadata.originalApexDigest = root_digest - # Set updated value to protobuf - pb.capexMetadata.CopyFrom(capex_metadata) - with open(capex_manifest_path, 'wb') as f: - f.write(pb.SerializeToString()) - return True - - -def ParseArgs(argv): - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(required=True, dest='cmd') - - # Handle sub-command "compress" - parser_compress = subparsers.add_parser('compress', - help='compresses an APEX') - parser_compress.add_argument('--input', type=str, required=True, - help='path to input APEX file that will be ' - 'compressed') - parser_compress.add_argument('--output', type=str, required=True, - help='output path to compressed APEX file') - apex_compression_tool_path_in_environ = \ - 'APEX_COMPRESSION_TOOL_PATH' in os.environ - parser_compress.add_argument( - '--apex_compression_tool_path', - required=not apex_compression_tool_path_in_environ, - default=os.environ['APEX_COMPRESSION_TOOL_PATH'].split(':') - if apex_compression_tool_path_in_environ else None, - type=lambda s: s.split(':'), - help="""A list of directories containing all the tools used by - apex_compression_tool (e.g. soong_zip etc.) separated by ':'. Can also - be set using the APEX_COMPRESSION_TOOL_PATH environment variable""") - parser_compress.set_defaults(func=RunCompress) - - return parser.parse_args(argv) - - -class TempDirectory(object): - - def __enter__(self): - self.name = tempfile.mkdtemp() - return self.name - - def __exit__(self, *unused): - shutil.rmtree(self.name) - - -def main(argv): - args = ParseArgs(argv) - - with TempDirectory() as work_dir: - success = args.func(args, work_dir) - - if not success: - sys.exit(1) - - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/tools/create_apex_skeleton.sh b/tools/create_apex_skeleton.sh deleted file mode 100644 index 818e119a..00000000 --- a/tools/create_apex_skeleton.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/sh - -# Creates an apex stub in a subdirectory named after the package name. Edit the APEX_NAME variable -# before running. - -APEX_NAME=com.android.yourpackagenamehere - -mkdir ${APEX_NAME} -cd ${APEX_NAME} - -cat > Android.bp <<EOF -// 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. -apex_key { - name: "${APEX_NAME}.key", - public_key: "${APEX_NAME}.avbpubkey", - private_key: "${APEX_NAME}.pem", -} - -android_app_certificate { - name: "${APEX_NAME}.certificate", - certificate: "${APEX_NAME}", -} - -apex { - name: "${APEX_NAME}", - manifest: "manifest.json", - file_contexts: ":apex.test-file_contexts", // Default, please edit, see go/android-apex-howto - key: "${APEX_NAME}.key", -} -EOF - -openssl genrsa -out ${APEX_NAME}.pem 4096 -avbtool extract_public_key --key ${APEX_NAME}.pem --output ${APEX_NAME}.avbpubkey - -cat > csr.conf <<EOF -[req] -default_bits = 4096 -distinguished_name = dn -prompt = no - -[dn] -C="US" -ST="California" -L="Mountain View" -O="Android" -OU="Android" -emailAddress="android@android.com" -CN="${APEX_NAME}" -EOF - -openssl req -x509 -config csr.conf -newkey rsa:4096 -nodes -days 999999 -keyout key.pem -out ${APEX_NAME}.x509.pem -rm csr.conf -openssl pkcs8 -topk8 -inform PEM -outform DER -in key.pem -out ${APEX_NAME}.pk8 -nocrypt -rm key.pem - -cat > manifest.json << EOF -{ - "name": "${APEX_NAME}", - "version": 1 -} -EOF diff --git a/tools/deapexer.py b/tools/deapexer.py index 92001c9d..b170d90f 100644 --- a/tools/deapexer.py +++ b/tools/deapexer.py @@ -24,29 +24,23 @@ To extract content of an APEX to the given directory: from __future__ import print_function import argparse -import apex_manifest -import enum import os import shutil import sys import subprocess import tempfile import zipfile - -BLOCK_SIZE = 4096 +import apex_manifest class ApexImageEntry(object): - def __init__(self, name, base_dir, permissions, size, ino, extents, is_directory=False, - is_symlink=False): + def __init__(self, name, base_dir, permissions, size, is_directory=False, is_symlink=False): self._name = name self._base_dir = base_dir self._permissions = permissions self._size = size self._is_directory = is_directory self._is_symlink = is_symlink - self._ino = ino - self._extents = extents @property def name(self): @@ -76,14 +70,6 @@ class ApexImageEntry(object): def size(self): return self._size - @property - def ino(self): - return self._ino - - @property - def extents(self): - return self._extents - def __str__(self): ret = '' if self._is_directory: @@ -166,43 +152,10 @@ class Apex(object): name = parts[5] if not name: continue - ino = parts[1] bits = parts[2] size = parts[6] - extents = [] - is_symlink = bits[1]=='2' - is_directory=bits[1]=='4' - - if not is_symlink and not is_directory: - process = subprocess.Popen([self._debugfs, '-R', 'dump_extents <%s>' % ino, - self._payload], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True) - stdout, _ = process.communicate() - # Output of dump_extents for an inode fragmented in 3 blocks (length and addresses represent - # block-sized sections): - # Level Entries Logical Physical Length Flags - # 0/ 0 1/ 3 0 - 0 18 - 18 1 - # 0/ 0 2/ 3 1 - 15 20 - 34 15 - # 0/ 0 3/ 3 16 - 1863 37 - 1884 1848 - res = str(stdout).splitlines() - res.pop(0) # the first line contains only columns names - left_length = int(size) - try: # dump_extents sometimes has an unexpected output - for line in res: - tokens = line.split() - offset = int(tokens[7]) * BLOCK_SIZE - length = min(int(tokens[-1]) * BLOCK_SIZE, left_length) - left_length -= length - extents.append((offset, length)) - if (left_length != 0): # dump_extents sometimes fails to display "hole" blocks - raise ValueError - except: - extents = [] # [] means that we failed to retrieve the file location successfully - entries.append(ApexImageEntry(name, base_dir=path, permissions=int(bits[3:], 8), size=size, - is_directory=is_directory, is_symlink=is_symlink, ino=ino, - extents=extents)) - + is_directory=bits[1]=='4', is_symlink=bits[1]=='2')) return ApexImageDirectory(path, entries, self) def _extract(self, path, dest): @@ -215,38 +168,17 @@ class Apex(object): def RunList(args): - if GetType(args.apex) == ApexType.COMPRESSED: - with tempfile.TemporaryDirectory() as temp: - decompressed_apex = os.path.join(temp, 'temp.apex') - decompress(args.apex, decompressed_apex) - args.apex = decompressed_apex - - RunList(args) - return - with Apex(args) as apex: for e in apex.list(is_recursive=True): if e.is_directory: continue - res = '' if args.size: - res += e.size + ' ' - res += e.full_path - if args.extents: - res += ' [' + '-'.join(str(x) for x in e.extents) + ']' - print(res) + print(e.size, e.full_path) + else: + print(e.full_path) def RunExtract(args): - if GetType(args.apex) == ApexType.COMPRESSED: - with tempfile.TemporaryDirectory() as temp: - decompressed_apex = os.path.join(temp, "temp.apex") - decompress(args.apex, decompressed_apex) - args.apex = decompressed_apex - - RunExtract(args) - return - with Apex(args) as apex: if not os.path.exists(args.dest): os.makedirs(args.dest, mode=0o755) @@ -254,75 +186,15 @@ def RunExtract(args): shutil.rmtree(os.path.join(args.dest, "lost+found")) -class ApexType(enum.Enum): - INVALID = 0 - UNCOMPRESSED = 1 - COMPRESSED = 2 - - -def GetType(apex_path): - with zipfile.ZipFile(apex_path, 'r') as zip_file: - names = zip_file.namelist() - has_payload = 'apex_payload.img' in names - has_original_apex = 'original_apex' in names - if has_payload and has_original_apex: - return ApexType.INVALID - if has_payload: - return ApexType.UNCOMPRESSED - if has_original_apex: - return ApexType.COMPRESSED - return ApexType.INVALID - - def RunInfo(args): - if args.print_type: - res = GetType(args.apex) - if res == ApexType.INVALID: - print(args.apex + ' is not a valid apex') - sys.exit(1) - print(res.name) - else: - manifest = apex_manifest.fromApex(args.apex) - print(apex_manifest.toJsonString(manifest)) - - -def RunDecompress(args): - """RunDecompress takes path to compressed APEX and decompresses it to - produce the original uncompressed APEX at give output path - - See apex_compression_tool.py#RunCompress for details on compressed APEX - structure. - - Args: - args.input: file path to compressed APEX - args.output: file path to where decompressed APEX will be placed - """ - compressed_apex_fp = args.input - decompressed_apex_fp = args.output - return decompress(compressed_apex_fp, decompressed_apex_fp) - -def decompress(compressed_apex_fp, decompressed_apex_fp): - if os.path.exists(decompressed_apex_fp): - print("Output path '" + decompressed_apex_fp + "' already exists") - sys.exit(1) - - with zipfile.ZipFile(compressed_apex_fp, 'r') as zip_obj: - if 'original_apex' not in zip_obj.namelist(): - print(compressed_apex_fp + ' is not a compressed APEX. Missing ' - "'original_apex' file inside it.") - sys.exit(1) - # Rename original_apex file to what user provided as output filename - original_apex_info = zip_obj.getinfo('original_apex') - original_apex_info.filename = os.path.basename(decompressed_apex_fp) - # Extract the original_apex as desired name - zip_obj.extract(original_apex_info, - path=os.path.dirname(decompressed_apex_fp)) + manifest = apex_manifest.fromApex(args.apex) + print(apex_manifest.toJsonString(manifest)) def main(argv): parser = argparse.ArgumentParser() - debugfs_default = None + debugfs_default = 'debugfs' # assume in PATH by default if 'ANDROID_HOST_OUT' in os.environ: debugfs_default = '%s/bin/debugfs_static' % os.environ['ANDROID_HOST_OUT'] parser.add_argument('--debugfs_path', help='The path to debugfs binary', default=debugfs_default) @@ -332,7 +204,6 @@ def main(argv): parser_list = subparsers.add_parser('list', help='prints content of an APEX to stdout') parser_list.add_argument('apex', type=str, help='APEX file') parser_list.add_argument('--size', help='also show the size of the files', action="store_true") - parser_list.add_argument('--extents', help='also show the location of the files', action="store_true") parser_list.set_defaults(func=RunList) parser_extract = subparsers.add_parser('extract', help='extracts content of an APEX to the given ' @@ -343,31 +214,10 @@ def main(argv): parser_info = subparsers.add_parser('info', help='prints APEX manifest') parser_info.add_argument('apex', type=str, help='APEX file') - parser_info.add_argument('--print-type', - help='Prints type of the apex (COMPRESSED or UNCOMPRESSED)', - action='store_true') parser_info.set_defaults(func=RunInfo) - # Handle sub-command "decompress" - parser_decompress = subparsers.add_parser('decompress', - help='decompresses a compressed ' - 'APEX') - parser_decompress.add_argument('--input', type=str, required=True, - help='path to compressed APEX file that ' - 'will be decompressed') - parser_decompress.add_argument('--output', type=str, required=True, - help='output directory path where ' - 'decompressed APEX will be extracted') - parser_decompress.set_defaults(func=RunDecompress) - args = parser.parse_args(argv) - debugfs_required_for_cmd = ['list', 'extract'] - if args.cmd in debugfs_required_for_cmd and not args.debugfs_path: - print('ANDROID_HOST_OUT environment variable is not defined, --debugfs_path must be set', - file=sys.stderr) - sys.exit(1) - args.func(args) |