问题背景:
因项目开发及测试需要,设备升级频率比较高,升级出现失败的情况肯定是有的,原因用多方面,如:故意使用非法的升级包,升级版本不匹配等等。
出现升级失败问题后,对于手机用户来说可以选择重启手机即可,而我们使用设备及环境不允许人为对其经常操作,所以如果升级失败,界面就停留在recovery界面,设备就无法正常工作。
解决方案:
设备需要实现一种自动恢复机制,自动重启设备,恢复到正常界面。
进入recovery升级界面,延迟20s后进行设备重启进入正常界面。
具体步骤:
android设备升级失败,往往会进入一个躺倒着的机器人界面:
进而进入到升级异常的界面,如下图:
对于我们项目的设备,该界面是无法进行选项操作的,一般到此界面,下发都有一些升级失败的提示,我们可以根据提示,在android源码里面找到该界面的显示绘制的地方。或者在利用界面关键词"Reboot system now"在android源码中搜索,这里我直接在androidxref上搜索,如下:
这样初步定位到该界面显示的代码在:bootable/recovery/下。
进一步分析源码,很容易得到界面选择的源码(http://androidxref.com/7.0.0_r1/xref/bootable/recovery/recovery.cpp#1046):
10251026// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION1027// means to take the default, which is to reboot or shutdown depending1028// on if the --shutdown_after flag was passed to recovery.1029static Device::BuiltinAction1030prompt_and_wait(Device* device, int status) {1031 for (;;) {1032 finish_recovery(NULL);1033 switch (status) {1034 case INSTALL_SUCCESS:1035 case INSTALL_NONE:1036 ui->SetBackground(RecoveryUI::NO_COMMAND);1037 break;10381039 case INSTALL_ERROR:1040 case INSTALL_CORRUPT:1041 ui->SetBackground(RecoveryUI::ERROR);1042 break;1043 }1044 ui->SetProgressType(RecoveryUI::EMPTY);10451046 int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), 0, 0, device);10471048 // device-specific code may take some action here. It may1049 // return one of the core actions handled in the switch1050 // statement below.1051 Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);10521053 bool should_wipe_cache = false;1054 switch (chosen_action) {1055 case Device::NO_ACTION:1056 break;10571058 case Device::REBOOT:1059 case Device::SHUTDOWN:1060 case Device::REBOOT_BOOTLOADER:1061 return chosen_action;10621063 case Device::WIPE_DATA:
也可以看到源码(http://androidxref.com/7.0.0_r1/xref/bootable/recovery/recovery.cpp#155)
153 * 6. finish_recovery() erases BCB154 * -- after this, rebooting will (try to) restart the main system --155 * 7. ** if install failed **156 * 7a. prompt_and_wait() shows an error icon and waits for the user157 * 7b; the user reboots (pulling the battery, etc) into the main system
头文件注释说明,如果安装失败,recovery.cpp中prompt_and_wait会显示失败错误信息并等待人为操作。
而我们要做的就是将1046行get_menu_selection人为选择改成自动选择重启。
首先看下get_menu_selection的实现:
658static int659get_menu_selection(const char* const * headers, const char* const * items,660 int menu_only, int initial_selection, Device* device) {661 // throw away keys pressed previously, so user doesn't662 // accidentally trigger menu items.663 ui->FlushKeys();664665 ui->StartMenu(headers, items, initial_selection);666 int selected = initial_selection;667 int chosen_item = -1;668669 while (chosen_item < 0) {670 int key = ui->WaitKey();671 int visible = ui->IsTextVisible();672673 if (key == -1) { // ui_wait_key() timed out674 if (ui->WasTextEverVisible()) {675 continue;676 } else {677 LOGI("timed out waiting for key input; rebooting.");678 ui->EndMenu();679 return 0; // XXX fixme680 }681 }682683 int action = device->HandleMenuKey(key, visible);684685 if (action < 0) {686 switch (action) {687 case Device::kHighlightUp:688 selected = ui->SelectMenu(--selected);689 break;690 case Device::kHighlightDown:691 selected = ui->SelectMenu(++selected);692 break;693 case Device::kInvokeItem:694 chosen_item = selected;695 break;696 case Device::kNoAction:697 break;698 }699 } else if (!menu_only) {700 chosen_item = action;701 }702 }703704 ui->EndMenu();705 return chosen_item;706}
可以看到:选项超时返回的key值是-1
(if (key == -1) { // ui_wait_key() timed out).
我们可以继续跟踪代码(ui->WaitKey()),可以得知,在此界面等待用户选择的超时时间是120s,源码(http://androidxref.com/7.0.0_r1/xref/bootable/recovery/ui.cpp#41)。
#define UI_WAIT_KEY_TIMEOUT_SEC 120
故根据这个代码思路,我这边的修改方案:
1.将选择界面超时时间修改为20s.即:
#define UI_WAIT_KEY_TIMEOUT_SEC 20
2.将get_menu_selection代码中等待选项超时后,默认选择项改为0,代码如下:
可以看到代码677行,日志打印的意思:等待输入超时,重启,也是return 是0。可见,原生android在有菜单选项时android默认的continue等待用户继续输入,无菜单选项时默认返回的也是重启。
我们这里因为菜单可见,所以走到675行的continue,进而继续等待用户输入,所以我们这里修改方案改成等待用户选择超时的时候直接return 0给默认选择了重启从而达到目的。