diff options
-rw-r--r-- | Android.mk | 19 | ||||
-rw-r--r-- | bootloader.cpp (renamed from bootloader.c) | 0 | ||||
-rw-r--r-- | bootloader.h | 8 | ||||
-rw-r--r-- | common.h | 82 | ||||
-rw-r--r-- | default_device.cpp | 92 | ||||
-rw-r--r-- | default_recovery_ui.c | 73 | ||||
-rw-r--r-- | device.h | 112 | ||||
-rw-r--r-- | install.cpp (renamed from install.c) | 96 | ||||
-rw-r--r-- | install.h | 8 | ||||
-rw-r--r-- | minui/minui.h | 8 | ||||
-rw-r--r-- | minzip/DirUtil.h | 8 | ||||
-rw-r--r-- | minzip/Zip.h | 8 | ||||
-rw-r--r-- | mtdutils/mounts.h | 8 | ||||
-rw-r--r-- | mtdutils/mtdutils.h | 8 | ||||
-rw-r--r-- | recovery.cpp (renamed from recovery.c) | 228 | ||||
-rw-r--r-- | recovery_ui.h | 87 | ||||
-rw-r--r-- | roots.cpp (renamed from roots.c) | 6 | ||||
-rw-r--r-- | roots.h | 8 | ||||
-rw-r--r-- | screen_ui.cpp | 488 | ||||
-rw-r--r-- | screen_ui.h | 110 | ||||
-rw-r--r-- | ui.c | 664 | ||||
-rw-r--r-- | ui.cpp | 222 | ||||
-rw-r--r-- | ui.h | 112 | ||||
-rw-r--r-- | updater/install.c | 3 | ||||
-rw-r--r-- | verifier.cpp (renamed from verifier.c) | 19 | ||||
-rw-r--r-- | verifier_test.cpp (renamed from verifier_test.c) | 41 | ||||
-rwxr-xr-x | verifier_test.sh | 8 |
27 files changed, 1437 insertions, 1089 deletions
diff --git a/Android.mk b/Android.mk index 282862fc9..a94ecc668 100644 --- a/Android.mk +++ b/Android.mk @@ -4,12 +4,13 @@ include $(CLEAR_VARS) commands_recovery_local_path := $(LOCAL_PATH) LOCAL_SRC_FILES := \ - recovery.c \ - bootloader.c \ - install.c \ - roots.c \ - ui.c \ - verifier.c + recovery.cpp \ + bootloader.cpp \ + install.cpp \ + roots.cpp \ + ui.cpp \ + screen_ui.cpp \ + verifier.cpp LOCAL_MODULE := recovery @@ -34,7 +35,7 @@ endif LOCAL_MODULE_TAGS := eng ifeq ($(TARGET_RECOVERY_UI_LIB),) - LOCAL_SRC_FILES += default_recovery_ui.c + LOCAL_SRC_FILES += default_device.cpp else LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) endif @@ -50,7 +51,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) -LOCAL_SRC_FILES := verifier_test.c verifier.c +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/bootloader.c b/bootloader.cpp index baaddc55f..baaddc55f 100644 --- a/bootloader.c +++ b/bootloader.cpp diff --git a/bootloader.h b/bootloader.h index 2e749aa12..712aa1a2d 100644 --- a/bootloader.h +++ b/bootloader.h @@ -17,6 +17,10 @@ #ifndef _RECOVERY_BOOTLOADER_H #define _RECOVERY_BOOTLOADER_H +#ifdef __cplusplus +extern "C" { +#endif + /* Bootloader Message * * This structure describes the content of a block in flash @@ -47,4 +51,8 @@ struct bootloader_message { int get_bootloader_message(struct bootloader_message *out); int set_bootloader_message(const struct bootloader_message *in); +#ifdef __cplusplus +} +#endif + #endif @@ -19,61 +19,12 @@ #include <stdio.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(char** headers, char** 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); - -// 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 - -// 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; - -// Show a rotating "barberpole" for ongoing operations. Updates automatically. -void ui_show_indeterminate_progress(); - -// Hide and reset the progress bar. -void ui_reset_progress(); +#ifdef __cplusplus +extern "C" { +#endif -#define LOGE(...) ui_print("E:" __VA_ARGS__) +// TODO: restore ui_print for LOGE +#define LOGE(...) fprintf(stdout, "E:" __VA_ARGS__) #define LOGW(...) fprintf(stdout, "W:" __VA_ARGS__) #define LOGI(...) fprintf(stdout, "I:" __VA_ARGS__) @@ -107,26 +58,11 @@ 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); +#ifdef __cplusplus +} +#endif + #endif // RECOVERY_COMMON_H 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/install.c b/install.cpp index 9d7595e1a..078343332 100644 --- a/install.c +++ b/install.cpp @@ -32,10 +32,19 @@ #include "mtdutils/mtdutils.h" #include "roots.h" #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) { @@ -46,7 +55,7 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { return INSTALL_CORRUPT; } - char* binary = "/tmp/update_binary"; + const char* binary = "/tmp/update_binary"; unlink(binary); int fd = creat(binary, 0755); if (fd < 0) { @@ -100,18 +109,19 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { // - the name of the package zip file. // - char** args = malloc(sizeof(char*) * 5); + const char** args = (const char**)malloc(sizeof(char*) * 5); args[0] = binary; args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk - args[2] = malloc(10); - sprintf(args[2], "%d", pipefd[1]); + char* temp = (char*)malloc(10); + sprintf(temp, "%d", pipefd[1]); + args[2] = temp; args[3] = (char*)path; args[4] = NULL; pid_t pid = fork(); if (pid == 0) { close(pipefd[0]); - execv(binary, args); + execv(binary, (char* const*)args); fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); _exit(-1); } @@ -132,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; @@ -188,31 +197,32 @@ load_keys(const char* filename, int* numKeys) { goto exit; } - int i; - bool done = false; - while (!done) { - ++*numKeys; - out = realloc(out, *numKeys * sizeof(RSAPublicKey)); - RSAPublicKey* key = out + (*numKeys - 1); - if (fscanf(f, " { %i , 0x%x , { %u", - &(key->len), &(key->n0inv), &(key->n[0])) != 3) { - goto exit; - } - if (key->len != RSANUMWORDS) { - LOGE("key length (%d) does not match expected size\n", key->len); - goto exit; - } - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; - } - if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; - } - fscanf(f, " } } "); + { + int i; + bool done = false; + while (!done) { + ++*numKeys; + out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey)); + RSAPublicKey* key = out + (*numKeys - 1); + if (fscanf(f, " { %i , 0x%x , { %u", + &(key->len), &(key->n0inv), &(key->n[0])) != 3) { + goto exit; + } + if (key->len != RSANUMWORDS) { + LOGE("key length (%d) does not match expected size\n", key->len); + goto exit; + } + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; + } + if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; + } + fscanf(f, " } } "); - // if the line ends in a comma, this file has more keys. - switch (fgetc(f)) { + // if the line ends in a comma, this file has more keys. + switch (fgetc(f)) { case ',': // more keys to come. break; @@ -224,6 +234,7 @@ load_keys(const char* filename, int* numKeys) { default: LOGE("unexpected character between keys\n"); goto exit; + } } } @@ -240,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) { @@ -250,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); @@ -261,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); @@ -286,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); } @@ -19,6 +19,10 @@ #include "common.h" +#ifdef __cplusplus +extern "C" { +#endif + enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT }; // Install the package specified by root_path. If INSTALL_SUCCESS is // returned and *wipe_cache is true on exit, caller should wipe the @@ -26,4 +30,8 @@ enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT }; int install_package(const char *root_path, int* wipe_cache, const char* install_file); +#ifdef __cplusplus +} +#endif + #endif // RECOVERY_INSTALL_H_ diff --git a/minui/minui.h b/minui/minui.h index 2e2f1f477..74da4e9c0 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -19,6 +19,10 @@ #include <stdbool.h> +#ifdef __cplusplus +extern "C" { +#endif + typedef void* gr_surface; typedef unsigned short gr_pixel; @@ -69,4 +73,8 @@ void ev_dispatch(void); int res_create_surface(const char* name, gr_surface* pSurface); void res_free_surface(gr_surface surface); +#ifdef __cplusplus +} +#endif + #endif diff --git a/minzip/DirUtil.h b/minzip/DirUtil.h index 5d881f562..0d5ea7ccb 100644 --- a/minzip/DirUtil.h +++ b/minzip/DirUtil.h @@ -20,6 +20,10 @@ #include <stdbool.h> #include <utime.h> +#ifdef __cplusplus +extern "C" { +#endif + /* Like "mkdir -p", try to guarantee that all directories * specified in path are present, creating as many directories * as necessary. The specified mode is passed to all mkdir @@ -48,4 +52,8 @@ int dirUnlinkHierarchy(const char *path); int dirSetHierarchyPermissions(const char *path, int uid, int gid, int dirMode, int fileMode); +#ifdef __cplusplus +} +#endif + #endif // MINZIP_DIRUTIL_H_ diff --git a/minzip/Zip.h b/minzip/Zip.h index 9f99fba5b..739dbf5f2 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -14,6 +14,10 @@ #include "Hash.h" #include "SysUtil.h" +#ifdef __cplusplus +extern "C" { +#endif + /* * One entry in the Zip archive. Treat this as opaque -- use accessors below. * @@ -210,4 +214,8 @@ bool mzExtractRecursive(const ZipArchive *pArchive, int flags, const struct utimbuf *timestamp, void (*callback)(const char *fn, void*), void *cookie); +#ifdef __cplusplus +} +#endif + #endif /*_MINZIP_ZIP*/ diff --git a/mtdutils/mounts.h b/mtdutils/mounts.h index 30b2927c2..d721355b8 100644 --- a/mtdutils/mounts.h +++ b/mtdutils/mounts.h @@ -17,6 +17,10 @@ #ifndef MTDUTILS_MOUNTS_H_ #define MTDUTILS_MOUNTS_H_ +#ifdef __cplusplus +extern "C" { +#endif + typedef struct MountedVolume MountedVolume; int scan_mounted_volumes(void); @@ -30,4 +34,8 @@ int unmount_mounted_volume(const MountedVolume *volume); int remount_read_only(const MountedVolume* volume); +#ifdef __cplusplus +} +#endif + #endif // MTDUTILS_MOUNTS_H_ diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h index 45d3ebc91..2708c4318 100644 --- a/mtdutils/mtdutils.h +++ b/mtdutils/mtdutils.h @@ -19,6 +19,10 @@ #include <sys/types.h> // for size_t, etc. +#ifdef __cplusplus +extern "C" { +#endif + typedef struct MtdPartition MtdPartition; int mtd_scan_partitions(void); @@ -53,4 +57,8 @@ off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */ off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos); int mtd_write_close(MtdWriteContext *); +#ifdef __cplusplus +} +#endif + #endif // MTDUTILS_H_ diff --git a/recovery.c b/recovery.cpp index 06d649809..a0d96d2aa 100644 --- a/recovery.c +++ b/recovery.cpp @@ -37,7 +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' }, @@ -59,7 +61,7 @@ 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; /* * The recovery tool communicates with the main system through /cache files. @@ -290,9 +292,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); @@ -349,7 +351,7 @@ copy_sideloaded_package(const char* original_path) { strcpy(copy_path, SIDELOAD_TEMP_DIR); strcat(copy_path, "/package.zip"); - char* buffer = malloc(BUFSIZ); + char* buffer = (char*)malloc(BUFSIZ); if (buffer == NULL) { LOGE("Failed to allocate buffer\n"); return NULL; @@ -396,22 +398,22 @@ copy_sideloaded_package(const char* original_path) { return strdup(copy_path); } -static char** -prepend_title(const char** headers) { - char* title[] = { "Android system recovery <" - EXPAND(RECOVERY_API_VERSION) "e>", - "", - NULL }; +static const char** +prepend_title(const char* const* headers) { + const char* title[] = { "Android system recovery <" + EXPAND(RECOVERY_API_VERSION) "e>", + "", + NULL }; // count the number of lines in our title, plus the // caller-provided headers. int count = 0; - char** p; + const char* const* p; for (p = title; *p; ++p, ++count); for (p = headers; *p; ++p, ++count); - char** new_headers = malloc((count+1) * sizeof(char*)); - char** h = new_headers; + const char** new_headers = (const char**)malloc((count+1) * sizeof(char*)); + const char** h = new_headers; for (p = title; *p; ++p, ++h) *h = *p; for (p = headers; *p; ++p, ++h) *h = *p; *h = NULL; @@ -420,46 +422,46 @@ prepend_title(const char** headers) { } static int -get_menu_selection(char** headers, char** items, int menu_only, - int initial_selection) { +get_menu_selection(const char* const * headers, const char* const * items, + int menu_only, int initial_selection, Device* device) { // 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(); - return ITEM_REBOOT; + ui->EndMenu(); + 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_menu_select(selected); + selected = ui->SelectMenu(selected); break; - case HIGHLIGHT_DOWN: + case Device::kHighlightDown: ++selected; - selected = ui_menu_select(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) { @@ -467,7 +469,7 @@ get_menu_selection(char** headers, char** items, int menu_only, } } - ui_end_menu(); + ui->EndMenu(); return chosen_item; } @@ -477,7 +479,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:", @@ -495,14 +497,14 @@ update_directory(const char* path, const char* unmount_when_done, return 0; } - char** headers = prepend_title(MENU_HEADERS); + const char** headers = prepend_title(MENU_HEADERS); int d_size = 0; int d_alloc = 10; - char** dirs = malloc(d_alloc * sizeof(char*)); + char** dirs = (char**)malloc(d_alloc * sizeof(char*)); int z_size = 1; int z_alloc = 10; - char** zips = malloc(z_alloc * sizeof(char*)); + char** zips = (char**)malloc(z_alloc * sizeof(char*)); zips[0] = strdup("../"); while ((de = readdir(d)) != NULL) { @@ -516,9 +518,9 @@ update_directory(const char* path, const char* unmount_when_done, if (d_size >= d_alloc) { d_alloc *= 2; - dirs = realloc(dirs, d_alloc * sizeof(char*)); + dirs = (char**)realloc(dirs, d_alloc * sizeof(char*)); } - dirs[d_size] = malloc(name_len + 2); + dirs[d_size] = (char*)malloc(name_len + 2); strcpy(dirs[d_size], de->d_name); dirs[d_size][name_len] = '/'; dirs[d_size][name_len+1] = '\0'; @@ -528,7 +530,7 @@ update_directory(const char* path, const char* unmount_when_done, strncasecmp(de->d_name + (name_len-4), ".zip", 4) == 0) { if (z_size >= z_alloc) { z_alloc *= 2; - zips = realloc(zips, z_alloc * sizeof(char*)); + zips = (char**)realloc(zips, z_alloc * sizeof(char*)); } zips[z_size++] = strdup(de->d_name); } @@ -541,7 +543,7 @@ update_directory(const char* path, const char* unmount_when_done, // append dirs to the zips list if (d_size + z_size + 1 > z_alloc) { z_alloc = d_size + z_size + 1; - zips = realloc(zips, z_alloc * sizeof(char*)); + zips = (char**)realloc(zips, z_alloc * sizeof(char*)); } memcpy(zips + z_size, dirs, d_size * sizeof(char*)); free(dirs); @@ -551,7 +553,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); @@ -566,7 +568,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 @@ -576,7 +578,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) { @@ -604,121 +606,121 @@ 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 char** title_headers = NULL; + static const char** title_headers = NULL; if (title_headers == NULL) { - char* headers[] = { "Confirm wipe of all user data?", - " THIS CAN NOT BE UNDONE.", - "", - NULL }; + const char* headers[] = { "Confirm wipe of all user data?", + " THIS CAN NOT BE UNDONE.", + "", + NULL }; title_headers = prepend_title((const char**)headers); } - char* items[] = { " No", - " No", - " No", - " No", - " No", - " No", - " No", - " Yes -- delete all user data", // [7] - " No", - " No", - " No", - NULL }; - - int chosen_item = get_menu_selection(title_headers, items, 1, 0); + const char* items[] = { " No", + " No", + " No", + " No", + " No", + " No", + " No", + " Yes -- delete all user data", // [7] + " No", + " No", + " No", + NULL }; + + 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(); + ui->Print("\n-- Wiping data...\n"); + device->WipeData(); erase_volume("/data"); erase_volume("/cache"); - ui_print("Data wipe complete.\n"); + ui->Print("Data wipe complete.\n"); } static void -prompt_and_wait() { - 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_reset_progress(); + 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_text_visible()); - if (!ui_text_visible()) return; + case Device::WIPE_DATA: + wipe_data(ui->IsTextVisible(), device); + if (!ui->IsTextVisible()) return; break; - case ITEM_WIPE_CACHE: - ui_print("\n-- Wiping cache...\n"); + case Device::WIPE_CACHE: + 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); + 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"); + 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; - 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"); + 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; - } } } @@ -737,9 +739,11 @@ main(int argc, char **argv) { freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL); printf("Starting recovery on %s", ctime(&start)); - device_ui_init(&ui_parameters); - ui_init(); - ui_set_background(BACKGROUND_ICON_INSTALLING); + Device* device = make_device(); + ui = device->GetUI(); + + ui->Init(); + ui->SetBackground(RecoveryUI::NONE); load_volume_table(); get_args(&argc, &argv); @@ -756,14 +760,14 @@ 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; } } - device_recovery_start(); + device->StartRecovery(); printf("Command:"); for (arg = 0; arg < argc; arg++) { @@ -777,7 +781,7 @@ main(int argc, char **argv) { // "/cache/foo". if (strncmp(update_package, "CACHE:", 6) == 0) { int len = strlen(update_package) + 10; - char* modified_path = malloc(len); + char* modified_path = (char*)malloc(len); strlcpy(modified_path, "/cache/", len); strlcat(modified_path, update_package+6, len); printf("(replacing path \"%s\" with \"%s\")\n", @@ -799,27 +803,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 (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"); + 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()) { - prompt_and_wait(); + if (status != INSTALL_SUCCESS) ui->SetBackground(RecoveryUI::ERROR); + if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { + prompt_and_wait(device); } // 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; } diff --git a/recovery_ui.h b/recovery_ui.h deleted file mode 100644 index 5f0177045..000000000 --- a/recovery_ui.h +++ /dev/null @@ -1,87 +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" - -// 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[]; - -#endif @@ -33,7 +33,7 @@ static Volume* device_volumes = NULL; static int parse_options(char* options, Volume* volume) { char* option; - while (option = strtok(options, ",")) { + while ((option = strtok(options, ","))) { options = NULL; if (strncmp(option, "length=", 7) == 0) { @@ -48,7 +48,7 @@ static int parse_options(char* options, Volume* volume) { void load_volume_table() { int alloc = 2; - device_volumes = malloc(alloc * sizeof(Volume)); + device_volumes = (Volume*)malloc(alloc * sizeof(Volume)); // Insert an entry for /tmp, which is the ramdisk and is always mounted. device_volumes[0].mount_point = "/tmp"; @@ -91,7 +91,7 @@ void load_volume_table() { if (mount_point && fs_type && device) { while (num_volumes >= alloc) { alloc *= 2; - device_volumes = realloc(device_volumes, alloc*sizeof(Volume)); + device_volumes = (Volume*)realloc(device_volumes, alloc*sizeof(Volume)); } device_volumes[num_volumes].mount_point = strdup(mount_point); device_volumes[num_volumes].fs_type = strdup(fs_type); @@ -19,6 +19,10 @@ #include "common.h" +#ifdef __cplusplus +extern "C" { +#endif + // Load and parse volume data from /etc/recovery.fstab. void load_volume_table(); @@ -38,4 +42,8 @@ int ensure_path_unmounted(const char* path); // it is mounted. int format_volume(const char* volume); +#ifdef __cplusplus +} +#endif + #endif // RECOVERY_ROOTS_H_ diff --git a/screen_ui.cpp b/screen_ui.cpp new file mode 100644 index 000000000..2a8652ecf --- /dev/null +++ b/screen_ui.cpp @@ -0,0 +1,488 @@ +/* + * 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 "common.h" +#include "device.h" +#include "minui/minui.h" +#include "screen_ui.h" +#include "ui.h" + +#define CHAR_WIDTH 10 +#define CHAR_HEIGHT 18 + +// 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() { + struct timeval tv; + gettimeofday(&tv, NULL); + 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), + + // 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); + 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 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, + install_overlay_offset_x, install_overlay_offset_y); +} + +// Clear the screen and draw the currently selected background icon (if any). +// Should only be called with updateMutex locked. +void ScreenRecoveryUI::draw_background_locked(Icon icon) +{ + pagesIdentical = false; + gr_color(0, 0, 0, 255); + gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + + if (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 == INSTALLING) { + draw_install_overlay_locked(installingFrame); + } + } +} + +// Draw the progress bar (if any) on the screen. Does not flip pages. +// Should only be called with updateMutex locked. +void ScreenRecoveryUI::draw_progress_locked() +{ + if (currentIcon == INSTALLING) { + draw_install_overlay_locked(installingFrame); + } + + 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; + + // Erase behind the progress bar (in case this was a progress-only update) + gr_color(0, 0, 0, 255); + gr_fill(dx, dy, width, height); + + if (progressBarType == DETERMINATE) { + float p = progressScopeStart + progress * progressScopeSize; + int pos = (int) (p * width); + + if (pos > 0) { + gr_blit(progressBarFill, 0, 0, pos, height, dx, dy); + } + if (pos < width-1) { + gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); + } + } + + if (progressBarType == INDETERMINATE) { + static int frame = 0; + gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy); + frame = (frame + 1) % indeterminate_frames; + } + } +} + +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 updateMutex locked. +void ScreenRecoveryUI::draw_screen_locked() +{ + draw_background_locked(currentIcon); + draw_progress_locked(); + + if (show_text) { + gr_color(0, 0, 0, 160); + gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + + int i = 0; + if (show_menu) { + gr_color(64, 96, 255, 255); + gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT, + gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1); + + for (; i < menu_top + menu_items; ++i) { + if (i == menu_top + menu_sel) { + gr_color(255, 255, 255, 255); + draw_text_line(i, menu[i]); + gr_color(64, 96, 255, 255); + } else { + draw_text_line(i, menu[i]); + } + } + gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1, + gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1); + ++i; + } + + gr_color(255, 255, 0, 255); + + for (; i < text_rows; ++i) { + draw_text_line(i, text[(i+text_top) % text_rows]); + } + } +} + +// Redraw everything on the screen and flip the screen (make it visible). +// 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 updateMutex locked. +void ScreenRecoveryUI::update_progress_locked() +{ + if (show_text || !pagesIdentical) { + draw_screen_locked(); // Must redraw the whole screen + pagesIdentical = true; + } else { + draw_progress_locked(); // Draw only the progress bar and overlays + } + gr_flip(); +} + +// Keeps the progress bar updated, even when the process is otherwise busy. +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(&updateMutex); + + int redraw = 0; + + // update the installation animation, if active + // skip this if we have a text overlay (too expensive to update) + 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 (progressBarType == INDETERMINATE && !show_text) { + redraw = 1; + } + + // move the progress bar forward on timed intervals, if configured + 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) { + progress = progress; + redraw = 1; + } + } + + if (redraw) update_progress_locked(); + + 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)); + } +} + +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(); + + text_col = text_row = 0; + text_rows = gr_fb_height() / CHAR_HEIGHT; + if (text_rows > kMaxRows) text_rows = kMaxRows; + text_top = 1; + + text_cols = gr_fb_width() / CHAR_WIDTH; + 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; + + progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames * + sizeof(gr_surface)); + 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 (installing_frames > 0) { + installationOverlay = (gr_surface*)malloc(installing_frames * + sizeof(gr_surface)); + for (i = 0; i < installing_frames; ++i) { + char filename[40]; + // "icon_installing_overlay01.png", + // "icon_installing_overlay02.png", ... + sprintf(filename, "icon_installing_overlay%02d", i+1); + LoadBitmap(filename, installationOverlay+i); + } + + // Adjust the offset to account for the positioning of the + // base image on the screen. + if (backgroundIcon[INSTALLING] != NULL) { + gr_surface bg = backgroundIcon[INSTALLING]; + 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); + + RecoveryUI::Init(); +} + +void ScreenRecoveryUI::SetBackground(Icon icon) +{ + pthread_mutex_lock(&updateMutex); + currentIcon = icon; + update_screen_locked(); + pthread_mutex_unlock(&updateMutex); +} + +void ScreenRecoveryUI::SetProgressType(ProgressType type) +{ + pthread_mutex_lock(&updateMutex); + if (progressBarType != type) { + progressBarType = type; + update_progress_locked(); + } + pthread_mutex_unlock(&updateMutex); +} + +void ScreenRecoveryUI::ShowProgress(float portion, float seconds) +{ + pthread_mutex_lock(&updateMutex); + progressBarType = DETERMINATE; + progressScopeStart += progressScopeSize; + progressScopeSize = portion; + progressScopeTime = now(); + progressScopeDuration = seconds; + progress = 0; + update_progress_locked(); + pthread_mutex_unlock(&updateMutex); +} + +void ScreenRecoveryUI::SetProgress(float fraction) +{ + pthread_mutex_lock(&updateMutex); + if (fraction < 0.0) fraction = 0.0; + if (fraction > 1.0) fraction = 1.0; + if (progressBarType == DETERMINATE && fraction > progress) { + // Skip updates that aren't visibly different. + 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(&updateMutex); +} + +void ScreenRecoveryUI::Print(const char *fmt, ...) +{ + char buf[256]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, 256, fmt, ap); + va_end(ap); + + fputs(buf, stdout); + + // This can get called before ui_init(), so be careful. + pthread_mutex_lock(&updateMutex); + if (text_rows > 0 && text_cols > 0) { + char *ptr; + for (ptr = buf; *ptr != '\0'; ++ptr) { + if (*ptr == '\n' || text_col >= text_cols) { + text[text_row][text_col] = '\0'; + text_col = 0; + text_row = (text_row + 1) % text_rows; + if (text_row == text_top) text_top = (text_top + 1) % text_rows; + } + if (*ptr != '\n') text[text_row][text_col++] = *ptr; + } + text[text_row][text_col] = '\0'; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); +} + +void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items, + int initial_selection) { + int i; + pthread_mutex_lock(&updateMutex); + if (text_rows > 0 && text_cols > 0) { + for (i = 0; i < text_rows; ++i) { + if (headers[i] == NULL) break; + strncpy(menu[i], headers[i], text_cols-1); + menu[i][text_cols-1] = '\0'; + } + menu_top = i; + for (; i < text_rows; ++i) { + if (items[i-menu_top] == NULL) break; + strncpy(menu[i], items[i-menu_top], text_cols-1); + menu[i][text_cols-1] = '\0'; + } + menu_items = i - menu_top; + show_menu = 1; + menu_sel = initial_selection; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); +} + +int ScreenRecoveryUI::SelectMenu(int sel) { + int old_sel; + pthread_mutex_lock(&updateMutex); + if (show_menu > 0) { + old_sel = menu_sel; + menu_sel = sel; + if (menu_sel < 0) menu_sel = 0; + if (menu_sel >= menu_items) menu_sel = menu_items-1; + sel = menu_sel; + if (menu_sel != old_sel) update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); + return sel; +} + +void ScreenRecoveryUI::EndMenu() { + int i; + pthread_mutex_lock(&updateMutex); + if (show_menu > 0 && text_rows > 0 && text_cols > 0) { + show_menu = 0; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); +} + +bool ScreenRecoveryUI::IsTextVisible() +{ + pthread_mutex_lock(&updateMutex); + int visible = show_text; + pthread_mutex_unlock(&updateMutex); + return visible; +} + +bool ScreenRecoveryUI::WasTextEverVisible() +{ + pthread_mutex_lock(&updateMutex); + int ever_visible = show_text_ever; + pthread_mutex_unlock(&updateMutex); + return ever_visible; +} + +void ScreenRecoveryUI::ShowText(bool visible) +{ + pthread_mutex_lock(&updateMutex); + show_text = visible; + if (show_text) show_text_ever = 1; + update_screen_locked(); + pthread_mutex_unlock(&updateMutex); +} diff --git a/screen_ui.h b/screen_ui.h new file mode 100644 index 000000000..34929ee1a --- /dev/null +++ b/screen_ui.h @@ -0,0 +1,110 @@ +/* + * 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(); + + // 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; + + pthread_t progress_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); + 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); + void progress_loop(); + + void LoadBitmap(const char* filename, gr_surface* surface); + +}; + +#endif // RECOVERY_UI_H @@ -1,664 +0,0 @@ -/* - * Copyright (C) 2007 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 "common.h" -#include <cutils/android_reboot.h> -#include "minui/minui.h" -#include "recovery_ui.h" - -#define MAX_COLS 96 -#define MAX_ROWS 32 - -#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 -}; - -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]; - -// Return the current time as a double (including fractions of a second). -static double now() { - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_sec + tv.tv_usec / 1000000.0; -} - -// 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]; - 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); -} - -// 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) -{ - gPagesIdentical = 0; - gr_color(0, 0, 0, 255); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - - if (icon) { - gr_surface surface = gBackgroundIcon[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); - } - } -} - -// 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() -{ - if (gCurrentIcon == BACKGROUND_ICON_INSTALLING) { - draw_install_overlay_locked(gInstallingFrame); - } - - 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); - - int dx = (gr_fb_width() - width)/2; - int dy = (3*gr_fb_height() + iconHeight - 2*height)/4; - - // Erase behind the progress bar (in case this was a progress-only update) - 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 (pos > 0) { - gr_blit(gProgressBarFill, 0, 0, pos, height, dx, dy); - } - if (pos < width-1) { - gr_blit(gProgressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); - } - } - - if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) { - static int frame = 0; - gr_blit(gProgressBarIndeterminate[frame], 0, 0, width, height, dx, dy); - frame = (frame + 1) % ui_parameters.indeterminate_frames; - } - } -} - -static void 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) -{ - draw_background_locked(gCurrentIcon); - draw_progress_locked(); - - if (show_text) { - gr_color(0, 0, 0, 160); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - - int i = 0; - if (show_menu) { - gr_color(64, 96, 255, 255); - gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT, - gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1); - - for (; i < menu_top + menu_items; ++i) { - if (i == menu_top + menu_sel) { - gr_color(255, 255, 255, 255); - draw_text_line(i, menu[i]); - gr_color(64, 96, 255, 255); - } else { - draw_text_line(i, menu[i]); - } - } - gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1, - gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1); - ++i; - } - - gr_color(255, 255, 0, 255); - - for (; i < text_rows; ++i) { - draw_text_line(i, text[(i+text_top) % text_rows]); - } - } -} - -// 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) -{ - 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) -{ - if (show_text || !gPagesIdentical) { - draw_screen_locked(); // Must redraw the whole screen - gPagesIdentical = 1; - } else { - draw_progress_locked(); // Draw only the progress bar and overlays - } - gr_flip(); -} - -// Keeps the progress bar updated, even when the process is otherwise busy. -static void *progress_thread(void *cookie) -{ - double interval = 1.0 / ui_parameters.update_fps; - for (;;) { - double start = now(); - pthread_mutex_lock(&gUpdateMutex); - - 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 && - ui_parameters.installing_frames > 0 && - !show_text) { - gInstallingFrame = - (gInstallingFrame + 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) { - 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; - float progress = 1.0 * elapsed / duration; - if (progress > 1.0) progress = 1.0; - if (progress > gProgress) { - gProgress = progress; - redraw = 1; - } - } - - if (redraw) update_progress_locked(); - - pthread_mutex_unlock(&gUpdateMutex); - 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; -} - -static int rel_sum = 0; - -static int 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) - 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. - rel_sum += ev.value; - if (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) { - fake_key = 1; - ev.type = EV_KEY; - ev.code = KEY_UP; - ev.value = 1; - rel_sum = 0; - } - } - } else { - rel_sum = 0; - } - - if (ev.type != EV_KEY || ev.code > KEY_MAX) - return 0; - - pthread_mutex_lock(&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; - } - 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); - } - 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); - } - - if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) { - android_reboot(ANDROID_RB_RESTART, 0, 0); - } - - return 0; -} - -// Reads input events, handles special hot keys, and adds to the key queue. -static void *input_thread(void *cookie) -{ - for (;;) { - if (!ev_wait(-1)) - ev_dispatch(); - } - return NULL; -} - -void ui_init(void) -{ - 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; - text_top = 1; - - text_cols = gr_fb_width() / CHAR_WIDTH; - if (text_cols > MAX_COLS - 1) text_cols = MAX_COLS - 1; - - 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 = 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); - } - } - - if (ui_parameters.installing_frames > 0) { - gInstallationOverlay = 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); - } - } - - // 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]; - 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; - } - - pthread_t t; - pthread_create(&t, NULL, progress_thread, NULL); - pthread_create(&t, NULL, input_thread, NULL); -} - -void ui_set_background(int icon) -{ - pthread_mutex_lock(&gUpdateMutex); - gCurrentIcon = icon; - update_screen_locked(); - pthread_mutex_unlock(&gUpdateMutex); -} - -void ui_show_indeterminate_progress() -{ - pthread_mutex_lock(&gUpdateMutex); - if (gProgressBarType != PROGRESSBAR_TYPE_INDETERMINATE) { - gProgressBarType = PROGRESSBAR_TYPE_INDETERMINATE; - update_progress_locked(); - } - pthread_mutex_unlock(&gUpdateMutex); -} - -void ui_show_progress(float portion, int seconds) -{ - pthread_mutex_lock(&gUpdateMutex); - gProgressBarType = PROGRESSBAR_TYPE_NORMAL; - gProgressScopeStart += gProgressScopeSize; - gProgressScopeSize = portion; - gProgressScopeTime = now(); - gProgressScopeDuration = seconds; - gProgress = 0; - update_progress_locked(); - pthread_mutex_unlock(&gUpdateMutex); -} - -void ui_set_progress(float fraction) -{ - pthread_mutex_lock(&gUpdateMutex); - if (fraction < 0.0) fraction = 0.0; - if (fraction > 1.0) fraction = 1.0; - if (gProgressBarType == PROGRESSBAR_TYPE_NORMAL && fraction > gProgress) { - // 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; - 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); -} - -void ui_print(const char *fmt, ...) -{ - char buf[256]; - va_list ap; - va_start(ap, fmt); - vsnprintf(buf, 256, fmt, ap); - va_end(ap); - - fputs(buf, stdout); - - // This can get called before ui_init(), so be careful. - pthread_mutex_lock(&gUpdateMutex); - if (text_rows > 0 && text_cols > 0) { - char *ptr; - for (ptr = buf; *ptr != '\0'; ++ptr) { - if (*ptr == '\n' || text_col >= text_cols) { - text[text_row][text_col] = '\0'; - text_col = 0; - text_row = (text_row + 1) % text_rows; - if (text_row == text_top) text_top = (text_top + 1) % text_rows; - } - if (*ptr != '\n') text[text_row][text_col++] = *ptr; - } - text[text_row][text_col] = '\0'; - update_screen_locked(); - } - pthread_mutex_unlock(&gUpdateMutex); -} - -void ui_start_menu(char** headers, char** items, int initial_selection) { - int i; - pthread_mutex_lock(&gUpdateMutex); - if (text_rows > 0 && text_cols > 0) { - for (i = 0; i < text_rows; ++i) { - if (headers[i] == NULL) break; - strncpy(menu[i], headers[i], text_cols-1); - menu[i][text_cols-1] = '\0'; - } - menu_top = i; - for (; i < text_rows; ++i) { - if (items[i-menu_top] == NULL) break; - strncpy(menu[i], items[i-menu_top], text_cols-1); - menu[i][text_cols-1] = '\0'; - } - menu_items = i - menu_top; - show_menu = 1; - menu_sel = initial_selection; - update_screen_locked(); - } - pthread_mutex_unlock(&gUpdateMutex); -} - -int ui_menu_select(int sel) { - int old_sel; - pthread_mutex_lock(&gUpdateMutex); - if (show_menu > 0) { - old_sel = menu_sel; - menu_sel = sel; - if (menu_sel < 0) menu_sel = 0; - if (menu_sel >= menu_items) menu_sel = menu_items-1; - sel = menu_sel; - if (menu_sel != old_sel) update_screen_locked(); - } - pthread_mutex_unlock(&gUpdateMutex); - return sel; -} - -void ui_end_menu() { - int i; - pthread_mutex_lock(&gUpdateMutex); - if (show_menu > 0 && text_rows > 0 && text_cols > 0) { - show_menu = 0; - update_screen_locked(); - } - pthread_mutex_unlock(&gUpdateMutex); -} - -int ui_text_visible() -{ - pthread_mutex_lock(&gUpdateMutex); - int visible = show_text; - pthread_mutex_unlock(&gUpdateMutex); - return visible; -} - -int ui_text_ever_visible() -{ - pthread_mutex_lock(&gUpdateMutex); - int ever_visible = show_text_ever; - pthread_mutex_unlock(&gUpdateMutex); - return ever_visible; -} - -void ui_show_text(int visible) -{ - pthread_mutex_lock(&gUpdateMutex); - show_text = visible; - if (show_text) show_text_ever = 1; - update_screen_locked(); - pthread_mutex_unlock(&gUpdateMutex); -} - -// Return true if USB is connected. -static int 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 ui_wait_key() -{ - 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; -} - -int ui_key_pressed(int key) -{ - // This is a volatile static array, don't bother locking - return key_pressed[key]; -} - -void ui_clear_key_queue() { - pthread_mutex_lock(&key_queue_mutex); - key_queue_len = 0; - pthread_mutex_unlock(&key_queue_mutex); -} @@ -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; +} @@ -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_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(); + + // 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; + + virtual bool IsTextVisible() = 0; + + virtual bool WasTextEverVisible() = 0; + + // 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; + + // --- key handling --- + + // Wait for keypress and return it. May return -1 after timeout. + virtual int WaitKey(); + + virtual bool IsKeyPressed(int key); + + // Erase any queued-up keys. + 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); + + // --- 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; + +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/updater/install.c b/updater/install.c index 7b4b99b01..f68bd03c8 100644 --- a/updater/install.c +++ b/updater/install.c @@ -792,11 +792,12 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { return NULL; } + char* partition = NULL; if (partition_value->type != VAL_STRING) { ErrorAbort(state, "partition argument to %s must be string", name); goto done; } - char* partition = partition_value->data; + partition = partition_value->data; if (strlen(partition) == 0) { ErrorAbort(state, "partition argument to %s can't be empty", name); goto done; diff --git a/verifier.c b/verifier.cpp index 729e085cf..1c5a41d1b 100644 --- a/verifier.c +++ b/verifier.cpp @@ -16,6 +16,7 @@ #include "common.h" #include "verifier.h" +#include "ui.h" #include "mincrypt/rsa.h" #include "mincrypt/sha.h" @@ -24,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. @@ -32,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) { @@ -69,8 +72,8 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey return VERIFY_FAILURE; } - int comment_size = footer[4] + (footer[5] << 8); - int signature_start = footer[0] + (footer[1] << 8); + size_t comment_size = footer[4] + (footer[5] << 8); + size_t signature_start = footer[0] + (footer[1] << 8); LOGI("comment is %d bytes; signature %d bytes from end\n", comment_size, signature_start); @@ -99,7 +102,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey // bytes) and the comment data. size_t signed_len = ftell(f) + EOCD_HEADER_SIZE - 2; - unsigned char* eocd = malloc(eocd_size); + unsigned char* eocd = (unsigned char*)malloc(eocd_size); if (eocd == NULL) { LOGE("malloc for EOCD record failed\n"); fclose(f); @@ -120,7 +123,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey return VERIFY_FAILURE; } - int i; + size_t i; for (i = 4; i < eocd_size-3; ++i) { if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b && eocd[i+2] == 0x05 && eocd[i+3] == 0x06) { @@ -138,7 +141,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey SHA_CTX ctx; SHA_init(&ctx); - unsigned char* buffer = malloc(BUFFER_SIZE); + unsigned char* buffer = (unsigned char*)malloc(BUFFER_SIZE); if (buffer == NULL) { LOGE("failed to alloc memory for sha1 buffer\n"); fclose(f); @@ -149,7 +152,7 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey size_t so_far = 0; fseek(f, 0, SEEK_SET); while (so_far < signed_len) { - int size = BUFFER_SIZE; + size_t size = BUFFER_SIZE; if (signed_len - so_far < size) size = signed_len - so_far; if (fread(buffer, 1, size, f) != size) { LOGE("failed to read data from %s (%s)\n", path, strerror(errno)); @@ -160,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; } } diff --git a/verifier_test.c b/verifier_test.cpp index 5b6c1f451..fe5519d79 100644 --- a/verifier_test.c +++ b/verifier_test.cpp @@ -19,6 +19,7 @@ #include <stdarg.h> #include "verifier.h" +#include "ui.h" // This is build/target/product/security/testkey.x509.pem after being // dumped out by dumpkey.jar. @@ -58,18 +59,36 @@ RSAPublicKey test_key = 367251975, 810756730, -1941182952, 1175080310 } }; -void ui_print(const char* fmt, ...) { - char buf[256]; - va_list ap; - va_start(ap, fmt); - vsnprintf(buf, 256, fmt, ap); - va_end(ap); +RecoveryUI* ui = NULL; - fputs(buf, stderr); -} +// verifier expects to find a UI object; we provide one that does +// nothing but print. +class FakeUI : public RecoveryUI { + void Init() { } + void SetBackground(Icon icon) { } -void ui_set_progress(float fraction) { -} + void SetProgressType(ProgressType determinate) { } + void ShowProgress(float portion, float seconds) { } + void SetProgress(float fraction) { } + + void ShowText(bool visible) { } + bool IsTextVisible() { return false; } + bool WasTextEverVisible() { return false; } + void Print(const char* fmt, ...) { + char buf[256]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, 256, fmt, ap); + va_end(ap); + + fputs(buf, stderr); + } + + void StartMenu(const char* const * headers, const char* const * items, + int initial_selection) { } + int SelectMenu(int sel) { return 0; } + void EndMenu() { } +}; int main(int argc, char **argv) { if (argc != 2) { @@ -77,6 +96,8 @@ int main(int argc, char **argv) { return 2; } + ui = new FakeUI(); + int result = verify_file(argv[1], &test_key, 1); if (result == VERIFY_SUCCESS) { printf("SUCCESS\n"); diff --git a/verifier_test.sh b/verifier_test.sh index 6350e80d3..a1de5c57b 100755 --- a/verifier_test.sh +++ b/verifier_test.sh @@ -1,11 +1,7 @@ #!/bin/bash # -# A test suite for applypatch. Run in a client where you have done -# envsetup, choosecombo, etc. -# -# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your -# system partition. -# +# A test suite for recovery's package signature verifier. Run in a +# client where you have done envsetup, lunch, etc. # # TODO: find some way to get this run regularly along with the rest of # the tests. |