博客
关于我
安卓recovery流程分析【第二篇】
阅读量:705 次
发布时间:2019-03-15

本文共 5559 字,大约阅读时间需要 18 分钟。

Android Recovery 源码解析与界面定制

Recovery 功能概述

Recovery 模块在 Android 系统中扮演着重要角色,主要提供以下功能:

  • 工厂重置:清除数据和缓存,可以通过 wipe_datawipe_cache 实现;
  • 升级管理:支持通过 SDカード刷升级(ODP)和 ADB sideload 升级;
  • OTA 升级:能够进行系统更新,类似于手动刷包。

Recovery 源码路径

在 Android 源码中,Recovery 模块位于 bootable/recovery 文件夹下。部分核心文件包括:

LOCAL_SRC_FILES := adb_install.cpp \                   asn1_decoder.cpp \                   bootloader.cpp \                   device.cpp \                   fuse_sdcard_provider.c \                   install.cpp \                   recovery.cpp \                   roots.cpp \                   screen_ui.cpp \                   ui.cpp \                   verifier.cpp

这些文件在编译完成后会输出到 out/recovery/root 目录。

recovery.cpp 及命令行参数

recovery.cpp 编译成一个可执行文件,存放在 /sbin/recovery,可通过终端运行。其命令行参数包括:

  • --send_intent:发送操作意图;
  • --adbd:启用 ADB sideload;
  • --update_package:指定 OTA 升级包路径;
  • --wipe_data:清除用户数据并重启;
  • --wipe_cache:清除缓存并重启;
  • --set_encrypted_filesystem:控制文件系统加密状态;
  • --just_exit:退出并重启。

Main 函数分析

main 函数入口开始解析:

#include 
#include
#include
#include
#include
#include
#include
<过渡>
#include

输入重定向

// 将日志输出重定向到 /tmp/recovery.log#define TEMPORARY_LOG_FILE "/tmp/recovery.log"redirect_stdio(TEMPORARY_LOG_FILE);#ifdef LogToSerial    freopen("/dev/ttyFIQ0", "a", stdout);    setbuf(stdout, NULL);    freopen("/dev/ttyFIQ0", "a", stderr);    setbuf(stderr, NULL);#endif

ADB Sideload

if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {    adb_main(0, DEFAULT_ADB_PORT);    return 0;}

挂载文件系统

// 加载分区表并准备文件系统static int load_volume_table() {    int emmcState = getEmmcState();    struct fstab fstab;    if (emmcState) {        fstab = fs_mgr_read_fstab("/etc/recovery.emmc.fstab");    } else {        fstab = fs_mgr_read_fstab("/etc/recovery.fstab");    }    // 遍历并输出文件系统表    printf("recovery filesystem table\n");    printf("=========================\n");    for (int i = 0; i < fstab.num_entries; ++i) {        struct Volume* v = &fstab.rec[i];        printf("  %d %s %s %s %lld\n", i, v->mount_point, v->fs_type,               v->blk_device, v->length);    }    printf("\n");    return fstab;}

控制参数解析

Recovery 和 Bootloader 之间通过 /misc 分区进行通信,数据结构体为 bootloader_message

