summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk13
-rw-r--r--applypatch/applypatch_modes.cpp18
-rw-r--r--applypatch/imgpatch.cpp5
-rw-r--r--fuse_sideload.cpp6
-rw-r--r--install.cpp386
-rw-r--r--minadbd/fuse_adb_provider_test.cpp20
-rw-r--r--minadbd/minadbd_services.cpp16
-rw-r--r--private/install.h27
-rw-r--r--recovery.cpp17
-rw-r--r--tests/Android.mk13
-rw-r--r--tests/component/applypatch_test.cpp65
-rw-r--r--tests/component/install_test.cpp221
-rw-r--r--tests/component/update_verifier_test.cpp83
-rw-r--r--update_verifier/Android.mk42
-rw-r--r--update_verifier/include/update_verifier/update_verifier.h24
-rw-r--r--update_verifier/update_verifier.cpp17
-rw-r--r--update_verifier/update_verifier_main.cpp23
17 files changed, 672 insertions, 324 deletions
diff --git a/Android.mk b/Android.mk
index 9e374de8f..5ce5cd7dc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -22,11 +22,10 @@ RECOVERY_FSTAB_VERSION := 2
# ===============================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := fuse_sideload.cpp
-LOCAL_CLANG := true
-LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter -Werror
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE
LOCAL_MODULE := libfusesideload
-LOCAL_STATIC_LIBRARIES := libcutils libc libcrypto
+LOCAL_STATIC_LIBRARIES := libcrypto
include $(BUILD_STATIC_LIBRARY)
# libmounts (static library)
@@ -45,7 +44,7 @@ include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
install.cpp
-LOCAL_CFLAGS := -Wno-unused-parameter -Werror
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
ifeq ($(AB_OTA_UPDATER),true)
@@ -55,6 +54,7 @@ endif
LOCAL_MODULE := librecovery
LOCAL_STATIC_LIBRARIES := \
libminui \
+ libvintf_recovery \
libcrypto_utils \
libcrypto \
libbase
@@ -91,11 +91,9 @@ endif
LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
LOCAL_CFLAGS += -Wno-unused-parameter -Werror
-LOCAL_CLANG := true
LOCAL_C_INCLUDES += \
system/vold \
- system/core/adb \
LOCAL_STATIC_LIBRARIES := \
librecovery \
@@ -114,6 +112,9 @@ LOCAL_STATIC_LIBRARIES := \
libfs_mgr \
libcrypto_utils \
libcrypto \
+ libvintf_recovery \
+ libvintf \
+ libtinyxml2 \
libbase \
libcutils \
libutils \
diff --git a/applypatch/applypatch_modes.cpp b/applypatch/applypatch_modes.cpp
index 7b191a801..aa32d57ef 100644
--- a/applypatch/applypatch_modes.cpp
+++ b/applypatch/applypatch_modes.cpp
@@ -44,19 +44,6 @@ static int CheckMode(int argc, const char** argv) {
return applypatch_check(argv[2], sha1);
}
-static int SpaceMode(int argc, const char** argv) {
- if (argc != 3) {
- return 2;
- }
-
- size_t bytes;
- if (!android::base::ParseUint(argv[2], &bytes) || bytes == 0) {
- printf("can't parse \"%s\" as byte count\n\n", argv[2]);
- return 1;
- }
- return CacheSizeCheck(bytes);
-}
-
// Parse arguments (which should be of the form "<sha1>:<filename>" into the
// new parallel arrays *sha1s and *files. Returns true on success.
static bool ParsePatchArgs(int argc, const char** argv, std::vector<std::string>* sha1s,
@@ -175,13 +162,12 @@ int applypatch_modes(int argc, const char** argv) {
"usage: %s [-b <bonus-file>] <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
"[<src-sha1>:<patch> ...]\n"
" or %s -c <file> [<sha1> ...]\n"
- " or %s -s <bytes>\n"
" or %s -l\n"
"\n"
"Filenames may be of the form\n"
" EMMC:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
"to specify reading from or writing to an EMMC partition.\n\n",
- argv[0], argv[0], argv[0], argv[0]);
+ argv[0], argv[0], argv[0]);
return 2;
}
@@ -191,8 +177,6 @@ int applypatch_modes(int argc, const char** argv) {
result = ShowLicenses();
} else if (strncmp(argv[1], "-c", 3) == 0) {
result = CheckMode(argc, argv);
- } else if (strncmp(argv[1], "-s", 3) == 0) {
- result = SpaceMode(argc, argv);
} else {
result = PatchMode(argc, argv);
}
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
index 7d8b7361c..3d905f7b4 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -100,7 +100,10 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value*
printf("source data too short\n");
return -1;
}
- ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx);
+ if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx) != 0) {
+ printf("Failed to apply bsdiff patch.\n");
+ return -1;
+ }
} else if (type == CHUNK_RAW) {
const char* raw_header = &patch->data[pos];
pos += 4;
diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp
index 1725e8823..279a976ad 100644
--- a/fuse_sideload.cpp
+++ b/fuse_sideload.cpp
@@ -226,11 +226,13 @@ static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse
return NO_STATUS;
}
-static int handle_flush(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
+static int handle_flush(void* /* data */, struct fuse_data* /* fd */,
+ const struct fuse_in_header* /* hdr */) {
return 0;
}
-static int handle_release(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) {
+static int handle_release(void* /* data */, struct fuse_data* /* fd */,
+ const struct fuse_in_header* /* hdr */) {
return 0;
}
diff --git a/install.cpp b/install.cpp
index 6dcd3565e..e945d13ab 100644
--- a/install.cpp
+++ b/install.cpp
@@ -44,11 +44,11 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <vintf/VintfObjectRecovery.h>
#include <ziparchive/zip_archive.h>
#include "common.h"
#include "error_code.h"
-#include "minui/minui.h"
#include "otautil/SysUtil.h"
#include "otautil/ThermalUtil.h"
#include "roots.h"
@@ -57,18 +57,9 @@
using namespace std::chrono_literals;
-#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary"
-static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
-static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
-#define PUBLIC_KEYS_FILE "/res/keys"
-static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
-static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
-
// Default allocation of progress bar segments to operations
static constexpr int VERIFICATION_PROGRESS_TIME = 60;
static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
-static constexpr float DEFAULT_FILES_PROGRESS_FRACTION = 0.4;
-static constexpr float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1;
static std::condition_variable finish_log_temperature;
@@ -87,62 +78,58 @@ static int parse_build_number(const std::string& str) {
return -1;
}
-bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data) {
- ZipString metadata_path(METADATA_PATH);
- ZipEntry meta_entry;
- if (meta_data == nullptr) {
- LOG(ERROR) << "string* meta_data can't be nullptr";
- return false;
- }
- if (FindEntry(zip, metadata_path, &meta_entry) != 0) {
- LOG(ERROR) << "Failed to find " << METADATA_PATH << " in update package";
- return false;
- }
+bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) {
+ CHECK(metadata != nullptr);
- meta_data->resize(meta_entry.uncompressed_length, '\0');
- if (ExtractToMemory(zip, &meta_entry, reinterpret_cast<uint8_t*>(&(*meta_data)[0]),
- meta_entry.uncompressed_length) != 0) {
- LOG(ERROR) << "Failed to read metadata in update package";
- return false;
- }
- return true;
+ static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
+ ZipString path(METADATA_PATH);
+ ZipEntry entry;
+ if (FindEntry(zip, path, &entry) != 0) {
+ LOG(ERROR) << "Failed to find " << METADATA_PATH;
+ return false;
+ }
+
+ uint32_t length = entry.uncompressed_length;
+ metadata->resize(length, '\0');
+ int32_t err = ExtractToMemory(zip, &entry, reinterpret_cast<uint8_t*>(&(*metadata)[0]), length);
+ if (err != 0) {
+ LOG(ERROR) << "Failed to extract " << METADATA_PATH << ": " << ErrorCodeString(err);
+ return false;
+ }
+ return true;
}
// Read the build.version.incremental of src/tgt from the metadata and log it to last_install.
static void read_source_target_build(ZipArchiveHandle zip, std::vector<std::string>& log_buffer) {
- std::string meta_data;
- if (!read_metadata_from_package(zip, &meta_data)) {
- return;
- }
- // Examples of the pre-build and post-build strings in metadata:
- // pre-build-incremental=2943039
- // post-build-incremental=2951741
- std::vector<std::string> lines = android::base::Split(meta_data, "\n");
- for (const std::string& line : lines) {
- std::string str = android::base::Trim(line);
- if (android::base::StartsWith(str, "pre-build-incremental")){
- int source_build = parse_build_number(str);
- if (source_build != -1) {
- log_buffer.push_back(android::base::StringPrintf("source_build: %d",
- source_build));
- }
- } else if (android::base::StartsWith(str, "post-build-incremental")) {
- int target_build = parse_build_number(str);
- if (target_build != -1) {
- log_buffer.push_back(android::base::StringPrintf("target_build: %d",
- target_build));
- }
- }
+ std::string metadata;
+ if (!read_metadata_from_package(zip, &metadata)) {
+ return;
+ }
+ // Examples of the pre-build and post-build strings in metadata:
+ // pre-build-incremental=2943039
+ // post-build-incremental=2951741
+ std::vector<std::string> lines = android::base::Split(metadata, "\n");
+ for (const std::string& line : lines) {
+ std::string str = android::base::Trim(line);
+ if (android::base::StartsWith(str, "pre-build-incremental")) {
+ int source_build = parse_build_number(str);
+ if (source_build != -1) {
+ log_buffer.push_back(android::base::StringPrintf("source_build: %d", source_build));
+ }
+ } else if (android::base::StartsWith(str, "post-build-incremental")) {
+ int target_build = parse_build_number(str);
+ if (target_build != -1) {
+ log_buffer.push_back(android::base::StringPrintf("target_build: %d", target_build));
+ }
}
+ }
}
-// Extract the update binary from the open zip archive |zip| located at |path|
-// and store into |cmd| the command line that should be called. The |status_fd|
-// is the file descriptor the child process should use to report back the
-// progress of the update.
-static int
-update_binary_command(const char* path, ZipArchiveHandle zip, int retry_count,
- int status_fd, std::vector<std::string>* cmd);
+// Extract the update binary from the open zip archive |zip| located at |path| and store into |cmd|
+// the command line that should be called. The |status_fd| is the file descriptor the child process
+// should use to report back the progress of the update.
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
+ int status_fd, std::vector<std::string>* cmd);
#ifdef AB_OTA_UPDATER
@@ -224,87 +211,90 @@ static int check_newer_ab_build(ZipArchiveHandle zip) {
return 0;
}
-static int
-update_binary_command(const char* path, ZipArchiveHandle zip, int retry_count,
- int status_fd, std::vector<std::string>* cmd)
-{
- int ret = check_newer_ab_build(zip);
- if (ret) {
- return ret;
- }
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int /* retry_count */,
+ int status_fd, std::vector<std::string>* cmd) {
+ 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 in the zip file.
- ZipString property_name(AB_OTA_PAYLOAD_PROPERTIES);
- ZipEntry properties_entry;
- if (FindEntry(zip, property_name, &properties_entry) != 0) {
- LOG(ERROR) << "Can't find " << AB_OTA_PAYLOAD_PROPERTIES;
- return INSTALL_CORRUPT;
- }
- std::vector<uint8_t> payload_properties(
- properties_entry.uncompressed_length);
- if (ExtractToMemory(zip, &properties_entry, payload_properties.data(),
- properties_entry.uncompressed_length) != 0) {
- LOG(ERROR) << "Can't extract " << AB_OTA_PAYLOAD_PROPERTIES;
- return INSTALL_CORRUPT;
- }
+ // For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset
+ // in the zip file.
+ static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
+ ZipString property_name(AB_OTA_PAYLOAD_PROPERTIES);
+ ZipEntry properties_entry;
+ if (FindEntry(zip, property_name, &properties_entry) != 0) {
+ LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES;
+ return INSTALL_CORRUPT;
+ }
+ uint32_t properties_entry_length = properties_entry.uncompressed_length;
+ std::vector<uint8_t> payload_properties(properties_entry_length);
+ int32_t err =
+ ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length);
+ if (err != 0) {
+ LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES << ": " << ErrorCodeString(err);
+ return INSTALL_CORRUPT;
+ }
- ZipString payload_name(AB_OTA_PAYLOAD);
- ZipEntry payload_entry;
- if (FindEntry(zip, payload_name, &payload_entry) != 0) {
- LOG(ERROR) << "Can't find " << AB_OTA_PAYLOAD;
- return INSTALL_CORRUPT;
- }
- long payload_offset = payload_entry.offset;
- *cmd = {
- "/sbin/update_engine_sideload",
- android::base::StringPrintf("--payload=file://%s", path),
- android::base::StringPrintf("--offset=%ld", payload_offset),
- "--headers=" + std::string(payload_properties.begin(),
- payload_properties.end()),
- android::base::StringPrintf("--status_fd=%d", status_fd),
- };
- return 0;
+ static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
+ ZipString payload_name(AB_OTA_PAYLOAD);
+ ZipEntry payload_entry;
+ if (FindEntry(zip, payload_name, &payload_entry) != 0) {
+ LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD;
+ return INSTALL_CORRUPT;
+ }
+ long payload_offset = payload_entry.offset;
+ *cmd = {
+ "/sbin/update_engine_sideload",
+ "--payload=file://" + path,
+ android::base::StringPrintf("--offset=%ld", payload_offset),
+ "--headers=" + std::string(payload_properties.begin(), payload_properties.end()),
+ android::base::StringPrintf("--status_fd=%d", status_fd),
+ };
+ return 0;
}
#else // !AB_OTA_UPDATER
-static int
-update_binary_command(const char* path, ZipArchiveHandle zip, int retry_count,
- int status_fd, std::vector<std::string>* cmd)
-{
- // On traditional updates we extract the update binary from the package.
- ZipString binary_name(ASSUMED_UPDATE_BINARY_NAME);
- ZipEntry binary_entry;
- if (FindEntry(zip, binary_name, &binary_entry) != 0) {
- return INSTALL_CORRUPT;
- }
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
+ int status_fd, std::vector<std::string>* cmd) {
+ CHECK(cmd != nullptr);
- const char* binary = "/tmp/update_binary";
- unlink(binary);
- int fd = creat(binary, 0755);
- if (fd < 0) {
- PLOG(ERROR) << "Can't make " << binary;
- return INSTALL_ERROR;
- }
- int error = ExtractEntryToFile(zip, &binary_entry, fd);
- close(fd);
+ // On traditional updates we extract the update binary from the package.
+ static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
+ ZipString binary_name(UPDATE_BINARY_NAME);
+ ZipEntry binary_entry;
+ if (FindEntry(zip, binary_name, &binary_entry) != 0) {
+ LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME;
+ return INSTALL_CORRUPT;
+ }
- if (error != 0) {
- LOG(ERROR) << "Can't copy " << ASSUMED_UPDATE_BINARY_NAME
- << " : " << ErrorCodeString(error);
- return INSTALL_ERROR;
- }
+ const char* binary = "/tmp/update_binary";
+ unlink(binary);
+ int fd = creat(binary, 0755);
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to create " << binary;
+ return INSTALL_ERROR;
+ }
- *cmd = {
- binary,
- EXPAND(RECOVERY_API_VERSION), // defined in Android.mk
- std::to_string(status_fd),
- path,
- };
- if (retry_count > 0)
- cmd->push_back("retry");
- return 0;
+ int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
+ close(fd);
+ if (error != 0) {
+ LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
+ return INSTALL_ERROR;
+ }
+
+ *cmd = {
+ binary,
+ EXPAND(RECOVERY_API_VERSION), // defined in Android.mk
+ std::to_string(status_fd),
+ path,
+ };
+ if (retry_count > 0) {
+ cmd->push_back("retry");
+ }
+ return 0;
}
#endif // !AB_OTA_UPDATER
@@ -546,10 +536,15 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) {
}
CloseArchive(zip_handle);
- // TODO(b/36814503): Enable the actual verification when VintfObject::CheckCompatibility() lands.
- // VintfObject::CheckCompatibility returns zero on success.
- // return (android::vintf::VintfObject::CheckCompatibility(compatibility_info, true) == 0);
- return true;
+ // VintfObjectRecovery::CheckCompatibility returns zero on success.
+ std::string err;
+ int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err);
+ if (result == 0) {
+ return true;
+ }
+
+ LOG(ERROR) << "Failed to verify package compatibility (result " << result << "): " << err;
+ return false;
}
static int
@@ -601,7 +596,6 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
// Additionally verify the compatibility of the package.
if (!verify_package_compatibility(zip)) {
- LOG(ERROR) << "Failed to verify package compatibility";
log_buffer.push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
sysReleaseMap(&map);
CloseArchive(zip);
@@ -623,80 +617,80 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount,
return result;
}
-int
-install_package(const char* path, bool* wipe_cache, const char* install_file,
- bool needs_mount, int retry_count)
-{
- modified_flash = true;
- auto start = std::chrono::system_clock::now();
+int install_package(const char* path, bool* wipe_cache, const char* install_file, bool needs_mount,
+ int retry_count) {
+ modified_flash = true;
+ auto start = std::chrono::system_clock::now();
+
+ int start_temperature = GetMaxValueFromThermalZone();
+ int max_temperature = start_temperature;
+
+ int result;
+ std::vector<std::string> log_buffer;
+ if (setup_install_mounts() != 0) {
+ LOG(ERROR) << "failed to set up expected mounts for install; aborting";
+ result = INSTALL_ERROR;
+ } else {
+ result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count,
+ &max_temperature);
+ }
- int start_temperature = GetMaxValueFromThermalZone();
- int max_temperature = start_temperature;
+ // Measure the time spent to apply OTA update in seconds.
+ std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
+ int time_total = static_cast<int>(duration.count());
- int result;
- std::vector<std::string> log_buffer;
- if (setup_install_mounts() != 0) {
- LOG(ERROR) << "failed to set up expected mounts for install; aborting";
- result = INSTALL_ERROR;
+ bool has_cache = volume_for_path("/cache") != nullptr;
+ // Skip logging the uncrypt_status on devices without /cache.
+ if (has_cache) {
+ static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status";
+ if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
+ LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS;
} else {
- result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count,
- &max_temperature);
- }
-
- // Measure the time spent to apply OTA update in seconds.
- std::chrono::duration<double> duration = std::chrono::system_clock::now() - start;
- int time_total = static_cast<int>(duration.count());
-
- bool has_cache = volume_for_path("/cache") != nullptr;
- // Skip logging the uncrypt_status on devices without /cache.
- if (has_cache) {
- if (ensure_path_mounted(UNCRYPT_STATUS) != 0) {
- LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS;
+ std::string uncrypt_status;
+ if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
+ PLOG(WARNING) << "failed to read uncrypt status";
+ } else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
+ LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
} else {
- std::string uncrypt_status;
- if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) {
- PLOG(WARNING) << "failed to read uncrypt status";
- } else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) {
- LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status;
- } else {
- log_buffer.push_back(android::base::Trim(uncrypt_status));
- }
+ log_buffer.push_back(android::base::Trim(uncrypt_status));
}
}
+ }
- // The first two lines need to be the package name and install result.
- std::vector<std::string> log_header = {
- path,
- result == INSTALL_SUCCESS ? "1" : "0",
- "time_total: " + std::to_string(time_total),
- "retry: " + std::to_string(retry_count),
- };
-
- int end_temperature = GetMaxValueFromThermalZone();
- max_temperature = std::max(end_temperature, max_temperature);
- if (start_temperature > 0) {
- log_buffer.push_back("temperature_start: " + std::to_string(start_temperature));
- }
- if (end_temperature > 0) {
- log_buffer.push_back("temperature_end: " + std::to_string(end_temperature));
- }
- if (max_temperature > 0) {
- log_buffer.push_back("temperature_max: " + std::to_string(max_temperature));
- }
+ // The first two lines need to be the package name and install result.
+ std::vector<std::string> log_header = {
+ path,
+ result == INSTALL_SUCCESS ? "1" : "0",
+ "time_total: " + std::to_string(time_total),
+ "retry: " + std::to_string(retry_count),
+ };
+
+ int end_temperature = GetMaxValueFromThermalZone();
+ max_temperature = std::max(end_temperature, max_temperature);
+ if (start_temperature > 0) {
+ log_buffer.push_back("temperature_start: " + std::to_string(start_temperature));
+ }
+ if (end_temperature > 0) {
+ log_buffer.push_back("temperature_end: " + std::to_string(end_temperature));
+ }
+ if (max_temperature > 0) {
+ log_buffer.push_back("temperature_max: " + std::to_string(max_temperature));
+ }
- std::string log_content = android::base::Join(log_header, "\n") + "\n" +
- android::base::Join(log_buffer, "\n") + "\n";
- if (!android::base::WriteStringToFile(log_content, install_file)) {
- PLOG(ERROR) << "failed to write " << install_file;
- }
+ std::string log_content =
+ android::base::Join(log_header, "\n") + "\n" + android::base::Join(log_buffer, "\n") + "\n";
+ if (!android::base::WriteStringToFile(log_content, install_file)) {
+ PLOG(ERROR) << "failed to write " << install_file;
+ }
- // Write a copy into last_log.
- LOG(INFO) << log_content;
+ // Write a copy into last_log.
+ LOG(INFO) << log_content;
- return result;
+ return result;
}
bool verify_package(const unsigned char* package_data, size_t package_size) {
+ static constexpr const char* PUBLIC_KEYS_FILE = "/res/keys";
std::vector<Certificate> loadedKeys;
if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) {
LOG(ERROR) << "Failed to load keys";
diff --git a/minadbd/fuse_adb_provider_test.cpp b/minadbd/fuse_adb_provider_test.cpp
index 0f2e881c7..31be2a64e 100644
--- a/minadbd/fuse_adb_provider_test.cpp
+++ b/minadbd/fuse_adb_provider_test.cpp
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#include "fuse_adb_provider.h"
-
-#include <gtest/gtest.h>
-
#include <errno.h>
#include <fcntl.h>
+#include <signal.h>
#include <sys/socket.h>
#include <string>
+#include <gtest/gtest.h>
+
#include "adb_io.h"
+#include "fuse_adb_provider.h"
TEST(fuse_adb_provider, read_block_adb) {
adb_data data = {};
@@ -46,9 +46,8 @@ TEST(fuse_adb_provider, read_block_adb) {
uint32_t block = 1234U;
const char expected_block[] = "00001234";
- ASSERT_EQ(0, read_block_adb(reinterpret_cast<void*>(&data), block,
- reinterpret_cast<uint8_t*>(block_data),
- sizeof(expected_data) - 1));
+ ASSERT_EQ(0, read_block_adb(static_cast<void*>(&data), block,
+ reinterpret_cast<uint8_t*>(block_data), sizeof(expected_data) - 1));
// Check that read_block_adb requested the right block.
char block_req[sizeof(expected_block)] = {};
@@ -80,9 +79,12 @@ TEST(fuse_adb_provider, read_block_adb_fail_write) {
ASSERT_EQ(0, close(sockets[1]));
+ // write(2) raises SIGPIPE since the reading end has been closed. Ignore the signal to avoid
+ // failing the test.
+ signal(SIGPIPE, SIG_IGN);
+
char buf[1];
- ASSERT_EQ(-EIO, read_block_adb(reinterpret_cast<void*>(&data), 0,
- reinterpret_cast<uint8_t*>(buf), 1));
+ ASSERT_EQ(-EIO, read_block_adb(static_cast<void*>(&data), 0, reinterpret_cast<uint8_t*>(buf), 1));
close(sockets[0]);
}
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index a6aa321ca..61c06cc0a 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -21,6 +21,7 @@
#include <string.h>
#include <unistd.h>
+#include <string>
#include <thread>
#include "adb.h"
@@ -28,33 +29,30 @@
#include "fuse_adb_provider.h"
#include "sysdeps.h"
-static void sideload_host_service(int sfd, void* data) {
- char* args = reinterpret_cast<char*>(data);
+static void sideload_host_service(int sfd, const std::string& args) {
int file_size;
int block_size;
- if (sscanf(args, "%d:%d", &file_size, &block_size) != 2) {
- printf("bad sideload-host arguments: %s\n", args);
+ if (sscanf(args.c_str(), "%d:%d", &file_size, &block_size) != 2) {
+ printf("bad sideload-host arguments: %s\n", args.c_str());
exit(1);
}
- free(args);
printf("sideload-host file size %d block size %d\n", file_size, block_size);
int result = run_adb_fuse(sfd, file_size, block_size);
printf("sideload_host finished\n");
- sleep(1);
exit(result == 0 ? 0 : 1);
}
-static int create_service_thread(void (*func)(int, void *), void *cookie) {
+static int create_service_thread(void (*func)(int, const std::string&), const std::string& args) {
int s[2];
if (adb_socketpair(s)) {
printf("cannot create service socket pair\n");
return -1;
}
- std::thread([s, func, cookie]() { func(s[1], cookie); }).detach();
+ std::thread([s, func, args]() { func(s[1], args); }).detach();
VLOG(SERVICES) << "service thread started, " << s[0] << ":" << s[1];
return s[0];
@@ -69,7 +67,7 @@ int service_to_fd(const char* name, const atransport* transport) {
// sideload-host).
exit(3);
} else if (!strncmp(name, "sideload-host:", 14)) {
- char* arg = strdup(name + 14);
+ std::string arg(name + 14);
ret = create_service_thread(sideload_host_service, arg);
}
if (ret >= 0) {
diff --git a/private/install.h b/private/install.h
new file mode 100644
index 000000000..12d303b01
--- /dev/null
+++ b/private/install.h
@@ -0,0 +1,27 @@
+/*
+ * 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 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.
+ */
+
+// Private headers exposed for testing purpose only.
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <ziparchive/zip_archive.h>
+
+int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count,
+ int status_fd, std::vector<std::string>* cmd);
diff --git a/recovery.cpp b/recovery.cpp
index 173baac16..3041d6cac 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -40,7 +40,6 @@
#include <string>
#include <vector>
-#include <adb.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -1607,14 +1606,22 @@ int main(int argc, char **argv) {
}
}
- if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) {
- copy_logs();
+ if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
ui->SetBackground(RecoveryUI::ERROR);
+ if (!ui->IsTextVisible()) {
+ sleep(5);
+ }
}
Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
- if ((status != INSTALL_SUCCESS && status != INSTALL_SKIPPED && !sideload_auto_reboot) ||
- ui->IsTextVisible()) {
+ // 1. If the recovery menu is visible, prompt and wait for commands.
+ // 2. If the state is INSTALL_NONE, wait for commands. (i.e. In user build, manually reboot into
+ // recovery to sideload a package.)
+ // 3. sideload_auto_reboot is an option only available in user-debug build, reboot the device
+ // without waiting.
+ // 4. In all other cases, reboot the device. Therefore, normal users will observe the device
+ // reboot after it shows the "error" screen for 5s.
+ if ((status == INSTALL_NONE && !sideload_auto_reboot) || ui->IsTextVisible()) {
Device::BuiltinAction temp = prompt_and_wait(device, status);
if (temp != Device::NO_ACTION) {
after = temp;
diff --git a/tests/Android.mk b/tests/Android.mk
index a1f0d4892..4e125ccce 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -85,6 +85,14 @@ LOCAL_CFLAGS := \
-Werror \
-D_FILE_OFFSET_BITS=64
+ifeq ($(AB_OTA_UPDATER),true)
+LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+endif
+
+ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true)
+LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1
+endif
+
LOCAL_MODULE := recovery_component_test
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_C_INCLUDES := bootable/recovery
@@ -97,6 +105,7 @@ LOCAL_SRC_FILES := \
component/sideload_test.cpp \
component/uncrypt_test.cpp \
component/updater_test.cpp \
+ component/update_verifier_test.cpp \
component/verifier_test.cpp
LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -124,10 +133,14 @@ LOCAL_STATIC_LIBRARIES := \
libverifier \
libotautil \
libmounts \
+ libupdate_verifier \
libdivsufsort \
libdivsufsort64 \
libfs_mgr \
liblog \
+ libvintf_recovery \
+ libvintf \
+ libtinyxml2 \
libselinux \
libext4_utils \
libsparse \
diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp
index 5cba68f8a..016fed9b1 100644
--- a/tests/component/applypatch_test.cpp
+++ b/tests/component/applypatch_test.cpp
@@ -105,9 +105,6 @@ class ApplyPatchTest : public ::testing::Test {
static size_t new_size;
};
-std::string ApplyPatchTest::old_file;
-std::string ApplyPatchTest::new_file;
-
static void cp(const std::string& src, const std::string& tgt) {
std::string cmd = "cp " + src + " " + tgt;
system(cmd.c_str());
@@ -132,48 +129,8 @@ class ApplyPatchCacheTest : public ApplyPatchTest {
}
};
-class ApplyPatchFullTest : public ApplyPatchCacheTest {
- public:
- static void SetUpTestCase() {
- ApplyPatchTest::SetUpTestCase();
-
- output_f = new TemporaryFile();
- output_loc = std::string(output_f->path);
-
- struct FileContents fc;
-
- ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc));
- patches.push_back(
- std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())));
-
- ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc));
- patches.push_back(
- std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())));
- }
-
- static void TearDownTestCase() {
- delete output_f;
- patches.clear();
- }
-
- static std::vector<std::unique_ptr<Value>> patches;
- static TemporaryFile* output_f;
- static std::string output_loc;
-};
-
-class ApplyPatchDoubleCacheTest : public ApplyPatchFullTest {
- public:
- virtual void SetUp() {
- ApplyPatchCacheTest::SetUp();
- cp(cache_file, "/cache/reallysaved.file");
- }
-
- virtual void TearDown() {
- cp("/cache/reallysaved.file", cache_file);
- ApplyPatchCacheTest::TearDown();
- }
-};
-
+std::string ApplyPatchTest::old_file;
+std::string ApplyPatchTest::new_file;
std::string ApplyPatchTest::rand_file;
std::string ApplyPatchTest::patch_file;
std::string ApplyPatchTest::cache_file;
@@ -184,10 +141,6 @@ std::string ApplyPatchTest::bad_sha1_b;
size_t ApplyPatchTest::old_size;
size_t ApplyPatchTest::new_size;
-std::vector<std::unique_ptr<Value>> ApplyPatchFullTest::patches;
-TemporaryFile* ApplyPatchFullTest::output_f;
-std::string ApplyPatchFullTest::output_loc;
-
TEST_F(ApplyPatchTest, CheckModeSkip) {
std::vector<std::string> sha1s;
ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
@@ -424,20 +377,6 @@ TEST(ApplyPatchModesTest, CheckModeInvalidArgs) {
ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
}
-TEST(ApplyPatchModesTest, SpaceModeInvalidArgs) {
- // Insufficient args.
- ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-s" }));
-
- // Invalid bytes arg.
- ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "x" }));
-
- // 0 is invalid.
- ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0" }));
-
- // 0x10 is fine.
- ASSERT_EQ(0, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0x10" }));
-}
-
TEST(ApplyPatchModesTest, ShowLicenses) {
ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
}
diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp
index 3b6fbc301..40201d76f 100644
--- a/tests/component/install_test.cpp
+++ b/tests/component/install_test.cpp
@@ -15,13 +15,22 @@
*/
#include <stdio.h>
+#include <unistd.h>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
+#include <vintf/VintfObjectRecovery.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_writer.h>
#include "install.h"
+#include "private/install.h"
TEST(InstallTest, verify_package_compatibility_no_entry) {
TemporaryFile temp_file;
@@ -55,3 +64,215 @@ TEST(InstallTest, verify_package_compatibility_invalid_entry) {
ASSERT_FALSE(verify_package_compatibility(zip));
CloseArchive(zip);
}
+
+TEST(InstallTest, read_metadata_from_package_smoke) {
+ TemporaryFile temp_file;
+ FILE* zip_file = fdopen(temp_file.fd, "w");
+ ZipWriter writer(zip_file);
+ ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored));
+ const std::string content("abcdefg");
+ 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));
+
+ ZipArchiveHandle zip;
+ ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
+ std::string metadata;
+ ASSERT_TRUE(read_metadata_from_package(zip, &metadata));
+ ASSERT_EQ(content, metadata);
+ CloseArchive(zip);
+
+ TemporaryFile temp_file2;
+ FILE* zip_file2 = fdopen(temp_file2.fd, "w");
+ ZipWriter writer2(zip_file2);
+ ASSERT_EQ(0, writer2.StartEntry("META-INF/com/android/metadata", kCompressDeflated));
+ ASSERT_EQ(0, writer2.WriteBytes(content.data(), content.size()));
+ ASSERT_EQ(0, writer2.FinishEntry());
+ ASSERT_EQ(0, writer2.Finish());
+ ASSERT_EQ(0, fclose(zip_file2));
+
+ ASSERT_EQ(0, OpenArchive(temp_file2.path, &zip));
+ metadata.clear();
+ ASSERT_TRUE(read_metadata_from_package(zip, &metadata));
+ ASSERT_EQ(content, metadata);
+ CloseArchive(zip);
+}
+
+TEST(InstallTest, read_metadata_from_package_no_entry) {
+ TemporaryFile temp_file;
+ FILE* zip_file = fdopen(temp_file.fd, "w");
+ ZipWriter writer(zip_file);
+ ASSERT_EQ(0, writer.StartEntry("dummy_entry", kCompressStored));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+ ASSERT_EQ(0, fclose(zip_file));
+
+ ZipArchiveHandle zip;
+ ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
+ std::string metadata;
+ ASSERT_FALSE(read_metadata_from_package(zip, &metadata));
+ CloseArchive(zip);
+}
+
+TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) {
+ TemporaryFile compatibility_zip_file;
+ FILE* compatibility_zip = fdopen(compatibility_zip_file.fd, "w");
+ ZipWriter compatibility_zip_writer(compatibility_zip);
+ ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated));
+ std::string malformed_xml = "malformed";
+ ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(malformed_xml.data(), malformed_xml.size()));
+ ASSERT_EQ(0, compatibility_zip_writer.FinishEntry());
+ ASSERT_EQ(0, compatibility_zip_writer.Finish());
+ ASSERT_EQ(0, fclose(compatibility_zip));
+
+ TemporaryFile temp_file;
+ FILE* zip_file = fdopen(temp_file.fd, "w");
+ ZipWriter writer(zip_file);
+ ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored));
+ std::string compatibility_zip_content;
+ ASSERT_TRUE(
+ android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content));
+ ASSERT_EQ(0,
+ writer.WriteBytes(compatibility_zip_content.data(), compatibility_zip_content.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+ ASSERT_EQ(0, fclose(zip_file));
+
+ 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;
+ FILE* compatibility_zip = fdopen(compatibility_zip_file.fd, "w");
+ ZipWriter compatibility_zip_writer(compatibility_zip);
+ ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated));
+ ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(system_manifest_xml_content.data(),
+ system_manifest_xml_content.size()));
+ ASSERT_EQ(0, compatibility_zip_writer.FinishEntry());
+ ASSERT_EQ(0, compatibility_zip_writer.Finish());
+ ASSERT_EQ(0, fclose(compatibility_zip));
+
+ TemporaryFile temp_file;
+ FILE* zip_file = fdopen(temp_file.fd, "w");
+ ZipWriter writer(zip_file);
+ ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored));
+ std::string compatibility_zip_content;
+ ASSERT_TRUE(
+ android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content));
+ ASSERT_EQ(0,
+ writer.WriteBytes(compatibility_zip_content.data(), compatibility_zip_content.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+ ASSERT_EQ(0, fclose(zip_file));
+
+ 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, update_binary_command_smoke) {
+#ifdef AB_OTA_UPDATER
+ TemporaryFile temp_file;
+ FILE* zip_file = fdopen(temp_file.fd, "w");
+ ZipWriter writer(zip_file);
+ ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.StartEntry("payload_properties.txt", kCompressStored));
+ const std::string properties = "some_properties";
+ ASSERT_EQ(0, writer.WriteBytes(properties.data(), properties.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ // A metadata entry is mandatory.
+ ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored));
+ 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");
+ ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+ ASSERT_EQ(0, fclose(zip_file));
+
+ ZipArchiveHandle zip;
+ ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
+ int status_fd = 10;
+ std::string path = "/path/to/update.zip";
+ std::vector<std::string> cmd;
+ ASSERT_EQ(0, update_binary_command(path, zip, 0, status_fd, &cmd));
+ ASSERT_EQ("/sbin/update_engine_sideload", cmd[0]);
+ ASSERT_EQ("--payload=file://" + path, cmd[1]);
+ ASSERT_EQ("--headers=" + properties, cmd[3]);
+ ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]);
+ CloseArchive(zip);
+#else
+ // Cannot test update_binary_command() because it tries to extract update-binary to /tmp.
+ GTEST_LOG_(INFO) << "Test skipped on non-A/B device.";
+#endif // AB_OTA_UPDATER
+}
+
+TEST(InstallTest, update_binary_command_invalid) {
+#ifdef AB_OTA_UPDATER
+ TemporaryFile temp_file;
+ FILE* zip_file = fdopen(temp_file.fd, "w");
+ ZipWriter writer(zip_file);
+ // Missing payload_properties.txt.
+ ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored));
+ ASSERT_EQ(0, writer.FinishEntry());
+ // A metadata entry is mandatory.
+ ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored));
+ 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");
+ ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size()));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+ ASSERT_EQ(0, fclose(zip_file));
+
+ ZipArchiveHandle zip;
+ ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
+ int status_fd = 10;
+ std::string path = "/path/to/update.zip";
+ std::vector<std::string> cmd;
+ ASSERT_EQ(INSTALL_CORRUPT, update_binary_command(path, zip, 0, status_fd, &cmd));
+ CloseArchive(zip);
+#else
+ // Cannot test update_binary_command() because it tries to extract update-binary to /tmp.
+ GTEST_LOG_(INFO) << "Test skipped on non-A/B device.";
+#endif // AB_OTA_UPDATER
+}
diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp
new file mode 100644
index 000000000..73b4478aa
--- /dev/null
+++ b/tests/component/update_verifier_test.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 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 <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <update_verifier/update_verifier.h>
+
+class UpdateVerifierTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+#ifdef PRODUCT_SUPPORTS_VERITY
+ verity_supported = true;
+#else
+ verity_supported = false;
+#endif
+ }
+
+ bool verity_supported;
+};
+
+TEST_F(UpdateVerifierTest, verify_image_no_care_map) {
+ // Non-existing care_map is allowed.
+ ASSERT_TRUE(verify_image("/doesntexist"));
+}
+
+TEST_F(UpdateVerifierTest, verify_image_smoke) {
+ // This test relies on dm-verity support.
+ if (!verity_supported) {
+ GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
+ return;
+ }
+
+ // The care map file can have only two or four lines.
+ TemporaryFile temp_file;
+ std::string content = "system\n2,0,1";
+ ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
+ ASSERT_TRUE(verify_image(temp_file.path));
+
+ // Leading and trailing newlines should be accepted.
+ ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", temp_file.path));
+ ASSERT_TRUE(verify_image(temp_file.path));
+}
+
+TEST_F(UpdateVerifierTest, verify_image_wrong_lines) {
+ // The care map file can have only two or four lines.
+ TemporaryFile temp_file;
+ ASSERT_FALSE(verify_image(temp_file.path));
+
+ ASSERT_TRUE(android::base::WriteStringToFile("line1", temp_file.path));
+ ASSERT_FALSE(verify_image(temp_file.path));
+
+ ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", temp_file.path));
+ ASSERT_FALSE(verify_image(temp_file.path));
+}
+
+TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) {
+ // This test relies on dm-verity support.
+ if (!verity_supported) {
+ GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
+ return;
+ }
+
+ TemporaryFile temp_file;
+ std::string content = "system\n2,1,0";
+ ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
+ ASSERT_FALSE(verify_image(temp_file.path));
+}
diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk
index 1acd5eca0..37d9bfed3 100644
--- a/update_verifier/Android.mk
+++ b/update_verifier/Android.mk
@@ -14,12 +14,43 @@
LOCAL_PATH := $(call my-dir)
+# libupdate_verifier (static library)
+# ===============================
include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SRC_FILES := update_verifier.cpp
+LOCAL_SRC_FILES := \
+ update_verifier.cpp
+
+LOCAL_MODULE := libupdate_verifier
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libcutils \
+ android.hardware.boot@1.0
+
+LOCAL_CFLAGS := -Wall -Werror
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH)/include
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+
+ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true)
+LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1
+endif
+
+include $(BUILD_STATIC_LIBRARY)
+
+# update_verifier (executable)
+# ===============================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ update_verifier_main.cpp
LOCAL_MODULE := update_verifier
+LOCAL_STATIC_LIBRARIES := \
+ libupdate_verifier
LOCAL_SHARED_LIBRARIES := \
libbase \
libcutils \
@@ -29,13 +60,8 @@ LOCAL_SHARED_LIBRARIES := \
libhidlbase \
android.hardware.boot@1.0
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_INIT_RC := update_verifier.rc
-ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true)
- LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1
-endif
-
include $(BUILD_EXECUTABLE)
diff --git a/update_verifier/include/update_verifier/update_verifier.h b/update_verifier/include/update_verifier/update_verifier.h
new file mode 100644
index 000000000..16b394e98
--- /dev/null
+++ b/update_verifier/include/update_verifier/update_verifier.h
@@ -0,0 +1,24 @@
+/*
+ * 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 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>
+
+int update_verifier(int argc, char** argv);
+
+// Exposed for testing purpose.
+bool verify_image(const std::string& care_map_name);
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index 350020f13..1950cbd83 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -35,6 +35,8 @@
* verifier reaches the end after the verification.
*/
+#include "update_verifier/update_verifier.h"
+
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -59,12 +61,6 @@ using android::hardware::boot::V1_0::IBootControl;
using android::hardware::boot::V1_0::BoolResult;
using android::hardware::boot::V1_0::CommandResult;
-constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt";
-constexpr auto DM_PATH_PREFIX = "/sys/block/";
-constexpr auto DM_PATH_SUFFIX = "/dm/name";
-constexpr auto DEV_PATH = "/dev/block/";
-constexpr int BLOCKSIZE = 4096;
-
// Find directories in format of "/sys/block/dm-X".
static int dm_name_filter(const dirent* de) {
if (android::base::StartsWith(de->d_name, "dm-")) {
@@ -82,6 +78,7 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
// (or "vendor"), then dm-X is a dm-wrapped system/vendor partition.
// Afterwards, update_verifier will read every block on the care_map_file of
// "/dev/block/dm-X" to ensure the partition's integrity.
+ static constexpr auto DM_PATH_PREFIX = "/sys/block/";
dirent** namelist;
int n = scandir(DM_PATH_PREFIX, &namelist, dm_name_filter, alphasort);
if (n == -1) {
@@ -93,6 +90,8 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
return false;
}
+ static constexpr auto DM_PATH_SUFFIX = "/dm/name";
+ static constexpr auto DEV_PATH = "/dev/block/";
std::string dm_block_device;
while (n--) {
std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX;
@@ -143,6 +142,7 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
return false;
}
+ static constexpr int BLOCKSIZE = 4096;
if (lseek64(fd.get(), static_cast<off64_t>(range_start) * BLOCKSIZE, SEEK_SET) == -1) {
PLOG(ERROR) << "lseek to " << range_start << " failed";
return false;
@@ -161,7 +161,7 @@ static bool read_blocks(const std::string& partition, const std::string& range_s
return true;
}
-static bool verify_image(const std::string& care_map_name) {
+bool verify_image(const std::string& care_map_name) {
android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
// If the device is flashed before the current boot, it may not have care_map.txt
// in /data/ota_package. To allow the device to continue booting in this situation,
@@ -205,7 +205,7 @@ static int reboot_device() {
while (true) pause();
}
-int main(int argc, char** argv) {
+int update_verifier(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
LOG(INFO) << "Started with arg " << i << ": " << argv[i];
}
@@ -238,6 +238,7 @@ int main(int argc, char** argv) {
return reboot_device();
}
+ static constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt";
if (!verify_image(CARE_MAP_FILE)) {
LOG(ERROR) << "Failed to verify all blocks in care map file.";
return reboot_device();
diff --git a/update_verifier/update_verifier_main.cpp b/update_verifier/update_verifier_main.cpp
new file mode 100644
index 000000000..46e8bbb59
--- /dev/null
+++ b/update_verifier/update_verifier_main.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 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.
+ */
+
+// See the comments in update_verifier.cpp.
+
+#include "update_verifier/update_verifier.h"
+
+int main(int argc, char** argv) {
+ return update_verifier(argc, argv);
+}