diff options
-rw-r--r-- | Android.mk | 4 | ||||
-rw-r--r-- | default_device.cpp | 92 | ||||
-rw-r--r-- | default_recovery_ui.c | 73 | ||||
-rw-r--r-- | device.h | 112 | ||||
-rw-r--r-- | install.cpp | 34 | ||||
-rw-r--r-- | recovery.cpp | 157 | ||||
-rw-r--r-- | recovery_ui.h | 95 | ||||
-rw-r--r-- | screen_ui.cpp (renamed from ui.cpp) | 464 | ||||
-rw-r--r-- | screen_ui.h | 127 | ||||
-rw-r--r-- | ui.h | 116 | ||||
-rw-r--r-- | updater/install.c | 3 | ||||
-rw-r--r-- | verifier.cpp | 6 |
12 files changed, 736 insertions, 547 deletions
diff --git a/Android.mk b/Android.mk index 2c81be676..be9ff9ec8 100644 --- a/Android.mk +++ b/Android.mk @@ -8,7 +8,7 @@ LOCAL_SRC_FILES := \ bootloader.cpp \ install.cpp \ roots.cpp \ - ui.cpp \ + screen_ui.cpp \ verifier.cpp LOCAL_MODULE := recovery @@ -34,7 +34,7 @@ endif LOCAL_MODULE_TAGS := eng ifeq ($(TARGET_RECOVERY_UI_LIB),) - LOCAL_SRC_FILES += default_recovery_ui.c + LOCAL_SRC_FILES += default_device.cpp else LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) endif diff --git a/default_device.cpp b/default_device.cpp new file mode 100644 index 000000000..265ed071e --- /dev/null +++ b/default_device.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <linux/input.h> + +#include "common.h" +#include "device.h" +#include "screen_ui.h" + +static const char* HEADERS[] = { "Volume up/down to move highlight;", + "enter button to select.", + "", + NULL }; + +static const char* ITEMS[] = {"reboot system now", + "apply update from external storage", + "apply update from cache", + "wipe data/factory reset", + "wipe cache partition", + NULL }; + +class DefaultUI : public ScreenRecoveryUI { + public: + virtual KeyAction CheckKey(int key) { + if (key == KEY_HOME) { + return TOGGLE; + } + return ENQUEUE; + } +}; + +class DefaultDevice : public Device { + public: + DefaultDevice() : + ui(new DefaultUI) { + } + + RecoveryUI* GetUI() { return ui; } + + int HandleMenuKey(int key, int visible) { + if (visible) { + switch (key) { + case KEY_DOWN: + case KEY_VOLUMEDOWN: + return kHighlightDown; + + case KEY_UP: + case KEY_VOLUMEUP: + return kHighlightUp; + + case KEY_ENTER: + return kInvokeItem; + } + } + + return kNoAction; + } + + BuiltinAction InvokeMenuItem(int menu_position) { + switch (menu_position) { + case 0: return REBOOT; + case 1: return APPLY_EXT; + case 2: return APPLY_CACHE; + case 3: return WIPE_DATA; + case 4: return WIPE_CACHE; + default: return NO_ACTION; + } + } + + const char* const* GetMenuHeaders() { return HEADERS; } + const char* const* GetMenuItems() { return ITEMS; } + + private: + RecoveryUI* ui; +}; + +Device* make_device() { + return new DefaultDevice(); +} diff --git a/default_recovery_ui.c b/default_recovery_ui.c deleted file mode 100644 index d56164e7e..000000000 --- a/default_recovery_ui.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <linux/input.h> - -#include "recovery_ui.h" -#include "common.h" - -char* MENU_HEADERS[] = { "Android system recovery utility", - "", - NULL }; - -char* MENU_ITEMS[] = { "reboot system now", - "apply update from external storage", - "wipe data/factory reset", - "wipe cache partition", - "apply update from cache", - NULL }; - -void device_ui_init(UIParameters* ui_parameters) { -} - -int device_recovery_start() { - return 0; -} - -int device_toggle_display(volatile char* key_pressed, int key_code) { - return key_code == KEY_HOME; -} - -int device_reboot_now(volatile char* key_pressed, int key_code) { - return 0; -} - -int device_handle_key(int key_code, int visible) { - if (visible) { - switch (key_code) { - case KEY_DOWN: - case KEY_VOLUMEDOWN: - return HIGHLIGHT_DOWN; - - case KEY_UP: - case KEY_VOLUMEUP: - return HIGHLIGHT_UP; - - case KEY_ENTER: - return SELECT_ITEM; - } - } - - return NO_ACTION; -} - -int device_perform_action(int which) { - return which; -} - -int device_wipe_data() { - return 0; -} diff --git a/device.h b/device.h new file mode 100644 index 000000000..8096a8d9f --- /dev/null +++ b/device.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _RECOVERY_DEVICE_H +#define _RECOVERY_DEVICE_H + +#include "ui.h" + +class Device { + public: + 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() = 0; + + // 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() { }; + + // enum KeyAction { NONE, TOGGLE, REBOOT }; + + // // Called in the input thread when a new key (key_code) is + // // pressed. *key_pressed is an array of KEY_MAX+1 bytes + // // indicating which other keys are already pressed. Return a + // // KeyAction to indicate action should be taken immediately. + // // These actions happen when recovery is not waiting for input + // // (eg, in the midst of installing a package). + // virtual KeyAction CheckImmediateKeyAction(volatile char* key_pressed, int key_code) = 0; + + // 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) = 0; + + enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE, + WIPE_DATA, WIPE_CACHE }; + + // 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) = 0; + + static const int kNoAction = -1; + static const int kHighlightUp = -2; + static const int kHighlightDown = -3; + static const int kInvokeItem = -4; + + // Called when 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 manually and selects the option from the + // menu.) Can perform whatever device-specific wiping actions are + // needed. Return 0 on success. The userdata and cache partitions + // are erased AFTER this returns (whether it returns success or not). + virtual int WipeData() { return 0; } + + // Return the headers (an array of strings, one per line, + // NULL-terminated) for the main menu. Typically these tell users + // what to push to move the selection and invoke the selected + // item. + virtual const char* const* GetMenuHeaders() = 0; + + // 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() = 0; +}; + +// 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/install.cpp b/install.cpp index 482e0d755..078343332 100644 --- a/install.cpp +++ b/install.cpp @@ -34,9 +34,17 @@ #include "verifier.h" #include "ui.h" +extern RecoveryUI* ui; + #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" +// Default allocation of progress bar segments to operations +static const int VERIFICATION_PROGRESS_TIME = 60; +static const float VERIFICATION_PROGRESS_FRACTION = 0.25; +static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; +static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; + // If the package contains an update binary, extract it and run it. static int try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { @@ -134,18 +142,17 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { float fraction = strtof(fraction_s, NULL); int seconds = strtol(seconds_s, NULL, 10); - ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), - seconds); + ui->ShowProgress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); } else if (strcmp(command, "set_progress") == 0) { char* fraction_s = strtok(NULL, " \n"); float fraction = strtof(fraction_s, NULL); - ui_set_progress(fraction); + ui->SetProgress(fraction); } else if (strcmp(command, "ui_print") == 0) { char* str = strtok(NULL, "\n"); if (str) { - ui_print("%s", str); + ui->Print("%s", str); } else { - ui_print("\n"); + ui->Print("\n"); } } else if (strcmp(command, "wipe_cache") == 0) { *wipe_cache = 1; @@ -244,9 +251,9 @@ exit: static int really_install_package(const char *path, int* wipe_cache) { - ui_set_background(BACKGROUND_ICON_INSTALLING); - ui_print("Finding update package...\n"); - ui_show_indeterminate_progress(); + ui->SetBackground(RecoveryUI::INSTALLING); + ui->Print("Finding update package...\n"); + ui->SetProgressType(RecoveryUI::INDETERMINATE); LOGI("Update location: %s\n", path); if (ensure_path_mounted(path) != 0) { @@ -254,7 +261,7 @@ really_install_package(const char *path, int* wipe_cache) return INSTALL_CORRUPT; } - ui_print("Opening update package...\n"); + ui->Print("Opening update package...\n"); int numKeys; RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); @@ -265,10 +272,9 @@ really_install_package(const char *path, int* wipe_cache) LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); // Give verification half the progress bar... - ui_print("Verifying update package...\n"); - ui_show_progress( - VERIFICATION_PROGRESS_FRACTION, - VERIFICATION_PROGRESS_TIME); + ui->Print("Verifying update package...\n"); + ui->SetProgressType(RecoveryUI::DETERMINATE); + ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); int err; err = verify_file(path, loadedKeys, numKeys); @@ -290,7 +296,7 @@ really_install_package(const char *path, int* wipe_cache) /* Verify and install the contents of the package. */ - ui_print("Installing update...\n"); + ui->Print("Installing update...\n"); return try_update_binary(path, &zip, wipe_cache); } diff --git a/recovery.cpp b/recovery.cpp index 7c1d7fb0f..d028cc917 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -37,8 +37,9 @@ #include "minui/minui.h" #include "minzip/DirUtil.h" #include "roots.h" -#include "recovery_ui.h" #include "ui.h" +#include "screen_ui.h" +#include "device.h" static const struct option OPTIONS[] = { { "send_intent", required_argument, NULL, 's' }, @@ -62,6 +63,8 @@ static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; extern UIParameters ui_parameters; // from ui.c +RecoveryUI* ui = NULL; + /* * The recovery tool communicates with the main system through /cache files. * /cache/recovery/command - INPUT - command line for tool, one arg per line @@ -291,9 +294,9 @@ finish_recovery(const char *send_intent) { static int erase_volume(const char *volume) { - ui_set_background(BACKGROUND_ICON_INSTALLING); - ui_show_indeterminate_progress(); - ui_print("Formatting %s...\n", volume); + ui->SetBackground(RecoveryUI::INSTALLING); + ui->SetProgressType(RecoveryUI::INDETERMINATE); + ui->Print("Formatting %s...\n", volume); ensure_path_unmounted(volume); @@ -398,7 +401,7 @@ copy_sideloaded_package(const char* original_path) { } static const char** -prepend_title(const char** headers) { +prepend_title(const char* const* headers) { const char* title[] = { "Android system recovery <" EXPAND(RECOVERY_API_VERSION) "e>", "", @@ -407,7 +410,7 @@ prepend_title(const char** headers) { // count the number of lines in our title, plus the // caller-provided headers. int count = 0; - const char** p; + const char* const* p; for (p = title; *p; ++p, ++count); for (p = headers; *p; ++p, ++count); @@ -422,45 +425,45 @@ prepend_title(const char** headers) { static int get_menu_selection(const char* const * headers, const char* const * items, - int menu_only, int initial_selection) { + int menu_only, int initial_selection, Device* device) { // throw away keys pressed previously, so user doesn't // accidentally trigger menu items. - ui_clear_key_queue(); + ui->FlushKeys(); - ui_start_menu(headers, items, initial_selection); + ui->StartMenu(headers, items, initial_selection); int selected = initial_selection; int chosen_item = -1; while (chosen_item < 0) { - int key = ui_wait_key(); - int visible = ui_text_visible(); + int key = ui->WaitKey(); + int visible = ui->IsTextVisible(); if (key == -1) { // ui_wait_key() timed out - if (ui_text_ever_visible()) { + if (ui->WasTextEverVisible()) { continue; } else { LOGI("timed out waiting for key input; rebooting.\n"); - ui_end_menu(); - return ITEM_REBOOT; + ui->EndMenu(); + return 0; // XXX fixme } } - int action = device_handle_key(key, visible); + int action = device->HandleMenuKey(key, visible); if (action < 0) { switch (action) { - case HIGHLIGHT_UP: + case Device::kHighlightUp: --selected; - selected = ui_menu_select(selected); + selected = ui->SelectMenu(selected); break; - case HIGHLIGHT_DOWN: + case Device::kHighlightDown: ++selected; - selected = ui_menu_select(selected); + selected = ui->SelectMenu(selected); break; - case SELECT_ITEM: + case Device::kInvokeItem: chosen_item = selected; break; - case NO_ACTION: + case Device::kNoAction: break; } } else if (!menu_only) { @@ -468,7 +471,7 @@ get_menu_selection(const char* const * headers, const char* const * items, } } - ui_end_menu(); + ui->EndMenu(); return chosen_item; } @@ -478,7 +481,7 @@ static int compare_string(const void* a, const void* b) { static int update_directory(const char* path, const char* unmount_when_done, - int* wipe_cache) { + int* wipe_cache, Device* device) { ensure_path_mounted(path); const char* MENU_HEADERS[] = { "Choose a package to install:", @@ -552,7 +555,7 @@ update_directory(const char* path, const char* unmount_when_done, int result; int chosen_item = 0; do { - chosen_item = get_menu_selection(headers, zips, 1, chosen_item); + chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device); char* item = zips[chosen_item]; int item_len = strlen(item); @@ -567,7 +570,7 @@ update_directory(const char* path, const char* unmount_when_done, strlcat(new_path, "/", PATH_MAX); strlcat(new_path, item, PATH_MAX); new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/' - result = update_directory(new_path, unmount_when_done, wipe_cache); + result = update_directory(new_path, unmount_when_done, wipe_cache, device); if (result >= 0) break; } else { // selected a zip file: attempt to install it, and return @@ -577,7 +580,7 @@ update_directory(const char* path, const char* unmount_when_done, strlcat(new_path, "/", PATH_MAX); strlcat(new_path, item, PATH_MAX); - ui_print("\n-- Install %s ...\n", path); + ui->Print("\n-- Install %s ...\n", path); set_sdcard_update_bootloader_message(); char* copy = copy_sideloaded_package(new_path); if (unmount_when_done != NULL) { @@ -605,7 +608,7 @@ update_directory(const char* path, const char* unmount_when_done, } static void -wipe_data(int confirm) { +wipe_data(int confirm, Device* device) { if (confirm) { static const char** title_headers = NULL; @@ -630,96 +633,96 @@ wipe_data(int confirm) { " No", NULL }; - int chosen_item = get_menu_selection(title_headers, items, 1, 0); + int chosen_item = get_menu_selection(title_headers, items, 1, 0, device); if (chosen_item != 7) { return; } } - ui_print("\n-- Wiping data...\n"); - device_wipe_data(); + ui->Print("\n-- Wiping data...\n"); + device->WipeData(); erase_volume("/data"); erase_volume("/cache"); - ui_print("Data wipe complete.\n"); + ui->Print("Data wipe complete.\n"); } static void -prompt_and_wait() { - const char** headers = prepend_title((const char**)MENU_HEADERS); +prompt_and_wait(Device* device) { + const char* const* headers = prepend_title(device->GetMenuHeaders()); for (;;) { finish_recovery(NULL); - ui_reset_progress(); + ui->SetProgressType(RecoveryUI::EMPTY); - int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0); + int chosen_item = get_menu_selection(headers, 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. - chosen_item = device_perform_action(chosen_item); + chosen_item = device->InvokeMenuItem(chosen_item); int status; int wipe_cache; switch (chosen_item) { - case ITEM_REBOOT: + case Device::REBOOT: return; - case ITEM_WIPE_DATA: - wipe_data(ui_text_visible()); - if (!ui_text_visible()) return; + case Device::WIPE_DATA: + wipe_data(ui->IsTextVisible(), device); + if (!ui->IsTextVisible()) return; break; - case ITEM_WIPE_CACHE: - ui_print("\n-- Wiping cache...\n"); + case Device::WIPE_CACHE: + ui->Print("\n-- Wiping cache...\n"); erase_volume("/cache"); - ui_print("Cache wipe complete.\n"); - if (!ui_text_visible()) return; + ui->Print("Cache wipe complete.\n"); + if (!ui->IsTextVisible()) return; break; - case ITEM_APPLY_SDCARD: - status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache); + case Device::APPLY_EXT: + status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache, device); if (status == INSTALL_SUCCESS && wipe_cache) { - ui_print("\n-- Wiping cache (at package request)...\n"); + ui->Print("\n-- Wiping cache (at package request)...\n"); if (erase_volume("/cache")) { - ui_print("Cache wipe failed.\n"); + ui->Print("Cache wipe failed.\n"); } else { - ui_print("Cache wipe complete.\n"); + ui->Print("Cache wipe complete.\n"); } } if (status >= 0) { if (status != INSTALL_SUCCESS) { - ui_set_background(BACKGROUND_ICON_ERROR); - ui_print("Installation aborted.\n"); - } else if (!ui_text_visible()) { + ui->SetBackground(RecoveryUI::ERROR); + ui->Print("Installation aborted.\n"); + } else if (!ui->IsTextVisible()) { return; // reboot if logs aren't visible } else { - ui_print("\nInstall from sdcard complete.\n"); + ui->Print("\nInstall from sdcard complete.\n"); } } break; - case ITEM_APPLY_CACHE: + + case Device::APPLY_CACHE: // Don't unmount cache at the end of this. - status = update_directory(CACHE_ROOT, NULL, &wipe_cache); + status = update_directory(CACHE_ROOT, NULL, &wipe_cache, device); if (status == INSTALL_SUCCESS && wipe_cache) { - ui_print("\n-- Wiping cache (at package request)...\n"); + ui->Print("\n-- Wiping cache (at package request)...\n"); if (erase_volume("/cache")) { - ui_print("Cache wipe failed.\n"); + ui->Print("Cache wipe failed.\n"); } else { - ui_print("Cache wipe complete.\n"); + ui->Print("Cache wipe complete.\n"); } } if (status >= 0) { if (status != INSTALL_SUCCESS) { - ui_set_background(BACKGROUND_ICON_ERROR); - ui_print("Installation aborted.\n"); - } else if (!ui_text_visible()) { + ui->SetBackground(RecoveryUI::ERROR); + ui->Print("Installation aborted.\n"); + } else if (!ui->IsTextVisible()) { return; // reboot if logs aren't visible } else { - ui_print("\nInstall from cache complete.\n"); + ui->Print("\nInstall from cache complete.\n"); } } break; - } } } @@ -738,9 +741,11 @@ main(int argc, char **argv) { freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL); printf("Starting recovery on %s", ctime(&start)); - device_ui_init(&ui_parameters); - ui_init(); - ui_set_background(BACKGROUND_ICON_INSTALLING); + Device* device = make_device(); + ui = device->GetUI(); + + ui->Init(); + ui->SetBackground(RecoveryUI::INSTALLING); load_volume_table(); get_args(&argc, &argv); @@ -757,14 +762,14 @@ main(int argc, char **argv) { case 'u': update_package = optarg; break; case 'w': wipe_data = wipe_cache = 1; break; case 'c': wipe_cache = 1; break; - case 't': ui_show_text(1); break; + case 't': ui->ShowText(true); break; case '?': LOGE("Invalid command argument\n"); continue; } } - device_recovery_start(); + device->StartRecovery(); printf("Command:"); for (arg = 0; arg < argc; arg++) { @@ -800,27 +805,27 @@ main(int argc, char **argv) { LOGE("Cache wipe (requested by package) failed."); } } - if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n"); + if (status != INSTALL_SUCCESS) ui->Print("Installation aborted.\n"); } else if (wipe_data) { - if (device_wipe_data()) status = INSTALL_ERROR; + if (device->WipeData()) status = INSTALL_ERROR; if (erase_volume("/data")) status = INSTALL_ERROR; if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; - if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n"); + if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n"); } else if (wipe_cache) { if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; - if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n"); + if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n"); } else { status = INSTALL_ERROR; // No command specified } - if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR); - if (status != INSTALL_SUCCESS || ui_text_visible()) { - prompt_and_wait(); + if (status != INSTALL_SUCCESS) ui->SetBackground(RecoveryUI::ERROR); + if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { + prompt_and_wait(device); } // Otherwise, get ready to boot the main system... finish_recovery(send_intent); - ui_print("Rebooting...\n"); + ui->Print("Rebooting...\n"); android_reboot(ANDROID_RB_RESTART, 0, 0); return EXIT_SUCCESS; } diff --git a/recovery_ui.h b/recovery_ui.h deleted file mode 100644 index 4c4baf542..000000000 --- a/recovery_ui.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _RECOVERY_UI_H -#define _RECOVERY_UI_H - -#include "common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Called before UI library is initialized. Can change things like -// how many frames are included in various animations, etc. -extern void device_ui_init(UIParameters* ui_parameters); - -// Called when recovery starts up. Returns 0. -extern int device_recovery_start(); - -// Called in the input thread when a new key (key_code) is pressed. -// *key_pressed is an array of KEY_MAX+1 bytes indicating which other -// keys are already pressed. Return true if the text display should -// be toggled. -extern int device_toggle_display(volatile char* key_pressed, int key_code); - -// Called in the input thread when a new key (key_code) is pressed. -// *key_pressed is an array of KEY_MAX+1 bytes indicating which other -// keys are already pressed. Return true if the device should reboot -// immediately. -extern int device_reboot_now(volatile char* key_pressed, int key_code); - -// Called from the main thread when recovery is waiting for input and -// a key is pressed. key is the code of the key pressed; visible is -// true if the recovery menu is being shown. Implementations can call -// ui_key_pressed() to discover if other keys are being held down. -// Return one of the defined constants below in order to: -// -// - move the menu highlight (HIGHLIGHT_*) -// - invoke the highlighted item (SELECT_ITEM) -// - do nothing (NO_ACTION) -// - invoke a specific action (a menu position: any non-negative number) -extern int device_handle_key(int key, int visible); - -// Perform a recovery action selected from the menu. 'which' 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. -extern int device_perform_action(int which); - -// Called when 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 manually and selects the option from the -// menu.) Can perform whatever device-specific wiping actions are -// needed. Return 0 on success. The userdata and cache partitions -// are erased after this returns (whether it returns success or not). -int device_wipe_data(); - -#define NO_ACTION -1 - -#define HIGHLIGHT_UP -2 -#define HIGHLIGHT_DOWN -3 -#define SELECT_ITEM -4 - -#define ITEM_REBOOT 0 -#define ITEM_APPLY_EXT 1 -#define ITEM_APPLY_SDCARD 1 // historical synonym for ITEM_APPLY_EXT -#define ITEM_WIPE_DATA 2 -#define ITEM_WIPE_CACHE 3 -#define ITEM_APPLY_CACHE 4 - -// Header text to display above the main menu. -extern char* MENU_HEADERS[]; - -// Text of menu items. -extern char* MENU_ITEMS[]; - -#ifdef __cplusplus -} -#endif - -#endif @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 The Android Open Source Project + * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,11 +31,9 @@ #include "common.h" #include <cutils/android_reboot.h> #include "minui/minui.h" -#include "recovery_ui.h" #include "ui.h" - -#define MAX_COLS 96 -#define MAX_ROWS 32 +#include "screen_ui.h" +#include "device.h" #define CHAR_WIDTH 10 #define CHAR_HEIGHT 18 @@ -49,53 +47,10 @@ UIParameters ui_parameters = { 13, 190, // installation icon overlay offset }; -static pthread_mutex_t gUpdateMutex = PTHREAD_MUTEX_INITIALIZER; -static gr_surface gBackgroundIcon[NUM_BACKGROUND_ICONS]; -static gr_surface *gInstallationOverlay; -static gr_surface *gProgressBarIndeterminate; -static gr_surface gProgressBarEmpty; -static gr_surface gProgressBarFill; - -static const struct { gr_surface* surface; const char *name; } BITMAPS[] = { - { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" }, - { &gBackgroundIcon[BACKGROUND_ICON_ERROR], "icon_error" }, - { &gProgressBarEmpty, "progress_empty" }, - { &gProgressBarFill, "progress_fill" }, - { NULL, NULL }, -}; - -static int gCurrentIcon = 0; -static int gInstallingFrame = 0; - -static enum ProgressBarType { - PROGRESSBAR_TYPE_NONE, - PROGRESSBAR_TYPE_INDETERMINATE, - PROGRESSBAR_TYPE_NORMAL, -} gProgressBarType = PROGRESSBAR_TYPE_NONE; - -// Progress bar scope of current operation -static float gProgressScopeStart = 0, gProgressScopeSize = 0, gProgress = 0; -static double gProgressScopeTime, gProgressScopeDuration; - -// Set to 1 when both graphics pages are the same (except for the progress bar) -static int gPagesIdentical = 0; - -// Log text overlay, displayed when a magic key is pressed -static char text[MAX_ROWS][MAX_COLS]; -static int text_cols = 0, text_rows = 0; -static int text_col = 0, text_row = 0, text_top = 0; -static int show_text = 0; -static int show_text_ever = 0; // has show_text ever been 1? - -static char menu[MAX_ROWS][MAX_COLS]; -static int show_menu = 0; -static int menu_top = 0, menu_items = 0, menu_sel = 0; - -// Key event input queue -static pthread_mutex_t key_queue_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t key_queue_cond = PTHREAD_COND_INITIALIZER; -static int key_queue[256], key_queue_len = 0; -static volatile char key_pressed[KEY_MAX + 1]; +// There's only (at most) one of these objects, and global callbacks +// (for pthread_create, and the input event system) need to find it, +// so use a global variable. +static ScreenRecoveryUI* self = NULL; // Return the current time as a double (including fractions of a second). static double now() { @@ -104,14 +59,41 @@ static double now() { return tv.tv_sec + tv.tv_usec / 1000000.0; } +ScreenRecoveryUI::ScreenRecoveryUI() : + currentIcon(NONE), + installingFrame(0), + progressBarType(EMPTY), + progressScopeStart(0), + progressScopeSize(0), + progress(0), + pagesIdentical(false), + text_cols(0), + text_rows(0), + text_col(0), + text_row(0), + text_top(0), + show_text(false), + show_text_ever(false), + show_menu(false), + menu_top(0), + menu_items(0), + menu_sel(0), + key_queue_len(0), + key_last_down(-1) { + pthread_mutex_init(&updateMutex, NULL); + pthread_mutex_init(&key_queue_mutex, NULL); + pthread_cond_init(&key_queue_cond, NULL); + self = this; +} + // Draw the given frame over the installation overlay animation. The // background is not cleared or draw with the base icon first; we // assume that the frame already contains some other frame of the // animation. Does nothing if no overlay animation is defined. -// Should only be called with gUpdateMutex locked. -static void draw_install_overlay_locked(int frame) { - if (gInstallationOverlay == NULL) return; - gr_surface surface = gInstallationOverlay[frame]; +// Should only be called with updateMutex locked. +void ScreenRecoveryUI::draw_install_overlay_locked(int frame) { + if (installationOverlay == NULL) return; + gr_surface surface = installationOverlay[frame]; int iconWidth = gr_get_width(surface); int iconHeight = gr_get_height(surface); gr_blit(surface, 0, 0, iconWidth, iconHeight, @@ -120,38 +102,38 @@ static void draw_install_overlay_locked(int frame) { } // Clear the screen and draw the currently selected background icon (if any). -// Should only be called with gUpdateMutex locked. -static void draw_background_locked(int icon) +// Should only be called with updateMutex locked. +void ScreenRecoveryUI::draw_background_locked(Icon icon) { - gPagesIdentical = 0; + pagesIdentical = false; gr_color(0, 0, 0, 255); gr_fill(0, 0, gr_fb_width(), gr_fb_height()); if (icon) { - gr_surface surface = gBackgroundIcon[icon]; + gr_surface surface = backgroundIcon[icon]; int iconWidth = gr_get_width(surface); int iconHeight = gr_get_height(surface); int iconX = (gr_fb_width() - iconWidth) / 2; int iconY = (gr_fb_height() - iconHeight) / 2; gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY); - if (icon == BACKGROUND_ICON_INSTALLING) { - draw_install_overlay_locked(gInstallingFrame); + if (icon == INSTALLING) { + draw_install_overlay_locked(installingFrame); } } } // Draw the progress bar (if any) on the screen. Does not flip pages. -// Should only be called with gUpdateMutex locked. -static void draw_progress_locked() +// Should only be called with updateMutex locked. +void ScreenRecoveryUI::draw_progress_locked() { - if (gCurrentIcon == BACKGROUND_ICON_INSTALLING) { - draw_install_overlay_locked(gInstallingFrame); + if (currentIcon == INSTALLING) { + draw_install_overlay_locked(installingFrame); } - if (gProgressBarType != PROGRESSBAR_TYPE_NONE) { - int iconHeight = gr_get_height(gBackgroundIcon[BACKGROUND_ICON_INSTALLING]); - int width = gr_get_width(gProgressBarEmpty); - int height = gr_get_height(gProgressBarEmpty); + if (progressBarType != EMPTY) { + int iconHeight = gr_get_height(backgroundIcon[INSTALLING]); + int width = gr_get_width(progressBarEmpty); + int height = gr_get_height(progressBarEmpty); int dx = (gr_fb_width() - width)/2; int dy = (3*gr_fb_height() + iconHeight - 2*height)/4; @@ -160,37 +142,37 @@ static void draw_progress_locked() gr_color(0, 0, 0, 255); gr_fill(dx, dy, width, height); - if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL) { - float progress = gProgressScopeStart + gProgress * gProgressScopeSize; - int pos = (int) (progress * width); + if (progressBarType == DETERMINATE) { + float p = progressScopeStart + progress * progressScopeSize; + int pos = (int) (p * width); if (pos > 0) { - gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy); + gr_blit(progressBarFill, 0, 0, pos, height, dx, dy); } if (pos < width-1) { - gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); + gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); } } - if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) { + if (progressBarType == INDETERMINATE) { static int frame = 0; - gr_blit(gProgressBarIndeterminate[frame], 0, 0, width, height, dx, dy); + gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy); frame = (frame + 1) % ui_parameters.indeterminate_frames; } } } -static void draw_text_line(int row, const char* t) { +void ScreenRecoveryUI::draw_text_line(int row, const char* t) { if (t[0] != '\0') { gr_text(0, (row+1)*CHAR_HEIGHT-1, t); } } // Redraw everything on the screen. Does not flip pages. -// Should only be called with gUpdateMutex locked. -static void draw_screen_locked(void) +// Should only be called with updateMutex locked. +void ScreenRecoveryUI::draw_screen_locked() { - draw_background_locked(gCurrentIcon); + draw_background_locked(currentIcon); draw_progress_locked(); if (show_text) { @@ -226,20 +208,20 @@ static void draw_screen_locked(void) } // Redraw everything on the screen and flip the screen (make it visible). -// Should only be called with gUpdateMutex locked. -static void update_screen_locked(void) +// Should only be called with updateMutex locked. +void ScreenRecoveryUI::update_screen_locked() { draw_screen_locked(); gr_flip(); } // Updates only the progress bar, if possible, otherwise redraws the screen. -// Should only be called with gUpdateMutex locked. -static void update_progress_locked(void) +// Should only be called with updateMutex locked. +void ScreenRecoveryUI::update_progress_locked() { - if (show_text || !gPagesIdentical) { + if (show_text || !pagesIdentical) { draw_screen_locked(); // Must redraw the whole screen - gPagesIdentical = 1; + pagesIdentical = true; } else { draw_progress_locked(); // Draw only the progress bar and overlays } @@ -247,46 +229,46 @@ static void update_progress_locked(void) } // Keeps the progress bar updated, even when the process is otherwise busy. -static void *progress_thread(void *cookie) +void* ScreenRecoveryUI::progress_thread(void *cookie) { double interval = 1.0 / ui_parameters.update_fps; for (;;) { double start = now(); - pthread_mutex_lock(&gUpdateMutex); + pthread_mutex_lock(&self->updateMutex); int redraw = 0; // update the installation animation, if active // skip this if we have a text overlay (too expensive to update) - if (gCurrentIcon == BACKGROUND_ICON_INSTALLING && + if (self->currentIcon == INSTALLING && ui_parameters.installing_frames > 0 && - !show_text) { - gInstallingFrame = - (gInstallingFrame + 1) % ui_parameters.installing_frames; + !self->show_text) { + self->installingFrame = + (self->installingFrame + 1) % ui_parameters.installing_frames; redraw = 1; } // update the progress bar animation, if active // skip this if we have a text overlay (too expensive to update) - if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE && !show_text) { + if (self->progressBarType == INDETERMINATE && !self->show_text) { redraw = 1; } // move the progress bar forward on timed intervals, if configured - int duration = gProgressScopeDuration; - if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL && duration > 0) { - double elapsed = now() - gProgressScopeTime; + int duration = self->progressScopeDuration; + if (self->progressBarType == DETERMINATE && duration > 0) { + double elapsed = now() - self->progressScopeTime; float progress = 1.0 * elapsed / duration; if (progress > 1.0) progress = 1.0; - if (progress > gProgress) { - gProgress = progress; + if (progress > progress) { + progress = progress; redraw = 1; } } - if (redraw) update_progress_locked(); + if (redraw) self->update_progress_locked(); - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&self->updateMutex); double end = now(); // minimum of 20ms delay between frames double delay = interval - (end-start); @@ -296,13 +278,10 @@ static void *progress_thread(void *cookie) return NULL; } -static int rel_sum = 0; - -static int input_callback(int fd, short revents, void *data) +int ScreenRecoveryUI::input_callback(int fd, short revents, void* data) { struct input_event ev; int ret; - int fake_key = 0; ret = ev_get_input(fd, revents, &ev); if (ret) @@ -316,59 +295,82 @@ static int input_callback(int fd, short revents, void *data) // the trackball. When it exceeds a threshold // (positive or negative), fake an up/down // key event. - rel_sum += ev.value; - if (rel_sum > 3) { - fake_key = 1; - ev.type = EV_KEY; - ev.code = KEY_DOWN; - ev.value = 1; - rel_sum = 0; - } else if (rel_sum < -3) { - fake_key = 1; - ev.type = EV_KEY; - ev.code = KEY_UP; - ev.value = 1; - rel_sum = 0; + self->rel_sum += ev.value; + if (self->rel_sum > 3) { + self->process_key(KEY_DOWN, 1); // press down key + self->process_key(KEY_DOWN, 0); // and release it + self->rel_sum = 0; + } else if (self->rel_sum < -3) { + self->process_key(KEY_UP, 1); // press up key + self->process_key(KEY_UP, 0); // and release it + self->rel_sum = 0; } } } else { - rel_sum = 0; + self->rel_sum = 0; } - if (ev.type != EV_KEY || ev.code > KEY_MAX) - return 0; + if (ev.type == EV_KEY && ev.code <= KEY_MAX) + self->process_key(ev.code, ev.value); + + return 0; +} + +// Process a key-up or -down event. A key is "registered" when it is +// pressed and then released, with no other keypresses or releases in +// between. Registered keys are passed to CheckKey() to see if it +// should trigger a visibility toggle, an immediate reboot, or be +// queued to be processed next time the foreground thread wants a key +// (eg, for the menu). +// +// We also keep track of which keys are currently down so that +// CheckKey can call IsKeyPressed to see what other keys are held when +// a key is registered. +// +// updown == 1 for key down events; 0 for key up events +void ScreenRecoveryUI::process_key(int key_code, int updown) { + bool register_key = false; pthread_mutex_lock(&key_queue_mutex); - if (!fake_key) { - // our "fake" keys only report a key-down event (no - // key-up), so don't record them in the key_pressed - // table. - key_pressed[ev.code] = ev.value; - } - const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); - if (ev.value > 0 && key_queue_len < queue_max) { - key_queue[key_queue_len++] = ev.code; - pthread_cond_signal(&key_queue_cond); + key_pressed[key_code] = updown; + if (updown) { + key_last_down = key_code; + } else { + if (key_last_down == key_code) + register_key = true; + key_last_down = -1; } pthread_mutex_unlock(&key_queue_mutex); - if (ev.value > 0 && device_toggle_display(key_pressed, ev.code)) { - pthread_mutex_lock(&gUpdateMutex); - show_text = !show_text; - if (show_text) show_text_ever = 1; - update_screen_locked(); - pthread_mutex_unlock(&gUpdateMutex); - } - - if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) { - android_reboot(ANDROID_RB_RESTART, 0, 0); + if (register_key) { + switch (CheckKey(key_code)) { + case RecoveryUI::TOGGLE: + pthread_mutex_lock(&updateMutex); + show_text = !show_text; + if (show_text) show_text_ever = true; + update_screen_locked(); + pthread_mutex_unlock(&updateMutex); + break; + + case RecoveryUI::REBOOT: + android_reboot(ANDROID_RB_RESTART, 0, 0); + break; + + case RecoveryUI::ENQUEUE: + pthread_mutex_lock(&key_queue_mutex); + const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); + if (key_queue_len < queue_max) { + key_queue[key_queue_len++] = key_code; + pthread_cond_signal(&key_queue_cond); + } + pthread_mutex_unlock(&key_queue_mutex); + break; + } } - - return 0; } // Reads input events, handles special hot keys, and adds to the key queue. -static void *input_thread(void *cookie) +void* ScreenRecoveryUI::input_thread(void *cookie) { for (;;) { if (!ev_wait(-1)) @@ -377,131 +379,119 @@ static void *input_thread(void *cookie) return NULL; } -void ui_init(void) +void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) { + int result = res_create_surface(filename, surface); + if (result < 0) { + LOGE("missing bitmap %s\n(Code %d)\n", filename, result); + } +} + +void ScreenRecoveryUI::Init() { gr_init(); ev_init(input_callback, NULL); text_col = text_row = 0; text_rows = gr_fb_height() / CHAR_HEIGHT; - if (text_rows > MAX_ROWS) text_rows = MAX_ROWS; + if (text_rows > kMaxRows) text_rows = kMaxRows; text_top = 1; text_cols = gr_fb_width() / CHAR_WIDTH; - if (text_cols > MAX_COLS - 1) text_cols = MAX_COLS - 1; + if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; + + LoadBitmap("icon_installing", &backgroundIcon[INSTALLING]); + LoadBitmap("icon_error", &backgroundIcon[ERROR]); + LoadBitmap("progress_empty", &progressBarEmpty); + LoadBitmap("progress_fill", &progressBarFill); int i; - for (i = 0; BITMAPS[i].name != NULL; ++i) { - int result = res_create_surface(BITMAPS[i].name, BITMAPS[i].surface); - if (result < 0) { - LOGE("Missing bitmap %s\n(Code %d)\n", BITMAPS[i].name, result); - } - } - gProgressBarIndeterminate = (gr_surface*)malloc(ui_parameters.indeterminate_frames * + progressBarIndeterminate = (gr_surface*)malloc(ui_parameters.indeterminate_frames * sizeof(gr_surface)); for (i = 0; i < ui_parameters.indeterminate_frames; ++i) { char filename[40]; // "indeterminate01.png", "indeterminate02.png", ... sprintf(filename, "indeterminate%02d", i+1); - int result = res_create_surface(filename, gProgressBarIndeterminate+i); - if (result < 0) { - LOGE("Missing bitmap %s\n(Code %d)\n", filename, result); - } + LoadBitmap(filename, progressBarIndeterminate+i); } if (ui_parameters.installing_frames > 0) { - gInstallationOverlay = (gr_surface*)malloc(ui_parameters.installing_frames * + installationOverlay = (gr_surface*)malloc(ui_parameters.installing_frames * sizeof(gr_surface)); for (i = 0; i < ui_parameters.installing_frames; ++i) { char filename[40]; // "icon_installing_overlay01.png", // "icon_installing_overlay02.png", ... sprintf(filename, "icon_installing_overlay%02d", i+1); - int result = res_create_surface(filename, gInstallationOverlay+i); - if (result < 0) { - LOGE("Missing bitmap %s\n(Code %d)\n", filename, result); - } + LoadBitmap(filename, installationOverlay+i); } // Adjust the offset to account for the positioning of the // base image on the screen. - if (gBackgroundIcon[BACKGROUND_ICON_INSTALLING] != NULL) { - gr_surface bg = gBackgroundIcon[BACKGROUND_ICON_INSTALLING]; + if (backgroundIcon[INSTALLING] != NULL) { + gr_surface bg = backgroundIcon[INSTALLING]; ui_parameters.install_overlay_offset_x += (gr_fb_width() - gr_get_width(bg)) / 2; ui_parameters.install_overlay_offset_y += (gr_fb_height() - gr_get_height(bg)) / 2; } } else { - gInstallationOverlay = NULL; + installationOverlay = NULL; } - pthread_t t; - pthread_create(&t, NULL, progress_thread, NULL); - pthread_create(&t, NULL, input_thread, NULL); + pthread_create(&progress_t, NULL, progress_thread, NULL); + pthread_create(&input_t, NULL, input_thread, NULL); } -void ui_set_background(int icon) +void ScreenRecoveryUI::SetBackground(Icon icon) { - pthread_mutex_lock(&gUpdateMutex); - gCurrentIcon = icon; + pthread_mutex_lock(&updateMutex); + currentIcon = icon; update_screen_locked(); - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); } -void ui_show_indeterminate_progress() +void ScreenRecoveryUI::SetProgressType(ProgressType type) { - pthread_mutex_lock(&gUpdateMutex); - if (gProgressBarType != PROGRESSBAR_TYPE_INDETERMINATE) { - gProgressBarType = PROGRESSBAR_TYPE_INDETERMINATE; + pthread_mutex_lock(&updateMutex); + if (progressBarType != type) { + progressBarType = type; update_progress_locked(); } - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); } -void ui_show_progress(float portion, int seconds) +void ScreenRecoveryUI::ShowProgress(float portion, float seconds) { - pthread_mutex_lock(&gUpdateMutex); - gProgressBarType = PROGRESSBAR_TYPE_NORMAL; - gProgressScopeStart += gProgressScopeSize; - gProgressScopeSize = portion; - gProgressScopeTime = now(); - gProgressScopeDuration = seconds; - gProgress = 0; + pthread_mutex_lock(&updateMutex); + progressBarType = DETERMINATE; + progressScopeStart += progressScopeSize; + progressScopeSize = portion; + progressScopeTime = now(); + progressScopeDuration = seconds; + progress = 0; update_progress_locked(); - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); } -void ui_set_progress(float fraction) +void ScreenRecoveryUI::SetProgress(float fraction) { - pthread_mutex_lock(&gUpdateMutex); + pthread_mutex_lock(&updateMutex); if (fraction < 0.0) fraction = 0.0; if (fraction > 1.0) fraction = 1.0; - if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL && fraction > gProgress) { + if (progressBarType == DETERMINATE && fraction > progress) { // Skip updates that aren't visibly different. - int width = gr_get_width(gProgressBarIndeterminate[0]); - float scale = width * gProgressScopeSize; - if ((int) (gProgress * scale) != (int) (fraction * scale)) { - gProgress = fraction; + int width = gr_get_width(progressBarIndeterminate[0]); + float scale = width * progressScopeSize; + if ((int) (progress * scale) != (int) (fraction * scale)) { + progress = fraction; update_progress_locked(); } } - pthread_mutex_unlock(&gUpdateMutex); -} - -void ui_reset_progress() -{ - pthread_mutex_lock(&gUpdateMutex); - gProgressBarType = PROGRESSBAR_TYPE_NONE; - gProgressScopeStart = gProgressScopeSize = 0; - gProgressScopeTime = gProgressScopeDuration = 0; - gProgress = 0; - update_screen_locked(); - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); } -void ui_print(const char *fmt, ...) +void ScreenRecoveryUI::Print(const char *fmt, ...) { char buf[256]; va_list ap; @@ -512,7 +502,7 @@ void ui_print(const char *fmt, ...) fputs(buf, stdout); // This can get called before ui_init(), so be careful. - pthread_mutex_lock(&gUpdateMutex); + pthread_mutex_lock(&updateMutex); if (text_rows > 0 && text_cols > 0) { char *ptr; for (ptr = buf; *ptr != '\0'; ++ptr) { @@ -527,13 +517,13 @@ void ui_print(const char *fmt, ...) text[text_row][text_col] = '\0'; update_screen_locked(); } - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); } -void ui_start_menu(const char* const * headers, const char* const * items, - int initial_selection) { +void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items, + int initial_selection) { int i; - pthread_mutex_lock(&gUpdateMutex); + pthread_mutex_lock(&updateMutex); if (text_rows > 0 && text_cols > 0) { for (i = 0; i < text_rows; ++i) { if (headers[i] == NULL) break; @@ -551,12 +541,12 @@ void ui_start_menu(const char* const * headers, const char* const * items, menu_sel = initial_selection; update_screen_locked(); } - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); } -int ui_menu_select(int sel) { +int ScreenRecoveryUI::SelectMenu(int sel) { int old_sel; - pthread_mutex_lock(&gUpdateMutex); + pthread_mutex_lock(&updateMutex); if (show_menu > 0) { old_sel = menu_sel; menu_sel = sel; @@ -565,47 +555,47 @@ int ui_menu_select(int sel) { sel = menu_sel; if (menu_sel != old_sel) update_screen_locked(); } - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); return sel; } -void ui_end_menu() { +void ScreenRecoveryUI::EndMenu() { int i; - pthread_mutex_lock(&gUpdateMutex); + pthread_mutex_lock(&updateMutex); if (show_menu > 0 && text_rows > 0 && text_cols > 0) { show_menu = 0; update_screen_locked(); } - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); } -int ui_text_visible() +bool ScreenRecoveryUI::IsTextVisible() { - pthread_mutex_lock(&gUpdateMutex); + pthread_mutex_lock(&updateMutex); int visible = show_text; - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); return visible; } -int ui_text_ever_visible() +bool ScreenRecoveryUI::WasTextEverVisible() { - pthread_mutex_lock(&gUpdateMutex); + pthread_mutex_lock(&updateMutex); int ever_visible = show_text_ever; - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); return ever_visible; } -void ui_show_text(int visible) +void ScreenRecoveryUI::ShowText(bool visible) { - pthread_mutex_lock(&gUpdateMutex); + pthread_mutex_lock(&updateMutex); show_text = visible; if (show_text) show_text_ever = 1; update_screen_locked(); - pthread_mutex_unlock(&gUpdateMutex); + pthread_mutex_unlock(&updateMutex); } // Return true if USB is connected. -static int usb_connected() { +bool ScreenRecoveryUI::usb_connected() { int fd = open("/sys/class/android_usb/android0/state", O_RDONLY); if (fd < 0) { printf("failed to open /sys/class/android_usb/android0/state: %s\n", @@ -623,7 +613,7 @@ static int usb_connected() { return connected; } -int ui_wait_key() +int ScreenRecoveryUI::WaitKey() { pthread_mutex_lock(&key_queue_mutex); @@ -653,14 +643,20 @@ int ui_wait_key() return key; } -int ui_key_pressed(int key) +bool ScreenRecoveryUI::IsKeyPressed(int key) { - // This is a volatile static array, don't bother locking - return key_pressed[key]; + pthread_mutex_lock(&key_queue_mutex); + int pressed = key_pressed[key]; + pthread_mutex_unlock(&key_queue_mutex); + return pressed; } -void ui_clear_key_queue() { +void ScreenRecoveryUI::FlushKeys() { pthread_mutex_lock(&key_queue_mutex); key_queue_len = 0; pthread_mutex_unlock(&key_queue_mutex); } + +RecoveryUI::KeyAction ScreenRecoveryUI::CheckKey(int key) { + return RecoveryUI::ENQUEUE; +} diff --git a/screen_ui.h b/screen_ui.h new file mode 100644 index 000000000..a5ec0d360 --- /dev/null +++ b/screen_ui.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RECOVERY_SCREEN_UI_H +#define RECOVERY_SCREEN_UI_H + +#include <pthread.h> + +#include "ui.h" +#include "minui/minui.h" + +// Implementation of RecoveryUI appropriate for devices with a screen +// (shows an icon + a progress bar, text logging, menu, etc.) +class ScreenRecoveryUI : public RecoveryUI { + public: + ScreenRecoveryUI(); + + void Init(); + + // overall recovery state ("background image") + void SetBackground(Icon icon); + + // progress indicator + void SetProgressType(ProgressType type); + void ShowProgress(float portion, float seconds); + void SetProgress(float fraction); + + // text log + void ShowText(bool visible); + bool IsTextVisible(); + bool WasTextEverVisible(); + + // key handling + int WaitKey(); + bool IsKeyPressed(int key); + void FlushKeys(); + // The default implementation of CheckKey enqueues all keys. + // Devices should typically override this to provide some way to + // toggle the log/menu display, and to do an immediate reboot. + KeyAction CheckKey(int key); + + // printing messages + void Print(const char* fmt, ...); // __attribute__((format(printf, 1, 2))); + + // menu display + void StartMenu(const char* const * headers, const char* const * items, + int initial_selection); + int SelectMenu(int sel); + void EndMenu(); + + private: + Icon currentIcon; + int installingFrame; + + pthread_mutex_t updateMutex; + gr_surface backgroundIcon[3]; + gr_surface *installationOverlay; + gr_surface *progressBarIndeterminate; + gr_surface progressBarEmpty; + gr_surface progressBarFill; + + ProgressType progressBarType; + + float progressScopeStart, progressScopeSize, progress; + double progressScopeTime, progressScopeDuration; + + // true when both graphics pages are the same (except for the + // progress bar) + bool pagesIdentical; + + static const int kMaxCols = 96; + static const int kMaxRows = 32; + + // Log text overlay, displayed when a magic key is pressed + char text[kMaxRows][kMaxCols]; + int text_cols, text_rows; + int text_col, text_row, text_top; + bool show_text; + bool show_text_ever; // has show_text ever been true? + + char menu[kMaxRows][kMaxCols]; + bool show_menu; + int menu_top, menu_items, menu_sel; + + // Key event input queue + pthread_mutex_t key_queue_mutex; + pthread_cond_t key_queue_cond; + int key_queue[256], key_queue_len; + char key_pressed[KEY_MAX + 1]; // under key_queue_mutex + int key_last_down; // under key_queue_mutex + int rel_sum; + + pthread_t progress_t; + pthread_t input_t; + + void draw_install_overlay_locked(int frame); + void draw_background_locked(Icon icon); + void draw_progress_locked(); + void draw_text_line(int row, const char* t); + void draw_screen_locked(); + void update_screen_locked(); + void update_progress_locked(); + static void* progress_thread(void* cookie); + static int input_callback(int fd, short revents, void* data); + void process_key(int key_code, int updown); + static void* input_thread(void* cookie); + + bool usb_connected(); + + void LoadBitmap(const char* filename, gr_surface* surface); + +}; + +#endif // RECOVERY_UI_H @@ -17,59 +17,75 @@ #ifndef RECOVERY_UI_H #define RECOVERY_UI_H -// Initialize the graphics system. -void ui_init(); - -// Use KEY_* codes from <linux/input.h> or KEY_DREAM_* from "minui/minui.h". -int ui_wait_key(); // waits for a key/button press, returns the code -int ui_key_pressed(int key); // returns >0 if the code is currently pressed -int ui_text_visible(); // returns >0 if text log is currently visible -int ui_text_ever_visible(); // returns >0 if text log was ever visible -void ui_show_text(int visible); -void ui_clear_key_queue(); - -// Write a message to the on-screen log shown with Alt-L (also to stderr). -// The screen is small, and users may need to report these messages to support, -// so keep the output short and not too cryptic. -void ui_print(const char *fmt, ...) __attribute__((format(printf, 1, 2))); - -// Display some header text followed by a menu of items, which appears -// at the top of the screen (in place of any scrolling ui_print() -// output, if necessary). -void ui_start_menu(const char* const * headers, const char* const * items, - int initial_selection); -// Set the menu highlight to the given index, and return it (capped to -// the range [0..numitems). -int ui_menu_select(int sel); -// End menu mode, resetting the text overlay so that ui_print() -// statements will be displayed. -void ui_end_menu(); - -// Set the icon (normally the only thing visible besides the progress bar). -enum { - BACKGROUND_ICON_NONE, - BACKGROUND_ICON_INSTALLING, - BACKGROUND_ICON_ERROR, - NUM_BACKGROUND_ICONS -}; -void ui_set_background(int icon); +// Abstract class for controlling the user interface during recovery. +class RecoveryUI { + public: + virtual ~RecoveryUI() { } + + // Initialize the object; called before anything else. + virtual void Init() = 0; + + // Set the overall recovery state ("background image"). + enum Icon { NONE, INSTALLING, ERROR }; + virtual void SetBackground(Icon icon) = 0; + + // --- progress indicator --- + enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE }; + virtual void SetProgressType(ProgressType determinate) = 0; + + // Show a progress bar and define the scope of the next operation: + // portion - fraction of the progress bar the next operation will use + // seconds - expected time interval (progress bar moves at this minimum rate) + virtual void ShowProgress(float portion, float seconds) = 0; + + // Set progress bar position (0.0 - 1.0 within the scope defined + // by the last call to ShowProgress). + virtual void SetProgress(float fraction) = 0; + + // --- text log --- + + virtual void ShowText(bool visible) = 0; + + virtual bool IsTextVisible() = 0; -// Show a progress bar and define the scope of the next operation: -// portion - fraction of the progress bar the next operation will use -// seconds - expected time interval (progress bar moves at this minimum rate) -void ui_show_progress(float portion, int seconds); -void ui_set_progress(float fraction); // 0.0 - 1.0 within the defined scope + virtual bool WasTextEverVisible() = 0; -// Default allocation of progress bar segments to operations -static const int VERIFICATION_PROGRESS_TIME = 60; -static const float VERIFICATION_PROGRESS_FRACTION = 0.25; -static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; -static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; + // Write a message to the on-screen log (shown if the user has + // toggled on the text display). + virtual void Print(const char* fmt, ...) = 0; // __attribute__((format(printf, 1, 2))) = 0; -// Show a rotating "barberpole" for ongoing operations. Updates automatically. -void ui_show_indeterminate_progress(); + // --- key handling --- -// Hide and reset the progress bar. -void ui_reset_progress(); + // Wait for keypress and return it. May return -1 after timeout. + virtual int WaitKey() = 0; + + virtual bool IsKeyPressed(int key) = 0; + + // Erase any queued-up keys. + virtual void FlushKeys() = 0; + + // Called on each keypress, even while operations are in progress. + // Return value indicates whether an immediate operation should be + // triggered (toggling the display, rebooting the device), or if + // the key should be enqueued for use by the main thread. + enum KeyAction { ENQUEUE, TOGGLE, REBOOT }; + virtual KeyAction CheckKey(int key) = 0; + + // --- menu display --- + + // Display some header text followed by a menu of items, which appears + // at the top of the screen (in place of any scrolling ui_print() + // output, if necessary). + virtual void StartMenu(const char* const * headers, const char* const * items, + int initial_selection) = 0; + + // Set the menu highlight to the given index, and return it (capped to + // the range [0..numitems). + virtual int SelectMenu(int sel) = 0; + + // End menu mode, resetting the text overlay so that ui_print() + // statements will be displayed. + virtual void EndMenu() = 0; +}; #endif // RECOVERY_UI_H diff --git a/updater/install.c b/updater/install.c index 7b4b99b01..f68bd03c8 100644 --- a/updater/install.c +++ b/updater/install.c @@ -792,11 +792,12 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { return NULL; } + char* partition = NULL; if (partition_value->type != VAL_STRING) { ErrorAbort(state, "partition argument to %s must be string", name); goto done; } - char* partition = partition_value->data; + partition = partition_value->data; if (strlen(partition) == 0) { ErrorAbort(state, "partition argument to %s can't be empty", name); goto done; diff --git a/verifier.cpp b/verifier.cpp index 58ca72393..1c5a41d1b 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -25,6 +25,8 @@ #include <stdio.h> #include <errno.h> +extern RecoveryUI* ui; + // Look for an RSA signature embedded in the .ZIP file comment given // the path to the zip. Verify it matches one of the given public // keys. @@ -33,7 +35,7 @@ // or no key matches the signature). int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys) { - ui_set_progress(0.0); + ui->SetProgress(0.0); FILE* f = fopen(path, "rb"); if (f == NULL) { @@ -161,7 +163,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey so_far += size; double f = so_far / (double)signed_len; if (f > frac + 0.02 || size == so_far) { - ui_set_progress(f); + ui->SetProgress(f); frac = f; } } |