struct bootloader_message {    char command[32];  // 启动时读取的命令    char status[32];  // 升级结果标识    char recovery[768];  // 保留区数据    char stage[32];    // 更新阶段    char reserved[224]; // 预留空间};
static void get_args(int *argc, char **argv) {    struct bootloader_message boot;    memset(&boot, 0, sizeof(boot));    get_bootloader_message(&boot);        if (boot.stage) {        stage = strndup(boot.stage, sizeof(boot.stage));    } else {        if (FILE* fp = fopen("/cache/recovery/command", "r")) {            // 从命令文件中获取参数,重新写回到 /misc        }        free(stage);    }    set.bootloader_message(&boot);}

命令行参数解析

// 使用 getopt_long 解析命令行选项const char* const OPTIONS[] = "abcdefghijklmnop qrstuall";static int main(int argc, char **argv) {    const char* short_options = "f: i: u: w: k: c: t: s: a: x: l: g: p: d: r: w:a";        while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {        switch (arg) {            case 'f':  activate_factory_mode();            case 'i':  set_send_intent();            case 'u':  set_update_package();            // ... 其他选项处理        }    }}

屏幕界面和功能选项

// 初始化 Device UIDevice* device = make_device();RecoveryUI* ui = device->get_ui();ui->set_stage(st_cur, st_max);ui->set_background(RecoveryUI::NONE);

分区挂载操作

int ensure_path_mounted(const char* path) {    Volume* v = volume_for_path(path);    int result;    // 创建挂载点目录    result = mkdir(v->mount_point, 0755);    if (result != 0) {        LOGE("failed to create %s dir: %s", v->mount_point, strerror(errno));        return -1;    }    // 根据文件系统类型挂载    if (v->fs_type == "yaffs2") {        // 通过 mtd 扫描并挂载分区        mtd_scan_partitions();        MtdPartition* p = mtd_find_partition_by_name(v->blk_device);        if (p) {            return mtd_mount_partition(p, v->mount_point, v->fs_type, 0);        } else {            LOGE("failed to find partition %s for %s",                   v->blk_device, v->mount_point);            return -1;        }    }    else if (v->fs_type == "ext4" || v->fs_type == "ext3") {        // 挂载为读取模式        return mount(v->blk_device, v->mount_point, v->fs_type,                   MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");    }    // ... 其他文件系统类型的挂载逻辑}

界面定制实现

device/vendor/recovery/recovery_ui.cpp 中实现 UI 界面。

头部显示与列表项

const char* HEADERS[] = {    "Volume up/down to move highlight",    "power button to select.",    "",    NULL};const char* ITEMS[] = {    "reboot system now",    "apply update from ADB",    "apply update from external storage",    "update rkimage from external storage",     "apply update from cache",    "wipe data/factory reset",    "wipe cache partition",    "recovery system from backup",    NULL};

ScreenRecoveryUI 类

class DeviceUI : public ScreenRecoveryUI {    int consecutive_power_keys;        bool CheckKey(int key) {        if (key == KEY_POWER) {            consecutive_power_keys++;            if (consecutive_power_keys >= 7) {                return REBOOT;            }        }        return NO_ACTION;    }};

实现设备类

class MyDevice : public Device {    RecoveryUI* ui;        int HandleMenuKey(int key) {        if (key == KEY_POWER) {            return kInvokeItem;        }        // ... 其他按键处理逻辑        return kNoAction;    }    // ... 调用菜单选项的处理函数    public:    static Device* make_device() {        return new MyDevice();    }    RecoveryUI* GetUI() { return ui; }};

通过上述构造和改写,文章的结构和内容已经被优化,符合用户的所有要求。

转载地址:http://tbwmz.baihongyu.com/

你可能感兴趣的文章
Node 裁切图片的方法
查看>>
Node+Express连接mysql实现增删改查
查看>>
node, nvm, npm,pnpm,以前简单的前端环境为什么越来越复杂
查看>>
Node-RED中Button按钮组件和TextInput文字输入组件的使用
查看>>
Node-RED中Switch开关和Dropdown选择组件的使用
查看>>
Node-RED中使用html节点爬取HTML网页资料之爬取Node-RED的最新版本
查看>>
Node-RED中使用JSON数据建立web网站
查看>>
Node-RED中使用json节点解析JSON数据
查看>>
Node-RED中使用node-random节点来实现随机数在折线图中显示
查看>>
Node-RED中使用node-red-browser-utils节点实现选择Windows操作系统中的文件并实现图片预览
查看>>
Node-RED中使用node-red-contrib-image-output节点实现图片预览
查看>>
Node-RED中使用node-red-node-ui-iframe节点实现内嵌iframe访问其他网站的效果
查看>>
Node-RED中使用Notification元件显示警告讯息框(温度过高提示)
查看>>
Node-RED中使用range范围节点实现从一个范围对应至另一个范围
查看>>
Node-RED中实现HTML表单提交和获取提交的内容
查看>>
Node-RED中将CSV数据写入txt文件并从文件中读取解析数据
查看>>
Node-RED中建立TCP服务端和客户端
查看>>
Node-RED中建立Websocket客户端连接
查看>>
Node-RED中建立静态网页和动态网页内容
查看>>
Node-RED中解析高德地图天气api的json数据显示天气仪表盘
查看>>