aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoug Zongker2011-10-31 11:34:15 -0500
committerDoug Zongker2011-10-31 17:51:07 -0500
commitdaefc1d442fb421606680feb9aeb59c133f4c427 (patch)
tree71b64ebdd66540aca7a523c73a47626ca519d039
parentb88aea8a89f9d3344022cdfe895397baac6c05e7 (diff)
downloadplatform-bootable-recovery-daefc1d442fb421606680feb9aeb59c133f4c427.tar.gz
platform-bootable-recovery-daefc1d442fb421606680feb9aeb59c133f4c427.tar.xz
platform-bootable-recovery-daefc1d442fb421606680feb9aeb59c133f4c427.zip
C++ class for device-specific code
Replace the device-specific functions with a class. Move some of the key handling (for log visibility toggling and rebooting) into the UI class. Fix up the key handling so there is less crosstalk between the immediate keys and the queued keys (an increasing annoyance on button-limited devices). Change-Id: I698f6fd21c67a1e55429312a0484b6c393cad46f
-rw-r--r--Android.mk14
-rw-r--r--default_device.cpp92
-rw-r--r--default_recovery_ui.c73
-rw-r--r--device.h112
-rw-r--r--recovery.cpp70
-rw-r--r--recovery_ui.h95
-rw-r--r--screen_ui.cpp105
-rw-r--r--screen_ui.h8
-rw-r--r--ui.h7
-rw-r--r--updater/install.c3
-rw-r--r--verifier_test.cpp46
11 files changed, 364 insertions, 261 deletions
diff --git a/Android.mk b/Android.mk
index c29ab523..be9ff9ec 100644
--- a/Android.mk
+++ b/Android.mk
@@ -34,7 +34,7 @@ endif
34LOCAL_MODULE_TAGS := eng 34LOCAL_MODULE_TAGS := eng
35 35
36ifeq ($(TARGET_RECOVERY_UI_LIB),) 36ifeq ($(TARGET_RECOVERY_UI_LIB),)
37 LOCAL_SRC_FILES += default_recovery_ui.c 37 LOCAL_SRC_FILES += default_device.cpp
38else 38else
39 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) 39 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB)
40endif 40endif
@@ -50,17 +50,17 @@ include $(BUILD_EXECUTABLE)
50 50
51include $(CLEAR_VARS) 51include $(CLEAR_VARS)
52 52
53#LOCAL_SRC_FILES := verifier_test.cpp verifier.cpp 53LOCAL_SRC_FILES := verifier_test.cpp verifier.cpp
54 54
55#LOCAL_MODULE := verifier_test 55LOCAL_MODULE := verifier_test
56 56
57#LOCAL_FORCE_STATIC_EXECUTABLE := true 57LOCAL_FORCE_STATIC_EXECUTABLE := true
58 58
59#LOCAL_MODULE_TAGS := tests 59LOCAL_MODULE_TAGS := tests
60 60
61#LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libstdc++ libc 61LOCAL_STATIC_LIBRARIES := libmincrypt libcutils libstdc++ libc
62 62
63#include $(BUILD_EXECUTABLE) 63include $(BUILD_EXECUTABLE)
64 64
65 65
66include $(commands_recovery_local_path)/minui/Android.mk 66include $(commands_recovery_local_path)/minui/Android.mk
diff --git a/default_device.cpp b/default_device.cpp
new file mode 100644
index 00000000..265ed071
--- /dev/null
+++ b/default_device.cpp
@@ -0,0 +1,92 @@
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <linux/input.h>
18
19#include "common.h"
20#include "device.h"
21#include "screen_ui.h"
22
23static const char* HEADERS[] = { "Volume up/down to move highlight;",
24 "enter button to select.",
25 "",
26 NULL };
27
28static const char* ITEMS[] = {"reboot system now",
29 "apply update from external storage",
30 "apply update from cache",
31 "wipe data/factory reset",
32 "wipe cache partition",
33 NULL };
34
35class DefaultUI : public ScreenRecoveryUI {
36 public:
37 virtual KeyAction CheckKey(int key) {
38 if (key == KEY_HOME) {
39 return TOGGLE;
40 }
41 return ENQUEUE;
42 }
43};
44
45class DefaultDevice : public Device {
46 public:
47 DefaultDevice() :
48 ui(new DefaultUI) {
49 }
50
51 RecoveryUI* GetUI() { return ui; }
52
53 int HandleMenuKey(int key, int visible) {
54 if (visible) {
55 switch (key) {
56 case KEY_DOWN:
57 case KEY_VOLUMEDOWN:
58 return kHighlightDown;
59
60 case KEY_UP:
61 case KEY_VOLUMEUP:
62 return kHighlightUp;
63
64 case KEY_ENTER:
65 return kInvokeItem;
66 }
67 }
68
69 return kNoAction;
70 }
71
72 BuiltinAction InvokeMenuItem(int menu_position) {
73 switch (menu_position) {
74 case 0: return REBOOT;
75 case 1: return APPLY_EXT;
76 case 2: return APPLY_CACHE;
77 case 3: return WIPE_DATA;
78 case 4: return WIPE_CACHE;
79 default: return NO_ACTION;
80 }
81 }
82
83 const char* const* GetMenuHeaders() { return HEADERS; }
84 const char* const* GetMenuItems() { return ITEMS; }
85
86 private:
87 RecoveryUI* ui;
88};
89
90Device* make_device() {
91 return new DefaultDevice();
92}
diff --git a/default_recovery_ui.c b/default_recovery_ui.c
deleted file mode 100644
index d56164e7..00000000
--- a/default_recovery_ui.c
+++ /dev/null
@@ -1,73 +0,0 @@
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <linux/input.h>
18
19#include "recovery_ui.h"
20#include "common.h"
21
22char* MENU_HEADERS[] = { "Android system recovery utility",
23 "",
24 NULL };
25
26char* MENU_ITEMS[] = { "reboot system now",
27 "apply update from external storage",
28 "wipe data/factory reset",
29 "wipe cache partition",
30 "apply update from cache",
31 NULL };
32
33void device_ui_init(UIParameters* ui_parameters) {
34}
35
36int device_recovery_start() {
37 return 0;
38}
39
40int device_toggle_display(volatile char* key_pressed, int key_code) {
41 return key_code == KEY_HOME;
42}
43
44int device_reboot_now(volatile char* key_pressed, int key_code) {
45 return 0;
46}
47
48int device_handle_key(int key_code, int visible) {
49 if (visible) {
50 switch (key_code) {
51 case KEY_DOWN:
52 case KEY_VOLUMEDOWN:
53 return HIGHLIGHT_DOWN;
54
55 case KEY_UP:
56 case KEY_VOLUMEUP:
57 return HIGHLIGHT_UP;
58
59 case KEY_ENTER:
60 return SELECT_ITEM;
61 }
62 }
63
64 return NO_ACTION;
65}
66
67int device_perform_action(int which) {
68 return which;
69}
70
71int device_wipe_data() {
72 return 0;
73}
diff --git a/device.h b/device.h
new file mode 100644
index 00000000..8096a8d9
--- /dev/null
+++ b/device.h
@@ -0,0 +1,112 @@
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _RECOVERY_DEVICE_H
18#define _RECOVERY_DEVICE_H
19
20#include "ui.h"
21
22class Device {
23 public:
24 virtual ~Device() { }
25
26 // Called to obtain the UI object that should be used to display
27 // the recovery user interface for this device. You should not
28 // have called Init() on the UI object already, the caller will do
29 // that after this method returns.
30 virtual RecoveryUI* GetUI() = 0;
31
32 // Called when recovery starts up (after the UI has been obtained
33 // and initialized and after the arguments have been parsed, but
34 // before anything else).
35 virtual void StartRecovery() { };
36
37 // enum KeyAction { NONE, TOGGLE, REBOOT };
38
39 // // Called in the input thread when a new key (key_code) is
40 // // pressed. *key_pressed is an array of KEY_MAX+1 bytes
41 // // indicating which other keys are already pressed. Return a
42 // // KeyAction to indicate action should be taken immediately.
43 // // These actions happen when recovery is not waiting for input
44 // // (eg, in the midst of installing a package).
45 // virtual KeyAction CheckImmediateKeyAction(volatile char* key_pressed, int key_code) = 0;
46
47 // Called from the main thread when recovery is at the main menu
48 // and waiting for input, and a key is pressed. (Note that "at"
49 // the main menu does not necessarily mean the menu is visible;
50 // recovery will be at the main menu with it invisible after an
51 // unsuccessful operation [ie OTA package failure], or if recovery
52 // is started with no command.)
53 //
54 // key is the code of the key just pressed. (You can call
55 // IsKeyPressed() on the RecoveryUI object you returned from GetUI
56 // if you want to find out if other keys are held down.)
57 //
58 // visible is true if the menu is visible.
59 //
60 // Return one of the defined constants below in order to:
61 //
62 // - move the menu highlight (kHighlight{Up,Down})
63 // - invoke the highlighted item (kInvokeItem)
64 // - do nothing (kNoAction)
65 // - invoke a specific action (a menu position: any non-negative number)
66 virtual int HandleMenuKey(int key, int visible) = 0;
67
68 enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE,
69 WIPE_DATA, WIPE_CACHE };
70
71 // Perform a recovery action selected from the menu.
72 // 'menu_position' will be the item number of the selected menu
73 // item, or a non-negative number returned from
74 // device_handle_key(). The menu will be hidden when this is
75 // called; implementations can call ui_print() to print
76 // information to the screen. If the menu position is one of the
77 // builtin actions, you can just return the corresponding enum
78 // value. If it is an action specific to your device, you
79 // actually perform it here and return NO_ACTION.
80 virtual BuiltinAction InvokeMenuItem(int menu_position) = 0;
81
82 static const int kNoAction = -1;
83 static const int kHighlightUp = -2;
84 static const int kHighlightDown = -3;
85 static const int kInvokeItem = -4;
86
87 // Called when we do a wipe data/factory reset operation (either via a
88 // reboot from the main system with the --wipe_data flag, or when the
89 // user boots into recovery manually and selects the option from the
90 // menu.) Can perform whatever device-specific wiping actions are
91 // needed. Return 0 on success. The userdata and cache partitions
92 // are erased AFTER this returns (whether it returns success or not).
93 virtual int WipeData() { return 0; }
94
95 // Return the headers (an array of strings, one per line,
96 // NULL-terminated) for the main menu. Typically these tell users
97 // what to push to move the selection and invoke the selected
98 // item.
99 virtual const char* const* GetMenuHeaders() = 0;
100
101 // Return the list of menu items (an array of strings,
102 // NULL-terminated). The menu_position passed to InvokeMenuItem
103 // will correspond to the indexes into this array.
104 virtual const char* const* GetMenuItems() = 0;
105};
106
107// The device-specific library must define this function (or the
108// default one will be used, if there is no device-specific library).
109// It returns the Device object that recovery should use.
110Device* make_device();
111
112#endif // _DEVICE_H
diff --git a/recovery.cpp b/recovery.cpp
index d1af3ac0..d028cc91 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -37,9 +37,9 @@
37#include "minui/minui.h" 37#include "minui/minui.h"
38#include "minzip/DirUtil.h" 38#include "minzip/DirUtil.h"
39#include "roots.h" 39#include "roots.h"
40#include "recovery_ui.h"
41#include "ui.h" 40#include "ui.h"
42#include "screen_ui.h" 41#include "screen_ui.h"
42#include "device.h"
43 43
44static const struct option OPTIONS[] = { 44static const struct option OPTIONS[] = {
45 { "send_intent", required_argument, NULL, 's' }, 45 { "send_intent", required_argument, NULL, 's' },
@@ -401,7 +401,7 @@ copy_sideloaded_package(const char* original_path) {
401} 401}
402 402
403static const char** 403static const char**
404prepend_title(const char** headers) { 404prepend_title(const char* const* headers) {
405 const char* title[] = { "Android system recovery <" 405 const char* title[] = { "Android system recovery <"
406 EXPAND(RECOVERY_API_VERSION) "e>", 406 EXPAND(RECOVERY_API_VERSION) "e>",
407 "", 407 "",
@@ -410,7 +410,7 @@ prepend_title(const char** headers) {
410 // count the number of lines in our title, plus the 410 // count the number of lines in our title, plus the
411 // caller-provided headers. 411 // caller-provided headers.
412 int count = 0; 412 int count = 0;
413 const char** p; 413 const char* const* p;
414 for (p = title; *p; ++p, ++count); 414 for (p = title; *p; ++p, ++count);
415 for (p = headers; *p; ++p, ++count); 415 for (p = headers; *p; ++p, ++count);
416 416
@@ -425,7 +425,7 @@ prepend_title(const char** headers) {
425 425
426static int 426static int
427get_menu_selection(const char* const * headers, const char* const * items, 427get_menu_selection(const char* const * headers, const char* const * items,
428 int menu_only, int initial_selection) { 428 int menu_only, int initial_selection, Device* device) {
429 // throw away keys pressed previously, so user doesn't 429 // throw away keys pressed previously, so user doesn't
430 // accidentally trigger menu items. 430 // accidentally trigger menu items.
431 ui->FlushKeys(); 431 ui->FlushKeys();
@@ -444,26 +444,26 @@ get_menu_selection(const char* const * headers, const char* const * items,
444 } else { 444 } else {
445 LOGI("timed out waiting for key input; rebooting.\n"); 445 LOGI("timed out waiting for key input; rebooting.\n");
446 ui->EndMenu(); 446 ui->EndMenu();
447 return ITEM_REBOOT; 447 return 0; // XXX fixme
448 } 448 }
449 } 449 }
450 450
451 int action = device_handle_key(key, visible); 451 int action = device->HandleMenuKey(key, visible);
452 452
453 if (action < 0) { 453 if (action < 0) {
454 switch (action) { 454 switch (action) {
455 case HIGHLIGHT_UP: 455 case Device::kHighlightUp:
456 --selected; 456 --selected;
457 selected = ui->SelectMenu(selected); 457 selected = ui->SelectMenu(selected);
458 break; 458 break;
459 case HIGHLIGHT_DOWN: 459 case Device::kHighlightDown:
460 ++selected; 460 ++selected;
461 selected = ui->SelectMenu(selected); 461 selected = ui->SelectMenu(selected);
462 break; 462 break;
463 case SELECT_ITEM: 463 case Device::kInvokeItem:
464 chosen_item = selected; 464 chosen_item = selected;
465 break; 465 break;
466 case NO_ACTION: 466 case Device::kNoAction:
467 break; 467 break;
468 } 468 }
469 } else if (!menu_only) { 469 } else if (!menu_only) {
@@ -481,7 +481,7 @@ static int compare_string(const void* a, const void* b) {
481 481
482static int 482static int
483update_directory(const char* path, const char* unmount_when_done, 483update_directory(const char* path, const char* unmount_when_done,
484 int* wipe_cache) { 484 int* wipe_cache, Device* device) {
485 ensure_path_mounted(path); 485 ensure_path_mounted(path);
486 486
487 const char* MENU_HEADERS[] = { "Choose a package to install:", 487 const char* MENU_HEADERS[] = { "Choose a package to install:",
@@ -555,7 +555,7 @@ update_directory(const char* path, const char* unmount_when_done,
555 int result; 555 int result;
556 int chosen_item = 0; 556 int chosen_item = 0;
557 do { 557 do {
558 chosen_item = get_menu_selection(headers, zips, 1, chosen_item); 558 chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device);
559 559
560 char* item = zips[chosen_item]; 560 char* item = zips[chosen_item];
561 int item_len = strlen(item); 561 int item_len = strlen(item);
@@ -570,7 +570,7 @@ update_directory(const char* path, const char* unmount_when_done,
570 strlcat(new_path, "/", PATH_MAX); 570 strlcat(new_path, "/", PATH_MAX);
571 strlcat(new_path, item, PATH_MAX); 571 strlcat(new_path, item, PATH_MAX);
572 new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/' 572 new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/'
573 result = update_directory(new_path, unmount_when_done, wipe_cache); 573 result = update_directory(new_path, unmount_when_done, wipe_cache, device);
574 if (result >= 0) break; 574 if (result >= 0) break;
575 } else { 575 } else {
576 // selected a zip file: attempt to install it, and return 576 // selected a zip file: attempt to install it, and return
@@ -608,7 +608,7 @@ update_directory(const char* path, const char* unmount_when_done,
608} 608}
609 609
610static void 610static void
611wipe_data(int confirm) { 611wipe_data(int confirm, Device* device) {
612 if (confirm) { 612 if (confirm) {
613 static const char** title_headers = NULL; 613 static const char** title_headers = NULL;
614 614
@@ -633,54 +633,54 @@ wipe_data(int confirm) {
633 " No", 633 " No",
634 NULL }; 634 NULL };
635 635
636 int chosen_item = get_menu_selection(title_headers, items, 1, 0); 636 int chosen_item = get_menu_selection(title_headers, items, 1, 0, device);
637 if (chosen_item != 7) { 637 if (chosen_item != 7) {
638 return; 638 return;
639 } 639 }
640 } 640 }
641 641
642 ui->Print("\n-- Wiping data...\n"); 642 ui->Print("\n-- Wiping data...\n");
643 device_wipe_data(); 643 device->WipeData();
644 erase_volume("/data"); 644 erase_volume("/data");
645 erase_volume("/cache"); 645 erase_volume("/cache");
646 ui->Print("Data wipe complete.\n"); 646 ui->Print("Data wipe complete.\n");
647} 647}
648 648
649static void 649static void
650prompt_and_wait() { 650prompt_and_wait(Device* device) {
651 const char** headers = prepend_title((const char**)MENU_HEADERS); 651 const char* const* headers = prepend_title(device->GetMenuHeaders());
652 652
653 for (;;) { 653 for (;;) {
654 finish_recovery(NULL); 654 finish_recovery(NULL);
655 ui->SetProgressType(RecoveryUI::EMPTY); 655 ui->SetProgressType(RecoveryUI::EMPTY);
656 656
657 int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0); 657 int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device);
658 658
659 // device-specific code may take some action here. It may 659 // device-specific code may take some action here. It may
660 // return one of the core actions handled in the switch 660 // return one of the core actions handled in the switch
661 // statement below. 661 // statement below.
662 chosen_item = device_perform_action(chosen_item); 662 chosen_item = device->InvokeMenuItem(chosen_item);
663 663
664 int status; 664 int status;
665 int wipe_cache; 665 int wipe_cache;
666 switch (chosen_item) { 666 switch (chosen_item) {
667 case ITEM_REBOOT: 667 case Device::REBOOT:
668 return; 668 return;
669 669
670 case ITEM_WIPE_DATA: 670 case Device::WIPE_DATA:
671 wipe_data(ui->IsTextVisible()); 671 wipe_data(ui->IsTextVisible(), device);
672 if (!ui->IsTextVisible()) return; 672 if (!ui->IsTextVisible()) return;
673 break; 673 break;
674 674
675 case ITEM_WIPE_CACHE: 675 case Device::WIPE_CACHE:
676 ui->Print("\n-- Wiping cache...\n"); 676 ui->Print("\n-- Wiping cache...\n");
677 erase_volume("/cache"); 677 erase_volume("/cache");
678 ui->Print("Cache wipe complete.\n"); 678 ui->Print("Cache wipe complete.\n");
679 if (!ui->IsTextVisible()) return; 679 if (!ui->IsTextVisible()) return;
680 break; 680 break;
681 681
682 case ITEM_APPLY_SDCARD: 682 case Device::APPLY_EXT:
683 status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache); 683 status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache, device);
684 if (status == INSTALL_SUCCESS && wipe_cache) { 684 if (status == INSTALL_SUCCESS && wipe_cache) {
685 ui->Print("\n-- Wiping cache (at package request)...\n"); 685 ui->Print("\n-- Wiping cache (at package request)...\n");
686 if (erase_volume("/cache")) { 686 if (erase_volume("/cache")) {
@@ -700,9 +700,10 @@ prompt_and_wait() {
700 } 700 }
701 } 701 }
702 break; 702 break;
703 case ITEM_APPLY_CACHE: 703
704 case Device::APPLY_CACHE:
704 // Don't unmount cache at the end of this. 705 // Don't unmount cache at the end of this.
705 status = update_directory(CACHE_ROOT, NULL, &wipe_cache); 706 status = update_directory(CACHE_ROOT, NULL, &wipe_cache, device);
706 if (status == INSTALL_SUCCESS && wipe_cache) { 707 if (status == INSTALL_SUCCESS && wipe_cache) {
707 ui->Print("\n-- Wiping cache (at package request)...\n"); 708 ui->Print("\n-- Wiping cache (at package request)...\n");
708 if (erase_volume("/cache")) { 709 if (erase_volume("/cache")) {
@@ -722,7 +723,6 @@ prompt_and_wait() {
722 } 723 }
723 } 724 }
724 break; 725 break;
725
726 } 726 }
727 } 727 }
728} 728}
@@ -741,10 +741,8 @@ main(int argc, char **argv) {
741 freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL); 741 freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
742 printf("Starting recovery on %s", ctime(&start)); 742 printf("Starting recovery on %s", ctime(&start));
743 743
744 // TODO: device_* should be a C++ class; init should return the 744 Device* device = make_device();
745 // appropriate UI for the device. 745 ui = device->GetUI();
746 device_ui_init(&ui_parameters);
747 ui = new ScreenRecoveryUI();
748 746
749 ui->Init(); 747 ui->Init();
750 ui->SetBackground(RecoveryUI::INSTALLING); 748 ui->SetBackground(RecoveryUI::INSTALLING);
@@ -771,7 +769,7 @@ main(int argc, char **argv) {
771 } 769 }
772 } 770 }
773 771
774 device_recovery_start(); 772 device->StartRecovery();
775 773
776 printf("Command:"); 774 printf("Command:");
777 for (arg = 0; arg < argc; arg++) { 775 for (arg = 0; arg < argc; arg++) {
@@ -809,7 +807,7 @@ main(int argc, char **argv) {
809 } 807 }
810 if (status != INSTALL_SUCCESS) ui->Print("Installation aborted.\n"); 808 if (status != INSTALL_SUCCESS) ui->Print("Installation aborted.\n");
811 } else if (wipe_data) { 809 } else if (wipe_data) {
812 if (device_wipe_data()) status = INSTALL_ERROR; 810 if (device->WipeData()) status = INSTALL_ERROR;
813 if (erase_volume("/data")) status = INSTALL_ERROR; 811 if (erase_volume("/data")) status = INSTALL_ERROR;
814 if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; 812 if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
815 if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n"); 813 if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n");
@@ -822,7 +820,7 @@ main(int argc, char **argv) {
822 820
823 if (status != INSTALL_SUCCESS) ui->SetBackground(RecoveryUI::ERROR); 821 if (status != INSTALL_SUCCESS) ui->SetBackground(RecoveryUI::ERROR);
824 if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { 822 if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
825 prompt_and_wait(); 823 prompt_and_wait(device);
826 } 824 }
827 825
828 // Otherwise, get ready to boot the main system... 826 // Otherwise, get ready to boot the main system...
diff --git a/recovery_ui.h b/recovery_ui.h
deleted file mode 100644
index 4c4baf54..00000000
--- a/recovery_ui.h
+++ /dev/null
@@ -1,95 +0,0 @@
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _RECOVERY_UI_H
18#define _RECOVERY_UI_H
19
20#include "common.h"
21
22#ifdef __cplusplus
23extern "C" {
24#endif
25
26// Called before UI library is initialized. Can change things like
27// how many frames are included in various animations, etc.
28extern void device_ui_init(UIParameters* ui_parameters);
29
30// Called when recovery starts up. Returns 0.
31extern int device_recovery_start();
32
33// Called in the input thread when a new key (key_code) is pressed.
34// *key_pressed is an array of KEY_MAX+1 bytes indicating which other
35// keys are already pressed. Return true if the text display should
36// be toggled.
37extern int device_toggle_display(volatile char* key_pressed, int key_code);
38
39// Called in the input thread when a new key (key_code) is pressed.
40// *key_pressed is an array of KEY_MAX+1 bytes indicating which other
41// keys are already pressed. Return true if the device should reboot
42// immediately.
43extern int device_reboot_now(volatile char* key_pressed, int key_code);
44
45// Called from the main thread when recovery is waiting for input and
46// a key is pressed. key is the code of the key pressed; visible is
47// true if the recovery menu is being shown. Implementations can call
48// ui_key_pressed() to discover if other keys are being held down.
49// Return one of the defined constants below in order to:
50//
51// - move the menu highlight (HIGHLIGHT_*)
52// - invoke the highlighted item (SELECT_ITEM)
53// - do nothing (NO_ACTION)
54// - invoke a specific action (a menu position: any non-negative number)
55extern int device_handle_key(int key, int visible);
56
57// Perform a recovery action selected from the menu. 'which' will be
58// the item number of the selected menu item, or a non-negative number
59// returned from device_handle_key(). The menu will be hidden when
60// this is called; implementations can call ui_print() to print
61// information to the screen.
62extern int device_perform_action(int which);
63
64// Called when we do a wipe data/factory reset operation (either via a
65// reboot from the main system with the --wipe_data flag, or when the
66// user boots into recovery manually and selects the option from the
67// menu.) Can perform whatever device-specific wiping actions are
68// needed. Return 0 on success. The userdata and cache partitions
69// are erased after this returns (whether it returns success or not).
70int device_wipe_data();
71
72#define NO_ACTION -1
73
74#define HIGHLIGHT_UP -2
75#define HIGHLIGHT_DOWN -3
76#define SELECT_ITEM -4
77
78#define ITEM_REBOOT 0
79#define ITEM_APPLY_EXT 1
80#define ITEM_APPLY_SDCARD 1 // historical synonym for ITEM_APPLY_EXT
81#define ITEM_WIPE_DATA 2
82#define ITEM_WIPE_CACHE 3
83#define ITEM_APPLY_CACHE 4
84
85// Header text to display above the main menu.
86extern char* MENU_HEADERS[];
87
88// Text of menu items.
89extern char* MENU_ITEMS[];
90
91#ifdef __cplusplus
92}
93#endif
94
95#endif
diff --git a/screen_ui.cpp b/screen_ui.cpp
index e6a31db2..a60b0468 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -31,9 +31,9 @@
31#include "common.h" 31#include "common.h"
32#include <cutils/android_reboot.h> 32#include <cutils/android_reboot.h>
33#include "minui/minui.h" 33#include "minui/minui.h"
34#include "recovery_ui.h"
35#include "ui.h" 34#include "ui.h"
36#include "screen_ui.h" 35#include "screen_ui.h"
36#include "device.h"
37 37
38#define CHAR_WIDTH 10 38#define CHAR_WIDTH 10
39#define CHAR_HEIGHT 18 39#define CHAR_HEIGHT 18
@@ -78,7 +78,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() :
78 menu_top(0), 78 menu_top(0),
79 menu_items(0), 79 menu_items(0),
80 menu_sel(0), 80 menu_sel(0),
81 key_queue_len(0) { 81 key_queue_len(0),
82 key_last_down(-1) {
82 pthread_mutex_init(&updateMutex, NULL); 83 pthread_mutex_init(&updateMutex, NULL);
83 pthread_mutex_init(&key_queue_mutex, NULL); 84 pthread_mutex_init(&key_queue_mutex, NULL);
84 pthread_cond_init(&key_queue_cond, NULL); 85 pthread_cond_init(&key_queue_cond, NULL);
@@ -281,7 +282,6 @@ int ScreenRecoveryUI::input_callback(int fd, short revents, void* data)
281{ 282{
282 struct input_event ev; 283 struct input_event ev;
283 int ret; 284 int ret;
284 int fake_key = 0;
285 285
286 ret = ev_get_input(fd, revents, &ev); 286 ret = ev_get_input(fd, revents, &ev);
287 if (ret) 287 if (ret)
@@ -297,16 +297,12 @@ int ScreenRecoveryUI::input_callback(int fd, short revents, void* data)
297 // key event. 297 // key event.
298 self->rel_sum += ev.value; 298 self->rel_sum += ev.value;
299 if (self->rel_sum > 3) { 299 if (self->rel_sum > 3) {
300 fake_key = 1; 300 self->process_key(KEY_DOWN, 1); // press down key
301 ev.type = EV_KEY; 301 self->process_key(KEY_DOWN, 0); // and release it
302 ev.code = KEY_DOWN;
303 ev.value = 1;
304 self->rel_sum = 0; 302 self->rel_sum = 0;
305 } else if (self->rel_sum < -3) { 303 } else if (self->rel_sum < -3) {
306 fake_key = 1; 304 self->process_key(KEY_UP, 1); // press up key
307 ev.type = EV_KEY; 305 self->process_key(KEY_UP, 0); // and release it
308 ev.code = KEY_UP;
309 ev.value = 1;
310 self->rel_sum = 0; 306 self->rel_sum = 0;
311 } 307 }
312 } 308 }
@@ -314,36 +310,63 @@ int ScreenRecoveryUI::input_callback(int fd, short revents, void* data)
314 self->rel_sum = 0; 310 self->rel_sum = 0;
315 } 311 }
316 312
317 if (ev.type != EV_KEY || ev.code > KEY_MAX) 313 if (ev.type == EV_KEY && ev.code <= KEY_MAX)
318 return 0; 314 self->process_key(ev.code, ev.value);
319 315
320 pthread_mutex_lock(&self->key_queue_mutex); 316 return 0;
321 if (!fake_key) { 317}
322 // our "fake" keys only report a key-down event (no
323 // key-up), so don't record them in the key_pressed
324 // table.
325 self->key_pressed[ev.code] = ev.value;
326 }
327 const int queue_max = sizeof(self->key_queue) / sizeof(self->key_queue[0]);
328 if (ev.value > 0 && self->key_queue_len < queue_max) {
329 self->key_queue[self->key_queue_len++] = ev.code;
330 pthread_cond_signal(&self->key_queue_cond);
331 }
332 pthread_mutex_unlock(&self->key_queue_mutex);
333 318
334 if (ev.value > 0 && device_toggle_display(self->key_pressed, ev.code)) { 319// Process a key-up or -down event. A key is "registered" when it is
335 pthread_mutex_lock(&self->updateMutex); 320// pressed and then released, with no other keypresses or releases in
336 self->show_text = !self->show_text; 321// between. Registered keys are passed to CheckKey() to see if it
337 if (self->show_text) self->show_text_ever = true; 322// should trigger a visibility toggle, an immediate reboot, or be
338 self->update_screen_locked(); 323// queued to be processed next time the foreground thread wants a key
339 pthread_mutex_unlock(&self->updateMutex); 324// (eg, for the menu).
340 } 325//
326// We also keep track of which keys are currently down so that
327// CheckKey can call IsKeyPressed to see what other keys are held when
328// a key is registered.
329//
330// updown == 1 for key down events; 0 for key up events
331void ScreenRecoveryUI::process_key(int key_code, int updown) {
332 bool register_key = false;
341 333
342 if (ev.value > 0 && device_reboot_now(self->key_pressed, ev.code)) { 334 pthread_mutex_lock(&key_queue_mutex);
343 android_reboot(ANDROID_RB_RESTART, 0, 0); 335 key_pressed[key_code] = updown;
336 if (updown) {
337 key_last_down = key_code;
338 } else {
339 if (key_last_down == key_code)
340 register_key = true;
341 key_last_down = -1;
344 } 342 }
343 pthread_mutex_unlock(&key_queue_mutex);
345 344
346 return 0; 345 if (register_key) {
346 switch (CheckKey(key_code)) {
347 case RecoveryUI::TOGGLE:
348 pthread_mutex_lock(&updateMutex);
349 show_text = !show_text;
350 if (show_text) show_text_ever = true;
351 update_screen_locked();
352 pthread_mutex_unlock(&updateMutex);
353 break;
354
355 case RecoveryUI::REBOOT:
356 android_reboot(ANDROID_RB_RESTART, 0, 0);
357 break;
358
359 case RecoveryUI::ENQUEUE:
360 pthread_mutex_lock(&key_queue_mutex);
361 const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]);
362 if (key_queue_len < queue_max) {
363 key_queue[key_queue_len++] = key_code;
364 pthread_cond_signal(&key_queue_cond);
365 }
366 pthread_mutex_unlock(&key_queue_mutex);
367 break;
368 }
369 }
347} 370}
348 371
349// Reads input events, handles special hot keys, and adds to the key queue. 372// Reads input events, handles special hot keys, and adds to the key queue.
@@ -622,8 +645,10 @@ int ScreenRecoveryUI::WaitKey()
622 645
623bool ScreenRecoveryUI::IsKeyPressed(int key) 646bool ScreenRecoveryUI::IsKeyPressed(int key)
624{ 647{
625 // This is a volatile static array, don't bother locking 648 pthread_mutex_lock(&key_queue_mutex);
626 return key_pressed[key]; 649 int pressed = key_pressed[key];
650 pthread_mutex_unlock(&key_queue_mutex);
651 return pressed;
627} 652}
628 653
629void ScreenRecoveryUI::FlushKeys() { 654void ScreenRecoveryUI::FlushKeys() {
@@ -631,3 +656,7 @@ void ScreenRecoveryUI::FlushKeys() {
631 key_queue_len = 0; 656 key_queue_len = 0;
632 pthread_mutex_unlock(&key_queue_mutex); 657 pthread_mutex_unlock(&key_queue_mutex);
633} 658}
659
660RecoveryUI::KeyAction ScreenRecoveryUI::CheckKey(int key) {
661 return RecoveryUI::ENQUEUE;
662}
diff --git a/screen_ui.h b/screen_ui.h
index 544f1543..a5ec0d36 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -47,6 +47,10 @@ class ScreenRecoveryUI : public RecoveryUI {
47 int WaitKey(); 47 int WaitKey();
48 bool IsKeyPressed(int key); 48 bool IsKeyPressed(int key);
49 void FlushKeys(); 49 void FlushKeys();
50 // The default implementation of CheckKey enqueues all keys.
51 // Devices should typically override this to provide some way to
52 // toggle the log/menu display, and to do an immediate reboot.
53 KeyAction CheckKey(int key);
50 54
51 // printing messages 55 // printing messages
52 void Print(const char* fmt, ...); // __attribute__((format(printf, 1, 2))); 56 void Print(const char* fmt, ...); // __attribute__((format(printf, 1, 2)));
@@ -95,7 +99,8 @@ class ScreenRecoveryUI : public RecoveryUI {
95 pthread_mutex_t key_queue_mutex; 99 pthread_mutex_t key_queue_mutex;
96 pthread_cond_t key_queue_cond; 100 pthread_cond_t key_queue_cond;
97 int key_queue[256], key_queue_len; 101 int key_queue[256], key_queue_len;
98 volatile char key_pressed[KEY_MAX + 1]; 102 char key_pressed[KEY_MAX + 1]; // under key_queue_mutex
103 int key_last_down; // under key_queue_mutex
99 int rel_sum; 104 int rel_sum;
100 105
101 pthread_t progress_t; 106 pthread_t progress_t;
@@ -110,6 +115,7 @@ class ScreenRecoveryUI : public RecoveryUI {
110 void update_progress_locked(); 115 void update_progress_locked();
111 static void* progress_thread(void* cookie); 116 static void* progress_thread(void* cookie);
112 static int input_callback(int fd, short revents, void* data); 117 static int input_callback(int fd, short revents, void* data);
118 void process_key(int key_code, int updown);
113 static void* input_thread(void* cookie); 119 static void* input_thread(void* cookie);
114 120
115 bool usb_connected(); 121 bool usb_connected();
diff --git a/ui.h b/ui.h
index 6150bfd5..3ca99a61 100644
--- a/ui.h
+++ b/ui.h
@@ -64,6 +64,13 @@ class RecoveryUI {
64 // Erase any queued-up keys. 64 // Erase any queued-up keys.
65 virtual void FlushKeys() = 0; 65 virtual void FlushKeys() = 0;
66 66
67 // Called on each keypress, even while operations are in progress.
68 // Return value indicates whether an immediate operation should be
69 // triggered (toggling the display, rebooting the device), or if
70 // the key should be enqueued for use by the main thread.
71 enum KeyAction { ENQUEUE, TOGGLE, REBOOT };
72 virtual KeyAction CheckKey(int key) = 0;
73
67 // --- menu display --- 74 // --- menu display ---
68 75
69 // Display some header text followed by a menu of items, which appears 76 // Display some header text followed by a menu of items, which appears
diff --git a/updater/install.c b/updater/install.c
index 7b4b99b0..f68bd03c 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -792,11 +792,12 @@ Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
792 return NULL; 792 return NULL;
793 } 793 }
794 794
795 char* partition = NULL;
795 if (partition_value->type != VAL_STRING) { 796 if (partition_value->type != VAL_STRING) {
796 ErrorAbort(state, "partition argument to %s must be string", name); 797 ErrorAbort(state, "partition argument to %s must be string", name);
797 goto done; 798 goto done;
798 } 799 }
799 char* partition = partition_value->data; 800 partition = partition_value->data;
800 if (strlen(partition) == 0) { 801 if (strlen(partition) == 0) {
801 ErrorAbort(state, "partition argument to %s can't be empty", name); 802 ErrorAbort(state, "partition argument to %s can't be empty", name);
802 goto done; 803 goto done;
diff --git a/verifier_test.cpp b/verifier_test.cpp
index 5b6c1f45..2448d8d5 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -19,6 +19,7 @@
19#include <stdarg.h> 19#include <stdarg.h>
20 20
21#include "verifier.h" 21#include "verifier.h"
22#include "ui.h"
22 23
23// This is build/target/product/security/testkey.x509.pem after being 24// This is build/target/product/security/testkey.x509.pem after being
24// dumped out by dumpkey.jar. 25// dumped out by dumpkey.jar.
@@ -58,18 +59,41 @@ RSAPublicKey test_key =
58 367251975, 810756730, -1941182952, 1175080310 } 59 367251975, 810756730, -1941182952, 1175080310 }
59 }; 60 };
60 61
61void ui_print(const char* fmt, ...) { 62RecoveryUI* ui = NULL;
62 char buf[256];
63 va_list ap;
64 va_start(ap, fmt);
65 vsnprintf(buf, 256, fmt, ap);
66 va_end(ap);
67 63
68 fputs(buf, stderr); 64// verifier expects to find a UI object; we provide one that does
69} 65// nothing but print.
66class FakeUI : public RecoveryUI {
67 void Init() { }
68 void SetBackground(Icon icon) { }
70 69
71void ui_set_progress(float fraction) { 70 void SetProgressType(ProgressType determinate) { }
72} 71 void ShowProgress(float portion, float seconds) { }
72 void SetProgress(float fraction) { }
73
74 void ShowText(bool visible) { }
75 bool IsTextVisible() { return false; }
76 bool WasTextEverVisible() { return false; }
77 void Print(const char* fmt, ...) {
78 char buf[256];
79 va_list ap;
80 va_start(ap, fmt);
81 vsnprintf(buf, 256, fmt, ap);
82 va_end(ap);
83
84 fputs(buf, stderr);
85 }
86
87 int WaitKey() { return 0; }
88 bool IsKeyPressed(int key) { return false; }
89 void FlushKeys() { }
90 KeyAction CheckKey(int key) { return ENQUEUE; }
91
92 void StartMenu(const char* const * headers, const char* const * items,
93 int initial_selection) { }
94 int SelectMenu(int sel) { return 0; }
95 void EndMenu() { }
96};
73 97
74int main(int argc, char **argv) { 98int main(int argc, char **argv) {
75 if (argc != 2) { 99 if (argc != 2) {
@@ -77,6 +101,8 @@ int main(int argc, char **argv) {
77 return 2; 101 return 2;
78 } 102 }
79 103
104 ui = new FakeUI();
105
80 int result = verify_file(argv[1], &test_key, 1); 106 int result = verify_file(argv[1], &test_key, 1);
81 if (result == VERIFY_SUCCESS) { 107 if (result == VERIFY_SUCCESS) {
82 printf("SUCCESS\n"); 108 printf("SUCCESS\n");