本文共 5559 字,大约阅读时间需要 18 分钟。
Android Recovery 源码解析与界面定制
Recovery 模块在 Android 系统中扮演着重要角色,主要提供以下功能:
wipe_data 和 wipe_cache 实现;在 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 编译成一个可执行文件,存放在 /sbin/recovery,可通过终端运行。其命令行参数包括:
--send_intent:发送操作意图;--adbd:启用 ADB sideload;--update_package:指定 OTA 升级包路径;--wipe_data:清除用户数据并重启;--wipe_cache:清除缓存并重启;--set_encrypted_filesystem:控制文件系统加密状态;--just_exit:退出并重启。从 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 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}; 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/