Refactor the code to check the metadata

The two functions check_wipe_package() and check_newer_ab_build() were
using the same flow; and checked the same device properties against the
metadata file in the package. These properties include: ota_type,
pre-device, and serial number.

Therefore, we can consolidate the checks to a single function; and
continue to check the fingerprint and timestamp only for AB updates.

This change also addresses the need to accept multiple serial number in
the wipe package.

Bug: 118401208
Test: unit tests pass
Change-Id: Ia6bc48fb6effcae059a2ff2cf71764b4136b4c00
This commit is contained in:
Tianjie Xu 2018-10-25 10:39:01 -07:00
parent d84d570d8c
commit 93b5bf261c
4 changed files with 415 additions and 152 deletions

View file

@ -32,9 +32,7 @@
#include <condition_variable> #include <condition_variable>
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <map>
#include <mutex> #include <mutex>
#include <string>
#include <thread> #include <thread>
#include <vector> #include <vector>
@ -47,7 +45,6 @@
#include <android-base/strings.h> #include <android-base/strings.h>
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include <vintf/VintfObjectRecovery.h> #include <vintf/VintfObjectRecovery.h>
#include <ziparchive/zip_archive.h>
#include "common.h" #include "common.h"
#include "otautil/error_code.h" #include "otautil/error_code.h"
@ -67,18 +64,7 @@ static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
static std::condition_variable finish_log_temperature; static std::condition_variable finish_log_temperature;
// This function parses and returns the build.version.incremental bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata) {
static std::string parse_build_number(const std::string& str) {
size_t pos = str.find('=');
if (pos != std::string::npos) {
return android::base::Trim(str.substr(pos+1));
}
LOG(ERROR) << "Failed to parse build number in " << str;
return "";
}
bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) {
CHECK(metadata != nullptr); CHECK(metadata != nullptr);
static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
@ -90,101 +76,79 @@ bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) {
} }
uint32_t length = entry.uncompressed_length; uint32_t length = entry.uncompressed_length;
metadata->resize(length, '\0'); std::string metadata_string(length, '\0');
int32_t err = ExtractToMemory(zip, &entry, reinterpret_cast<uint8_t*>(&(*metadata)[0]), length); int32_t err =
ExtractToMemory(zip, &entry, reinterpret_cast<uint8_t*>(&metadata_string[0]), length);
if (err != 0) { if (err != 0) {
LOG(ERROR) << "Failed to extract " << METADATA_PATH << ": " << ErrorCodeString(err); LOG(ERROR) << "Failed to extract " << METADATA_PATH << ": " << ErrorCodeString(err);
return false; return false;
} }
for (const std::string& line : android::base::Split(metadata_string, "\n")) {
size_t eq = line.find('=');
if (eq != std::string::npos) {
metadata->emplace(android::base::Trim(line.substr(0, eq)),
android::base::Trim(line.substr(eq + 1)));
}
}
return true; return true;
} }
// Read the build.version.incremental of src/tgt from the metadata and log it to last_install. // Gets the value for the given key in |metadata|. Returns an emtpy string if the key isn't
static void read_source_target_build(ZipArchiveHandle zip, std::vector<std::string>* log_buffer) { // present.
std::string metadata; static std::string get_value(const std::map<std::string, std::string>& metadata,
if (!read_metadata_from_package(zip, &metadata)) { const std::string& key) {
return; const auto& it = metadata.find(key);
} return (it == metadata.end()) ? "" : it->second;
// Examples of the pre-build and post-build strings in metadata: }
// pre-build-incremental=2943039
// post-build-incremental=2951741 static std::string OtaTypeToString(OtaType type) {
std::vector<std::string> lines = android::base::Split(metadata, "\n"); switch (type) {
for (const std::string& line : lines) { case OtaType::AB:
std::string str = android::base::Trim(line); return "AB";
if (android::base::StartsWith(str, "pre-build-incremental")) { case OtaType::BLOCK:
std::string source_build = parse_build_number(str); return "BLOCK";
if (!source_build.empty()) { case OtaType::BRICK:
log_buffer->push_back("source_build: " + source_build); return "BRICK";
}
} else if (android::base::StartsWith(str, "post-build-incremental")) {
std::string target_build = parse_build_number(str);
if (!target_build.empty()) {
log_buffer->push_back("target_build: " + target_build);
}
}
} }
} }
// Parses the metadata of the OTA package in |zip| and checks whether we are allowed to accept this // Read the build.version.incremental of src/tgt from the metadata and log it to last_install.
// A/B package. Downgrading is not allowed unless explicitly enabled in the package and only for static void ReadSourceTargetBuild(const std::map<std::string, std::string>& metadata,
std::vector<std::string>* log_buffer) {
// Examples of the pre-build and post-build strings in metadata:
// pre-build-incremental=2943039
// post-build-incremental=2951741
auto source_build = get_value(metadata, "pre-build-incremental");
if (!source_build.empty()) {
log_buffer->push_back("source_build: " + source_build);
}
auto target_build = get_value(metadata, "post-build-incremental");
if (!target_build.empty()) {
log_buffer->push_back("target_build: " + target_build);
}
}
// Checks the build version, fingerprint and timestamp in the metadata of the A/B package.
// Downgrading is not allowed unless explicitly enabled in the package and only for
// incremental packages. // incremental packages.
static int check_newer_ab_build(ZipArchiveHandle zip) { static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) {
std::string metadata_str;
if (!read_metadata_from_package(zip, &metadata_str)) {
return INSTALL_CORRUPT;
}
std::map<std::string, std::string> metadata;
for (const std::string& line : android::base::Split(metadata_str, "\n")) {
size_t eq = line.find('=');
if (eq != std::string::npos) {
metadata[line.substr(0, eq)] = line.substr(eq + 1);
}
}
std::string value = android::base::GetProperty("ro.product.device", "");
const std::string& pkg_device = metadata["pre-device"];
if (pkg_device != value || pkg_device.empty()) {
LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << value;
return INSTALL_ERROR;
}
// We allow the package to not have any serialno; and we also allow it to carry multiple serial
// numbers split by "|"; e.g. serialno=serialno1|serialno2|serialno3 ... We will fail the
// verification if the device's serialno doesn't match any of these carried numbers.
value = android::base::GetProperty("ro.serialno", "");
const std::string& pkg_serial_no = metadata["serialno"];
if (!pkg_serial_no.empty()) {
bool match = false;
for (const std::string& number : android::base::Split(pkg_serial_no, "|")) {
if (value == android::base::Trim(number)) {
match = true;
break;
}
}
if (!match) {
LOG(ERROR) << "Package is for serial " << pkg_serial_no;
return INSTALL_ERROR;
}
}
if (metadata["ota-type"] != "AB") {
LOG(ERROR) << "Package is not A/B";
return INSTALL_ERROR;
}
// Incremental updates should match the current build. // Incremental updates should match the current build.
value = android::base::GetProperty("ro.build.version.incremental", ""); auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", "");
const std::string& pkg_pre_build = metadata["pre-build-incremental"]; auto pkg_pre_build = get_value(metadata, "pre-build-incremental");
if (!pkg_pre_build.empty() && pkg_pre_build != value) { if (!pkg_pre_build.empty() && pkg_pre_build != device_pre_build) {
LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected " << value; LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected "
<< device_pre_build;
return INSTALL_ERROR; return INSTALL_ERROR;
} }
value = android::base::GetProperty("ro.build.fingerprint", ""); auto device_fingerprint = android::base::GetProperty("ro.build.fingerprint", "");
const std::string& pkg_pre_build_fingerprint = metadata["pre-build"]; auto pkg_pre_build_fingerprint = get_value(metadata, "pre-build");
if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != value) { if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != device_fingerprint) {
LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected " LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected "
<< value; << device_fingerprint;
return INSTALL_ERROR; return INSTALL_ERROR;
} }
@ -194,10 +158,11 @@ static int check_newer_ab_build(ZipArchiveHandle zip) {
int64_t pkg_post_timestamp = 0; int64_t pkg_post_timestamp = 0;
// We allow to full update to the same version we are running, in case there // We allow to full update to the same version we are running, in case there
// is a problem with the current copy of that version. // is a problem with the current copy of that version.
if (metadata["post-timestamp"].empty() || auto pkg_post_timestamp_string = get_value(metadata, "post-timestamp");
!android::base::ParseInt(metadata["post-timestamp"].c_str(), &pkg_post_timestamp) || if (pkg_post_timestamp_string.empty() ||
!android::base::ParseInt(pkg_post_timestamp_string, &pkg_post_timestamp) ||
pkg_post_timestamp < build_timestamp) { pkg_post_timestamp < build_timestamp) {
if (metadata["ota-downgrade"] != "yes") { if (get_value(metadata, "ota-downgrade") != "yes") {
LOG(ERROR) << "Update package is older than the current build, expected a build " LOG(ERROR) << "Update package is older than the current build, expected a build "
"newer than timestamp " "newer than timestamp "
<< build_timestamp << " but package has timestamp " << pkg_post_timestamp << build_timestamp << " but package has timestamp " << pkg_post_timestamp
@ -213,13 +178,55 @@ static int check_newer_ab_build(ZipArchiveHandle zip) {
return 0; return 0;
} }
int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) {
auto package_ota_type = get_value(metadata, "ota-type");
auto expected_ota_type = OtaTypeToString(ota_type);
if (ota_type != OtaType::AB && ota_type != OtaType::BRICK) {
LOG(INFO) << "Skip package metadata check for ota type " << expected_ota_type;
return 0;
}
if (package_ota_type != expected_ota_type) {
LOG(ERROR) << "Unexpected ota package type, expects " << expected_ota_type << ", actual "
<< package_ota_type;
return INSTALL_ERROR;
}
auto device = android::base::GetProperty("ro.product.device", "");
auto pkg_device = get_value(metadata, "pre-device");
if (pkg_device != device || pkg_device.empty()) {
LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << device;
return INSTALL_ERROR;
}
// We allow the package to not have any serialno; and we also allow it to carry multiple serial
// numbers split by "|"; e.g. serialno=serialno1|serialno2|serialno3 ... We will fail the
// verification if the device's serialno doesn't match any of these carried numbers.
auto pkg_serial_no = get_value(metadata, "serialno");
if (!pkg_serial_no.empty()) {
auto device_serial_no = android::base::GetProperty("ro.serialno", "");
bool serial_number_match = false;
for (const auto& number : android::base::Split(pkg_serial_no, "|")) {
if (device_serial_no == android::base::Trim(number)) {
serial_number_match = true;
}
}
if (!serial_number_match) {
LOG(ERROR) << "Package is for serial " << pkg_serial_no;
return INSTALL_ERROR;
}
}
if (ota_type == OtaType::AB) {
return CheckAbSpecificMetadata(metadata);
}
return 0;
}
int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd, int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
std::vector<std::string>* cmd) { std::vector<std::string>* cmd) {
CHECK(cmd != nullptr); CHECK(cmd != nullptr);
int ret = check_newer_ab_build(zip);
if (ret != 0) {
return ret;
}
// For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset // For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset
// in the zip file. // in the zip file.
@ -311,20 +318,33 @@ static void log_max_temperature(int* max_temperature, const std::atomic<bool>& l
static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache, static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
std::vector<std::string>* log_buffer, int retry_count, std::vector<std::string>* log_buffer, int retry_count,
int* max_temperature) { int* max_temperature) {
read_source_target_build(zip, log_buffer); std::map<std::string, std::string> metadata;
if (!ReadMetadataFromPackage(zip, &metadata)) {
LOG(ERROR) << "Failed to parse metadata in the zip file";
return INSTALL_CORRUPT;
}
bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
// Verifies against the metadata in the package first.
if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : 0;
check_status != 0) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
return check_status;
}
ReadSourceTargetBuild(metadata, log_buffer);
int pipefd[2]; int pipefd[2];
pipe(pipefd); pipe(pipefd);
bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
std::vector<std::string> args; std::vector<std::string> args;
int ret = is_ab ? SetUpAbUpdateCommands(package, zip, pipefd[1], &args) if (int update_status =
: SetUpNonAbUpdateCommands(package, zip, retry_count, pipefd[1], &args); is_ab ? SetUpAbUpdateCommands(package, zip, pipefd[1], &args)
if (ret) { : SetUpNonAbUpdateCommands(package, zip, retry_count, pipefd[1], &args);
update_status != 0) {
close(pipefd[0]); close(pipefd[0]);
close(pipefd[1]); close(pipefd[1]);
log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
return ret; return update_status;
} }
// When executing the update binary contained in the package, the // When executing the update binary contained in the package, the

View file

@ -19,6 +19,7 @@
#include <stddef.h> #include <stddef.h>
#include <map>
#include <string> #include <string>
#include <ziparchive/zip_archive.h> #include <ziparchive/zip_archive.h>
@ -33,6 +34,12 @@ enum InstallResult {
INSTALL_KEY_INTERRUPTED INSTALL_KEY_INTERRUPTED
}; };
enum class OtaType {
AB,
BLOCK,
BRICK,
};
// Installs the given update package. If INSTALL_SUCCESS is returned and *wipe_cache is true on // Installs the given update package. If INSTALL_SUCCESS is returned and *wipe_cache is true on
// exit, caller should wipe the cache partition. // exit, caller should wipe the cache partition.
int install_package(const std::string& package, bool* wipe_cache, bool needs_mount, int install_package(const std::string& package, bool* wipe_cache, bool needs_mount,
@ -42,12 +49,17 @@ int install_package(const std::string& package, bool* wipe_cache, bool needs_mou
// otherwise return false. // otherwise return false.
bool verify_package(const unsigned char* package_data, size_t package_size); bool verify_package(const unsigned char* package_data, size_t package_size);
// Read meta data file of the package, write its content in the string pointed by meta_data. // Reads meta data file of the package; parses each line in the format "key=value"; and writes the
// Return true if succeed, otherwise return false. // result to |metadata|. Return true if succeed, otherwise return false.
bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata); bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata);
// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the // Verifies the compatibility info in a Treble-compatible package. Returns true directly if the
// entry doesn't exist. // entry doesn't exist.
bool verify_package_compatibility(ZipArchiveHandle package_zip); bool verify_package_compatibility(ZipArchiveHandle package_zip);
// Checks if the the metadata in the OTA package has expected values. Returns 0 on success.
// Mandatory checks: ota-type, pre-device and serial number(if presents)
// AB OTA specific checks: pre-build version, fingerprint, timestamp.
int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type);
#endif // RECOVERY_INSTALL_H_ #endif // RECOVERY_INSTALL_H_

View file

@ -520,34 +520,17 @@ static bool check_wipe_package(size_t wipe_package_size) {
LOG(ERROR) << "Can't open wipe package : " << ErrorCodeString(err); LOG(ERROR) << "Can't open wipe package : " << ErrorCodeString(err);
return false; return false;
} }
std::string metadata;
if (!read_metadata_from_package(zip, &metadata)) { std::map<std::string, std::string> metadata;
CloseArchive(zip); if (!ReadMetadataFromPackage(zip, &metadata)) {
return false; LOG(ERROR) << "Failed to parse metadata in the zip file";
return false;
} }
int result = CheckPackageMetadata(metadata, OtaType::BRICK);
CloseArchive(zip); CloseArchive(zip);
// Check metadata return result == 0;
std::vector<std::string> lines = android::base::Split(metadata, "\n");
bool ota_type_matched = false;
bool device_type_matched = false;
bool has_serial_number = false;
bool serial_number_matched = false;
for (const auto& line : lines) {
if (line == "ota-type=BRICK") {
ota_type_matched = true;
} else if (android::base::StartsWith(line, "pre-device=")) {
std::string device_type = line.substr(strlen("pre-device="));
std::string real_device_type = android::base::GetProperty("ro.build.product", "");
device_type_matched = (device_type == real_device_type);
} else if (android::base::StartsWith(line, "serialno=")) {
std::string serial_no = line.substr(strlen("serialno="));
std::string real_serial_no = android::base::GetProperty("ro.serialno", "");
has_serial_number = true;
serial_number_matched = (serial_no == real_serial_no);
}
}
return ota_type_matched && device_type_matched && (!has_serial_number || serial_number_matched);
} }
// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE. // Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE.

