summaryrefslogtreecommitdiffstats
path: root/recovery.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'recovery.cpp')
-rw-r--r--recovery.cpp185
1 files changed, 147 insertions, 38 deletions
diff --git a/recovery.cpp b/recovery.cpp
index 12df61db8..7419bac00 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -47,6 +47,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
#include <cutils/properties.h> /* for property_list */
#include <healthd/BatteryMonitor.h>
@@ -55,7 +56,6 @@
#include <selinux/selinux.h>
#include "adb_install.h"
-#include "bootloader.h"
#include "common.h"
#include "device.h"
#include "error_code.h"
@@ -65,6 +65,7 @@
#include "minadbd/minadbd.h"
#include "minui/minui.h"
#include "minzip/DirUtil.h"
+#include "minzip/Zip.h"
#include "roots.h"
#include "ui.h"
#include "screen_ui.h"
@@ -85,10 +86,17 @@ static const struct option OPTIONS[] = {
{ "shutdown_after", no_argument, NULL, 'p' },
{ "reason", required_argument, NULL, 'r' },
{ "security", no_argument, NULL, 'e'},
- { "brick", no_argument, NULL, 0 },
+ { "wipe_ab", no_argument, NULL, 0 },
+ { "wipe_package_size", required_argument, NULL, 0 },
{ NULL, 0, NULL, 0 },
};
+// More bootreasons can be found in "system/core/bootstat/bootstat.cpp".
+static const std::vector<std::string> bootreason_blacklist {
+ "kernel_panic",
+ "Panic",
+};
+
static const char *CACHE_LOG_DIR = "/cache/recovery";
static const char *COMMAND_FILE = "/cache/recovery/command";
static const char *LOG_FILE = "/cache/recovery/log";
@@ -112,7 +120,7 @@ static const int BATTERY_READ_TIMEOUT_IN_SEC = 10;
// So we should check battery with a slightly lower limitation.
static const int BATTERY_OK_PERCENTAGE = 20;
static const int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
-constexpr const char* RECOVERY_BRICK = "/etc/recovery.brick";
+constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe";
RecoveryUI* ui = NULL;
static const char* locale = "en_US";
@@ -301,9 +309,13 @@ static void redirect_stdio(const char* filename) {
// - the contents of COMMAND_FILE (one per line)
static void
get_args(int *argc, char ***argv) {
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
+ bootloader_message boot = {};
+ std::string err;
+ if (!read_bootloader_message(&boot, &err)) {
+ LOG(ERROR) << err;
+ // If fails, leave a zeroed bootloader_message.
+ memset(&boot, 0, sizeof(boot));
+ }
stage = strndup(boot.stage, sizeof(boot.stage));
if (boot.command[0] != 0) {
@@ -368,16 +380,20 @@ get_args(int *argc, char ***argv) {
strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
strlcat(boot.recovery, "\n", sizeof(boot.recovery));
}
- set_bootloader_message(&boot);
+ if (!write_bootloader_message(boot, &err)) {
+ LOG(ERROR) << err;
+ }
}
static void
set_sdcard_update_bootloader_message() {
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
+ bootloader_message boot = {};
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
- set_bootloader_message(&boot);
+ std::string err;
+ if (!write_bootloader_message(boot, &err)) {
+ LOG(ERROR) << err;
+ }
}
// Read from kernel log into buffer and write out to file.
@@ -528,9 +544,11 @@ finish_recovery() {
copy_logs();
// Reset to normal system boot so recovery won't cycle indefinitely.
- struct bootloader_message boot;
- memset(&boot, 0, sizeof(boot));
- set_bootloader_message(&boot);
+ bootloader_message boot = {};
+ std::string err;
+ if (!write_bootloader_message(boot, &err)) {
+ LOG(ERROR) << err;
+ }
// Remove the command file, so recovery won't repeat indefinitely.
if (has_cache) {
@@ -898,15 +916,79 @@ static bool secure_wipe_partition(const std::string& partition) {
return true;
}
-// Brick the current device, with a secure wipe of all the partitions in
-// RECOVERY_BRICK.
-static bool brick_device() {
+// Check if the wipe package matches expectation:
+// 1. verify the package.
+// 2. check metadata (ota-type, pre-device and serial number if having one).
+static bool check_wipe_package(size_t wipe_package_size) {
+ if (wipe_package_size == 0) {
+ LOG(ERROR) << "wipe_package_size is zero";
+ return false;
+ }
+ std::string wipe_package;
+ std::string err_str;
+ if (!read_wipe_package(&wipe_package, wipe_package_size, &err_str)) {
+ PLOG(ERROR) << "Failed to read wipe package";
+ return false;
+ }
+ if (!verify_package(reinterpret_cast<const unsigned char*>(wipe_package.data()),
+ wipe_package.size())) {
+ LOG(ERROR) << "Failed to verify package";
+ return false;
+ }
+
+ // Extract metadata
+ ZipArchive zip;
+ int err = mzOpenZipArchive(reinterpret_cast<unsigned char*>(&wipe_package[0]),
+ wipe_package.size(), &zip);
+ if (err != 0) {
+ LOG(ERROR) << "Can't open wipe package";
+ return false;
+ }
+ std::string metadata;
+ if (!read_metadata_from_package(&zip, &metadata)) {
+ mzCloseZipArchive(&zip);
+ return false;
+ }
+ mzCloseZipArchive(&zip);
+
+ // Check metadata
+ 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="));
+ char real_device_type[PROPERTY_VALUE_MAX];
+ property_get("ro.build.product", real_device_type, "");
+ device_type_matched = (device_type == real_device_type);
+ } else if (android::base::StartsWith(line, "serialno=")) {
+ std::string serial_no = line.substr(strlen("serialno="));
+ char real_serial_no[PROPERTY_VALUE_MAX];
+ property_get("ro.serialno", real_serial_no, "");
+ 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);
+}
+
+// Wipe the current A/B device, with a secure wipe of all the partitions in
+// RECOVERY_WIPE.
+static bool wipe_ab_device(size_t wipe_package_size) {
ui->SetBackground(RecoveryUI::ERASING);
ui->SetProgressType(RecoveryUI::INDETERMINATE);
+ if (!check_wipe_package(wipe_package_size)) {
+ LOG(ERROR) << "Failed to verify wipe package";
+ return false;
+ }
std::string partition_list;
- if (!android::base::ReadFileToString(RECOVERY_BRICK, &partition_list)) {
- LOG(ERROR) << "failed to read \"" << RECOVERY_BRICK << "\"";
+ if (!android::base::ReadFileToString(RECOVERY_WIPE, &partition_list)) {
+ LOG(ERROR) << "failed to read \"" << RECOVERY_WIPE << "\"";
return false;
}
@@ -1311,7 +1393,7 @@ static bool is_battery_ok() {
}
static void set_retry_bootloader_message(int retry_count, int argc, char** argv) {
- struct bootloader_message boot {};
+ bootloader_message boot = {};
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
@@ -1330,7 +1412,37 @@ static void set_retry_bootloader_message(int retry_count, int argc, char** argv)
snprintf(buffer, sizeof(buffer), "--retry_count=%d\n", retry_count+1);
strlcat(boot.recovery, buffer, sizeof(boot.recovery));
}
- set_bootloader_message(&boot);
+ std::string err;
+ if (!write_bootloader_message(boot, &err)) {
+ LOG(ERROR) << err;
+ }
+}
+
+static bool bootreason_in_blacklist() {
+ char bootreason[PROPERTY_VALUE_MAX];
+ if (property_get("ro.boot.bootreason", bootreason, nullptr) > 0) {
+ for (const auto& str : bootreason_blacklist) {
+ if (strcasecmp(str.c_str(), bootreason) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void log_failure_code(ErrorCode code, const char *update_package) {
+ std::vector<std::string> log_buffer = {
+ update_package,
+ "0", // install result
+ "error: " + std::to_string(code),
+ };
+ std::string log_content = android::base::Join(log_buffer, "\n");
+ if (!android::base::WriteStringToFile(log_content, TEMPORARY_INSTALL_FILE)) {
+ PLOG(ERROR) << "failed to write " << TEMPORARY_INSTALL_FILE;
+ }
+
+ // Also write the info into last_log.
+ LOG(INFO) << log_content;
}
static ssize_t logbasename(
@@ -1428,7 +1540,8 @@ int main(int argc, char **argv) {
const char *update_package = NULL;
bool should_wipe_data = false;
bool should_wipe_cache = false;
- bool should_brick = false;
+ bool should_wipe_ab = false;
+ size_t wipe_package_size = 0;
bool show_text = false;
bool sideload = false;
bool sideload_auto_reboot = false;
@@ -1462,8 +1575,11 @@ int main(int argc, char **argv) {
case 'r': reason = optarg; break;
case 'e': security_update = true; break;
case 0: {
- if (strcmp(OPTIONS[option_index].name, "brick") == 0) {
- should_brick = true;
+ if (strcmp(OPTIONS[option_index].name, "wipe_ab") == 0) {
+ should_wipe_ab = true;
+ break;
+ } else if (strcmp(OPTIONS[option_index].name, "wipe_package_size") == 0) {
+ android::base::ParseUint(optarg, &wipe_package_size);
break;
}
break;
@@ -1554,19 +1670,12 @@ int main(int argc, char **argv) {
BATTERY_OK_PERCENTAGE);
// Log the error code to last_install when installation skips due to
// low battery.
- std::vector<std::string> log_buffer = {
- update_package,
- "0", // install result
- "error: " + std::to_string(kLowBattery),
- };
- std::string log_content = android::base::Join(log_buffer, "\n");
- if (!android::base::WriteStringToFile(log_content, LAST_INSTALL_FILE)) {
- PLOG(ERROR) << "failed to write " << LAST_INSTALL_FILE;
- }
-
- // Also write the info into last_log.
- LOG(INFO) << log_content;
-
+ log_failure_code(kLowBattery, update_package);
+ status = INSTALL_SKIPPED;
+ } else if (bootreason_in_blacklist()) {
+ // Skip update-on-reboot when bootreason is kernel_panic or similar
+ ui->Print("bootreason is in the blacklist; skip OTA installation\n");
+ log_failure_code(kBootreasonInBlacklist, update_package);
status = INSTALL_SKIPPED;
} else {
status = install_package(update_package, &should_wipe_cache,
@@ -1609,8 +1718,8 @@ int main(int argc, char **argv) {
if (!wipe_cache(false, device)) {
status = INSTALL_ERROR;
}
- } else if (should_brick) {
- if (!brick_device()) {
+ } else if (should_wipe_ab) {
+ if (!wipe_ab_device(wipe_package_size)) {
status = INSTALL_ERROR;
}
} else if (sideload) {