From 4a01f36d210491e78765b5f6875ecd840f8c3b1e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 26 Apr 2019 23:41:49 -0700 Subject: tests: Merge recovery_component_test into recovery_unit_test. Most of the tests in component/ are in fact unit tests. And it doesn't look practically beneficial to distinguish between the two: - They have the same test setup; - We always run both (recovery_unit_test and recovery_component_test) at the same time; - Breaking any of them would be equally bad. This CL merges the tests in recovery_component_test into recovery_unit_test to save the effort to maintain both. Test: Run recovery_unit_test on marlin (via `adb sync data`). Test: `atest recovery_unit_test` Change-Id: I93ff32e7219cd83425a4bcfe5613978a8dd48d75 --- tests/unit/install_test.cpp | 598 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 tests/unit/install_test.cpp (limited to 'tests/unit/install_test.cpp') diff --git a/tests/unit/install_test.cpp b/tests/unit/install_test.cpp new file mode 100644 index 000000000..36820f837 --- /dev/null +++ b/tests/unit/install_test.cpp @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agree to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "install/install.h" +#include "install/wipe_device.h" +#include "otautil/paths.h" +#include "private/setup_commands.h" + +static void BuildZipArchive(const std::map& file_map, int fd, + int compression_type) { + FILE* zip_file = fdopen(fd, "w"); + ZipWriter writer(zip_file); + for (const auto& [name, content] : file_map) { + ASSERT_EQ(0, writer.StartEntry(name.c_str(), compression_type)); + ASSERT_EQ(0, writer.WriteBytes(content.data(), content.size())); + ASSERT_EQ(0, writer.FinishEntry()); + } + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); +} + +TEST(InstallTest, verify_package_compatibility_no_entry) { + TemporaryFile temp_file; + // The archive must have something to be opened correctly. + BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); + + // Doesn't contain compatibility zip entry. + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + ASSERT_TRUE(verify_package_compatibility(zip)); + CloseArchive(zip); +} + +TEST(InstallTest, verify_package_compatibility_invalid_entry) { + TemporaryFile temp_file; + BuildZipArchive({ { "compatibility.zip", "" } }, temp_file.release(), kCompressStored); + + // Empty compatibility zip entry. + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + ASSERT_FALSE(verify_package_compatibility(zip)); + CloseArchive(zip); +} + +TEST(InstallTest, read_metadata_from_package_smoke) { + TemporaryFile temp_file; + const std::string content("abc=defg"); + BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file.release(), + kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + std::map metadata; + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + ASSERT_EQ("defg", metadata["abc"]); + CloseArchive(zip); + + TemporaryFile temp_file2; + BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file2.release(), + kCompressDeflated); + + ASSERT_EQ(0, OpenArchive(temp_file2.path, &zip)); + metadata.clear(); + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + ASSERT_EQ("defg", metadata["abc"]); + CloseArchive(zip); +} + +TEST(InstallTest, read_metadata_from_package_no_entry) { + TemporaryFile temp_file; + BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + std::map metadata; + ASSERT_FALSE(ReadMetadataFromPackage(zip, &metadata)); + CloseArchive(zip); +} + +TEST(InstallTest, read_wipe_ab_partition_list) { + std::vector partition_list = { + "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b", + "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b", + "/dev/block/bootdevice/by-name/userdata", "# Wipe the boot partitions last", + "/dev/block/bootdevice/by-name/boot_a", "/dev/block/bootdevice/by-name/boot_b", + }; + TemporaryFile temp_file; + BuildZipArchive({ { "recovery.wipe", android::base::Join(partition_list, '\n') } }, + temp_file.release(), kCompressDeflated); + std::string wipe_package; + ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &wipe_package)); + + auto package = Package::CreateMemoryPackage( + std::vector(wipe_package.begin(), wipe_package.end()), nullptr); + + auto read_partition_list = GetWipePartitionList(package.get()); + std::vector expected = { + "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b", + "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b", + "/dev/block/bootdevice/by-name/userdata", "/dev/block/bootdevice/by-name/boot_a", + "/dev/block/bootdevice/by-name/boot_b", + }; + ASSERT_EQ(expected, read_partition_list); +} + +TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { + TemporaryFile compatibility_zip_file; + std::string malformed_xml = "malformed"; + BuildZipArchive({ { "system_manifest.xml", malformed_xml } }, compatibility_zip_file.release(), + kCompressDeflated); + + TemporaryFile temp_file; + std::string compatibility_zip_content; + ASSERT_TRUE( + android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); + BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), + kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + std::vector compatibility_info; + compatibility_info.push_back(malformed_xml); + // Malformed compatibility zip is expected to be rejected by libvintf. But we defer that to + // libvintf. + std::string err; + bool result = + android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0; + ASSERT_EQ(result, verify_package_compatibility(zip)); + CloseArchive(zip); +} + +TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml) { + static constexpr const char* system_manifest_xml_path = "/system/manifest.xml"; + if (access(system_manifest_xml_path, R_OK) == -1) { + GTEST_LOG_(INFO) << "Test skipped on devices w/o /system/manifest.xml."; + return; + } + std::string system_manifest_xml_content; + ASSERT_TRUE( + android::base::ReadFileToString(system_manifest_xml_path, &system_manifest_xml_content)); + TemporaryFile compatibility_zip_file; + BuildZipArchive({ { "system_manifest.xml", system_manifest_xml_content } }, + compatibility_zip_file.release(), kCompressDeflated); + + TemporaryFile temp_file; + std::string compatibility_zip_content; + ASSERT_TRUE( + android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); + BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), + kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + std::vector compatibility_info; + compatibility_info.push_back(system_manifest_xml_content); + std::string err; + bool result = + android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0; + // Make sure the result is consistent with libvintf library. + ASSERT_EQ(result, verify_package_compatibility(zip)); + CloseArchive(zip); +} + +TEST(InstallTest, SetUpNonAbUpdateCommands) { + TemporaryFile temp_file; + static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; + BuildZipArchive({ { UPDATE_BINARY_NAME, "" } }, temp_file.release(), kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + int status_fd = 10; + std::string package = "/path/to/update.zip"; + TemporaryDir td; + std::string binary_path = std::string(td.path) + "/update_binary"; + Paths::Get().set_temporary_update_binary(binary_path); + std::vector cmd; + ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd)); + ASSERT_EQ(4U, cmd.size()); + ASSERT_EQ(binary_path, cmd[0]); + ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION + ASSERT_EQ(std::to_string(status_fd), cmd[2]); + ASSERT_EQ(package, cmd[3]); + struct stat sb; + ASSERT_EQ(0, stat(binary_path.c_str(), &sb)); + ASSERT_EQ(static_cast(0755), sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + + // With non-zero retry count. update_binary will be removed automatically. + cmd.clear(); + ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 2, status_fd, &cmd)); + ASSERT_EQ(5U, cmd.size()); + ASSERT_EQ(binary_path, cmd[0]); + ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION + ASSERT_EQ(std::to_string(status_fd), cmd[2]); + ASSERT_EQ(package, cmd[3]); + ASSERT_EQ("retry", cmd[4]); + sb = {}; + ASSERT_EQ(0, stat(binary_path.c_str(), &sb)); + ASSERT_EQ(static_cast(0755), sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + + CloseArchive(zip); +} + +TEST(InstallTest, SetUpNonAbUpdateCommands_MissingUpdateBinary) { + TemporaryFile temp_file; + // The archive must have something to be opened correctly. + BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); + + // Missing update binary. + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + int status_fd = 10; + std::string package = "/path/to/update.zip"; + TemporaryDir td; + Paths::Get().set_temporary_update_binary(std::string(td.path) + "/update_binary"); + std::vector cmd; + ASSERT_EQ(INSTALL_CORRUPT, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd)); + CloseArchive(zip); +} + +static void VerifyAbUpdateCommands(const std::string& serialno, bool success = true) { + TemporaryFile temp_file; + + const std::string properties = "some_properties"; + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); + ASSERT_NE("", timestamp); + + std::vector meta{ "ota-type=AB", "pre-device=" + device, + "post-timestamp=" + timestamp }; + if (!serialno.empty()) { + meta.push_back("serialno=" + serialno); + } + std::string metadata_string = android::base::Join(meta, "\n"); + + BuildZipArchive({ { "payload.bin", "" }, + { "payload_properties.txt", properties }, + { "META-INF/com/android/metadata", metadata_string } }, + temp_file.release(), kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + ZipString payload_name("payload.bin"); + ZipEntry payload_entry; + ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry)); + + std::map metadata; + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + if (success) { + ASSERT_EQ(0, CheckPackageMetadata(metadata, OtaType::AB)); + + int status_fd = 10; + std::string package = "/path/to/update.zip"; + std::vector cmd; + ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); + ASSERT_EQ(5U, cmd.size()); + ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]); + ASSERT_EQ("--payload=file://" + package, cmd[1]); + ASSERT_EQ("--offset=" + std::to_string(payload_entry.offset), cmd[2]); + ASSERT_EQ("--headers=" + properties, cmd[3]); + ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); + } else { + ASSERT_EQ(INSTALL_ERROR, CheckPackageMetadata(metadata, OtaType::AB)); + } + CloseArchive(zip); +} + +TEST(InstallTest, SetUpAbUpdateCommands) { + // Empty serialno will pass the verification. + VerifyAbUpdateCommands({}); +} + +TEST(InstallTest, SetUpAbUpdateCommands_MissingPayloadPropertiesTxt) { + TemporaryFile temp_file; + + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); + ASSERT_NE("", timestamp); + std::string metadata = android::base::Join( + std::vector{ + "ota-type=AB", "pre-device=" + device, "post-timestamp=" + timestamp, + }, + "\n"); + + BuildZipArchive( + { + { "payload.bin", "" }, + { "META-INF/com/android/metadata", metadata }, + }, + temp_file.release(), kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + int status_fd = 10; + std::string package = "/path/to/update.zip"; + std::vector cmd; + ASSERT_EQ(INSTALL_CORRUPT, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); + CloseArchive(zip); +} + +TEST(InstallTest, SetUpAbUpdateCommands_MultipleSerialnos) { + std::string serialno = android::base::GetProperty("ro.serialno", ""); + ASSERT_NE("", serialno); + + // Single matching serialno will pass the verification. + VerifyAbUpdateCommands(serialno); + + static constexpr char alphabet[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + auto generator = []() { return alphabet[rand() % (sizeof(alphabet) - 1)]; }; + + // Generate 900 random serial numbers. + std::string random_serialno; + for (size_t i = 0; i < 900; i++) { + generate_n(back_inserter(random_serialno), serialno.size(), generator); + random_serialno.append("|"); + } + // Random serialnos should fail the verification. + VerifyAbUpdateCommands(random_serialno, false); + + std::string long_serialno = random_serialno + serialno + "|"; + for (size_t i = 0; i < 99; i++) { + generate_n(back_inserter(long_serialno), serialno.size(), generator); + long_serialno.append("|"); + } + // String with the matching serialno should pass the verification. + VerifyAbUpdateCommands(long_serialno); +} + +static void test_check_package_metadata(const std::string& metadata_string, OtaType ota_type, + int exptected_result) { + TemporaryFile temp_file; + BuildZipArchive( + { + { "META-INF/com/android/metadata", metadata_string }, + }, + temp_file.release(), kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + + std::map metadata; + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + ASSERT_EQ(exptected_result, CheckPackageMetadata(metadata, ota_type)); + CloseArchive(zip); +} + +TEST(InstallTest, CheckPackageMetadata_ota_type) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + // ota-type must be present + std::string metadata = android::base::Join( + std::vector{ + "pre-device=" + device, + "post-timestamp=" + std::to_string(std::numeric_limits::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + // Checks if ota-type matches + metadata = android::base::Join( + std::vector{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=" + std::to_string(std::numeric_limits::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); + + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_device_type) { + // device type can not be empty + std::string metadata = android::base::Join( + std::vector{ + "ota-type=BRICK", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + + // device type mismatches + metadata = android::base::Join( + std::vector{ + "ota-type=BRICK", + "pre-device=dummy_device_type", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + // Serial number doesn't need to exist + std::string metadata = android::base::Join( + std::vector{ + "ota-type=BRICK", + "pre-device=" + device, + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, 0); + + // Serial number mismatches + metadata = android::base::Join( + std::vector{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=dummy_serial", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + + std::string serialno = android::base::GetProperty("ro.serialno", ""); + ASSERT_NE("", serialno); + metadata = android::base::Join( + std::vector{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=" + serialno, + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, 0); +} + +TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + std::string serialno = android::base::GetProperty("ro.serialno", ""); + ASSERT_NE("", serialno); + + std::vector serial_numbers; + // Creates a dummy serial number string. + for (char c = 'a'; c <= 'z'; c++) { + serial_numbers.emplace_back(serialno.size(), c); + } + + // No matched serialno found. + std::string metadata = android::base::Join( + std::vector{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=" + android::base::Join(serial_numbers, '|'), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + + serial_numbers.emplace_back(serialno); + std::shuffle(serial_numbers.begin(), serial_numbers.end(), std::default_random_engine()); + metadata = android::base::Join( + std::vector{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=" + android::base::Join(serial_numbers, '|'), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, 0); +} + +TEST(InstallTest, CheckPackageMetadata_ab_build_version) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + std::string build_version = android::base::GetProperty("ro.build.version.incremental", ""); + ASSERT_NE("", build_version); + + std::string metadata = android::base::Join( + std::vector{ + "ota-type=AB", + "pre-device=" + device, + "pre-build-incremental=" + build_version, + "post-timestamp=" + std::to_string(std::numeric_limits::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); + + metadata = android::base::Join( + std::vector{ + "ota-type=AB", + "pre-device=" + device, + "pre-build-incremental=dummy_build", + "post-timestamp=" + std::to_string(std::numeric_limits::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); + ASSERT_NE("", finger_print); + + std::string metadata = android::base::Join( + std::vector{ + "ota-type=AB", + "pre-device=" + device, + "pre-build=" + finger_print, + "post-timestamp=" + std::to_string(std::numeric_limits::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); + + metadata = android::base::Join( + std::vector{ + "ota-type=AB", + "pre-device=" + device, + "pre-build=dummy_build_fingerprint", + "post-timestamp=" + std::to_string(std::numeric_limits::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + // post timestamp is required for upgrade. + std::string metadata = android::base::Join( + std::vector{ + "ota-type=AB", + "pre-device=" + device, + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + // post timestamp should be larger than the timestamp on device. + metadata = android::base::Join( + std::vector{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=0", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + // fingerprint is required for downgrade + metadata = android::base::Join( + std::vector{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=0", + "ota-downgrade=yes", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); + ASSERT_NE("", finger_print); + + metadata = android::base::Join( + std::vector{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=0", + "pre-build=" + finger_print, + "ota-downgrade=yes", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); +} -- cgit v1.2.3