View file

@ -20,6 +20,7 @@
#include <unistd.h> #include <unistd.h>
#include <algorithm> #include <algorithm>
#include <random>
#include <string> #include <string>
#include <vector> #include <vector>
@ -74,15 +75,15 @@ TEST(InstallTest, verify_package_compatibility_invalid_entry) {
TEST(InstallTest, read_metadata_from_package_smoke) { TEST(InstallTest, read_metadata_from_package_smoke) {
TemporaryFile temp_file; TemporaryFile temp_file;
const std::string content("abcdefg"); const std::string content("abc=defg");
BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file.release(), BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file.release(),
kCompressStored); kCompressStored);
ZipArchiveHandle zip; ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
std::string metadata; std::map<std::string, std::string> metadata;
ASSERT_TRUE(read_metadata_from_package(zip, &metadata)); ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata));
ASSERT_EQ(content, metadata); ASSERT_EQ("defg", metadata["abc"]);
CloseArchive(zip); CloseArchive(zip);
TemporaryFile temp_file2; TemporaryFile temp_file2;
@ -91,8 +92,8 @@ TEST(InstallTest, read_metadata_from_package_smoke) {
ASSERT_EQ(0, OpenArchive(temp_file2.path, &zip)); ASSERT_EQ(0, OpenArchive(temp_file2.path, &zip));
metadata.clear(); metadata.clear();
ASSERT_TRUE(read_metadata_from_package(zip, &metadata)); ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata));
ASSERT_EQ(content, metadata); ASSERT_EQ("defg", metadata["abc"]);
CloseArchive(zip); CloseArchive(zip);
} }
@ -102,8 +103,8 @@ TEST(InstallTest, read_metadata_from_package_no_entry) {
ZipArchiveHandle zip; ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
std::string metadata; std::map<std::string, std::string> metadata;
ASSERT_FALSE(read_metadata_from_package(zip, &metadata)); ASSERT_FALSE(ReadMetadataFromPackage(zip, &metadata));
CloseArchive(zip); CloseArchive(zip);
} }
@ -235,11 +236,11 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t
if (!serialno.empty()) { if (!serialno.empty()) {
meta.push_back("serialno=" + serialno); meta.push_back("serialno=" + serialno);
} }
std::string metadata = android::base::Join(meta, "\n"); std::string metadata_string = android::base::Join(meta, "\n");
BuildZipArchive({ { "payload.bin", "" }, BuildZipArchive({ { "payload.bin", "" },
{ "payload_properties.txt", properties }, { "payload_properties.txt", properties },
{ "META-INF/com/android/metadata", metadata } }, { "META-INF/com/android/metadata", metadata_string } },
temp_file.release(), kCompressStored); temp_file.release(), kCompressStored);
ZipArchiveHandle zip; ZipArchiveHandle zip;
@ -247,10 +248,15 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t
ZipString payload_name("payload.bin"); ZipString payload_name("payload.bin");
ZipEntry payload_entry; ZipEntry payload_entry;
ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry)); ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry));
int status_fd = 10;
std::string package = "/path/to/update.zip"; std::map<std::string, std::string> metadata;
std::vector<std::string> cmd; ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata));
if (success) { if (success) {
ASSERT_EQ(0, CheckPackageMetadata(metadata, OtaType::AB));
int status_fd = 10;
std::string package = "/path/to/update.zip";
std::vector<std::string> cmd;
ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
ASSERT_EQ(5U, cmd.size()); ASSERT_EQ(5U, cmd.size());
ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]); ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]);
@ -259,7 +265,7 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t
ASSERT_EQ("--headers=" + properties, cmd[3]); ASSERT_EQ("--headers=" + properties, cmd[3]);
ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]);
} else { } else {
ASSERT_EQ(INSTALL_ERROR, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); ASSERT_EQ(INSTALL_ERROR, CheckPackageMetadata(metadata, OtaType::AB));
} }
CloseArchive(zip); CloseArchive(zip);
} }
@ -282,8 +288,12 @@ TEST(InstallTest, SetUpAbUpdateCommands_MissingPayloadPropertiesTxt) {
}, },
"\n"); "\n");
BuildZipArchive({ { "payload.bin", "" }, { "META-INF/com/android/metadata", metadata } }, BuildZipArchive(
temp_file.release(), kCompressStored); {
{ "payload.bin", "" },
{ "META-INF/com/android/metadata", metadata },
},
temp_file.release(), kCompressStored);
ZipArchiveHandle zip; ZipArchiveHandle zip;
ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
@ -322,3 +332,241 @@ TEST(InstallTest, SetUpAbUpdateCommands_MultipleSerialnos) {
// String with the matching serialno should pass the verification. // String with the matching serialno should pass the verification.
VerifyAbUpdateCommands(long_serialno); 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<std::string, std::string> 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<std::string>{
"pre-device=" + device,
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
},
"\n");
test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
// Checks if ota-type matches
metadata = android::base::Join(
std::vector<std::string>{
"ota-type=AB",
"pre-device=" + device,
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::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<std::string>{
"ota-type=BRICK",
},
"\n");
test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
// device type mismatches
metadata = android::base::Join(
std::vector<std::string>{
"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<std::string>{
"ota-type=BRICK",
"pre-device=" + device,
},
"\n");
test_check_package_metadata(metadata, OtaType::BRICK, 0);
// Serial number mismatches
metadata = android::base::Join(
std::vector<std::string>{
"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<std::string>{
"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<std::string> serial_numbers;
// Creates a dummy serial number string.
for (size_t c = 'a'; c <= 'z'; c++) {
serial_numbers.emplace_back(c, serialno.size());
}
// No matched serialno found.
std::string metadata = android::base::Join(
std::vector<std::string>{
"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<std::string>{
"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<std::string>{
"ota-type=AB",
"pre-device=" + device,
"pre-build-incremental=" + build_version,
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
},
"\n");
test_check_package_metadata(metadata, OtaType::AB, 0);
metadata = android::base::Join(
std::vector<std::string>{
"ota-type=AB",
"pre-device=" + device,
"pre-build-incremental=dummy_build",
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::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<std::string>{
"ota-type=AB",
"pre-device=" + device,
"pre-build=" + finger_print,
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
},
"\n");
test_check_package_metadata(metadata, OtaType::AB, 0);
metadata = android::base::Join(
std::vector<std::string>{
"ota-type=AB",
"pre-device=" + device,
"pre-build=dummy_build_fingerprint",
"post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::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<std::string>{
"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<std::string>{
"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<std::string>{
"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<std::string>{
"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);
}