diff options
Diffstat (limited to 'recovery.cpp')
-rw-r--r-- | recovery.cpp | 710 |
1 files changed, 592 insertions, 118 deletions
diff --git a/recovery.cpp b/recovery.cpp index b7a5458..03425dc 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -31,6 +31,8 @@ #include <time.h> #include <unistd.h> +#include <chrono> + #include <base/file.h> #include <base/stringprintf.h> @@ -45,18 +47,60 @@ #include "ui.h" #include "screen_ui.h" #include "device.h" + +#include "voldclient.h" + #include "adb_install.h" #include "adb.h" #include "fuse_sideload.h" #include "fuse_sdcard_provider.h" +extern "C" { +#include "recovery_cmds.h" +} + struct selabel_handle *sehandle; +#ifdef HAVE_OEMLOCK + +/* + * liboemlock must supply the following C symbols: + * + * - int oemlock_get() + * + * Returns the current state of the OEM lock, if available. + * -1: Not available and/or error + * 0: Unlocked + * 1: Locked + * + * - int oemlock_set(int lock) + * + * Sets the state of the OEM lock. The "lock" parameter will be set + * to 0 for unlock and 1 for lock. + * + * Returns 0 on success, -1 on error + */ +extern "C" { +int oemlock_get(); +int oemlock_set(int lock); +} + +enum OemLockOp { + OEM_LOCK_NONE, + OEM_LOCK_UNLOCK +}; + +static OemLockOp oem_lock = OEM_LOCK_NONE; + +#endif + static const struct option OPTIONS[] = { { "send_intent", required_argument, NULL, 'i' }, { "update_package", required_argument, NULL, 'u' }, + { "headless", no_argument, NULL, 'h' }, { "wipe_data", no_argument, NULL, 'w' }, { "wipe_cache", no_argument, NULL, 'c' }, + { "wipe_media", no_argument, NULL, 'm' }, { "show_text", no_argument, NULL, 't' }, { "sideload", no_argument, NULL, 's' }, { "sideload_auto_reboot", no_argument, NULL, 'a' }, @@ -75,7 +119,6 @@ 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"; static const char *CACHE_ROOT = "/cache"; -static const char *SDCARD_ROOT = "/sdcard"; 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"; @@ -88,6 +131,8 @@ char* stage = NULL; char* reason = NULL; bool modified_flash = false; +#include "mtdutils/mounts.h" + /* * The recovery tool communicates with the main system through /cache files. * /cache/recovery/command - INPUT - command line for tool, one arg per line @@ -151,8 +196,7 @@ 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) { +FILE* fopen_path(const char *path, const char *mode) { if (ensure_path_mounted(path) != 0) { LOGE("Can't mount %s\n", path); return NULL; @@ -166,23 +210,102 @@ fopen_path(const char *path, const char *mode) { return fp; } +// 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)); + fclose(fp); +} + bool is_ro_debuggable() { char value[PROPERTY_VALUE_MAX+1]; return (property_get("ro.debuggable", value, NULL) == 1 && value[0] == '1'); } static void redirect_stdio(const char* filename) { - // If these fail, there's not really anywhere to complain... - freopen(filename, "a", stdout); setbuf(stdout, NULL); - freopen(filename, "a", stderr); setbuf(stderr, NULL); -} + int pipefd[2]; + if (pipe(pipefd) == -1) { + LOGE("pipe failed: %s\n", strerror(errno)); -// 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)); - fclose(fp); + // Fall back to traditional logging mode without timestamps. + // If these fail, there's not really anywhere to complain... + freopen(filename, "a", stdout); setbuf(stdout, NULL); + freopen(filename, "a", stderr); setbuf(stderr, NULL); + + return; + } + + pid_t pid = fork(); + if (pid == -1) { + LOGE("fork failed: %s\n", strerror(errno)); + + // Fall back to traditional logging mode without timestamps. + // If these fail, there's not really anywhere to complain... + freopen(filename, "a", stdout); setbuf(stdout, NULL); + freopen(filename, "a", stderr); setbuf(stderr, NULL); + + return; + } + + if (pid == 0) { + /// Close the unused write end. + close(pipefd[1]); + + auto start = std::chrono::steady_clock::now(); + + // 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)); + close(pipefd[0]); + _exit(1); + } + + FILE* pipe_fp = fdopen(pipefd[0], "r"); + if (pipe_fp == nullptr) { + LOGE("fdopen failed: %s\n", strerror(errno)); + check_and_fclose(log_fp, filename); + close(pipefd[0]); + _exit(1); + } + + char* line = nullptr; + size_t len = 0; + while (getline(&line, &len, pipe_fp) != -1) { + auto now = std::chrono::steady_clock::now(); + double duration = std::chrono::duration_cast<std::chrono::duration<double>>( + now - start).count(); + if (line[0] == '\n') { + fprintf(log_fp, "[%12.6lf]\n", duration); + } else { + fprintf(log_fp, "[%12.6lf] %s", duration, line); + } + fflush(log_fp); + } + + LOGE("getline failed: %s\n", strerror(errno)); + + free(line); + check_and_fclose(log_fp, filename); + close(pipefd[0]); + _exit(1); + } else { + // Redirect stdout/stderr to the logger process. + // Close the unused read end. + close(pipefd[0]); + + setbuf(stdout, nullptr); + setbuf(stderr, nullptr); + + if (dup2(pipefd[1], STDOUT_FILENO) == -1) { + LOGE("dup2 stdout failed: %s\n", strerror(errno)); + } + if (dup2(pipefd[1], STDERR_FILENO) == -1) { + LOGE("dup2 stderr failed: %s\n", strerror(errno)); + } + + close(pipefd[1]); + } } // command line args come from, in decreasing precedence: @@ -213,6 +336,14 @@ get_args(int *argc, char ***argv) { (*argv)[0] = strdup(arg); for (*argc = 1; *argc < MAX_ARGS; ++*argc) { if ((arg = strtok(NULL, "\n")) == NULL) break; + // Arguments that may only be passed in BCB +#ifdef HAVE_OEMLOCK + if (strcmp(arg, "--oemunlock") == 0) { + oem_lock = OEM_LOCK_UNLOCK; + --*argc; + continue; + } +#endif (*argv)[*argc] = strdup(arg); } LOGI("Got arguments from boot message\n"); @@ -326,14 +457,18 @@ static void rotate_logs(int max) { ensure_path_mounted(LAST_KMSG_FILE); for (int i = max-1; i >= 0; --i) { - std::string old_log = android::base::StringPrintf((i == 0) ? "%s" : "%s.%d", - LAST_LOG_FILE, 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((i == 0) ? "%s" : "%s.%d", - LAST_KMSG_FILE, i); + 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()); } @@ -417,15 +552,15 @@ typedef struct _saved_log_file { struct _saved_log_file* next; } saved_log_file; -static bool erase_volume(const char* volume) { +static bool erase_volume(const char* volume, bool force = false) { bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); + saved_log_file* head = NULL; + ui->SetBackground(RecoveryUI::ERASING); ui->SetProgressType(RecoveryUI::INDETERMINATE); - saved_log_file* head = NULL; - - if (is_cache) { + if (!force && 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 @@ -472,10 +607,12 @@ static bool erase_volume(const char* volume) { ui->Print("Formatting %s...\n", volume); - ensure_path_unmounted(volume); - int result = format_volume(volume); + if (volume[0] == '/') { + ensure_path_unmounted(volume); + } + int result = format_volume(volume, force); - if (is_cache) { + if (!force && is_cache) { while (head) { FILE* f = fopen_path(head->name, "wb"); if (f) { @@ -498,21 +635,37 @@ static bool erase_volume(const char* volume) { copy_logs(); } + ui->SetBackground(RecoveryUI::NONE); + ui->SetProgressType(RecoveryUI::EMPTY); + return (result == 0); } -static int +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(); + // Count items to detect valid values for absolute selection + int header_count = 0; + if (headers) { + while (headers[header_count] != NULL) + ++header_count; + } + int item_count = 0; + while (items[item_count] != NULL) + ++item_count; + ui->StartMenu(headers, items, initial_selection); int selected = initial_selection; int chosen_item = -1; - while (chosen_item < 0) { + while (chosen_item < 0 && + chosen_item != Device::kGoBack && + chosen_item != Device::kGoHome && + chosen_item != Device::kRefresh) { int key = ui->WaitKey(); int visible = ui->IsTextVisible(); @@ -524,10 +677,29 @@ get_menu_selection(const char* const * headers, const char* const * items, ui->EndMenu(); return 0; // XXX fixme } + } else if (key == -2) { // we are returning from ui_cancel_wait_key(): no action + return Device::kNoAction; + } + else if (key == -6) { + return Device::kRefresh; } int action = device->HandleMenuKey(key, visible); + if (action >= 0) { + action &= ~KEY_FLAG_ABS; + if (action < header_count || action >= header_count + item_count) { + action = Device::kNoAction; + } + else { + // Absolute selection. Update selected item and give some + // feedback in the UI by selecting the item for a short time. + selected = ui->SelectMenu(action, true); + action = Device::kInvokeItem; + usleep(50*1000); + } + } + if (action < 0) { switch (action) { case Device::kHighlightUp: @@ -541,6 +713,15 @@ get_menu_selection(const char* const * headers, const char* const * items, break; case Device::kNoAction: break; + case Device::kGoBack: + chosen_item = Device::kGoBack; + break; + case Device::kGoHome: + chosen_item = Device::kGoHome; + break; + case Device::kRefresh: + chosen_item = Device::kRefresh; + break; } } else if (!menu_only) { chosen_item = action; @@ -548,6 +729,9 @@ get_menu_selection(const char* const * headers, const char* const * items, } ui->EndMenu(); + if (chosen_item == Device::kGoHome) { + device->GoHome(); + } return chosen_item; } @@ -557,8 +741,6 @@ static int compare_string(const void* a, const void* b) { // Returns a malloc'd path, or NULL. static char* browse_directory(const char* path, Device* device) { - ensure_path_mounted(path); - DIR* d = opendir(path); if (d == NULL) { LOGE("error opening %s: %s\n", path, strerror(errno)); @@ -617,21 +799,26 @@ static char* browse_directory(const char* path, Device* device) { z_size += d_size; zips[z_size] = NULL; - const char* headers[] = { "Choose a package to install:", path, NULL }; + const char* headers[] = { path, NULL }; char* result; int chosen_item = 0; while (true) { chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device); - - char* item = zips[chosen_item]; - int item_len = strlen(item); - if (chosen_item == 0) { // item 0 is always "../" + if (chosen_item == Device::kGoHome) { + // go up and stop browsing + result = strdup(""); + break; + } + if (chosen_item == 0 || chosen_item == Device::kGoBack) { // go up but continue browsing (if the caller is update_directory) result = NULL; break; } + char* item = zips[chosen_item]; + int item_len = strlen(item); + char new_path[PATH_MAX]; strlcpy(new_path, path, PATH_MAX); strlcat(new_path, "/", PATH_MAX); @@ -664,7 +851,7 @@ static bool yes_no(Device* device, const char* question1, const char* question2) } // Return true on success. -static bool wipe_data(int should_confirm, Device* device) { +static bool wipe_data(int should_confirm, Device* device, bool force = false) { if (should_confirm && !yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!")) { return false; } @@ -672,15 +859,39 @@ static bool wipe_data(int should_confirm, Device* device) { modified_flash = true; ui->Print("\n-- Wiping data...\n"); - bool success = + bool success; +retry: + success = device->PreWipeData() && - erase_volume("/data") && + erase_volume("/data", force) && erase_volume("/cache") && device->PostWipeData(); + if (!success && !force) { + if (!should_confirm || yes_no(device, "Wipe failed, format instead?", " THIS CAN NOT BE UNDONE!")) { + force = true; + goto retry; + } + } ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); return success; } +static bool wipe_media(int should_confirm, Device* device) { + if (should_confirm && !yes_no(device, "Wipe all user media?", " THIS CAN NOT BE UNDONE!")) { + return false; + } + + modified_flash = true; + + ui->Print("\n-- Wiping media...\n"); + bool success = + device->PreWipeMedia() && + erase_volume("media") && + device->PostWipeMedia(); + ui->Print("Media wipe %s.\n", success ? "complete" : "failed"); + return success; +} + // Return true on success. static bool wipe_cache(bool should_confirm, Device* device) { if (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) { @@ -695,6 +906,20 @@ static bool wipe_cache(bool should_confirm, Device* device) { return success; } +// Return true on success. +static bool wipe_system(Device* device) { + if (!yes_no(device, "Wipe system?", " THIS CAN NOT BE UNDONE!")) { + return false; + } + + modified_flash = true; + + ui->Print("\n-- Wiping system...\n"); + bool success = erase_volume("/system"); + ui->Print("System wipe %s.\n", success ? "complete" : "failed"); + return success; +} + static void choose_recovery_file(Device* device) { // "Back" + KEEP_LOG_COUNT * 2 + terminating nullptr entry char* entries[1 + KEEP_LOG_COUNT * 2 + 1]; @@ -706,7 +931,10 @@ static void choose_recovery_file(Device* device) { // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x for (int i = 0; i < KEEP_LOG_COUNT; i++) { char* log_file; - if (asprintf(&log_file, (i == 0) ? "%s" : "%s.%d", LAST_LOG_FILE, i) == -1) { + 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; } @@ -717,7 +945,9 @@ static void choose_recovery_file(Device* device) { } char* kmsg_file; - if (asprintf(&kmsg_file, (i == 0) ? "%s" : "%s.%d", LAST_KMSG_FILE, i) == -1) { + 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; } @@ -734,12 +964,11 @@ static void choose_recovery_file(Device* device) { while (true) { int chosen_item = get_menu_selection(headers, entries, 1, 0, device); - if (strcmp(entries[chosen_item], "Back") == 0) break; + if (chosen_item == Device::kGoHome) break; + if (chosen_item == Device::kGoBack) break; + if (chosen_item >= 0 && strcmp(entries[chosen_item], "Back") == 0) break; - // TODO: do we need to redirect? ShowFile could just avoid writing to stdio. - redirect_stdio("/dev/null"); ui->ShowFile(entries[chosen_item]); - redirect_stdio(TEMPORARY_LOG_FILE); } for (size_t i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) { @@ -747,29 +976,101 @@ static void choose_recovery_file(Device* device) { } } -static int apply_from_sdcard(Device* device, bool* wipe_cache) { +static int apply_from_storage(Device* device, const std::string& id, bool* wipe_cache) { modified_flash = true; - if (ensure_path_mounted(SDCARD_ROOT) != 0) { - ui->Print("\n-- Couldn't mount %s.\n", SDCARD_ROOT); + int status; + + if (!vdc->volumeMount(id)) { return INSTALL_ERROR; } - char* path = browse_directory(SDCARD_ROOT, device); - if (path == NULL) { + VolumeInfo vi = vdc->getVolume(id); + + char* path = browse_directory(vi.mInternalPath.c_str(), device); + if (path == NULL || *path == '\0') { ui->Print("\n-- No package file selected.\n"); - return INSTALL_ERROR; + vdc->volumeUnmount(vi.mId); + free(path); + return INSTALL_NONE; } + ui->ClearText(); + ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); + ui->Print("\n-- Install %s ...\n", path); set_sdcard_update_bootloader_message(); void* token = start_sdcard_fuse(path); - int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, + vdc->volumeUnmount(vi.mId, true); + + status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, TEMPORARY_INSTALL_FILE, false); finish_sdcard_fuse(token); - ensure_path_unmounted(SDCARD_ROOT); + free(path); + return status; +} + +static int +show_apply_update_menu(Device* device) { + static const char* headers[] = { "Apply update", NULL }; + char* menu_items[MAX_NUM_MANAGED_VOLUMES + 1 + 1]; + std::vector<VolumeInfo> volumes = vdc->getVolumes(); + + const int item_sideload = 0; + int n, i; + std::vector<VolumeInfo>::iterator vitr; + +refresh: + menu_items[item_sideload] = strdup("Apply from ADB"); + + n = item_sideload + 1; + for (vitr = volumes.begin(); vitr != volumes.end(); ++vitr) { + menu_items[n] = (char*)malloc(256); + sprintf(menu_items[n], "Choose from %s", vitr->mLabel.c_str()); + ++n; + } + menu_items[n] = NULL; + + bool wipe_cache; + int status = INSTALL_ERROR; + + int chosen = get_menu_selection(headers, menu_items, 0, 0, device); + for (i = 0; i < n; ++i) { + free(menu_items[i]); + } + if (chosen == Device::kRefresh) { + goto refresh; + } + if (chosen == Device::kGoHome) { + return INSTALL_NONE; + } + if (chosen == Device::kGoBack) { + return INSTALL_NONE; + } + if (chosen == item_sideload) { + static const char* headers[] = { "ADB Sideload", + NULL + }; + static const char* list[] = { "Cancel sideload", NULL }; + + start_sideload(ui, &wipe_cache, TEMPORARY_INSTALL_FILE); + int item = get_menu_selection(headers, list, 0, 0, device); + if (item != Device::kNoAction) { + stop_sideload(); + } + status = wait_sideload(); + } + else { + std::string id = volumes[chosen - 1].mId; + status = apply_from_storage(device, id, &wipe_cache); + } + + if (status != INSTALL_SUCCESS && status != INSTALL_NONE) { + ui->DialogShowErrorLog("Install failed"); + } + return status; } @@ -783,12 +1084,12 @@ prompt_and_wait(Device* device, int status) { switch (status) { case INSTALL_SUCCESS: case INSTALL_NONE: - ui->SetBackground(RecoveryUI::NO_COMMAND); + ui->SetBackground(RecoveryUI::NONE); break; case INSTALL_ERROR: case INSTALL_CORRUPT: - ui->SetBackground(RecoveryUI::ERROR); + ui->SetBackground(RecoveryUI::D_ERROR); break; } ui->SetProgressType(RecoveryUI::EMPTY); @@ -801,62 +1102,91 @@ prompt_and_wait(Device* device, int status) { Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item); bool should_wipe_cache = false; - switch (chosen_action) { - case Device::NO_ACTION: - break; + for (;;) { + switch (chosen_action) { + case Device::NO_ACTION: + break; - case Device::REBOOT: - case Device::SHUTDOWN: - case Device::REBOOT_BOOTLOADER: - return chosen_action; + case Device::REBOOT: + case Device::SHUTDOWN: + case Device::REBOOT_RECOVERY: + case Device::REBOOT_BOOTLOADER: + return chosen_action; - case Device::WIPE_DATA: - wipe_data(ui->IsTextVisible(), device); - if (!ui->IsTextVisible()) return Device::NO_ACTION; - break; + case Device::WIPE_DATA: + wipe_data(ui->IsTextVisible(), device); + if (!ui->IsTextVisible()) return Device::NO_ACTION; + break; - case Device::WIPE_CACHE: - wipe_cache(ui->IsTextVisible(), device); - if (!ui->IsTextVisible()) return Device::NO_ACTION; - break; + case Device::WIPE_FULL: + wipe_data(ui->IsTextVisible(), device, true); + if (!ui->IsTextVisible()) return 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::WIPE_CACHE: + wipe_cache(ui->IsTextVisible(), device); + if (!ui->IsTextVisible()) return Device::NO_ACTION; + break; + + case Device::APPLY_UPDATE: + { + status = show_apply_update_menu(device); - if (status == INSTALL_SUCCESS && should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; + if (status == INSTALL_SUCCESS && should_wipe_cache) { + if (!wipe_cache(false, device)) { + status = INSTALL_ERROR; + } + } + + if (status >= 0 && status != INSTALL_NONE) { + if (status != INSTALL_SUCCESS) { + ui->SetBackground(RecoveryUI::D_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 complete.\n"); } } + break; + } + break; + + case Device::VIEW_RECOVERY_LOGS: + choose_recovery_file(device); + 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 + 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 { - ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card"); + if (ensure_path_mounted("/system") != -1) { + ui->Print("Mounted /system.\n"); + } } - } - break; - case Device::VIEW_RECOVERY_LOGS: - choose_recovery_file(device); - break; + break; - case Device::MOUNT_SYSTEM: - if (ensure_path_mounted("/system") != -1) { - ui->Print("Mounted /system.\n"); - } - break; + case Device::WIPE_SYSTEM: + wipe_system(device); + break; + } + if (status == Device::kRefresh) { + status = 0; + continue; + } + break; } } } @@ -885,6 +1215,40 @@ load_locale_from_cache() { } } +static const char *key_src = "/data/misc/adb/adb_keys"; +static const char *key_dest = "/adb_keys"; + + +static void +setup_adbd() { + struct stat f; + // Mount /data and copy adb_keys to root if it exists + ensure_path_mounted("/data"); + if (stat(key_src, &f) == 0) { + FILE *file_src = fopen(key_src, "r"); + if (file_src == NULL) { + LOGE("Can't open %s\n", key_src); + } else { + FILE *file_dest = fopen(key_dest, "w"); + if (file_dest == NULL) { + LOGE("Can't open %s\n", key_dest); + } else { + char buf[4096]; + while (fgets(buf, sizeof(buf), file_src)) fputs(buf, file_dest); + check_and_fclose(file_dest, key_dest); + + // Enable secure adbd + property_set("ro.adb.secure", "1"); + } + check_and_fclose(file_src, key_src); + } + } + ensure_path_unmounted("/data"); + + // Trigger (re)start of adb daemon + property_set("service.adb.root", "1"); +} + static RecoveryUI* gCurrentUI = NULL; void @@ -903,12 +1267,32 @@ ui_print(const char* format, ...) { } } -int -main(int argc, char **argv) { - time_t start = time(NULL); +extern "C" int toybox_driver(int argc, char **argv); - redirect_stdio(TEMPORARY_LOG_FILE); +static int write_file(const char *path, const char *value) +{ + int fd, ret, len; + + fd = open(path, O_WRONLY|O_CREAT, 0622); + if (fd < 0) + return -errno; + + len = strlen(value); + do { + ret = write(fd, value, len); + } while (ret < 0 && errno == EINTR); + + close(fd); + if (ret < 0) { + return -errno; + } else { + return 0; + } +} + +int +main(int argc, char **argv) { // If this binary is started with the single argument "--adbd", // instead of being the normal recovery binary, it turns into kind // of a stripped-down version of adbd that only supports the @@ -921,6 +1305,43 @@ main(int argc, char **argv) { return 0; } + // Handle alternative invocations + char* command = argv[0]; + char* stripped = strrchr(argv[0], '/'); + if (stripped) + command = stripped + 1; + + if (strcmp(command, "recovery") != 0) { + struct recovery_cmd cmd = get_command(command); + if (cmd.name) + return cmd.main_func(argc, argv); + + if (!strcmp(command, "setup_adbd")) { + load_volume_table(); + setup_adbd(); + return 0; + } + if (strstr(argv[0], "start")) { + property_set("ctl.start", argv[1]); + return 0; + } + if (strstr(argv[0], "stop")) { + property_set("ctl.stop", argv[1]); + return 0; + } + return toybox_driver(argc, argv); + } + + // Clear umask for packages that copy files out to /tmp and then over + // to /system without properly setting all permissions (eg. gapps). + umask(0); + + time_t start = time(NULL); + + // redirect_stdio should be called only in non-sideload mode. Otherwise + // we may have two logger instances with different timestamps. + redirect_stdio(TEMPORARY_LOG_FILE); + printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start)); load_volume_table(); @@ -930,9 +1351,11 @@ main(int argc, char **argv) { const char *update_package = NULL; bool should_wipe_data = false; bool should_wipe_cache = false; + bool should_wipe_media = false; bool show_text = false; bool sideload = false; bool sideload_auto_reboot = false; + bool headless = false; bool just_exit = false; bool shutdown_after = false; @@ -941,8 +1364,10 @@ main(int argc, char **argv) { switch (arg) { case 'i': send_intent = optarg; break; case 'u': update_package = optarg; break; + case 'h': headless = true; break; case 'w': should_wipe_data = true; break; case 'c': should_wipe_cache = true; break; + case 'm': should_wipe_media = true; break; case 't': show_text = true; break; case 's': sideload = true; break; case 'a': sideload = true; sideload_auto_reboot = true; break; @@ -975,6 +1400,9 @@ main(int argc, char **argv) { ui = device->GetUI(); gCurrentUI = ui; + vdc = new VoldClient(device); + vdc->start(); + ui->SetLocale(locale); ui->Init(); @@ -986,6 +1414,9 @@ main(int argc, char **argv) { ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); + /*enable the backlight*/ + write_file("/sys/class/leds/lcd-backlight/brightness", "128"); + struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } }; @@ -1011,11 +1442,15 @@ main(int argc, char **argv) { if (strncmp(update_package, "CACHE:", 6) == 0) { int len = strlen(update_package) + 10; char* modified_path = (char*)malloc(len); - 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; + 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"); } } printf("\n"); @@ -1023,10 +1458,20 @@ main(int argc, char **argv) { property_list(print_property, NULL); printf("\n"); - ui->Print("Supported API: %d\n", RECOVERY_API_VERSION); - int status = INSTALL_SUCCESS; +#ifdef HAVE_OEMLOCK + if (oem_lock == OEM_LOCK_UNLOCK) { + device->PreWipeData(); + if (erase_volume("/data", true)) status = INSTALL_ERROR; + if (should_wipe_cache && erase_volume("/cache", true)) status = INSTALL_ERROR; + device->PostWipeData(); + if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n"); + if (oemlock_set(0)) status = INSTALL_ERROR; + // Force reboot regardless of actual status + status = INSTALL_SUCCESS; + } else +#endif if (update_package != NULL) { status = install_package(update_package, &should_wipe_cache, TEMPORARY_INSTALL_FILE, true); if (status == INSTALL_SUCCESS && should_wipe_cache) { @@ -1043,13 +1488,17 @@ main(int argc, char **argv) { } } } else if (should_wipe_data) { - if (!wipe_data(false, device)) { + if (!wipe_data(false, device, should_wipe_media)) { status = INSTALL_ERROR; } } else if (should_wipe_cache) { if (!wipe_cache(false, device)) { status = INSTALL_ERROR; } + } else if (should_wipe_media) { + if (!wipe_media(false, device)) { + status = INSTALL_ERROR; + } } else if (sideload) { // 'adb reboot sideload' acts the same as user presses key combinations // to enter the sideload mode. When 'sideload-auto-reboot' is used, text @@ -1060,7 +1509,8 @@ main(int argc, char **argv) { if (!sideload_auto_reboot) { ui->ShowText(true); } - status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE); + start_sideload(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE); + status = wait_sideload(); if (status == INSTALL_SUCCESS && should_wipe_cache) { if (!wipe_cache(false, device)) { status = INSTALL_ERROR; @@ -1074,21 +1524,25 @@ main(int argc, char **argv) { status = INSTALL_NONE; // No command specified ui->SetBackground(RecoveryUI::NO_COMMAND); - // http://b/17489952 - // If this is an eng or userdebug build, automatically turn on the - // text display if no command is specified. - if (is_ro_debuggable()) { - ui->ShowText(true); - } + // Always show menu if no command is specified + ui->ShowText(true); } if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) { copy_logs(); - ui->SetBackground(RecoveryUI::ERROR); + ui->SetBackground(RecoveryUI::D_ERROR); } Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; - if ((status != INSTALL_SUCCESS && !sideload_auto_reboot) || ui->IsTextVisible()) { + if (headless) { + ui->ShowText(true); + ui->SetHeadlessMode(); + finish_recovery(NULL); + for (;;) { + pause(); + } + } + else if ((status != INSTALL_SUCCESS && !sideload_auto_reboot) || ui->IsTextVisible()) { Device::BuiltinAction temp = prompt_and_wait(device, status); if (temp != Device::NO_ACTION) { after = temp; @@ -1098,20 +1552,40 @@ main(int argc, char **argv) { // Save logs and clean up before rebooting or shutting down. finish_recovery(send_intent); + vdc->unmountAll(); + vdc->stop(); + + sync(); + + write_file("/sys/class/leds/lcd-backlight/brightness", "0"); + gr_fb_blank(true); + switch (after) { case Device::SHUTDOWN: ui->Print("Shutting down...\n"); property_set(ANDROID_RB_PROPERTY, "shutdown,"); break; + case Device::REBOOT_RECOVERY: + ui->Print("Rebooting recovery...\n"); + property_set(ANDROID_RB_PROPERTY, "reboot,recovery"); + break; + case Device::REBOOT_BOOTLOADER: +#ifdef DOWNLOAD_MODE + ui->Print("Rebooting to download mode...\n"); + property_set(ANDROID_RB_PROPERTY, "reboot,download"); +#else ui->Print("Rebooting to bootloader...\n"); property_set(ANDROID_RB_PROPERTY, "reboot,bootloader"); +#endif break; default: + char reason[PROPERTY_VALUE_MAX]; + snprintf(reason, PROPERTY_VALUE_MAX, "reboot,%s", device->GetRebootReason()); ui->Print("Rebooting...\n"); - property_set(ANDROID_RB_PROPERTY, "reboot,"); + property_set(ANDROID_RB_PROPERTY, reason); break; } sleep(5); // should reboot before this finishes |