diff options
-rw-r--r-- | Android.mk | 14 | ||||
-rw-r--r-- | default_device.cpp | 92 | ||||
-rw-r--r-- | default_recovery_ui.c | 73 | ||||
-rw-r--r-- | device.h | 112 | ||||
-rw-r--r-- | recovery.cpp | 70 | ||||
-rw-r--r-- | recovery_ui.h | 95 | ||||
-rw-r--r-- | screen_ui.cpp | 105 | ||||
-rw-r--r-- | screen_ui.h | 8 | ||||
-rw-r--r-- | ui.h | 7 | ||||
-rw-r--r-- | updater/install.c | 3 | ||||
-rw-r--r-- | verifier_test.cpp | 46 |
11 files changed, 364 insertions, 261 deletions
diff --git a/Android.mk b/Android.mk index c29ab523f..be9ff9ec8 100644 --- a/Android.mk +++ b/Android.mk @@ -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 @@ -50,17 +50,17 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -#LOCAL_SRC_FILES := verifier_test.cpp verifier.cpp +LOCAL_SRC_FILES := verifier_test.cpp verifier.cpp -#LOCAL_MODULE := verifier_test +LOCAL_MODULE := verifier_test -#LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_FORCE_STATIC_EXECUTABLE := true -#LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_TAGS := tests -#LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libstdc++ libc +LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libstdc++ libc -#include $(BUILD_EXECUTABLE) +include $(BUILD_EXECUTABLE) include $(commands_recovery_local_path)/minui/Android.mk 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/recovery.cpp b/recovery.cpp index d1af3ac05..d028cc917 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -37,9 +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' }, @@ -401,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>", "", @@ -410,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); @@ -425,7 +425,7 @@ 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->FlushKeys(); @@ -444,26 +444,26 @@ get_menu_selection(const char* const * headers, const char* const * items, } else { LOGI("timed out waiting for key input; rebooting.\n"); ui->EndMenu(); - return ITEM_REBOOT; + 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->SelectMenu(selected); break; - case HIGHLIGHT_DOWN: + case Device::kHighlightDown: ++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) { @@ -481,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:", @@ -555,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); @@ -570,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 @@ -608,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; @@ -633,54 +633,54 @@ 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(); + device->WipeData(); erase_volume("/data"); erase_volume("/cache"); 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->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->IsTextVisible()); + case Device::WIPE_DATA: + wipe_data(ui->IsTextVisible(), device); if (!ui->IsTextVisible()) return; break; - case ITEM_WIPE_CACHE: + case Device::WIPE_CACHE: ui->Print("\n-- Wiping cache...\n"); erase_volume("/cache"); 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"); if (erase_volume("/cache")) { @@ -700,9 +700,10 @@ prompt_and_wait() { } } 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"); if (erase_volume("/cache")) { @@ -722,7 +723,6 @@ prompt_and_wait() { } } break; - } } } @@ -741,10 +741,8 @@ main(int argc, char **argv) { freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL); printf("Starting recovery on %s", ctime(&start)); - // TODO: device_* should be a C++ class; init should return the - // appropriate UI for the device. - device_ui_init(&ui_parameters); - ui = new ScreenRecoveryUI(); + Device* device = make_device(); + ui = device->GetUI(); ui->Init(); ui->SetBackground(RecoveryUI::INSTALLING); @@ -771,7 +769,7 @@ main(int argc, char **argv) { } } - device_recovery_start(); + device->StartRecovery(); printf("Command:"); for (arg = 0; arg < argc; arg++) { @@ -809,7 +807,7 @@ main(int argc, char **argv) { } 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"); @@ -822,7 +820,7 @@ main(int argc, char **argv) { if (status != INSTALL_SUCCESS) ui->SetBackground(RecoveryUI::ERROR); if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { - prompt_and_wait(); + prompt_and_wait(device); } // Otherwise, get ready to boot the main system... 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 diff --git a/screen_ui.cpp b/screen_ui.cpp index e6a31db28..a60b04682 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -31,9 +31,9 @@ #include "common.h" #include <cutils/android_reboot.h> #include "minui/minui.h" -#include "recovery_ui.h" #include "ui.h" #include "screen_ui.h" +#include "device.h" #define CHAR_WIDTH 10 #define CHAR_HEIGHT 18 @@ -78,7 +78,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() : menu_top(0), menu_items(0), menu_sel(0), - key_queue_len(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); @@ -281,7 +282,6 @@ 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) @@ -297,16 +297,12 @@ int ScreenRecoveryUI::input_callback(int fd, short revents, void* data) // key event. self->rel_sum += ev.value; if (self->rel_sum > 3) { - fake_key = 1; - ev.type = EV_KEY; - ev.code = KEY_DOWN; - ev.value = 1; + 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) { - fake_key = 1; - ev.type = EV_KEY; - ev.code = KEY_UP; - ev.value = 1; + self->process_key(KEY_UP, 1); // press up key + self->process_key(KEY_UP, 0); // and release it self->rel_sum = 0; } } @@ -314,36 +310,63 @@ int ScreenRecoveryUI::input_callback(int fd, short revents, void* data) 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); - pthread_mutex_lock(&self->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. - self->key_pressed[ev.code] = ev.value; - } - const int queue_max = sizeof(self->key_queue) / sizeof(self->key_queue[0]); - if (ev.value > 0 && self->key_queue_len < queue_max) { - self->key_queue[self->key_queue_len++] = ev.code; - pthread_cond_signal(&self->key_queue_cond); - } - pthread_mutex_unlock(&self->key_queue_mutex); + return 0; +} - if (ev.value > 0 && device_toggle_display(self->key_pressed, ev.code)) { - pthread_mutex_lock(&self->updateMutex); - self->show_text = !self->show_text; - if (self->show_text) self->show_text_ever = true; - self->update_screen_locked(); - pthread_mutex_unlock(&self->updateMutex); - } +// 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; - if (ev.value > 0 && device_reboot_now(self->key_pressed, ev.code)) { - android_reboot(ANDROID_RB_RESTART, 0, 0); + pthread_mutex_lock(&key_queue_mutex); + 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); - return 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; + } + } } // Reads input events, handles special hot keys, and adds to the key queue. @@ -622,8 +645,10 @@ int ScreenRecoveryUI::WaitKey() 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 ScreenRecoveryUI::FlushKeys() { @@ -631,3 +656,7 @@ void ScreenRecoveryUI::FlushKeys() { 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 index 544f1543c..a5ec0d360 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -47,6 +47,10 @@ class ScreenRecoveryUI : public RecoveryUI { 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))); @@ -95,7 +99,8 @@ class ScreenRecoveryUI : public RecoveryUI { pthread_mutex_t key_queue_mutex; pthread_cond_t key_queue_cond; int key_queue[256], key_queue_len; - volatile char key_pressed[KEY_MAX + 1]; + 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; @@ -110,6 +115,7 @@ class ScreenRecoveryUI : public RecoveryUI { 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(); @@ -64,6 +64,13 @@ class RecoveryUI { // 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 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_test.cpp b/verifier_test.cpp index 5b6c1f451..2448d8d58 100644 --- a/verifier_test.cpp +++ b/verifier_test.cpp @@ -19,6 +19,7 @@ #include <stdarg.h> #include "verifier.h" +#include "ui.h" // This is build/target/product/security/testkey.x509.pem after being // dumped out by dumpkey.jar. @@ -58,18 +59,41 @@ RSAPublicKey test_key = 367251975, 810756730, -1941182952, 1175080310 } }; -void ui_print(const char* fmt, ...) { - char buf[256]; - va_list ap; - va_start(ap, fmt); - vsnprintf(buf, 256, fmt, ap); - va_end(ap); +RecoveryUI* ui = NULL; - fputs(buf, stderr); -} +// verifier expects to find a UI object; we provide one that does +// nothing but print. +class FakeUI : public RecoveryUI { + void Init() { } + void SetBackground(Icon icon) { } -void ui_set_progress(float fraction) { -} + void SetProgressType(ProgressType determinate) { } + void ShowProgress(float portion, float seconds) { } + void SetProgress(float fraction) { } + + void ShowText(bool visible) { } + bool IsTextVisible() { return false; } + bool WasTextEverVisible() { return false; } + void Print(const char* fmt, ...) { + char buf[256]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, 256, fmt, ap); + va_end(ap); + + fputs(buf, stderr); + } + + int WaitKey() { return 0; } + bool IsKeyPressed(int key) { return false; } + void FlushKeys() { } + KeyAction CheckKey(int key) { return ENQUEUE; } + + void StartMenu(const char* const * headers, const char* const * items, + int initial_selection) { } + int SelectMenu(int sel) { return 0; } + void EndMenu() { } +}; int main(int argc, char **argv) { if (argc != 2) { @@ -77,6 +101,8 @@ int main(int argc, char **argv) { return 2; } + ui = new FakeUI(); + int result = verify_file(argv[1], &test_key, 1); if (result == VERIFY_SUCCESS) { printf("SUCCESS\n"); |