aboutsummaryrefslogtreecommitdiffstats
path: root/recovery.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'recovery.cpp')
-rw-r--r--recovery.cpp710
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