The non-A/B package installation is subject to TOC/TOU flaw if the attacker can switch the package in the middle of installation. And the most pratical case is to store the package on an external device, e.g. a sdcard, and swap the device in the middle. To prevent that, we can adopt the same protection as used in sideloading a package with FUSE. Specifically, when we install the package with FUSE, we read the entire package to cryptographically verify its signature. The hash for each transfer block is recorded in the memory (TOC), and the subsequent reads (TOU) will be rejected upon dectecting a mismatch. This CL forces the package installation with FUSE when the package stays on a removable media. Bug: 136498130 Test: Run bin/recovery --update_package with various paths; and packages are installed from FUSE as expected Change-Id: Ibc9b095036a2fa624e8edf6c347ed4f12aef072f
626 lines
22 KiB
C++
626 lines
22 KiB
C++
/*
|
|
* 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 <stdio.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
#include <random>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/properties.h>
|
|
#include <android-base/strings.h>
|
|
#include <gtest/gtest.h>
|
|
#include <vintf/VintfObjectRecovery.h>
|
|
#include <ziparchive/zip_archive.h>
|
|
#include <ziparchive/zip_writer.h>
|
|
|
|
#include "install/install.h"
|
|
#include "install/wipe_device.h"
|
|
#include "otautil/paths.h"
|
|
#include "private/setup_commands.h"
|
|
#include "recovery_utils/roots.h"
|
|
|
|
static void BuildZipArchive(const std::map<std::string, std::string>& 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<std::string, std::string> 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<std::string, std::string> metadata;
|
|
ASSERT_FALSE(ReadMetadataFromPackage(zip, &metadata));
|
|
CloseArchive(zip);
|
|
}
|
|
|
|
TEST(InstallTest, read_wipe_ab_partition_list) {
|
|
std::vector<std::string> 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<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr);
|
|
|
|
auto read_partition_list = GetWipePartitionList(package.get());
|
|
std::vector<std::string> 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<std::string> 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<std::string> 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<std::string> cmd;
|
|
ASSERT_TRUE(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<mode_t>(0755), sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
|
|
|
|
// With non-zero retry count. update_binary will be removed automatically.
|
|
cmd.clear();
|
|
ASSERT_TRUE(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<mode_t>(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<std::string> cmd;
|
|
ASSERT_FALSE(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<std::string> 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));
|
|
ZipEntry payload_entry;
|
|
ASSERT_EQ(0, FindEntry(zip, "payload.bin", &payload_entry));
|
|
|
|
std::map<std::string, std::string> metadata;
|
|
ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata));
|
|
if (success) {
|
|
ASSERT_TRUE(CheckPackageMetadata(metadata, OtaType::AB));
|
|
|
|
int status_fd = 10;
|
|
std::string package = "/path/to/update.zip";
|
|
std::vector<std::string> cmd;
|
|
ASSERT_TRUE(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_FALSE(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<std::string>{
|
|
"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<std::string> cmd;
|
|
ASSERT_FALSE(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 TestCheckPackageMetadata(const std::string& metadata_string, OtaType ota_type,
|
|
bool 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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, false);
|
|
|
|
// 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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, true);
|
|
|
|
TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
|
|
}
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
|
|
|
|
// device type mismatches
|
|
metadata = android::base::Join(
|
|
std::vector<std::string>{
|
|
"ota-type=BRICK",
|
|
"pre-device=dummy_device_type",
|
|
},
|
|
"\n");
|
|
TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
|
|
}
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::BRICK, true);
|
|
|
|
// Serial number mismatches
|
|
metadata = android::base::Join(
|
|
std::vector<std::string>{
|
|
"ota-type=BRICK",
|
|
"pre-device=" + device,
|
|
"serialno=dummy_serial",
|
|
},
|
|
"\n");
|
|
TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::BRICK, true);
|
|
}
|
|
|
|
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 (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<std::string>{
|
|
"ota-type=BRICK",
|
|
"pre-device=" + device,
|
|
"serialno=" + android::base::Join(serial_numbers, '|'),
|
|
},
|
|
"\n");
|
|
TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::BRICK, true);
|
|
}
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, true);
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, false);
|
|
}
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, true);
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, false);
|
|
}
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, false);
|
|
|
|
// 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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, false);
|
|
|
|
// 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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, false);
|
|
|
|
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");
|
|
TestCheckPackageMetadata(metadata, OtaType::AB, true);
|
|
}
|
|
|
|
TEST(InstallTest, SetupPackageMount_package_path) {
|
|
load_volume_table();
|
|
bool install_with_fuse;
|
|
|
|
// Setup should fail if the input path doesn't exist.
|
|
ASSERT_FALSE(SetupPackageMount("/does_not_exist", &install_with_fuse));
|
|
|
|
// Package should be installed with fuse if it's not in /cache.
|
|
TemporaryDir temp_dir;
|
|
TemporaryFile update_package(temp_dir.path);
|
|
ASSERT_TRUE(SetupPackageMount(update_package.path, &install_with_fuse));
|
|
ASSERT_TRUE(install_with_fuse);
|
|
|
|
// Setup should fail if the input path isn't canonicalized.
|
|
std::string uncanonical_package_path = android::base::Join(
|
|
std::vector<std::string>{
|
|
temp_dir.path,
|
|
"..",
|
|
android::base::Basename(temp_dir.path),
|
|
android::base::Basename(update_package.path),
|
|
},
|
|
'/');
|
|
|
|
ASSERT_EQ(0, access(uncanonical_package_path.c_str(), R_OK));
|
|
ASSERT_FALSE(SetupPackageMount(uncanonical_package_path, &install_with_fuse));
|
|
}
|