博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
安卓recovery流程分析【第二篇】
阅读量:702 次
发布时间:2019-03-15

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

Android Recovery 源码解析和界面定制

Recovery主要功能

源码路径和主要原文件
recoverycpp
命令行参数
main 函数
界面定制
实现Recovery UI
实现头部显示和列表项
实现ScreenRecoveryUI
实现设备类
添加编译实现
Android Recovery 源码解析和界面定制
Recovery主要功能
深入了解recovery源码前,先浏览下recovery能够给我们提供哪些功能;

首先是我们熟悉的恢复工厂设置 (清除数据,清楚缓存)–> wipe_data wipe_cache

刷升级包,可以通过sdcard升级,通常说的卡刷,有些还提供ADB sideload升级;
可以进行系统的系统的OTA升级,本质上同手动刷包一样;
源码路径和主要原文件
在Android源码环境中,recovery的源码主要在bootable/recovery文件下,另外再device目录下,会根据各个设备定制自己的接口以及UI界面,也就是文章后半部分分析的界面定制的内容;

在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最后是编译成一个可执行的命令,放在recovery文件系统中的/sbin/recovery;所以我们可以在终端中直接运行该命令,具体的参数如下:

–send_intent=anystring - 传递给recovery的信息

–adbd -adb sideload升级
–update_package=path - 指定OTA升级包
–wipe_data - 清楚用户数据并重启
–wipe_cache - 清楚缓存并重启
–set_encrypted_filesystem=on|off - 使能或者关闭文件系统加密
–just_exit - 退出并重启

recovery.cpp的main 函数

从main入口函数分析recovery的主要源码:

输出重定向

redirect_stdio(TEMPORARY_LOG_FILE);//redirect log to serial output#ifdef LogToSerialfreopen("/dev/ttyFIQ0", "a", stdout); setbuf(stdout, NULL);freopen("/dev/ttyFIQ0", "a", stderr); setbuf(stderr, NULL);#endif

这部分代码很容易理解,主要作用是输出log到/tem/recovery.log文件中

执行adb sideload分支

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

判断命令行参数是否为–adbd,并执行adb_main函数,这部分代码在后续adb_install.cpp中分析;

填充fstab结构体

在main函数中调用 load_volume_table(),读取/etc/recovery.emmc.fstab文件内容,并填充fstab结构体,但是并没有执行挂载操作:

load_volume_table函数在roots.cpp文件中,也是很容易理解:

