如何实现android设备进入recovery界面后自动重启

问题背景:

因项目开发及测试需要,设备升级频率比较高,升级出现失败的情况肯定是有的,原因用多方面,如:故意使用非法的升级包,升级版本不匹配等等。

出现升级失败问题后,对于手机用户来说可以选择重启手机即可,而我们使用设备及环境不允许人为对其经常操作,所以如果升级失败,界面就停留在recovery界面,设备就无法正常工作。

解决方案:

设备需要实现一种自动恢复机制,自动重启设备,恢复到正常界面。

进入recovery升级界面,延迟20s后进行设备重启进入正常界面。

具体步骤:

android设备升级失败,往往会进入一个躺倒着的机器人界面:

如何实现android设备进入recovery界面后自动重启_第1张图片

进而进入到升级异常的界面,如下图:

如何实现android设备进入recovery界面后自动重启_第2张图片

对于我们项目的设备,该界面是无法进行选项操作的,一般到此界面,下发都有一些升级失败的提示,我们可以根据提示,在android源码里面找到该界面的显示绘制的地方。或者在利用界面关键词"Reboot system now"在android源码中搜索,这里我直接在androidxref上搜索,如下:

如何实现android设备进入recovery界面后自动重启_第3张图片

这样初步定位到该界面显示的代码在:bootable/recovery/下。

进一步分析源码,很容易得到界面选择的源码(http://androidxref.com/7.0.0_r1/xref/bootable/recovery/recovery.cpp#1046):

 

1025
1026// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER.  Returning NO_ACTION
1027// means to take the default, which is to reboot or shutdown depending
1028// on if the --shutdown_after flag was passed to recovery.
1029static Device::BuiltinAction
1030prompt_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;
1038
1039            case INSTALL_ERROR:
1040            case INSTALL_CORRUPT:
1041                ui->SetBackground(RecoveryUI::ERROR);
1042                break;
1043        }
1044        ui->SetProgressType(RecoveryUI::EMPTY);
1045
1046        int chosen_item = get_menu_selection(nullptr, device->GetMenuItems(), 0, 0, device);
1047
1048        // device-specific code may take some action here.  It may
1049        // return one of the core actions handled in the switch
1050        // statement below.
1051        Device::BuiltinAction chosen_action = device->InvokeMenuItem(chosen_item);
1052
1053        bool should_wipe_cache = false;
1054        switch (chosen_action) {
1055            case Device::NO_ACTION:
1056                break;
1057
1058            case Device::REBOOT:
1059            case Device::SHUTDOWN:
1060            case Device::REBOOT_BOOTLOADER:
1061                return chosen_action;
1062
1063            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.\n");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,代码如下:

如何实现android设备进入recovery界面后自动重启_第4张图片

可以看到代码677行,日志打印的意思:等待输入超时,重启,也是return 是0。可见,原生android在有菜单选项时android默认的continue等待用户继续输入,无菜单选项时默认返回的也是重启。

我们这里因为菜单可见,所以走到675行的continue,进而继续等待用户输入,所以我们这里修改方案改成等待用户选择超时的时候直接return 0给默认选择了重启从而达到目的。

文末思考:

为什么不将选择菜单界面设置未不可见让674行的条件不成立而是程序直接在else里面的" return 0;"?

                                                                 

你可能感兴趣的:(android)