summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoug Zongker <dougz@android.com>2011-11-01 19:00:20 +0100
committerDoug Zongker <dougz@android.com>2011-11-04 22:09:48 +0100
commit32a0a47a596509792fde8cdbf8b4b0705708b2be (patch)
tree55406d412c0a355dc4bdcc3fc0f2136a68af77d3
parentC++ class for device-specific code (diff)
downloadandroid_bootable_recovery-32a0a47a596509792fde8cdbf8b4b0705708b2be.tar
android_bootable_recovery-32a0a47a596509792fde8cdbf8b4b0705708b2be.tar.gz
android_bootable_recovery-32a0a47a596509792fde8cdbf8b4b0705708b2be.tar.bz2
android_bootable_recovery-32a0a47a596509792fde8cdbf8b4b0705708b2be.tar.lz
android_bootable_recovery-32a0a47a596509792fde8cdbf8b4b0705708b2be.tar.xz
android_bootable_recovery-32a0a47a596509792fde8cdbf8b4b0705708b2be.tar.zst
android_bootable_recovery-32a0a47a596509792fde8cdbf8b4b0705708b2be.zip
Diffstat (limited to '')
-rw-r--r--Android.mk5
-rw-r--r--common.h19
-rw-r--r--recovery.cpp4
-rw-r--r--screen_ui.cpp254
-rw-r--r--screen_ui.h29
-rw-r--r--ui.cpp222
-rw-r--r--ui.h31
-rw-r--r--verifier_test.cpp5
8 files changed, 298 insertions, 271 deletions
diff --git a/Android.mk b/Android.mk
index be9ff9ec8..a94ecc668 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
@@ -50,7 +51,7 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := verifier_test.cpp verifier.cpp
+LOCAL_SRC_FILES := verifier_test.cpp verifier.cpp ui.cpp
LOCAL_MODULE := verifier_test
@@ -58,7 +59,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libstdc++ libc
+LOCAL_STATIC_LIBRARIES := libmincrypt libminui libcutils libstdc++ libc
include $(BUILD_EXECUTABLE)
diff --git a/common.h b/common.h
index 88807b880..a1168cdbb 100644
--- a/common.h
+++ b/common.h
@@ -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);
diff --git a/ui.cpp b/ui.cpp
new file mode 100644
index 000000000..fd370a79f
--- /dev/null
+++ b/ui.cpp
@@ -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;
+}
diff --git a/ui.h b/ui.h
index 3ca99a614..750b99333 100644
--- a/ui.h
+++ b/ui.h
@@ -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
diff --git a/verifier_test.cpp b/verifier_test.cpp
index 2448d8d58..fe5519d79 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -84,11 +84,6 @@ class FakeUI : public RecoveryUI {
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; }