void load_volume_table(){...int emmcState = getEmmcState();//判断是否为emmc设备if(emmcState) {    fstab = fs_mgr_read_fstab("/etc/recovery.emmc.fstab");}else {    fstab = fs_mgr_read_fstab("/etc/recovery.fstab");}...//读取文件中每个条目内容,填充fstab结构体ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk");...//日志打印fstable信息printf("recovery filesystem table\n");printf("=========================\n");for (i = 0; i < fstab->num_entries; ++i) {    Volume* v = &fstab->recs[i];    printf("  %d %s %s %s %lld\n", i, v->mount_point, v->fs_type,           v->blk_device, v->length);}printf("\n");}

读取控制参数

recovery 和 bootloader 必须通过内存的一个特定分区,才能进行相互的通信,这个分区一般是/misc;
对应的信息数据结构体为bootloader_message;
参照源码中bootloader_message 的注释

struct bootloader_message {char command[32];//bootloader 启动时读取改数据,决定是否进入recovery模式char status[32];//由bootloader进行更新,标识升级的结果;char recovery[768];//由Android系统进行写入,recovery从中读取信息;char stage[32];char reserved[224];};

recovery 根据命令行参数,再从/misc分区中解析出对应的参数,进行后续的操作,具体的调用函数为get_args(&argc, &argv);

static voidget_args(int *argc, char ***argv) {struct bootloader_message boot;//参数结构体memset(&boot, 0, sizeof(boot));get_bootloader_message(&boot);  // 具体的读取信息的函数,可能为空的情况stage = strndup(boot.stage, sizeof(boot.stage));... // 如果上述情况为空,则从/cache/recovery/command获取参数,其中 COMMAND_FILE=/cache/recovery/commandif (*argc <= 1) {    FILE *fp = fopen_path(COMMAND_FILE, "r");    if (fp != NULL) {        char *token;        char *argv0 = (*argv)[0];        *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);        (*argv)[0] = argv0;  // use the same program name        char buf[MAX_ARG_LENGTH];        for (*argc = 1; *argc < MAX_ARGS; ++*argc) {            if (!fgets(buf, sizeof(buf), fp)) break;            token = strtok(buf, "\r\n");            if (token != NULL) {                (*argv)[*argc] = strdup(token);  // Strip newline.            } else {                --*argc;            }        }        check_and_fclose(fp, COMMAND_FILE);        LOGI("Got arguments from %s\n", COMMAND_FILE);    }}//把从/cache/recovery/command获取参数重新写回到/misc分区// --> write the arguments we have back into the bootloader control block// always boot into recovery after this (until finish_recovery() is called)strlcpy(boot.command, "boot-recovery", sizeof(boot.command));strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));int i;for (i = 1; i < *argc; ++i) {    strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));    strlcat(boot.recovery, "\n", sizeof(boot.recovery));}set_bootloader_message(&boot);}

解析命令行参数

while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {    switch (arg) {    case 'f': factory_mode = optarg; bFactoryMode = true; break;    case 'i': send_intent = optarg; break;    case 'u': update_package = optarg; break;    case 'w': should_wipe_data = true; break;    case 'k':  update_rkimage = optarg;break;    case 'c': should_wipe_cache = true; break;    case 't': show_text = true; break;    case 's': sideload = true; break;    case 'a': sideload = true; sideload_auto_reboot = true; break;    case 'x': just_exit = true; break;    case 'l': locale = optarg; break;    case 'g': {        if (stage == NULL || *stage == '\0') {            char buffer[20] = "1/";            strncat(buffer, optarg, sizeof(buffer)-3);            stage = strdup(buffer);        }        break;    }    case 'f'+'w': //fw_update        if((optarg)&&(!sdboot_update_package)){        sdboot_update_package = strdup(optarg);        }        break;    case 'd': //demo_copy        if((optarg)&&(! demo_copy_path)){            demo_copy_path = strdup(optarg);        }        break;    case 'p': shutdown_after = true; break;    case 'r': reason = optarg; break;    case 'w'+'a': { should_wipe_all = should_wipe_data = should_wipe_cache = true;show_text = true;} break;    case '?':        LOGE("Invalid command argument\n");        continue;    }}

这部分代码很简单,就是通过getopt_long进行命令行参数的解析并赋值;

显示界面和功能选项

接下来就是创建device,显示对应UI界面和功能选项;

Device* device = make_device();//可以自己实现一个设备ui = device->GetUI();gCurrentUI = ui;//赋值ui界面ui->SetLocale(locale);//获取归属地信息ui->Init();//初始化,可以重载,在init中实现相应功能ui->SetStage(st_cur, st_max);ui->SetBackground(RecoveryUI::NONE);

进行分区挂载操作

ensure_path_mountedint ensure_path_mounted(const char* path) {...Volume* v = volume_for_path(path);//根据路径名获取分区信息...int result;result = scan_mounted_volumes();const MountedVolume* mv =    find_mounted_volume_by_mount_point(v->mount_point);//根据挂载点,获取已挂载分区的信息,如果不为空,说明已经成功挂载if (mv) {    // volume is already mounted    return 0;}result = mkdir(v->mount_point, 0755);  // 创建对应目录,确保目录存在,也有可能目录已经存在if (result!=0){    printf("failed to create %s dir,err=%s!\n",v->mount_point,strerror(errno));}// 根据文件系统类型,执行mount操作if (strcmp(v->fs_type, "yaffs2") == 0) {    // mount an MTD partition as a YAFFS2 filesystem.    mtd_scan_partitions();    const MtdPartition* partition;    partition = mtd_find_partition_by_name(v->blk_device);    if (partition == NULL) {        LOGE("failed to find \"%s\" partition to mount at \"%s\"\n",             v->blk_device, v->mount_point);        return -1;    }    return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0);} else if (strcmp(v->fs_type, "ext4") == 0 ||           strcmp(v->fs_type, "ext3") == 0) {    result = mount(v->blk_device, v->mount_point, v->fs_type,                   MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");    if (result == 0) return 0;    LOGE("failed to mount %s %s (%s)\n", v->mount_point, v->blk_device, strerror(errno));    return -1;} else if (strcmp(v->fs_type, "vfat") == 0) {    result = mount(v->blk_device, v->mount_point, v->fs_type,                   MS_NOATIME | MS_NODEV | MS_NODIRATIME, "shortname=mixed,utf8");    if (result == 0) return 0;    LOGW("trying mount %s to ntfs\n", v->blk_device);    result = mount(v->blk_device, v->mount_point, "ntfs",                       MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");    if (result == 0) return 0;    char *sec_dev = v->fs_options;    if(sec_dev != NULL) {        char *temp = strchr(sec_dev, ',');        if(temp) {            temp[0] = '\0';        }        result = mount(sec_dev, v->mount_point, v->fs_type,                               MS_NOATIME | MS_NODEV | MS_NODIRATIME, "shortname=mixed,utf8");        if (result == 0) return 0;        LOGW("trying mount %s to ntfs\n", sec_dev);        result = mount(sec_dev, v->mount_point, "ntfs",                           MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");        if (result == 0) return 0;    }    LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));    return -1;}else if (strcmp(v->fs_type, "ntfs") == 0) {    LOGW("trying mount %s to ntfs\n", v->blk_device);    result = mount(v->blk_device, v->mount_point, "ntfs",                       MS_NOATIME | MS_NODEV | MS_NODIRATIME, "");    if (result == 0) return 0;    LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno));    return -1;}LOGE("unknown fs_type \"%s\" for %s\n", v->fs_type, v->mount_point);return -1;}

界面定制

实现Recovery UI
在自己的设备目录下:device/vendor/recovery/recovery_ui.cpp

#include 
#include
#include
#include
#include "common.h"#include "device.h"#include "screen_ui.h"

实现头部显示和列表项

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 { public:    DeviceUI () :    consecutive_power_keys(0) {}//实现自己的识别key类型的功能,可以为不同的输入设备适配recovery功能virtual KeyAction CheckKey(int key) {    if (IsKeyPressed(KEY_POWER) && key == KEY_VOLUMEUP) {        return TOGGLE;    }    if (key == KEY_POWER) {        ++consecutive_power_keys;        if (consecutive_power_keys >= 7) {            return REBOOT;        }    } else {        consecutive_power_keys = 0;    }    return ENQUEUE;}  private:int consecutive_power_keys;};

实现设备类

class MyDevice : public Device {  public:RkDevice() :    ui(new DeviceUI ) {}RecoveryUI* GetUI() { return ui; }int HandleMenuKey(int key_code, int visible) {    if (visible) {        switch (key_code) {          case KEY_DOWN:          case KEY_VOLUMEDOWN:            return kHighlightDown;          case KEY_UP:          case KEY_VOLUMEUP:            return kHighlightUp;          case KEY_ENTER:          case KEY_POWER:            return kInvokeItem;        }    }    return kNoAction;}BuiltinAction InvokeMenuItem(int menu_position) {    switch (menu_position) {      case 0: return REBOOT;      //case 1: return APPLY_ADB_SIDELOAD;      case 1: return APPLY_EXT;      case 2: return APPLY_INT_RKIMG;      case 3: return APPLY_CACHE;      case 4: return WIPE_DATA;      case 5: return WIPE_CACHE;      case 6: return RECOVER_SYSTEM;      default: return NO_ACTION;    }}const char* const* GetMenuHeaders() { return HEADERS; }const char* const* GetMenuItems() { return ITEMS; }  private:RecoveryUI* ui;};//创建自己实现的设备Device* make_device() {return new MyDevice ;}

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

你可能感兴趣的文章