diff options
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | install.cpp | 34 | ||||
-rw-r--r-- | recovery.cpp | 95 | ||||
-rw-r--r-- | screen_ui.cpp (renamed from ui.cpp) | 393 | ||||
-rw-r--r-- | screen_ui.h | 121 | ||||
-rw-r--r-- | ui.h | 109 | ||||
-rw-r--r-- | verifier.cpp | 6 |
7 files changed, 436 insertions, 324 deletions
diff --git a/Android.mk b/Android.mk index 2c81be676..527aa1b77 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 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..d1af3ac05 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -39,6 +39,7 @@ #include "roots.h" #include "recovery_ui.h" #include "ui.h" +#include "screen_ui.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); @@ -425,22 +428,22 @@ get_menu_selection(const char* const * headers, const char* const * items, int menu_only, int initial_selection) { // 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(); + ui->EndMenu(); return ITEM_REBOOT; } } @@ -451,11 +454,11 @@ get_menu_selection(const char* const * headers, const char* const * items, switch (action) { case HIGHLIGHT_UP: --selected; - selected = ui_menu_select(selected); + selected = ui->SelectMenu(selected); break; case HIGHLIGHT_DOWN: ++selected; - selected = ui_menu_select(selected); + selected = ui->SelectMenu(selected); break; case SELECT_ITEM: chosen_item = selected; @@ -468,7 +471,7 @@ get_menu_selection(const char* const * headers, const char* const * items, } } - ui_end_menu(); + ui->EndMenu(); return chosen_item; } @@ -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) { @@ -636,11 +639,11 @@ wipe_data(int confirm) { } } - ui_print("\n-- Wiping data...\n"); + ui->Print("\n-- Wiping data...\n"); device_wipe_data(); erase_volume("/data"); erase_volume("/cache"); - ui_print("Data wipe complete.\n"); + ui->Print("Data wipe complete.\n"); } static void @@ -649,7 +652,7 @@ prompt_and_wait() { for (;;) { finish_recovery(NULL); - ui_reset_progress(); + ui->SetProgressType(RecoveryUI::EMPTY); int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0); @@ -665,35 +668,35 @@ prompt_and_wait() { return; case ITEM_WIPE_DATA: - wipe_data(ui_text_visible()); - if (!ui_text_visible()) return; + wipe_data(ui->IsTextVisible()); + if (!ui->IsTextVisible()) return; break; case ITEM_WIPE_CACHE: - ui_print("\n-- Wiping cache...\n"); + 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); 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; @@ -701,21 +704,21 @@ prompt_and_wait() { // Don't unmount cache at the end of this. status = update_directory(CACHE_ROOT, NULL, &wipe_cache); 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,13 @@ 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_init(); - ui_set_background(BACKGROUND_ICON_INSTALLING); + ui = new ScreenRecoveryUI(); + + ui->Init(); + ui->SetBackground(RecoveryUI::INSTALLING); load_volume_table(); get_args(&argc, &argv); @@ -757,7 +764,7 @@ 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; @@ -800,27 +807,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 (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()) { + if (status != INSTALL_SUCCESS) ui->SetBackground(RecoveryUI::ERROR); + if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { prompt_and_wait(); } // 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; } @@ -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. @@ -33,9 +33,7 @@ #include "minui/minui.h" #include "recovery_ui.h" #include "ui.h" - -#define MAX_COLS 96 -#define MAX_ROWS 32 +#include "screen_ui.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,40 @@ 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) { + 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 +101,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 +141,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 +207,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 +228,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,9 +277,7 @@ 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; @@ -316,51 +295,51 @@ 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) { + self->rel_sum += ev.value; + if (self->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) { + self->rel_sum = 0; + } else if (self->rel_sum < -3) { fake_key = 1; ev.type = EV_KEY; ev.code = KEY_UP; ev.value = 1; - rel_sum = 0; + self->rel_sum = 0; } } } else { - rel_sum = 0; + self->rel_sum = 0; } if (ev.type != EV_KEY || ev.code > KEY_MAX) return 0; - pthread_mutex_lock(&key_queue_mutex); + 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. - key_pressed[ev.code] = ev.value; + self->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); + 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(&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); + pthread_mutex_unlock(&self->key_queue_mutex); + + 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); } - if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) { + if (ev.value > 0 && device_reboot_now(self->key_pressed, ev.code)) { android_reboot(ANDROID_RB_RESTART, 0, 0); } @@ -368,7 +347,7 @@ static int input_callback(int fd, short revents, void *data) } // 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 +356,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 +479,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 +494,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 +518,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 +532,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 +590,7 @@ static int usb_connected() { return connected; } -int ui_wait_key() +int ScreenRecoveryUI::WaitKey() { pthread_mutex_lock(&key_queue_mutex); @@ -653,13 +620,13 @@ 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]; } -void ui_clear_key_queue() { +void ScreenRecoveryUI::FlushKeys() { pthread_mutex_lock(&key_queue_mutex); key_queue_len = 0; pthread_mutex_unlock(&key_queue_mutex); diff --git a/screen_ui.h b/screen_ui.h new file mode 100644 index 000000000..544f1543c --- /dev/null +++ b/screen_ui.h @@ -0,0 +1,121 @@ +/* + * 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(); + + // 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; + volatile char key_pressed[KEY_MAX + 1]; + 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); + static void* input_thread(void* cookie); + + bool usb_connected(); + + void LoadBitmap(const char* filename, gr_surface* surface); + +}; + +#endif // RECOVERY_UI_H @@ -17,59 +17,68 @@ #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; -// 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 IsTextVisible() = 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; + virtual bool WasTextEverVisible() = 0; -// Show a rotating "barberpole" for ongoing operations. Updates automatically. -void ui_show_indeterminate_progress(); + // 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; -// Hide and reset the progress bar. -void ui_reset_progress(); + // --- key handling --- + + // 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; + + // --- 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/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; } } |