summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoug Zongker <dougz@android.com>2011-10-31 17:34:15 +0100
committerDoug Zongker <dougz@android.com>2011-10-31 21:50:32 +0100
commit7d0542f28045640dfab6a259ae7bd796e653d66f (patch)
tree801e5acd7cb5a101182f325e1d4f343581b35801
parentrefactor ui functions into a class (diff)
downloadandroid_bootable_recovery-7d0542f28045640dfab6a259ae7bd796e653d66f.tar
android_bootable_recovery-7d0542f28045640dfab6a259ae7bd796e653d66f.tar.gz
android_bootable_recovery-7d0542f28045640dfab6a259ae7bd796e653d66f.tar.bz2
android_bootable_recovery-7d0542f28045640dfab6a259ae7bd796e653d66f.tar.lz
android_bootable_recovery-7d0542f28045640dfab6a259ae7bd796e653d66f.tar.xz
android_bootable_recovery-7d0542f28045640dfab6a259ae7bd796e653d66f.tar.zst
android_bootable_recovery-7d0542f28045640dfab6a259ae7bd796e653d66f.zip
-rw-r--r--Android.mk2
-rw-r--r--default_device.cpp92
-rw-r--r--default_recovery_ui.c73
-rw-r--r--device.h112
-rw-r--r--recovery.cpp70
-rw-r--r--recovery_ui.h95
-rw-r--r--screen_ui.cpp105
-rw-r--r--screen_ui.h8
-rw-r--r--ui.h7
-rw-r--r--updater/install.c3
10 files changed, 322 insertions, 245 deletions
diff --git a/Android.mk b/Android.mk
index 527aa1b77..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
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();
diff --git a/ui.h b/ui.h
index 6150bfd50..3ca99a614 100644
--- a/ui.h
+++ b/ui.h
@@ -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;