diff options
-rw-r--r-- | common.h | 4 | ||||
-rw-r--r-- | device.cpp | 2 | ||||
-rw-r--r-- | device.h | 152 | ||||
-rw-r--r-- | recovery.cpp | 298 |
4 files changed, 220 insertions, 236 deletions
@@ -20,6 +20,8 @@ #include <stdio.h> #include <stdarg.h> +#include <string> + #define STRINGIFY(x) #x #define EXPAND(x) STRINGIFY(x) @@ -29,7 +31,7 @@ extern RecoveryUI* ui; extern bool modified_flash; // The current stage, e.g. "1/2". -extern const char* stage; +extern std::string stage; // The reason argument provided in "--reason=". extern const char* reason; diff --git a/device.cpp b/device.cpp index e717dddf7..61501869e 100644 --- a/device.cpp +++ b/device.cpp @@ -60,7 +60,7 @@ Device::BuiltinAction Device::InvokeMenuItem(int menu_position) { return menu_position < 0 ? NO_ACTION : MENU_ACTIONS[menu_position]; } -int Device::HandleMenuKey(int key, int visible) { +int Device::HandleMenuKey(int key, bool visible) { if (!visible) { return kNoAction; } @@ -20,96 +20,90 @@ #include "ui.h" class Device { - public: - explicit Device(RecoveryUI* ui) : ui_(ui) { } - virtual ~Device() { } + public: + explicit Device(RecoveryUI* ui) : ui_(ui) {} + virtual ~Device() {} - // Called to obtain the UI object that should be used to display - // the recovery user interface for this device. You should not - // have called Init() on the UI object already, the caller will do - // that after this method returns. - virtual RecoveryUI* GetUI() { return ui_; } + // Called to obtain the UI object that should be used to display the recovery user interface for + // this device. You should not have called Init() on the UI object already, the caller will do + // that after this method returns. + virtual RecoveryUI* GetUI() { + return ui_; + } - // Called when recovery starts up (after the UI has been obtained - // and initialized and after the arguments have been parsed, but - // before anything else). - virtual void StartRecovery() { }; + // Called when recovery starts up (after the UI has been obtained and initialized and after the + // arguments have been parsed, but before anything else). + virtual void StartRecovery() {}; - // Called from the main thread when recovery is at the main menu - // and waiting for input, and a key is pressed. (Note that "at" - // the main menu does not necessarily mean the menu is visible; - // recovery will be at the main menu with it invisible after an - // unsuccessful operation [ie OTA package failure], or if recovery - // is started with no command.) - // - // key is the code of the key just pressed. (You can call - // IsKeyPressed() on the RecoveryUI object you returned from GetUI - // if you want to find out if other keys are held down.) - // - // visible is true if the menu is visible. - // - // Return one of the defined constants below in order to: - // - // - move the menu highlight (kHighlight{Up,Down}) - // - invoke the highlighted item (kInvokeItem) - // - do nothing (kNoAction) - // - invoke a specific action (a menu position: any non-negative number) - virtual int HandleMenuKey(int key, int visible); + // Called from the main thread when recovery is at the main menu and waiting for input, and a key + // is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible; + // recovery will be at the main menu with it invisible after an unsuccessful operation [ie OTA + // package failure], or if recovery is started with no command.) + // + // 'key' is the code of the key just pressed. (You can call IsKeyPressed() on the RecoveryUI + // object you returned from GetUI if you want to find out if other keys are held down.) + // + // 'visible' is true if the menu is visible. + // + // Returns one of the defined constants below in order to: + // + // - move the menu highlight (kHighlight{Up,Down}) + // - invoke the highlighted item (kInvokeItem) + // - do nothing (kNoAction) + // - invoke a specific action (a menu position: any non-negative number) + virtual int HandleMenuKey(int key, bool visible); - enum BuiltinAction { - NO_ACTION = 0, - REBOOT = 1, - APPLY_SDCARD = 2, - // APPLY_CACHE was 3. - APPLY_ADB_SIDELOAD = 4, - WIPE_DATA = 5, - WIPE_CACHE = 6, - REBOOT_BOOTLOADER = 7, - SHUTDOWN = 8, - VIEW_RECOVERY_LOGS = 9, - MOUNT_SYSTEM = 10, - RUN_GRAPHICS_TEST = 11, - }; + enum BuiltinAction { + NO_ACTION = 0, + REBOOT = 1, + APPLY_SDCARD = 2, + // APPLY_CACHE was 3. + APPLY_ADB_SIDELOAD = 4, + WIPE_DATA = 5, + WIPE_CACHE = 6, + REBOOT_BOOTLOADER = 7, + SHUTDOWN = 8, + VIEW_RECOVERY_LOGS = 9, + MOUNT_SYSTEM = 10, + RUN_GRAPHICS_TEST = 11, + }; - // Return the list of menu items (an array of strings, - // NULL-terminated). The menu_position passed to InvokeMenuItem - // will correspond to the indexes into this array. - virtual const char* const* GetMenuItems(); + // Return the list of menu items (an array of strings, NULL-terminated). The menu_position passed + // to InvokeMenuItem will correspond to the indexes into this array. + virtual const char* const* GetMenuItems(); - // Perform a recovery action selected from the menu. - // 'menu_position' will be the item number of the selected menu - // item, or a non-negative number returned from - // device_handle_key(). The menu will be hidden when this is - // called; implementations can call ui_print() to print - // information to the screen. If the menu position is one of the - // builtin actions, you can just return the corresponding enum - // value. If it is an action specific to your device, you - // actually perform it here and return NO_ACTION. - virtual BuiltinAction InvokeMenuItem(int menu_position); + // Perform a recovery action selected from the menu. 'menu_position' will be the item number of + // the selected menu item, or a non-negative number returned from HandleMenuKey(). The menu will + // be hidden when this is called; implementations can call ui_print() to print information to the + // screen. If the menu position is one of the builtin actions, you can just return the + // corresponding enum value. If it is an action specific to your device, you actually perform it + // here and return NO_ACTION. + virtual BuiltinAction InvokeMenuItem(int menu_position); - static const int kNoAction = -1; - static const int kHighlightUp = -2; - static const int kHighlightDown = -3; - static const int kInvokeItem = -4; + static const int kNoAction = -1; + static const int kHighlightUp = -2; + static const int kHighlightDown = -3; + static const int kInvokeItem = -4; - // Called before and after we do a wipe data/factory reset operation, - // either via a reboot from the main system with the --wipe_data flag, - // or when the user boots into recovery image manually and selects the - // option from the menu, to perform whatever device-specific wiping - // actions are needed. - // Return true on success; returning false from PreWipeData will prevent - // the regular wipe, and returning false from PostWipeData will cause - // the wipe to be considered a failure. - virtual bool PreWipeData() { return true; } - virtual bool PostWipeData() { return true; } + // Called before and after we do a wipe data/factory reset operation, either via a reboot from the + // main system with the --wipe_data flag, or when the user boots into recovery image manually and + // selects the option from the menu, to perform whatever device-specific wiping actions as needed. + // Returns true on success; returning false from PreWipeData will prevent the regular wipe, and + // returning false from PostWipeData will cause the wipe to be considered a failure. + virtual bool PreWipeData() { + return true; + } - private: - RecoveryUI* ui_; + virtual bool PostWipeData() { + return true; + } + + private: + RecoveryUI* ui_; }; -// The device-specific library must define this function (or the -// default one will be used, if there is no device-specific library). -// It returns the Device object that recovery should use. +// The device-specific library must define this function (or the default one will be used, if there +// is no device-specific library). It returns the Device object that recovery should use. Device* make_device(); #endif // _DEVICE_H diff --git a/recovery.cpp b/recovery.cpp index 5c60ce655..b5cd3484b 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -83,7 +83,6 @@ 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'}, @@ -129,7 +128,7 @@ static bool has_cache = false; RecoveryUI* ui = nullptr; bool modified_flash = false; -const char* stage = nullptr; +std::string stage; const char* reason = nullptr; struct selabel_handle* sehandle; @@ -309,7 +308,7 @@ static std::vector<std::string> get_args(const int argc, char** const argv) { // If fails, leave a zeroed bootloader_message. boot = {}; } - stage = strndup(boot.stage, sizeof(boot.stage)); + stage = std::string(boot.stage); if (boot.command[0] != 0) { std::string boot_command = std::string(boot.command, sizeof(boot.command)); @@ -608,54 +607,55 @@ static bool erase_volume(const char* volume) { 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 { - LOG(INFO) << "timed out waiting for key input; rebooting."; - ui->EndMenu(); - return 0; // XXX fixme - } - } +// 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. +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) { // ui_wait_key() timed out + if (ui->WasTextEverVisible()) { + continue; + } else { + LOG(INFO) << "timed out waiting for key input; rebooting."; + 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; - } + 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; + ui->EndMenu(); + return chosen_item; } // Returns the selected filename, or an empty string. @@ -700,7 +700,7 @@ static std::string browse_directory(const std::string& path, Device* device) { int chosen_item = 0; while (true) { - chosen_item = get_menu_selection(headers, entries, 1, chosen_item, device); + chosen_item = get_menu_selection(headers, entries, true, chosen_item, device); const std::string& item = zips[chosen_item]; if (chosen_item == 0) { @@ -727,7 +727,7 @@ 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); } @@ -750,25 +750,25 @@ static bool wipe_data(Device* device) { } static bool prompt_and_wipe_data(Device* device) { - const char* const headers[] = { - "Boot halted, user data is corrupt", - "Wipe all user data to recover", - NULL - }; - const char* const items[] = { - "Retry boot", - "Wipe user data", - NULL - }; - for (;;) { - int chosen_item = get_menu_selection(headers, items, 1, 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); - } + const char* const headers[] = { + "Boot halted, user data is corrupt", + "Wipe all user data to recover", + NULL + }; + const char* const items[] = { + "Retry boot", + "Wipe user data", + 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. @@ -922,98 +922,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)); + 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); + } - unsigned int n = 0; + if (ensure_path_mounted(log_file.c_str()) == 0 && access(log_file.c_str(), R_OK) == 0) { + entries.push_back(std::move(log_file)); + } + }; - 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; - } + // Add LAST_LOG_FILE + LAST_LOG_FILE.x + add_to_entries(LAST_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; - } - } + // Add LAST_KMSG_FILE + LAST_KMSG_FILE.x + add_to_entries(LAST_KMSG_FILE); + } + } else { + // If cache partition is not found, view /tmp/recovery.log instead. + if (access(TEMPORARY_LOG_FILE, R_OK) == -1) { + return; } else { - // If cache partition is not found, view /tmp/recovery.log instead. - if (access(TEMPORARY_LOG_FILE, R_OK) == -1) { - return; - } else{ - entries[n++] = strdup(TEMPORARY_LOG_FILE); - } + entries.push_back(TEMPORARY_LOG_FILE); } + } - entries[n++] = strdup("Back"); + entries.push_back("Back"); - const char* headers[] = { "Select file to view", nullptr }; + 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); - int chosen_item = 0; - while (true) { - chosen_item = get_menu_selection(headers, entries, 1, chosen_item, device); - if (strcmp(entries[chosen_item], "Back") == 0) break; + const char* headers[] = { "Select file to view", nullptr }; - ui->ShowFile(entries[chosen_item]); - } + 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; - for (size_t i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) { - free(entries[i]); - } + ui->ShowFile(entries[chosen_item].c_str()); + } } -static void run_graphics_test(Device* device) { - // Switch to graphics screen. - ui->ShowText(false); - - ui->SetProgressType(RecoveryUI::INDETERMINATE); - ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); - sleep(1); - - ui->SetBackground(RecoveryUI::ERROR); - sleep(1); +static void run_graphics_test() { + // Switch to graphics screen. + ui->ShowText(false); - ui->SetBackground(RecoveryUI::NO_COMMAND); - sleep(1); + ui->SetProgressType(RecoveryUI::INDETERMINATE); + ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); + sleep(1); - ui->SetBackground(RecoveryUI::ERASING); - sleep(1); + ui->SetBackground(RecoveryUI::ERROR); + sleep(1); - ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); + ui->SetBackground(RecoveryUI::NO_COMMAND); + sleep(1); - 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->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); + ui->ShowText(true); } // How long (in seconds) we wait for the fuse-provided package file to @@ -1115,7 +1111,7 @@ prompt_and_wait(Device* device, int status) { } ui->SetProgressType(RecoveryUI::EMPTY); - int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), 0, 0, device); + int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), false, 0, device); // device-specific code may take some action here. It may // return one of the core actions handled in the switch @@ -1181,7 +1177,7 @@ prompt_and_wait(Device* device, int status) { break; case Device::RUN_GRAPHICS_TEST: - run_graphics_test(device); + run_graphics_test(); break; case Device::MOUNT_SYSTEM: @@ -1422,14 +1418,6 @@ 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; @@ -1461,7 +1449,7 @@ int main(int argc, char **argv) { } printf("locale is [%s]\n", locale.c_str()); - printf("stage is [%s]\n", stage); + printf("stage is [%s]\n", stage.c_str()); printf("reason is [%s]\n", reason); Device* device = make_device(); @@ -1476,7 +1464,7 @@ int main(int argc, char **argv) { 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); } |