diff options
Diffstat (limited to '')
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | common.h | 19 | ||||
-rw-r--r-- | recovery.cpp | 4 | ||||
-rw-r--r-- | screen_ui.cpp | 254 | ||||
-rw-r--r-- | screen_ui.h | 29 | ||||
-rw-r--r-- | ui.cpp | 222 | ||||
-rw-r--r-- | ui.h | 31 |
7 files changed, 296 insertions, 264 deletions
diff --git a/Android.mk b/Android.mk index be9ff9ec8..cc848904c 100644 --- a/Android.mk +++ b/Android.mk @@ -8,6 +8,7 @@ LOCAL_SRC_FILES := \ bootloader.cpp \ install.cpp \ roots.cpp \ + ui.cpp \ screen_ui.cpp \ verifier.cpp @@ -58,25 +58,6 @@ typedef struct { // (that much). } Volume; -typedef struct { - // number of frames in indeterminate progress bar animation - int indeterminate_frames; - - // number of frames per second to try to maintain when animating - int update_fps; - - // number of frames in installing animation. may be zero for a - // static installation icon. - int installing_frames; - - // the install icon is animated by drawing images containing the - // changing part over the base icon. These specify the - // coordinates of the upper-left corner. - int install_overlay_offset_x; - int install_overlay_offset_y; - -} UIParameters; - // fopen a file, mounting volumes and making parent dirs as necessary. FILE* fopen_path(const char *path, const char *mode); diff --git a/recovery.cpp b/recovery.cpp index d028cc917..a0d96d2aa 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -61,8 +61,6 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; -extern UIParameters ui_parameters; // from ui.c - RecoveryUI* ui = NULL; /* @@ -745,7 +743,7 @@ main(int argc, char **argv) { ui = device->GetUI(); ui->Init(); - ui->SetBackground(RecoveryUI::INSTALLING); + ui->SetBackground(RecoveryUI::NONE); load_volume_table(); get_args(&argc, &argv); diff --git a/screen_ui.cpp b/screen_ui.cpp index a60b04682..2a8652ecf 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -29,24 +29,14 @@ #include <unistd.h> #include "common.h" -#include <cutils/android_reboot.h> +#include "device.h" #include "minui/minui.h" -#include "ui.h" #include "screen_ui.h" -#include "device.h" +#include "ui.h" #define CHAR_WIDTH 10 #define CHAR_HEIGHT 18 -#define UI_WAIT_KEY_TIMEOUT_SEC 120 - -UIParameters ui_parameters = { - 6, // indeterminate progress bar frames - 20, // fps - 7, // installation icon frames (0 == static image) - 13, 190, // installation icon overlay offset -}; - // 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. @@ -78,11 +68,18 @@ ScreenRecoveryUI::ScreenRecoveryUI() : menu_top(0), menu_items(0), menu_sel(0), - key_queue_len(0), - key_last_down(-1) { + + // These values are correct for the default image resources + // provided with the android platform. Devices which use + // different resources should have a subclass of ScreenRecoveryUI + // that overrides Init() to set these values appropriately and + // then call the superclass Init(). + animation_fps(20), + indeterminate_frames(6), + installing_frames(7), + install_overlay_offset_x(13), + install_overlay_offset_y(190) { pthread_mutex_init(&updateMutex, NULL); - pthread_mutex_init(&key_queue_mutex, NULL); - pthread_cond_init(&key_queue_cond, NULL); self = this; } @@ -97,8 +94,7 @@ void ScreenRecoveryUI::draw_install_overlay_locked(int frame) { int iconWidth = gr_get_width(surface); int iconHeight = gr_get_height(surface); gr_blit(surface, 0, 0, iconWidth, iconHeight, - ui_parameters.install_overlay_offset_x, - ui_parameters.install_overlay_offset_y); + install_overlay_offset_x, install_overlay_offset_y); } // Clear the screen and draw the currently selected background icon (if any). @@ -157,7 +153,7 @@ void ScreenRecoveryUI::draw_progress_locked() if (progressBarType == INDETERMINATE) { static int frame = 0; gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy); - frame = (frame + 1) % ui_parameters.indeterminate_frames; + frame = (frame + 1) % indeterminate_frames; } } } @@ -229,35 +225,36 @@ void ScreenRecoveryUI::update_progress_locked() } // Keeps the progress bar updated, even when the process is otherwise busy. -void* ScreenRecoveryUI::progress_thread(void *cookie) -{ - double interval = 1.0 / ui_parameters.update_fps; +void* ScreenRecoveryUI::progress_thread(void *cookie) { + self->progress_loop(); + return NULL; +} + +void ScreenRecoveryUI::progress_loop() { + double interval = 1.0 / animation_fps; for (;;) { double start = now(); - pthread_mutex_lock(&self->updateMutex); + pthread_mutex_lock(&updateMutex); int redraw = 0; // update the installation animation, if active // skip this if we have a text overlay (too expensive to update) - if (self->currentIcon == INSTALLING && - ui_parameters.installing_frames > 0 && - !self->show_text) { - self->installingFrame = - (self->installingFrame + 1) % ui_parameters.installing_frames; + if (currentIcon == INSTALLING && installing_frames > 0 && !show_text) { + installingFrame = (installingFrame + 1) % installing_frames; redraw = 1; } // update the progress bar animation, if active // skip this if we have a text overlay (too expensive to update) - if (self->progressBarType == INDETERMINATE && !self->show_text) { + if (progressBarType == INDETERMINATE && !show_text) { redraw = 1; } // move the progress bar forward on timed intervals, if configured - int duration = self->progressScopeDuration; - if (self->progressBarType == DETERMINATE && duration > 0) { - double elapsed = now() - self->progressScopeTime; + int duration = progressScopeDuration; + if (progressBarType == DETERMINATE && duration > 0) { + double elapsed = now() - progressScopeTime; float progress = 1.0 * elapsed / duration; if (progress > 1.0) progress = 1.0; if (progress > progress) { @@ -266,117 +263,15 @@ void* ScreenRecoveryUI::progress_thread(void *cookie) } } - if (redraw) self->update_progress_locked(); + if (redraw) update_progress_locked(); - pthread_mutex_unlock(&self->updateMutex); + pthread_mutex_unlock(&updateMutex); double end = now(); // minimum of 20ms delay between frames double delay = interval - (end-start); if (delay < 0.02) delay = 0.02; usleep((long)(delay * 1000000)); } - return NULL; -} - -int ScreenRecoveryUI::input_callback(int fd, short revents, void* data) -{ - struct input_event ev; - int ret; - - ret = ev_get_input(fd, revents, &ev); - if (ret) - return -1; - - if (ev.type == EV_SYN) { - return 0; - } else if (ev.type == EV_REL) { - if (ev.code == REL_Y) { - // accumulate the up or down motion reported by - // the trackball. When it exceeds a threshold - // (positive or negative), fake an up/down - // key event. - 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 { - self->rel_sum = 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); - 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 (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. -void* ScreenRecoveryUI::input_thread(void *cookie) -{ - for (;;) { - if (!ev_wait(-1)) - ev_dispatch(); - } - return NULL; } void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) { @@ -389,7 +284,6 @@ void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) { void ScreenRecoveryUI::Init() { gr_init(); - ev_init(input_callback, NULL); text_col = text_row = 0; text_rows = gr_fb_height() / CHAR_HEIGHT; @@ -406,19 +300,19 @@ void ScreenRecoveryUI::Init() int i; - progressBarIndeterminate = (gr_surface*)malloc(ui_parameters.indeterminate_frames * + progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames * sizeof(gr_surface)); - for (i = 0; i < ui_parameters.indeterminate_frames; ++i) { + for (i = 0; i < indeterminate_frames; ++i) { char filename[40]; // "indeterminate01.png", "indeterminate02.png", ... sprintf(filename, "indeterminate%02d", i+1); LoadBitmap(filename, progressBarIndeterminate+i); } - if (ui_parameters.installing_frames > 0) { - installationOverlay = (gr_surface*)malloc(ui_parameters.installing_frames * + if (installing_frames > 0) { + installationOverlay = (gr_surface*)malloc(installing_frames * sizeof(gr_surface)); - for (i = 0; i < ui_parameters.installing_frames; ++i) { + for (i = 0; i < installing_frames; ++i) { char filename[40]; // "icon_installing_overlay01.png", // "icon_installing_overlay02.png", ... @@ -430,17 +324,16 @@ void ScreenRecoveryUI::Init() // base image on the screen. 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; + install_overlay_offset_x += (gr_fb_width() - gr_get_width(bg)) / 2; + install_overlay_offset_y += (gr_fb_height() - gr_get_height(bg)) / 2; } } else { installationOverlay = NULL; } pthread_create(&progress_t, NULL, progress_thread, NULL); - pthread_create(&input_t, NULL, input_thread, NULL); + + RecoveryUI::Init(); } void ScreenRecoveryUI::SetBackground(Icon icon) @@ -593,70 +486,3 @@ void ScreenRecoveryUI::ShowText(bool visible) update_screen_locked(); pthread_mutex_unlock(&updateMutex); } - -// Return true if USB is 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", - strerror(errno)); - return 0; - } - - char buf; - /* USB is connected if android_usb state is CONNECTED or CONFIGURED */ - int connected = (read(fd, &buf, 1) == 1) && (buf == 'C'); - if (close(fd) < 0) { - printf("failed to close /sys/class/android_usb/android0/state: %s\n", - strerror(errno)); - } - return connected; -} - -int ScreenRecoveryUI::WaitKey() -{ - pthread_mutex_lock(&key_queue_mutex); - - // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is - // plugged in. - do { - struct timeval now; - struct timespec timeout; - gettimeofday(&now, NULL); - timeout.tv_sec = now.tv_sec; - timeout.tv_nsec = now.tv_usec * 1000; - timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC; - - int rc = 0; - while (key_queue_len == 0 && rc != ETIMEDOUT) { - rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, - &timeout); - } - } while (usb_connected() && key_queue_len == 0); - - int key = -1; - if (key_queue_len > 0) { - key = key_queue[0]; - memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len); - } - pthread_mutex_unlock(&key_queue_mutex); - return key; -} - -bool ScreenRecoveryUI::IsKeyPressed(int key) -{ - pthread_mutex_lock(&key_queue_mutex); - int pressed = key_pressed[key]; - pthread_mutex_unlock(&key_queue_mutex); - return pressed; -} - -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 index a5ec0d360..34929ee1a 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -43,15 +43,6 @@ class ScreenRecoveryUI : public RecoveryUI { 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))); @@ -95,16 +86,12 @@ class ScreenRecoveryUI : public RecoveryUI { 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; + + int animation_fps; + int indeterminate_frames; + int installing_frames; + int install_overlay_offset_x, install_overlay_offset_y; void draw_install_overlay_locked(int frame); void draw_background_locked(Icon icon); @@ -114,11 +101,7 @@ class ScreenRecoveryUI : public RecoveryUI { 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 progress_loop(); void LoadBitmap(const char* filename, gr_surface* surface); @@ -0,0 +1,222 @@ +/* + * 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. + */ + +#include <errno.h> +#include <fcntl.h> +#include <linux/input.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +#include <cutils/android_reboot.h> + +#include "common.h" +#include "device.h" +#include "minui/minui.h" +#include "screen_ui.h" +#include "ui.h" + +#define UI_WAIT_KEY_TIMEOUT_SEC 120 + +// 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 RecoveryUI* self = NULL; + +RecoveryUI::RecoveryUI() : + key_queue_len(0), + key_last_down(-1) { + pthread_mutex_init(&key_queue_mutex, NULL); + pthread_cond_init(&key_queue_cond, NULL); + self = this; +} + +void RecoveryUI::Init() { + ev_init(input_callback, NULL); + pthread_create(&input_t, NULL, input_thread, NULL); +} + + +int RecoveryUI::input_callback(int fd, short revents, void* data) +{ + struct input_event ev; + int ret; + + ret = ev_get_input(fd, revents, &ev); + if (ret) + return -1; + + if (ev.type == EV_SYN) { + return 0; + } else if (ev.type == EV_REL) { + if (ev.code == REL_Y) { + // accumulate the up or down motion reported by + // the trackball. When it exceeds a threshold + // (positive or negative), fake an up/down + // key event. + 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 { + self->rel_sum = 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 RecoveryUI::process_key(int key_code, int updown) { + bool register_key = false; + + 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); + + if (register_key) { + switch (CheckKey(key_code)) { + case RecoveryUI::TOGGLE: + ShowText(!IsTextVisible()); + 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. +void* RecoveryUI::input_thread(void *cookie) +{ + for (;;) { + if (!ev_wait(-1)) + ev_dispatch(); + } + return NULL; +} + +int RecoveryUI::WaitKey() +{ + pthread_mutex_lock(&key_queue_mutex); + + // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is + // plugged in. + do { + struct timeval now; + struct timespec timeout; + gettimeofday(&now, NULL); + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = now.tv_usec * 1000; + timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC; + + int rc = 0; + while (key_queue_len == 0 && rc != ETIMEDOUT) { + rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, + &timeout); + } + } while (usb_connected() && key_queue_len == 0); + + int key = -1; + if (key_queue_len > 0) { + key = key_queue[0]; + memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len); + } + pthread_mutex_unlock(&key_queue_mutex); + return key; +} + +// Return true if USB is connected. +bool RecoveryUI::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", + strerror(errno)); + return 0; + } + + char buf; + /* USB is connected if android_usb state is CONNECTED or CONFIGURED */ + int connected = (read(fd, &buf, 1) == 1) && (buf == 'C'); + if (close(fd) < 0) { + printf("failed to close /sys/class/android_usb/android0/state: %s\n", + strerror(errno)); + } + return connected; +} + +bool RecoveryUI::IsKeyPressed(int key) +{ + pthread_mutex_lock(&key_queue_mutex); + int pressed = key_pressed[key]; + pthread_mutex_unlock(&key_queue_mutex); + return pressed; +} + +void RecoveryUI::FlushKeys() { + pthread_mutex_lock(&key_queue_mutex); + key_queue_len = 0; + pthread_mutex_unlock(&key_queue_mutex); +} + +RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { + return RecoveryUI::ENQUEUE; +} @@ -17,13 +17,18 @@ #ifndef RECOVERY_UI_H #define RECOVERY_UI_H +#include <linux/input.h> +#include <pthread.h> + // Abstract class for controlling the user interface during recovery. class RecoveryUI { public: + RecoveryUI(); + virtual ~RecoveryUI() { } // Initialize the object; called before anything else. - virtual void Init() = 0; + virtual void Init(); // Set the overall recovery state ("background image"). enum Icon { NONE, INSTALLING, ERROR }; @@ -57,19 +62,19 @@ class RecoveryUI { // --- key handling --- // Wait for keypress and return it. May return -1 after timeout. - virtual int WaitKey() = 0; + virtual int WaitKey(); - virtual bool IsKeyPressed(int key) = 0; + virtual bool IsKeyPressed(int key); // Erase any queued-up keys. - virtual void FlushKeys() = 0; + virtual void FlushKeys(); // 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; + virtual KeyAction CheckKey(int key); // --- menu display --- @@ -86,6 +91,22 @@ class RecoveryUI { // End menu mode, resetting the text overlay so that ui_print() // statements will be displayed. virtual void EndMenu() = 0; + +private: + // 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 input_t; + + static void* input_thread(void* cookie); + static int input_callback(int fd, short revents, void* data); + void process_key(int key_code, int updown); + bool usb_connected(); }; #endif // RECOVERY_UI_H |