summaryrefslogtreecommitdiffstats
path: root/recovery.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'recovery.cpp')
-rw-r--r--recovery.cpp1551
1 files changed, 702 insertions, 849 deletions
diff --git a/recovery.cpp b/recovery.cpp
index 0f0b978e7..c1a31b6a8 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -34,23 +34,30 @@
#include <time.h>
#include <unistd.h>
+#include <algorithm>
#include <chrono>
+#include <memory>
#include <string>
#include <vector>
#include <adb.h>
-#include <android/log.h> /* Android Log Priority Tags */
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#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>
-#include <log/logger.h> /* Android Log packet format */
-#include <private/android_logger.h> /* private pmsg functions */
-
+#include <cutils/properties.h> /* for property_list */
#include <healthd/BatteryMonitor.h>
+#include <private/android_logger.h> /* private pmsg functions */
+#include <private/android_filesystem_config.h> /* for AID_SYSTEM */
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+#include <ziparchive/zip_archive.h>
#include "adb_install.h"
#include "common.h"
@@ -59,18 +66,16 @@
#include "fuse_sdcard_provider.h"
#include "fuse_sideload.h"
#include "install.h"
+#include "minadbd/minadbd.h"
#include "minui/minui.h"
-#include "minzip/DirUtil.h"
-#include "minzip/Zip.h"
+#include "otautil/DirUtil.h"
#include "roots.h"
-#include "ui.h"
-#include "unique_fd.h"
+#include "rotate_logs.h"
#include "screen_ui.h"
-
-struct selabel_handle *sehandle;
+#include "stub_ui.h"
+#include "ui.h"
static const struct option OPTIONS[] = {
- { "send_intent", required_argument, NULL, 'i' },
{ "update_package", required_argument, NULL, 'u' },
{ "retry_count", required_argument, NULL, 'n' },
{ "wipe_data", no_argument, NULL, 'w' },
@@ -80,12 +85,12 @@ static const struct option OPTIONS[] = {
{ "sideload_auto_reboot", no_argument, NULL, 'a' },
{ "just_exit", no_argument, NULL, 'x' },
{ "locale", required_argument, NULL, 'l' },
- { "stages", required_argument, NULL, 'g' },
{ "shutdown_after", no_argument, NULL, 'p' },
{ "reason", required_argument, NULL, 'r' },
{ "security", no_argument, NULL, 'e'},
{ "wipe_ab", no_argument, NULL, 0 },
{ "wipe_package_size", required_argument, NULL, 0 },
+ { "prompt_and_wipe_data", no_argument, NULL, 0 },
{ NULL, 0, NULL, 0 },
};
@@ -97,7 +102,6 @@ static const std::vector<std::string> bootreason_blacklist {
static const char *CACHE_LOG_DIR = "/cache/recovery";
static const char *COMMAND_FILE = "/cache/recovery/command";
-static const char *INTENT_FILE = "/cache/recovery/intent";
static const char *LOG_FILE = "/cache/recovery/log";
static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
static const char *LOCALE_FILE = "/cache/recovery/last_locale";
@@ -110,7 +114,6 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
-static const int KEEP_LOG_COUNT = 10;
// We will try to apply the update package 5 times at most in case of an I/O error.
static const int EIO_RETRY_COUNT = 4;
static const int BATTERY_READ_TIMEOUT_IN_SEC = 10;
@@ -119,25 +122,28 @@ 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_WIPE = "/etc/recovery.wipe";
+static constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe";
+static constexpr const char* DEFAULT_LOCALE = "en-US";
-RecoveryUI* ui = NULL;
-static const char* locale = "en_US";
-char* stage = NULL;
-char* reason = NULL;
-bool modified_flash = false;
+static std::string locale;
static bool has_cache = false;
+RecoveryUI* ui = nullptr;
+bool modified_flash = false;
+std::string stage;
+const char* reason = nullptr;
+struct selabel_handle* sehandle;
+
/*
* The recovery tool communicates with the main system through /cache files.
* /cache/recovery/command - INPUT - command line for tool, one arg per line
* /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
- * /cache/recovery/intent - OUTPUT - intent that was passed in
*
* The arguments which may be supplied in the recovery.command file:
- * --send_intent=anystring - write the text out to recovery.intent
* --update_package=path - verify install an OTA package file
* --wipe_data - erase user data (and cache), then reboot
+ * --prompt_and_wipe_data - prompt the user that data is corrupt,
+ * with their consent erase user data (and cache), then reboot
* --wipe_cache - wipe cache (but not user data), then reboot
* --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
* --just_exit - do nothing; exit and reboot
@@ -170,30 +176,13 @@ static bool has_cache = false;
* -- after this, rebooting will (try to) restart the main system --
* 7. ** if install failed **
* 7a. prompt_and_wait() shows an error icon and waits for the user
- * 7b; the user reboots (pulling the battery, etc) into the main system
- * 8. main() calls maybe_install_firmware_update()
- * ** if the update contained radio/hboot firmware **:
- * 8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
- * -- after this, rebooting will reformat cache & restart main system --
- * 8b. m_i_f_u() writes firmware image into raw cache partition
- * 8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
- * -- after this, rebooting will attempt to reinstall firmware --
- * 8d. bootloader tries to flash firmware
- * 8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
- * -- after this, rebooting will reformat cache & restart main system --
- * 8f. erase_volume() reformats /cache
- * 8g. finish_recovery() erases BCB
- * -- after this, rebooting will (try to) restart the main system --
- * 9. main() calls reboot() to boot main system
+ * 7b. the user reboots (pulling the battery, etc) into the main system
*/
-static const int MAX_ARG_LENGTH = 4096;
-static const int MAX_ARGS = 100;
-
// open a given path, mounting partitions as necessary
FILE* fopen_path(const char *path, const char *mode) {
if (ensure_path_mounted(path) != 0) {
- LOGE("Can't mount %s\n", path);
+ LOG(ERROR) << "Can't mount " << path;
return NULL;
}
@@ -208,19 +197,31 @@ FILE* fopen_path(const char *path, const char *mode) {
// close a file, log an error if the error indicator is set
static void check_and_fclose(FILE *fp, const char *name) {
fflush(fp);
- if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno));
+ if (fsync(fileno(fp)) == -1) {
+ PLOG(ERROR) << "Failed to fsync " << name;
+ }
+ if (ferror(fp)) {
+ PLOG(ERROR) << "Error in " << name;
+ }
fclose(fp);
}
bool is_ro_debuggable() {
- char value[PROPERTY_VALUE_MAX+1];
- return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1');
+ return android::base::GetBoolProperty("ro.debuggable", false);
+}
+
+bool reboot(const std::string& command) {
+ std::string cmd = command;
+ if (android::base::GetBoolProperty("ro.boot.quiescent", false)) {
+ cmd += ",quiescent";
+ }
+ return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd);
}
static void redirect_stdio(const char* filename) {
int pipefd[2];
if (pipe(pipefd) == -1) {
- LOGE("pipe failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "pipe failed";
// Fall back to traditional logging mode without timestamps.
// If these fail, there's not really anywhere to complain...
@@ -232,7 +233,7 @@ static void redirect_stdio(const char* filename) {
pid_t pid = fork();
if (pid == -1) {
- LOGE("fork failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "fork failed";
// Fall back to traditional logging mode without timestamps.
// If these fail, there's not really anywhere to complain...
@@ -251,17 +252,17 @@ static void redirect_stdio(const char* filename) {
// Child logger to actually write to the log file.
FILE* log_fp = fopen(filename, "a");
if (log_fp == nullptr) {
- LOGE("fopen \"%s\" failed: %s\n", filename, strerror(errno));
+ PLOG(ERROR) << "fopen \"" << filename << "\" failed";
close(pipefd[0]);
- _exit(1);
+ _exit(EXIT_FAILURE);
}
FILE* pipe_fp = fdopen(pipefd[0], "r");
if (pipe_fp == nullptr) {
- LOGE("fdopen failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "fdopen failed";
check_and_fclose(log_fp, filename);
close(pipefd[0]);
- _exit(1);
+ _exit(EXIT_FAILURE);
}
char* line = nullptr;
@@ -278,12 +279,12 @@ static void redirect_stdio(const char* filename) {
fflush(log_fp);
}
- LOGE("getline failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "getline failed";
free(line);
check_and_fclose(log_fp, filename);
close(pipefd[0]);
- _exit(1);
+ _exit(EXIT_FAILURE);
} else {
// Redirect stdout/stderr to the logger process.
// Close the unused read end.
@@ -293,10 +294,10 @@ static void redirect_stdio(const char* filename) {
setbuf(stderr, nullptr);
if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
- LOGE("dup2 stdout failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "dup2 stdout failed";
}
if (dup2(pipefd[1], STDERR_FILENO) == -1) {
- LOGE("dup2 stderr failed: %s\n", strerror(errno));
+ PLOG(ERROR) << "dup2 stderr failed";
}
close(pipefd[1]);
@@ -307,104 +308,95 @@ static void redirect_stdio(const char* filename) {
// - the actual command line
// - the bootloader control block (one per line, after "recovery")
// - the contents of COMMAND_FILE (one per line)
-static void
-get_args(int *argc, char ***argv) {
- bootloader_message boot = {};
- std::string err;
- if (!read_bootloader_message(&boot, &err)) {
- LOGE("%s\n", err.c_str());
- // If fails, leave a zeroed bootloader_message.
- memset(&boot, 0, sizeof(boot));
- }
- stage = strndup(boot.stage, sizeof(boot.stage));
-
- if (boot.command[0] != 0 && boot.command[0] != 255) {
- LOGI("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
- }
-
- if (boot.status[0] != 0 && boot.status[0] != 255) {
- LOGI("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
- }
-
- // --- if arguments weren't supplied, look in the bootloader control block
- if (*argc <= 1) {
- boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
- const char *arg = strtok(boot.recovery, "\n");
- if (arg != NULL && !strcmp(arg, "recovery")) {
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = strdup(arg);
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if ((arg = strtok(NULL, "\n")) == NULL) break;
- (*argv)[*argc] = strdup(arg);
- }
- LOGI("Got arguments from boot message\n");
- } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
- LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
- }
- }
-
- // --- if that doesn't work, try the command file (if we have /cache).
- if (*argc <= 1 && has_cache) {
- FILE *fp = fopen_path(COMMAND_FILE, "r");
- if (fp != NULL) {
- char *token;
- char *argv0 = (*argv)[0];
- *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
- (*argv)[0] = argv0; // use the same program name
-
- char buf[MAX_ARG_LENGTH];
- for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
- if (!fgets(buf, sizeof(buf), fp)) break;
- token = strtok(buf, "\r\n");
- if (token != NULL) {
- (*argv)[*argc] = strdup(token); // Strip newline.
- } else {
- --*argc;
- }
- }
-
- check_and_fclose(fp, COMMAND_FILE);
- LOGI("Got arguments from %s\n", COMMAND_FILE);
- }
- }
-
- // --> write the arguments we have back into the bootloader control block
- // always boot into recovery after this (until finish_recovery() is called)
- strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
- strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
- int i;
- for (i = 1; i < *argc; ++i) {
- strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
- strlcat(boot.recovery, "\n", sizeof(boot.recovery));
- }
- if (!write_bootloader_message(boot, &err)) {
- LOGE("%s\n", err.c_str());
- }
+static std::vector<std::string> get_args(const int argc, char** const argv) {
+ CHECK_GT(argc, 0);
+
+ bootloader_message boot = {};
+ std::string err;
+ if (!read_bootloader_message(&boot, &err)) {
+ LOG(ERROR) << err;
+ // If fails, leave a zeroed bootloader_message.
+ boot = {};
+ }
+ stage = std::string(boot.stage);
+
+ if (boot.command[0] != 0) {
+ std::string boot_command = std::string(boot.command, sizeof(boot.command));
+ LOG(INFO) << "Boot command: " << boot_command;
+ }
+
+ if (boot.status[0] != 0) {
+ std::string boot_status = std::string(boot.status, sizeof(boot.status));
+ LOG(INFO) << "Boot status: " << boot_status;
+ }
+
+ std::vector<std::string> args(argv, argv + argc);
+
+ // --- if arguments weren't supplied, look in the bootloader control block
+ if (args.size() == 1) {
+ boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
+ std::string boot_recovery(boot.recovery);
+ std::vector<std::string> tokens = android::base::Split(boot_recovery, "\n");
+ if (!tokens.empty() && tokens[0] == "recovery") {
+ for (auto it = tokens.begin() + 1; it != tokens.end(); it++) {
+ // Skip empty and '\0'-filled tokens.
+ if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
+ }
+ LOG(INFO) << "Got " << args.size() << " arguments from boot message";
+ } else if (boot.recovery[0] != 0) {
+ LOG(ERROR) << "Bad boot message: \"" << boot_recovery << "\"";
+ }
+ }
+
+ // --- if that doesn't work, try the command file (if we have /cache).
+ if (args.size() == 1 && has_cache) {
+ std::string content;
+ if (ensure_path_mounted(COMMAND_FILE) == 0 &&
+ android::base::ReadFileToString(COMMAND_FILE, &content)) {
+ std::vector<std::string> tokens = android::base::Split(content, "\n");
+ // All the arguments in COMMAND_FILE are needed (unlike the BCB message,
+ // COMMAND_FILE doesn't use filename as the first argument).
+ for (auto it = tokens.begin(); it != tokens.end(); it++) {
+ // Skip empty and '\0'-filled tokens.
+ if (!it->empty() && (*it)[0] != '\0') args.push_back(std::move(*it));
+ }
+ LOG(INFO) << "Got " << args.size() << " arguments from " << COMMAND_FILE;
+ }
+ }
+
+ // Write the arguments (excluding the filename in args[0]) back into the
+ // bootloader control block. So the device will always boot into recovery to
+ // finish the pending work, until finish_recovery() is called.
+ std::vector<std::string> options(args.cbegin() + 1, args.cend());
+ if (!update_bootloader_message(options, &err)) {
+ LOG(ERROR) << "Failed to set BCB message: " << err;
+ }
+
+ return args;
}
-static void
-set_sdcard_update_bootloader_message() {
- bootloader_message boot = {};
- strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
- strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
- std::string err;
- if (!write_bootloader_message(boot, &err)) {
- LOGE("%s\n", err.c_str());
- }
+// Set the BCB to reboot back into recovery (it won't resume the install from
+// sdcard though).
+static void set_sdcard_update_bootloader_message() {
+ std::vector<std::string> options;
+ std::string err;
+ if (!update_bootloader_message(options, &err)) {
+ LOG(ERROR) << "Failed to set BCB message: " << err;
+ }
}
// Read from kernel log into buffer and write out to file.
static void save_kernel_log(const char* destination) {
int klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0);
if (klog_buf_len <= 0) {
- LOGE("Error getting klog size: %s\n", strerror(errno));
+ PLOG(ERROR) << "Error getting klog size";
return;
}
std::string buffer(klog_buf_len, 0);
int n = klogctl(KLOG_READ_ALL, &buffer[0], klog_buf_len);
if (n == -1) {
- LOGE("Error in reading klog: %s\n", strerror(errno));
+ PLOG(ERROR) << "Error in reading klog";
return;
}
buffer.resize(n);
@@ -424,17 +416,17 @@ static void copy_log_file_to_pmsg(const char* source, const char* destination) {
}
// How much of the temp log we have copied to the copy in cache.
-static long tmplog_offset = 0;
+static off_t tmplog_offset = 0;
static void copy_log_file(const char* source, const char* destination, bool append) {
FILE* dest_fp = fopen_path(destination, append ? "a" : "w");
if (dest_fp == nullptr) {
- LOGE("Can't open %s\n", destination);
+ PLOG(ERROR) << "Can't open " << destination;
} else {
FILE* source_fp = fopen(source, "r");
if (source_fp != nullptr) {
if (append) {
- fseek(source_fp, tmplog_offset, SEEK_SET); // Since last write
+ fseeko(source_fp, tmplog_offset, SEEK_SET); // Since last write
}
char buf[4096];
size_t bytes;
@@ -442,7 +434,7 @@ static void copy_log_file(const char* source, const char* destination, bool appe
fwrite(buf, 1, bytes, dest_fp);
}
if (append) {
- tmplog_offset = ftell(source_fp);
+ tmplog_offset = ftello(source_fp);
}
check_and_fclose(source_fp, source);
}
@@ -450,37 +442,6 @@ static void copy_log_file(const char* source, const char* destination, bool appe
}
}
-// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max.
-// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max.
-// Overwrite any existing last_log.$max and last_kmsg.$max.
-static void rotate_logs(int max) {
- // Logs should only be rotated once.
- static bool rotated = false;
- if (rotated) {
- return;
- }
- rotated = true;
- ensure_path_mounted(LAST_LOG_FILE);
- ensure_path_mounted(LAST_KMSG_FILE);
-
- for (int i = max-1; i >= 0; --i) {
- std::string old_log = android::base::StringPrintf("%s", LAST_LOG_FILE);
- if (i > 0) {
- old_log += "." + std::to_string(i);
- }
- std::string new_log = android::base::StringPrintf("%s.%d", LAST_LOG_FILE, i+1);
- // Ignore errors if old_log doesn't exist.
- rename(old_log.c_str(), new_log.c_str());
-
- std::string old_kmsg = android::base::StringPrintf("%s", LAST_KMSG_FILE);
- if (i > 0) {
- old_kmsg += "." + std::to_string(i);
- }
- std::string new_kmsg = android::base::StringPrintf("%s.%d", LAST_KMSG_FILE, i+1);
- rename(old_kmsg.c_str(), new_kmsg.c_str());
- }
-}
-
static void copy_logs() {
// We only rotate and record the log of the current session if there are
// actual attempts to modify the flash, such as wipes, installs from BCB
@@ -499,7 +460,9 @@ static void copy_logs() {
return;
}
- rotate_logs(KEEP_LOG_COUNT);
+ ensure_path_mounted(LAST_LOG_FILE);
+ ensure_path_mounted(LAST_KMSG_FILE);
+ rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE);
// Copy logs to cache so the system can find out what happened.
copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true);
@@ -507,60 +470,43 @@ static void copy_logs() {
copy_log_file(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE, false);
save_kernel_log(LAST_KMSG_FILE);
chmod(LOG_FILE, 0600);
- chown(LOG_FILE, 1000, 1000); // system user
+ chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM);
chmod(LAST_KMSG_FILE, 0600);
- chown(LAST_KMSG_FILE, 1000, 1000); // system user
+ chown(LAST_KMSG_FILE, AID_SYSTEM, AID_SYSTEM);
chmod(LAST_LOG_FILE, 0640);
chmod(LAST_INSTALL_FILE, 0644);
sync();
}
// clear the recovery command and prepare to boot a (hopefully working) system,
-// copy our log file to cache as well (for the system to read), and
-// record any intent we were asked to communicate back to the system.
-// this function is idempotent: call it as many times as you like.
-static void
-finish_recovery(const char *send_intent) {
- // By this point, we're ready to return to the main system...
- if (send_intent != NULL && has_cache) {
- FILE *fp = fopen_path(INTENT_FILE, "w");
- if (fp == NULL) {
- LOGE("Can't open %s\n", INTENT_FILE);
- } else {
- fputs(send_intent, fp);
- check_and_fclose(fp, INTENT_FILE);
- }
- }
-
+// copy our log file to cache as well (for the system to read). This function is
+// idempotent: call it as many times as you like.
+static void finish_recovery() {
// Save the locale to cache, so if recovery is next started up
// without a --locale argument (eg, directly from the bootloader)
// it will use the last-known locale.
- if (locale != NULL) {
- size_t len = strlen(locale);
- __pmsg_write(LOCALE_FILE, locale, len);
- if (has_cache) {
- LOGI("Saving locale \"%s\"\n", locale);
- FILE* fp = fopen_path(LOCALE_FILE, "w");
- fwrite(locale, 1, len, fp);
- fflush(fp);
- fsync(fileno(fp));
- check_and_fclose(fp, LOCALE_FILE);
+ if (!locale.empty() && has_cache) {
+ LOG(INFO) << "Saving locale \"" << locale << "\"";
+
+ FILE* fp = fopen_path(LOCALE_FILE, "w");
+ if (!android::base::WriteStringToFd(locale, fileno(fp))) {
+ PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE;
}
+ check_and_fclose(fp, LOCALE_FILE);
}
copy_logs();
// Reset to normal system boot so recovery won't cycle indefinitely.
- bootloader_message boot = {};
std::string err;
- if (!write_bootloader_message(boot, &err)) {
- LOGE("%s\n", err.c_str());
+ if (!clear_bootloader_message(&err)) {
+ LOG(ERROR) << "Failed to clear BCB message: " << err;
}
// Remove the command file, so recovery won't repeat indefinitely.
if (has_cache) {
if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
- LOGW("Can't unlink %s\n", COMMAND_FILE);
+ LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
}
ensure_path_unmounted(CACHE_ROOT);
}
@@ -568,287 +514,239 @@ finish_recovery(const char *send_intent) {
sync(); // For good measure.
}
-typedef struct _saved_log_file {
- char* name;
- struct stat st;
- unsigned char* data;
- struct _saved_log_file* next;
-} saved_log_file;
+struct saved_log_file {
+ std::string name;
+ struct stat sb;
+ std::string data;
+};
static bool erase_volume(const char* volume) {
- bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
- bool is_data = (strcmp(volume, DATA_ROOT) == 0);
+ bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
+ bool is_data = (strcmp(volume, DATA_ROOT) == 0);
- ui->SetBackground(RecoveryUI::ERASING);
- ui->SetProgressType(RecoveryUI::INDETERMINATE);
+ ui->SetBackground(RecoveryUI::ERASING);
+ ui->SetProgressType(RecoveryUI::INDETERMINATE);
- saved_log_file* head = NULL;
-
- if (is_cache) {
- // If we're reformatting /cache, we load any past logs
- // (i.e. "/cache/recovery/last_*") and the current log
- // ("/cache/recovery/log") into memory, so we can restore them after
- // the reformat.
-
- ensure_path_mounted(volume);
-
- DIR* d;
- struct dirent* de;
- d = opendir(CACHE_LOG_DIR);
- if (d) {
- char path[PATH_MAX];
- strcpy(path, CACHE_LOG_DIR);
- strcat(path, "/");
- int path_len = strlen(path);
- while ((de = readdir(d)) != NULL) {
- if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
- saved_log_file* p = (saved_log_file*) malloc(sizeof(saved_log_file));
- strcpy(path+path_len, de->d_name);
- p->name = strdup(path);
- if (stat(path, &(p->st)) == 0) {
- // truncate files to 512kb
- if (p->st.st_size > (1 << 19)) {
- p->st.st_size = 1 << 19;
- }
- p->data = (unsigned char*) malloc(p->st.st_size);
- FILE* f = fopen(path, "rb");
- fread(p->data, 1, p->st.st_size, f);
- fclose(f);
- p->next = head;
- head = p;
- } else {
- free(p);
- }
- }
- }
- closedir(d);
- } else {
- if (errno != ENOENT) {
- printf("opendir failed: %s\n", strerror(errno));
- }
- }
- }
+ std::vector<saved_log_file> log_files;
+
+ if (is_cache) {
+ // If we're reformatting /cache, we load any past logs
+ // (i.e. "/cache/recovery/last_*") and the current log
+ // ("/cache/recovery/log") into memory, so we can restore them after
+ // the reformat.
- ui->Print("Formatting %s...\n", volume);
+ ensure_path_mounted(volume);
- ensure_path_unmounted(volume);
+ struct dirent* de;
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
+ if (d) {
+ while ((de = readdir(d.get())) != nullptr) {
+ if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
+ std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
+
+ struct stat sb;
+ if (stat(path.c_str(), &sb) == 0) {
+ // truncate files to 512kb
+ if (sb.st_size > (1 << 19)) {
+ sb.st_size = 1 << 19;
+ }
- int result;
+ std::string data(sb.st_size, '\0');
+ FILE* f = fopen(path.c_str(), "rb");
+ fread(&data[0], 1, data.size(), f);
+ fclose(f);
- if (is_data && reason && strcmp(reason, "convert_fbe") == 0) {
- // Create convert_fbe breadcrumb file to signal to init
- // to convert to file based encryption, not full disk encryption
- if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
- ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno));
- return true;
+ log_files.emplace_back(saved_log_file{ path, sb, data });
+ }
}
- FILE* f = fopen(CONVERT_FBE_FILE, "wb");
- if (!f) {
- ui->Print("Failed to convert to file encryption %s\n", strerror(errno));
- return true;
+ }
+ } else {
+ if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
+ }
+ }
+ }
+
+ ui->Print("Formatting %s...\n", volume);
+
+ ensure_path_unmounted(volume);
+
+ int result;
+
+ if (is_data && reason && strcmp(reason, "convert_fbe") == 0) {
+ // Create convert_fbe breadcrumb file to signal to init
+ // to convert to file based encryption, not full disk encryption
+ if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
+ ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno));
+ return true;
+ }
+ FILE* f = fopen(CONVERT_FBE_FILE, "wb");
+ if (!f) {
+ ui->Print("Failed to convert to file encryption %s\n", strerror(errno));
+ return true;
+ }
+ fclose(f);
+ result = format_volume(volume, CONVERT_FBE_DIR);
+ remove(CONVERT_FBE_FILE);
+ rmdir(CONVERT_FBE_DIR);
+ } else {
+ result = format_volume(volume);
+ }
+
+ if (is_cache) {
+ // Re-create the log dir and write back the log entries.
+ if (ensure_path_mounted(CACHE_LOG_DIR) == 0 &&
+ dirCreateHierarchy(CACHE_LOG_DIR, 0777, nullptr, false, sehandle) == 0) {
+ for (const auto& log : log_files) {
+ if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
+ log.sb.st_gid)) {
+ PLOG(ERROR) << "Failed to write to " << log.name;
}
- fclose(f);
- result = format_volume(volume, CONVERT_FBE_DIR);
- remove(CONVERT_FBE_FILE);
- rmdir(CONVERT_FBE_DIR);
+ }
} else {
- result = format_volume(volume);
+ PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR;
}
- if (is_cache) {
- while (head) {
- FILE* f = fopen_path(head->name, "wb");
- if (f) {
- fwrite(head->data, 1, head->st.st_size, f);
- fclose(f);
- chmod(head->name, head->st.st_mode);
- chown(head->name, head->st.st_uid, head->st.st_gid);
- }
- free(head->name);
- free(head->data);
- saved_log_file* temp = head->next;
- free(head);
- head = temp;
- }
-
- // Any part of the log we'd copied to cache is now gone.
- // Reset the pointer so we copy from the beginning of the temp
- // log.
- tmplog_offset = 0;
- copy_logs();
- }
+ // Any part of the log we'd copied to cache is now gone.
+ // Reset the pointer so we copy from the beginning of the temp
+ // log.
+ tmplog_offset = 0;
+ copy_logs();
+ }
- return (result == 0);
+ return (result == 0);
}
-static int
-get_menu_selection(const char* const * headers, const char* const * items,
- int menu_only, int initial_selection, Device* device) {
- // throw away keys pressed previously, so user doesn't
- // accidentally trigger menu items.
- ui->FlushKeys();
-
- ui->StartMenu(headers, items, initial_selection);
- int selected = initial_selection;
- int chosen_item = -1;
-
- while (chosen_item < 0) {
- int key = ui->WaitKey();
- int visible = ui->IsTextVisible();
-
- if (key == -1) { // ui_wait_key() timed out
- if (ui->WasTextEverVisible()) {
- continue;
- } else {
- LOGI("timed out waiting for key input; rebooting.\n");
- ui->EndMenu();
- return 0; // XXX fixme
- }
- }
-
- int action = device->HandleMenuKey(key, visible);
-
- if (action < 0) {
- switch (action) {
- case Device::kHighlightUp:
- selected = ui->SelectMenu(--selected);
- break;
- case Device::kHighlightDown:
- selected = ui->SelectMenu(++selected);
- break;
- case Device::kInvokeItem:
- chosen_item = selected;
- break;
- case Device::kNoAction:
- break;
- }
- } else if (!menu_only) {
- chosen_item = action;
- }
- }
-
- ui->EndMenu();
- return chosen_item;
+// Display a menu with the specified 'headers' and 'items'. Device specific HandleMenuKey() may
+// return a positive number beyond the given range. Caller sets 'menu_only' to true to ensure only
+// a menu item gets selected. 'initial_selection' controls the initial cursor location. Returns the
+// (non-negative) chosen item number, or -1 if timed out waiting for input.
+static int get_menu_selection(const char* const* headers, const char* const* items, bool menu_only,
+ int initial_selection, Device* device) {
+ // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
+ ui->FlushKeys();
+
+ ui->StartMenu(headers, items, initial_selection);
+
+ int selected = initial_selection;
+ int chosen_item = -1;
+ while (chosen_item < 0) {
+ int key = ui->WaitKey();
+ if (key == -1) { // WaitKey() timed out.
+ if (ui->WasTextEverVisible()) {
+ continue;
+ } else {
+ LOG(INFO) << "Timed out waiting for key input; rebooting.";
+ ui->EndMenu();
+ return -1;
+ }
+ }
+
+ bool visible = ui->IsTextVisible();
+ int action = device->HandleMenuKey(key, visible);
+
+ if (action < 0) {
+ switch (action) {
+ case Device::kHighlightUp:
+ selected = ui->SelectMenu(--selected);
+ break;
+ case Device::kHighlightDown:
+ selected = ui->SelectMenu(++selected);
+ break;
+ case Device::kInvokeItem:
+ chosen_item = selected;
+ break;
+ case Device::kNoAction:
+ break;
+ }
+ } else if (!menu_only) {
+ chosen_item = action;
+ }
+ }
+
+ ui->EndMenu();
+ return chosen_item;
}
-static int compare_string(const void* a, const void* b) {
- return strcmp(*(const char**)a, *(const char**)b);
-}
+// Returns the selected filename, or an empty string.
+static std::string browse_directory(const std::string& path, Device* device) {
+ ensure_path_mounted(path.c_str());
-// Returns a malloc'd path, or NULL.
-static char* browse_directory(const char* path, Device* device) {
- ensure_path_mounted(path);
+ std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
+ if (!d) {
+ PLOG(ERROR) << "error opening " << path;
+ return "";
+ }
- DIR* d = opendir(path);
- if (d == NULL) {
- LOGE("error opening %s: %s\n", path, strerror(errno));
- return NULL;
- }
+ std::vector<std::string> dirs;
+ std::vector<std::string> zips = { "../" }; // "../" is always the first entry.
- int d_size = 0;
- int d_alloc = 10;
- char** dirs = (char**)malloc(d_alloc * sizeof(char*));
- int z_size = 1;
- int z_alloc = 10;
- char** zips = (char**)malloc(z_alloc * sizeof(char*));
- zips[0] = strdup("../");
+ dirent* de;
+ while ((de = readdir(d.get())) != nullptr) {
+ std::string name(de->d_name);
- struct dirent* de;
- while ((de = readdir(d)) != NULL) {
- int name_len = strlen(de->d_name);
-
- if (de->d_type == DT_DIR) {
- // skip "." and ".." entries
- if (name_len == 1 && de->d_name[0] == '.') continue;
- if (name_len == 2 && de->d_name[0] == '.' &&
- de->d_name[1] == '.') continue;
-
- if (d_size >= d_alloc) {
- d_alloc *= 2;
- dirs = (char**)realloc(dirs, d_alloc * sizeof(char*));
- }
- dirs[d_size] = (char*)malloc(name_len + 2);
- strcpy(dirs[d_size], de->d_name);
- dirs[d_size][name_len] = '/';
- dirs[d_size][name_len+1] = '\0';
- ++d_size;
- } else if (de->d_type == DT_REG &&
- name_len >= 4 &&
- strncasecmp(de->d_name + (name_len-4), ".zip", 4) == 0) {
- if (z_size >= z_alloc) {
- z_alloc *= 2;
- zips = (char**)realloc(zips, z_alloc * sizeof(char*));
- }
- zips[z_size++] = strdup(de->d_name);
- }
+ if (de->d_type == DT_DIR) {
+ // Skip "." and ".." entries.
+ if (name == "." || name == "..") continue;
+ dirs.push_back(name + "/");
+ } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
+ zips.push_back(name);
}
- closedir(d);
+ }
- qsort(dirs, d_size, sizeof(char*), compare_string);
- qsort(zips, z_size, sizeof(char*), compare_string);
+ std::sort(dirs.begin(), dirs.end());
+ std::sort(zips.begin(), zips.end());
- // append dirs to the zips list
- if (d_size + z_size + 1 > z_alloc) {
- z_alloc = d_size + z_size + 1;
- zips = (char**)realloc(zips, z_alloc * sizeof(char*));
- }
- memcpy(zips + z_size, dirs, d_size * sizeof(char*));
- free(dirs);
- z_size += d_size;
- zips[z_size] = NULL;
+ // Append dirs to the zips list.
+ zips.insert(zips.end(), dirs.begin(), dirs.end());
- const char* headers[] = { "Choose a package to install:", path, NULL };
+ const char* entries[zips.size() + 1];
+ entries[zips.size()] = nullptr;
+ for (size_t i = 0; i < zips.size(); i++) {
+ entries[i] = zips[i].c_str();
+ }
- char* result;
- int chosen_item = 0;
- while (true) {
- chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device);
+ const char* headers[] = { "Choose a package to install:", path.c_str(), nullptr };
- char* item = zips[chosen_item];
- int item_len = strlen(item);
- if (chosen_item == 0) { // item 0 is always "../"
- // go up but continue browsing (if the caller is update_directory)
- result = NULL;
- break;
- }
+ int chosen_item = 0;
+ while (true) {
+ chosen_item = get_menu_selection(headers, entries, true, chosen_item, device);
- char new_path[PATH_MAX];
- strlcpy(new_path, path, PATH_MAX);
- strlcat(new_path, "/", PATH_MAX);
- strlcat(new_path, item, PATH_MAX);
-
- if (item[item_len-1] == '/') {
- // recurse down into a subdirectory
- new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/'
- result = browse_directory(new_path, device);
- if (result) break;
- } else {
- // selected a zip file: return the malloc'd path to the caller.
- result = strdup(new_path);
- break;
- }
+ const std::string& item = zips[chosen_item];
+ if (chosen_item == 0) {
+ // Go up but continue browsing (if the caller is browse_directory).
+ return "";
}
- for (int i = 0; i < z_size; ++i) free(zips[i]);
- free(zips);
+ std::string new_path = path + "/" + item;
+ if (new_path.back() == '/') {
+ // Recurse down into a subdirectory.
+ new_path.pop_back();
+ std::string result = browse_directory(new_path, device);
+ if (!result.empty()) return result;
+ } else {
+ // Selected a zip file: return the path to the caller.
+ return new_path;
+ }
+ }
- return result;
+ // Unreachable.
}
static bool yes_no(Device* device, const char* question1, const char* question2) {
const char* headers[] = { question1, question2, NULL };
const char* items[] = { " No", " Yes", NULL };
- int chosen_item = get_menu_selection(headers, items, 1, 0, device);
+ int chosen_item = get_menu_selection(headers, items, true, 0, device);
return (chosen_item == 1);
}
-// Return true on success.
-static bool wipe_data(int should_confirm, Device* device) {
- if (should_confirm && !yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!")) {
- return false;
- }
+static bool ask_to_wipe_data(Device* device) {
+ return yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!");
+}
+// Return true on success.
+static bool wipe_data(Device* device) {
modified_flash = true;
ui->Print("\n-- Wiping data...\n");
@@ -861,6 +759,30 @@ static bool wipe_data(int should_confirm, Device* device) {
return success;
}
+static bool prompt_and_wipe_data(Device* device) {
+ const char* const headers[] = {
+ "Can't load Android system. Your data may be corrupt.",
+ "If you continue to get this message, you may need to",
+ "perform a factory data reset and erase all user data",
+ "stored on this device.",
+ NULL
+ };
+ const char* const items[] = {
+ "Try again",
+ "Factory data reset",
+ NULL
+ };
+ for (;;) {
+ int chosen_item = get_menu_selection(headers, items, true, 0, device);
+ if (chosen_item != 1) {
+ return true; // Just reboot, no wipe; not a failure, user asked for it
+ }
+ if (ask_to_wipe_data(device)) {
+ return wipe_data(device);
+ }
+ }
+}
+
// Return true on success.
static bool wipe_cache(bool should_confirm, Device* device) {
if (!has_cache) {
@@ -880,47 +802,45 @@ static bool wipe_cache(bool should_confirm, Device* device) {
return success;
}
-// Secure-wipe a given partition. It uses BLKSECDISCARD, if supported.
-// Otherwise, it goes with BLKDISCARD (if device supports BLKDISCARDZEROES) or
-// BLKZEROOUT.
+// Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with
+// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT.
static bool secure_wipe_partition(const std::string& partition) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY)));
- if (fd.get() == -1) {
- LOGE("failed to open \"%s\": %s\n", partition.c_str(), strerror(errno));
- return false;
- }
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open \"" << partition << "\"";
+ return false;
+ }
- uint64_t range[2] = {0, 0};
- if (ioctl(fd.get(), BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) {
- LOGE("failed to get partition size: %s\n", strerror(errno));
+ uint64_t range[2] = { 0, 0 };
+ if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) {
+ PLOG(ERROR) << "Failed to get partition size";
+ return false;
+ }
+ LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1];
+
+ LOG(INFO) << " Trying BLKSECDISCARD...";
+ if (ioctl(fd, BLKSECDISCARD, &range) == -1) {
+ PLOG(WARNING) << " Failed";
+
+ // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT.
+ unsigned int zeroes;
+ if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) {
+ LOG(INFO) << " Trying BLKDISCARD...";
+ if (ioctl(fd, BLKDISCARD, &range) == -1) {
+ PLOG(ERROR) << " Failed";
return false;
+ }
+ } else {
+ LOG(INFO) << " Trying BLKZEROOUT...";
+ if (ioctl(fd, BLKZEROOUT, &range) == -1) {
+ PLOG(ERROR) << " Failed";
+ return false;
+ }
}
- printf("Secure-wiping \"%s\" from %" PRIu64 " to %" PRIu64 ".\n",
- partition.c_str(), range[0], range[1]);
-
- printf("Trying BLKSECDISCARD...\t");
- if (ioctl(fd.get(), BLKSECDISCARD, &range) == -1) {
- printf("failed: %s\n", strerror(errno));
-
- // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT.
- unsigned int zeroes;
- if (ioctl(fd.get(), BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) {
- printf("Trying BLKDISCARD...\t");
- if (ioctl(fd.get(), BLKDISCARD, &range) == -1) {
- printf("failed: %s\n", strerror(errno));
- return false;
- }
- } else {
- printf("Trying BLKZEROOUT...\t");
- if (ioctl(fd.get(), BLKZEROOUT, &range) == -1) {
- printf("failed: %s\n", strerror(errno));
- return false;
- }
- }
- }
+ }
- printf("done\n");
- return true;
+ LOG(INFO) << " Done";
+ return true;
}
// Check if the wipe package matches expectation:
@@ -928,35 +848,35 @@ static bool secure_wipe_partition(const std::string& partition) {
// 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) {
- LOGE("wipe_package_size is zero.\n");
+ 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)) {
- LOGE("Failed to read wipe package: %s\n", err_str.c_str());
+ PLOG(ERROR) << "Failed to read wipe package";
return false;
}
if (!verify_package(reinterpret_cast<const unsigned char*>(wipe_package.data()),
wipe_package.size())) {
- LOGE("Failed to verify package.\n");
+ 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);
+ ZipArchiveHandle zip;
+ int err = OpenArchiveFromMemory(static_cast<void*>(&wipe_package[0]), wipe_package.size(),
+ "wipe_package", &zip);
if (err != 0) {
- LOGE("Can't open wipe package: %s\n", err != -1 ? strerror(err) : "bad");
+ LOG(ERROR) << "Can't open wipe package : " << ErrorCodeString(err);
return false;
}
std::string metadata;
- if (!read_metadata_from_package(&zip, &metadata)) {
- mzCloseZipArchive(&zip);
+ if (!read_metadata_from_package(zip, &metadata)) {
+ CloseArchive(zip);
return false;
}
- mzCloseZipArchive(&zip);
+ CloseArchive(zip);
// Check metadata
std::vector<std::string> lines = android::base::Split(metadata, "\n");
@@ -969,13 +889,11 @@ static bool check_wipe_package(size_t wipe_package_size) {
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, "");
+ 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="));
- char real_serial_no[PROPERTY_VALUE_MAX];
- property_get("ro.serialno", real_serial_no, "");
+ std::string real_serial_no = android::base::GetProperty("ro.serialno", "");
has_serial_number = true;
serial_number_matched = (serial_no == real_serial_no);
}
@@ -990,12 +908,12 @@ static bool wipe_ab_device(size_t wipe_package_size) {
ui->SetProgressType(RecoveryUI::INDETERMINATE);
if (!check_wipe_package(wipe_package_size)) {
- LOGE("Failed to verify wipe package\n");
+ LOG(ERROR) << "Failed to verify wipe package";
return false;
}
std::string partition_list;
if (!android::base::ReadFileToString(RECOVERY_WIPE, &partition_list)) {
- LOGE("failed to read \"%s\".\n", RECOVERY_WIPE);
+ LOG(ERROR) << "failed to read \"" << RECOVERY_WIPE << "\"";
return false;
}
@@ -1014,98 +932,94 @@ static bool wipe_ab_device(size_t wipe_package_size) {
}
static void choose_recovery_file(Device* device) {
- // "Back" + KEEP_LOG_COUNT * 2 + terminating nullptr entry
- char* entries[1 + KEEP_LOG_COUNT * 2 + 1];
- memset(entries, 0, sizeof(entries));
-
- unsigned int n = 0;
-
- if (has_cache) {
- // Add LAST_LOG_FILE + LAST_LOG_FILE.x
- // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x
- for (int i = 0; i < KEEP_LOG_COUNT; i++) {
- char* log_file;
- int ret;
- ret = (i == 0) ? asprintf(&log_file, "%s", LAST_LOG_FILE) :
- asprintf(&log_file, "%s.%d", LAST_LOG_FILE, i);
- if (ret == -1) {
- // memory allocation failure - return early. Should never happen.
- return;
- }
- if ((ensure_path_mounted(log_file) != 0) || (access(log_file, R_OK) == -1)) {
- free(log_file);
- } else {
- entries[n++] = log_file;
- }
-
- char* kmsg_file;
- ret = (i == 0) ? asprintf(&kmsg_file, "%s", LAST_KMSG_FILE) :
- asprintf(&kmsg_file, "%s.%d", LAST_KMSG_FILE, i);
- if (ret == -1) {
- // memory allocation failure - return early. Should never happen.
- return;
- }
- if ((ensure_path_mounted(kmsg_file) != 0) || (access(kmsg_file, R_OK) == -1)) {
- free(kmsg_file);
- } else {
- entries[n++] = kmsg_file;
- }
- }
- } else {
- // If cache partition is not found, view /tmp/recovery.log instead.
- ui->Print("No /cache partition found.\n");
- if (access(TEMPORARY_LOG_FILE, R_OK) == -1) {
- return;
- } else{
- entries[n++] = strdup(TEMPORARY_LOG_FILE);
+ std::vector<std::string> entries;
+ if (has_cache) {
+ for (int i = 0; i < KEEP_LOG_COUNT; i++) {
+ auto add_to_entries = [&](const char* filename) {
+ std::string log_file(filename);
+ if (i > 0) {
+ log_file += "." + std::to_string(i);
}
- }
- entries[n++] = strdup("Back");
-
- const char* headers[] = { "Select file to view", nullptr };
+ if (ensure_path_mounted(log_file.c_str()) == 0 && access(log_file.c_str(), R_OK) == 0) {
+ entries.push_back(std::move(log_file));
+ }
+ };
- while (true) {
- int chosen_item = get_menu_selection(headers, entries, 1, 0, device);
- if (strcmp(entries[chosen_item], "Back") == 0) break;
+ // Add LAST_LOG_FILE + LAST_LOG_FILE.x
+ add_to_entries(LAST_LOG_FILE);
- ui->ShowFile(entries[chosen_item]);
+ // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x
+ add_to_entries(LAST_KMSG_FILE);
}
-
- for (size_t i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) {
- free(entries[i]);
+ } else {
+ // If cache partition is not found, view /tmp/recovery.log instead.
+ if (access(TEMPORARY_LOG_FILE, R_OK) == -1) {
+ return;
+ } else {
+ entries.push_back(TEMPORARY_LOG_FILE);
}
-}
+ }
-static void run_graphics_test(Device* device) {
- // Switch to graphics screen.
- ui->ShowText(false);
+ entries.push_back("Back");
- ui->SetProgressType(RecoveryUI::INDETERMINATE);
- ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
- sleep(1);
+ std::vector<const char*> menu_entries(entries.size());
+ std::transform(entries.cbegin(), entries.cend(), menu_entries.begin(),
+ [](const std::string& entry) { return entry.c_str(); });
+ menu_entries.push_back(nullptr);
- ui->SetBackground(RecoveryUI::ERROR);
- sleep(1);
+ const char* headers[] = { "Select file to view", nullptr };
- ui->SetBackground(RecoveryUI::NO_COMMAND);
- sleep(1);
+ int chosen_item = 0;
+ while (true) {
+ chosen_item = get_menu_selection(headers, menu_entries.data(), true, chosen_item, device);
+ if (entries[chosen_item] == "Back") break;
- ui->SetBackground(RecoveryUI::ERASING);
- sleep(1);
-
- ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
-
- ui->SetProgressType(RecoveryUI::DETERMINATE);
- ui->ShowProgress(1.0, 10.0);
- float fraction = 0.0;
- for (size_t i = 0; i < 100; ++i) {
- fraction += .01;
- ui->SetProgress(fraction);
- usleep(100000);
- }
+ ui->ShowFile(entries[chosen_item].c_str());
+ }
+}
- ui->ShowText(true);
+static void run_graphics_test() {
+ // Switch to graphics screen.
+ ui->ShowText(false);
+
+ ui->SetProgressType(RecoveryUI::INDETERMINATE);
+ ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
+ sleep(1);
+
+ ui->SetBackground(RecoveryUI::ERROR);
+ sleep(1);
+
+ ui->SetBackground(RecoveryUI::NO_COMMAND);
+ sleep(1);
+
+ ui->SetBackground(RecoveryUI::ERASING);
+ sleep(1);
+
+ // Calling SetBackground() after SetStage() to trigger a redraw.
+ ui->SetStage(1, 3);
+ ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
+ sleep(1);
+ ui->SetStage(2, 3);
+ ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
+ sleep(1);
+ ui->SetStage(3, 3);
+ ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
+ sleep(1);
+
+ ui->SetStage(-1, -1);
+ ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
+
+ ui->SetProgressType(RecoveryUI::DETERMINATE);
+ ui->ShowProgress(1.0, 10.0);
+ float fraction = 0.0;
+ for (size_t i = 0; i < 100; ++i) {
+ fraction += .01;
+ ui->SetProgress(fraction);
+ usleep(100000);
+ }
+
+ ui->ShowText(true);
}
// How long (in seconds) we wait for the fuse-provided package file to
@@ -1120,14 +1034,14 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {
return INSTALL_ERROR;
}
- char* path = browse_directory(SDCARD_ROOT, device);
- if (path == NULL) {
+ std::string path = browse_directory(SDCARD_ROOT, device);
+ if (path.empty()) {
ui->Print("\n-- No package file selected.\n");
ensure_path_unmounted(SDCARD_ROOT);
return INSTALL_ERROR;
}
- ui->Print("\n-- Install %s ...\n", path);
+ ui->Print("\n-- Install %s ...\n", path.c_str());
set_sdcard_update_bootloader_message();
// We used to use fuse in a thread as opposed to a process. Since accessing
@@ -1135,7 +1049,7 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {
// to deadlock when a page fault occurs. (Bug: 26313124)
pid_t child;
if ((child = fork()) == 0) {
- bool status = start_sdcard_fuse(path);
+ bool status = start_sdcard_fuse(path.c_str());
_exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
}
@@ -1158,7 +1072,7 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {
sleep(1);
continue;
} else {
- LOGE("Timed out waiting for the fuse-provided package.\n");
+ LOG(ERROR) << "Timed out waiting for the fuse-provided package.";
result = INSTALL_ERROR;
kill(child, SIGKILL);
break;
@@ -1180,118 +1094,116 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- LOGE("Error exit from the fuse process: %d\n", WEXITSTATUS(status));
+ LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status);
}
ensure_path_unmounted(SDCARD_ROOT);
return result;
}
-// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION
-// means to take the default, which is to reboot or shutdown depending
-// on if the --shutdown_after flag was passed to recovery.
-static Device::BuiltinAction
-prompt_and_wait(Device* device, int status) {
- for (;;) {
- finish_recovery(NULL);
- switch (status) {
- case INSTALL_SUCCESS:
- case INSTALL_NONE:
- ui->SetBackground(RecoveryUI::NO_COMMAND);
- break;
-
- case INSTALL_ERROR:
- case INSTALL_CORRUPT:
- ui->SetBackground(RecoveryUI::ERROR);
- break;
- }
- ui->SetProgressType(RecoveryUI::EMPTY);
-
- int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), 0, 0, device);
-
- // device-specific code may take some action here. It may
- // return one of the core actions handled in the switch
- // statement below.
- Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);
+// Returns REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default,
+// which is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery.
+static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
+ for (;;) {
+ finish_recovery();
+ switch (status) {
+ case INSTALL_SUCCESS:
+ case INSTALL_NONE:
+ ui->SetBackground(RecoveryUI::NO_COMMAND);
+ break;
- bool should_wipe_cache = false;
- switch (chosen_action) {
- case Device::NO_ACTION:
- break;
+ case INSTALL_ERROR:
+ case INSTALL_CORRUPT:
+ ui->SetBackground(RecoveryUI::ERROR);
+ break;
+ }
+ ui->SetProgressType(RecoveryUI::EMPTY);
- case Device::REBOOT:
- case Device::SHUTDOWN:
- case Device::REBOOT_BOOTLOADER:
- return chosen_action;
+ int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), false, 0, device);
- case Device::WIPE_DATA:
- wipe_data(ui->IsTextVisible(), device);
- if (!ui->IsTextVisible()) return Device::NO_ACTION;
- break;
+ // Device-specific code may take some action here. It may return one of the core actions
+ // handled in the switch statement below.
+ Device::BuiltinAction chosen_action =
+ (chosen_item == -1) ? Device::REBOOT : device->InvokeMenuItem(chosen_item);
- case Device::WIPE_CACHE:
- wipe_cache(ui->IsTextVisible(), device);
- if (!ui->IsTextVisible()) return Device::NO_ACTION;
- break;
+ bool should_wipe_cache = false;
+ switch (chosen_action) {
+ case Device::NO_ACTION:
+ break;
- case Device::APPLY_ADB_SIDELOAD:
- case Device::APPLY_SDCARD:
- {
- bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
- if (adb) {
- status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
- } else {
- status = apply_from_sdcard(device, &should_wipe_cache);
- }
+ case Device::REBOOT:
+ case Device::SHUTDOWN:
+ case Device::REBOOT_BOOTLOADER:
+ return chosen_action;
- if (status == INSTALL_SUCCESS && should_wipe_cache) {
- if (!wipe_cache(false, device)) {
- status = INSTALL_ERROR;
- }
- }
+ case Device::WIPE_DATA:
+ if (ui->IsTextVisible()) {
+ if (ask_to_wipe_data(device)) {
+ wipe_data(device);
+ }
+ } else {
+ wipe_data(device);
+ return Device::NO_ACTION;
+ }
+ break;
- if (status != INSTALL_SUCCESS) {
- ui->SetBackground(RecoveryUI::ERROR);
- ui->Print("Installation aborted.\n");
- copy_logs();
- } else if (!ui->IsTextVisible()) {
- return Device::NO_ACTION; // reboot if logs aren't visible
- } else {
- ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card");
- }
- }
- break;
+ case Device::WIPE_CACHE:
+ wipe_cache(ui->IsTextVisible(), device);
+ if (!ui->IsTextVisible()) return Device::NO_ACTION;
+ break;
- case Device::VIEW_RECOVERY_LOGS:
- choose_recovery_file(device);
- break;
+ case Device::APPLY_ADB_SIDELOAD:
+ case Device::APPLY_SDCARD:
+ {
+ bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
+ if (adb) {
+ status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE);
+ } else {
+ status = apply_from_sdcard(device, &should_wipe_cache);
+ }
+
+ if (status == INSTALL_SUCCESS && should_wipe_cache) {
+ if (!wipe_cache(false, device)) {
+ status = INSTALL_ERROR;
+ }
+ }
+
+ if (status != INSTALL_SUCCESS) {
+ ui->SetBackground(RecoveryUI::ERROR);
+ ui->Print("Installation aborted.\n");
+ copy_logs();
+ } else if (!ui->IsTextVisible()) {
+ return Device::NO_ACTION; // reboot if logs aren't visible
+ } else {
+ ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card");
+ }
+ }
+ break;
- case Device::RUN_GRAPHICS_TEST:
- run_graphics_test(device);
- break;
+ case Device::VIEW_RECOVERY_LOGS:
+ choose_recovery_file(device);
+ break;
- case Device::MOUNT_SYSTEM:
- char system_root_image[PROPERTY_VALUE_MAX];
- property_get("ro.build.system_root_image", system_root_image, "");
-
- // For a system image built with the root directory (i.e.
- // system_root_image == "true"), we mount it to /system_root, and symlink /system
- // to /system_root/system to make adb shell work (the symlink is created through
- // the build system).
- // Bug: 22855115
- if (strcmp(system_root_image, "true") == 0) {
- if (ensure_path_mounted_at("/", "/system_root") != -1) {
- ui->Print("Mounted /system.\n");
- }
- } else {
- if (ensure_path_mounted("/system") != -1) {
- ui->Print("Mounted /system.\n");
- }
- }
+ case Device::RUN_GRAPHICS_TEST:
+ run_graphics_test();
+ break;
- break;
+ case Device::MOUNT_SYSTEM:
+ // For a system image built with the root directory (i.e. system_root_image == "true"), we
+ // mount it to /system_root, and symlink /system to /system_root/system to make adb shell
+ // work (the symlink is created through the build system). (Bug: 22855115)
+ if (android::base::GetBoolProperty("ro.build.system_root_image", false)) {
+ if (ensure_path_mounted_at("/", "/system_root") != -1) {
+ ui->Print("Mounted /system.\n");
+ }
+ } else {
+ if (ensure_path_mounted("/system") != -1) {
+ ui->Print("Mounted /system.\n");
+ }
}
+ break;
}
+ }
}
static void
@@ -1299,40 +1211,44 @@ print_property(const char *key, const char *name, void *cookie) {
printf("%s=%s\n", key, name);
}
-static void
-load_locale_from_cache() {
- FILE* fp = fopen_path(LOCALE_FILE, "r");
- char buffer[80];
- if (fp != NULL) {
- fgets(buffer, sizeof(buffer), fp);
- int j = 0;
- unsigned int i;
- for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) {
- if (!isspace(buffer[i])) {
- buffer[j++] = buffer[i];
- }
- }
- buffer[j] = 0;
- locale = strdup(buffer);
- check_and_fclose(fp, LOCALE_FILE);
+static std::string load_locale_from_cache() {
+ if (ensure_path_mounted(LOCALE_FILE) != 0) {
+ LOG(ERROR) << "Can't mount " << LOCALE_FILE;
+ return "";
}
-}
-static RecoveryUI* gCurrentUI = NULL;
+ std::string content;
+ if (!android::base::ReadFileToString(LOCALE_FILE, &content)) {
+ PLOG(ERROR) << "Can't read " << LOCALE_FILE;
+ return "";
+ }
-void
-ui_print(const char* format, ...) {
- char buffer[256];
+ return android::base::Trim(content);
+}
+void ui_print(const char* format, ...) {
+ std::string buffer;
va_list ap;
va_start(ap, format);
- vsnprintf(buffer, sizeof(buffer), format, ap);
+ android::base::StringAppendV(&buffer, format, ap);
va_end(ap);
- if (gCurrentUI != NULL) {
- gCurrentUI->Print("%s", buffer);
+ if (ui != nullptr) {
+ ui->Print("%s", buffer.c_str());
+ } else {
+ fputs(buffer.c_str(), stdout);
+ }
+}
+
+static constexpr char log_characters[] = "VDIWEF";
+
+void UiLogger(android::base::LogId id, android::base::LogSeverity severity,
+ const char* tag, const char* file, unsigned int line,
+ const char* message) {
+ if (severity >= android::base::ERROR && ui != nullptr) {
+ ui->Print("E:%s\n", message);
} else {
- fputs(buffer, stdout);
+ fprintf(stdout, "%c:%s\n", log_characters[severity], message);
}
}
@@ -1390,42 +1306,32 @@ static bool is_battery_ok() {
}
}
-static void set_retry_bootloader_message(int retry_count, int argc, char** argv) {
- bootloader_message boot = {};
- strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
- strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
-
- for (int i = 1; i < argc; ++i) {
- if (strstr(argv[i], "retry_count") == nullptr) {
- strlcat(boot.recovery, argv[i], sizeof(boot.recovery));
- strlcat(boot.recovery, "\n", sizeof(boot.recovery));
- }
+static void set_retry_bootloader_message(int retry_count, const std::vector<std::string>& args) {
+ std::vector<std::string> options;
+ for (const auto& arg : args) {
+ if (!android::base::StartsWith(arg, "--retry_count")) {
+ options.push_back(arg);
}
+ }
- // Initialize counter to 1 if it's not in BCB, otherwise increment it by 1.
- if (retry_count == 0) {
- strlcat(boot.recovery, "--retry_count=1\n", sizeof(boot.recovery));
- } else {
- char buffer[20];
- snprintf(buffer, sizeof(buffer), "--retry_count=%d\n", retry_count+1);
- strlcat(boot.recovery, buffer, sizeof(boot.recovery));
- }
- std::string err;
- if (!write_bootloader_message(boot, &err)) {
- LOGE("%s\n", err.c_str());
- }
+ // Increment the retry counter by 1.
+ options.push_back(android::base::StringPrintf("--retry_count=%d", retry_count + 1));
+ std::string err;
+ if (!update_bootloader_message(options, &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;
+ std::string bootreason = android::base::GetProperty("ro.boot.bootreason", "");
+ if (!bootreason.empty()) {
+ for (const auto& str : bootreason_blacklist) {
+ if (strcasecmp(str.c_str(), bootreason.c_str()) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
}
static void log_failure_code(ErrorCode code, const char *update_package) {
@@ -1436,68 +1342,23 @@ static void log_failure_code(ErrorCode code, const char *update_package) {
};
std::string log_content = android::base::Join(log_buffer, "\n");
if (!android::base::WriteStringToFile(log_content, TEMPORARY_INSTALL_FILE)) {
- LOGE("failed to write %s: %s\n", TEMPORARY_INSTALL_FILE, strerror(errno));
+ PLOG(ERROR) << "failed to write " << TEMPORARY_INSTALL_FILE;
}
// Also write the info into last_log.
- LOGI("%s\n", log_content.c_str());
-}
-
-static ssize_t logbasename(
- log_id_t /* logId */,
- char /* prio */,
- const char *filename,
- const char * /* buf */, size_t len,
- void *arg) {
- if (strstr(LAST_KMSG_FILE, filename) ||
- strstr(LAST_LOG_FILE, filename)) {
- bool *doRotate = reinterpret_cast<bool *>(arg);
- *doRotate = true;
- }
- return len;
-}
-
-static ssize_t logrotate(
- log_id_t logId,
- char prio,
- const char *filename,
- const char *buf, size_t len,
- void *arg) {
- bool *doRotate = reinterpret_cast<bool *>(arg);
- if (!*doRotate) {
- return __android_log_pmsg_file_write(logId, prio, filename, buf, len);
- }
-
- std::string name(filename);
- size_t dot = name.find_last_of(".");
- std::string sub = name.substr(0, dot);
-
- if (!strstr(LAST_KMSG_FILE, sub.c_str()) &&
- !strstr(LAST_LOG_FILE, sub.c_str())) {
- return __android_log_pmsg_file_write(logId, prio, filename, buf, len);
- }
-
- // filename rotation
- if (dot == std::string::npos) {
- name += ".1";
- } else {
- std::string number = name.substr(dot + 1);
- if (!isdigit(number.data()[0])) {
- name += ".1";
- } else {
- unsigned long long i = std::stoull(number);
- name = sub + "." + std::to_string(i + 1);
- }
- }
-
- return __android_log_pmsg_file_write(logId, prio, name.c_str(), buf, len);
+ LOG(INFO) << log_content;
}
int main(int argc, char **argv) {
+ // We don't have logcat yet under recovery; so we'll print error on screen and
+ // log to stdout (which is redirected to recovery.log) as we used to do.
+ android::base::InitLogging(argv, &UiLogger);
+
// Take last pmsg contents and rewrite it to the current pmsg session.
static const char filter[] = "recovery/";
// Do we need to rotate?
bool doRotate = false;
+
__android_log_pmsg_file_read(
LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter,
logbasename, &doRotate);
@@ -1514,7 +1375,7 @@ int main(int argc, char **argv) {
// only way recovery should be run with this argument is when it
// starts a copy of itself from the apply_from_adb() function.
if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
- adb_server_main(0, DEFAULT_ADB_PORT, -1);
+ minadbd_main();
return 0;
}
@@ -1529,11 +1390,14 @@ int main(int argc, char **argv) {
load_volume_table();
has_cache = volume_for_path(CACHE_ROOT) != nullptr;
- get_args(&argc, &argv);
+ std::vector<std::string> args = get_args(argc, argv);
+ std::vector<char*> args_to_parse(args.size());
+ std::transform(args.cbegin(), args.cend(), args_to_parse.begin(),
+ [](const std::string& arg) { return const_cast<char*>(arg.c_str()); });
- const char *send_intent = NULL;
const char *update_package = NULL;
bool should_wipe_data = false;
+ bool should_prompt_and_wipe_data = false;
bool should_wipe_cache = false;
bool should_wipe_ab = false;
size_t wipe_package_size = 0;
@@ -1547,9 +1411,9 @@ int main(int argc, char **argv) {
int arg;
int option_index;
- while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
+ while ((arg = getopt_long(args_to_parse.size(), args_to_parse.data(), "", OPTIONS,
+ &option_index)) != -1) {
switch (arg) {
- case 'i': send_intent = optarg; break;
case 'n': android::base::ParseInt(optarg, &retry_count, 0); break;
case 'u': update_package = optarg; break;
case 'w': should_wipe_data = true; break;
@@ -1559,64 +1423,67 @@ int main(int argc, char **argv) {
case 'a': sideload = true; sideload_auto_reboot = true; break;
case 'x': just_exit = true; break;
case 'l': locale = optarg; break;
- case 'g': {
- if (stage == NULL || *stage == '\0') {
- char buffer[20] = "1/";
- strncat(buffer, optarg, sizeof(buffer)-3);
- stage = strdup(buffer);
- }
- break;
- }
case 'p': shutdown_after = true; break;
case 'r': reason = optarg; break;
case 'e': security_update = true; break;
case 0: {
- if (strcmp(OPTIONS[option_index].name, "wipe_ab") == 0) {
+ std::string option = OPTIONS[option_index].name;
+ if (option == "wipe_ab") {
should_wipe_ab = true;
- break;
- } else if (strcmp(OPTIONS[option_index].name, "wipe_package_size") == 0) {
+ } else if (option == "wipe_package_size") {
android::base::ParseUint(optarg, &wipe_package_size);
- break;
+ } else if (option == "prompt_and_wipe_data") {
+ should_prompt_and_wipe_data = true;
}
break;
}
case '?':
- LOGE("Invalid command argument\n");
+ LOG(ERROR) << "Invalid command argument";
continue;
}
}
- if (locale == nullptr && has_cache) {
- load_locale_from_cache();
+ if (locale.empty()) {
+ if (has_cache) {
+ locale = load_locale_from_cache();
+ }
+
+ if (locale.empty()) {
+ locale = DEFAULT_LOCALE;
+ }
}
- printf("locale is [%s]\n", locale);
- printf("stage is [%s]\n", stage);
+
+ printf("locale is [%s]\n", locale.c_str());
+ printf("stage is [%s]\n", stage.c_str());
printf("reason is [%s]\n", reason);
Device* device = make_device();
- ui = device->GetUI();
- gCurrentUI = ui;
+ if (android::base::GetBoolProperty("ro.boot.quiescent", false)) {
+ printf("Quiescent recovery mode.\n");
+ ui = new StubRecoveryUI();
+ } else {
+ ui = device->GetUI();
+
+ if (!ui->Init(locale)) {
+ printf("Failed to initialize UI, use stub UI instead.\n");
+ ui = new StubRecoveryUI();
+ }
+ }
- ui->SetLocale(locale);
- ui->Init();
// Set background string to "installing security update" for security update,
// otherwise set it to "installing system update".
ui->SetSystemUpdateText(security_update);
int st_cur, st_max;
- if (stage != NULL && sscanf(stage, "%d/%d", &st_cur, &st_max) == 2) {
+ if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) {
ui->SetStage(st_cur, st_max);
}
ui->SetBackground(RecoveryUI::NONE);
if (show_text) ui->ShowText(true);
- struct selinux_opt seopts[] = {
- { SELABEL_OPT_PATH, "/file_contexts" }
- };
-
- sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
-
+ sehandle = selinux_android_file_context_handle();
+ selinux_android_set_sehandle(sehandle);
if (!sehandle) {
ui->Print("Warning: No file_contexts\n");
}
@@ -1624,30 +1491,10 @@ int main(int argc, char **argv) {
device->StartRecovery();
printf("Command:");
- for (arg = 0; arg < argc; arg++) {
- printf(" \"%s\"", argv[arg]);
- }
- printf("\n");
-
- if (update_package) {
- // For backwards compatibility on the cache partition only, if
- // we're given an old 'root' path "CACHE:foo", change it to
- // "/cache/foo".
- if (strncmp(update_package, "CACHE:", 6) == 0) {
- int len = strlen(update_package) + 10;
- char* modified_path = (char*)malloc(len);
- if (modified_path) {
- strlcpy(modified_path, "/cache/", len);
- strlcat(modified_path, update_package+6, len);
- printf("(replacing path \"%s\" with \"%s\")\n",
- update_package, modified_path);
- update_package = modified_path;
- }
- else
- printf("modified_path allocation failed\n");
- }
+ for (const auto& arg : args) {
+ printf(" \"%s\"", arg.c_str());
}
- printf("\n");
+ printf("\n\n");
property_list(print_property, NULL);
printf("\n");
@@ -1685,13 +1532,12 @@ int main(int argc, char **argv) {
// times before we abandon this OTA update.
if (status == INSTALL_RETRY && retry_count < EIO_RETRY_COUNT) {
copy_logs();
- set_retry_bootloader_message(retry_count, argc, argv);
+ set_retry_bootloader_message(retry_count, args);
// Print retry count on screen.
ui->Print("Retry attempt %d\n", retry_count);
// Reboot and retry the update
- int ret = property_set(ANDROID_RB_PROPERTY, "reboot,recovery");
- if (ret < 0) {
+ if (!reboot("reboot,recovery")) {
ui->Print("Reboot failed\n");
} else {
while (true) {
@@ -1708,9 +1554,16 @@ int main(int argc, char **argv) {
}
}
} else if (should_wipe_data) {
- if (!wipe_data(false, device)) {
+ if (!wipe_data(device)) {
+ status = INSTALL_ERROR;
+ }
+ } else if (should_prompt_and_wipe_data) {
+ ui->ShowText(true);
+ ui->SetBackground(RecoveryUI::ERROR);
+ if (!prompt_and_wipe_data(device)) {
status = INSTALL_ERROR;
}
+ ui->ShowText(false);
} else if (should_wipe_cache) {
if (!wipe_cache(false, device)) {
status = INSTALL_ERROR;
@@ -1766,26 +1619,26 @@ int main(int argc, char **argv) {
}
// Save logs and clean up before rebooting or shutting down.
- finish_recovery(send_intent);
+ finish_recovery();
switch (after) {
case Device::SHUTDOWN:
ui->Print("Shutting down...\n");
- property_set(ANDROID_RB_PROPERTY, "shutdown,");
+ android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,");
break;
case Device::REBOOT_BOOTLOADER:
ui->Print("Rebooting to bootloader...\n");
- property_set(ANDROID_RB_PROPERTY, "reboot,bootloader");
+ android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
break;
default:
ui->Print("Rebooting...\n");
- property_set(ANDROID_RB_PROPERTY, "reboot,");
+ reboot("reboot,");
break;
}
while (true) {
- pause();
+ pause();
}
// Should be unreachable.
return EXIT_SUCCESS;