简介
recovery 作为系统的恢复工具,有以下几点功能:
- 首先是我们熟悉的恢复工厂设置 –> wipe_data wipe_cache
- 刷升级包,可以通过sdcard升级,通常说的卡刷,有些还提供ADB sideload升级;
- 可以进行系统的系统的OTA升级,本质上同手动刷包一样;
recovery 与 主系统交互是通过 /cache 目录下的文件:
- /cache/recovery/command 作为recovery的输入参数,以行为分割
- /cache/recovery/log 收集recovery的日志文件
- /cache/recovery/intent 输出后续操作指令
recovery最后是编译成一个可执行的命令,放在recovery文件系统中的/sbin/recovery;所以我们可以在终端中直接运行该命令,具体的参数如下:
1 2 3 4 5 6 7
| --send_intent=anystring - 传递给recovery的信息 --adbd -adb sideload升级 --update_package=path - 指定OTA升级包 --wipe_data - 清楚用户数据并重启 --wipe_cache - 清楚缓存并重启 --set_encrypted_filesystem=on|off - 使能或者关闭文件系统加密 --just_exit - 退出并重启
|
recovery生成
先看一下 build/core/Makefile 的依赖文件:
1 2 3 4 5 6 7 8 9 10 11 12
| $(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $(MINIGZIP) \ $(INSTALLED_RAMDISK_TARGET) \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(INTERNAL_RECOVERYIMAGE_FILES) \ $(recovery_initrc) $(recovery_sepolicy) $(recovery_kernel) \ $(INSTALLED_2NDBOOTLOADER_TARGET) \ $(recovery_build_prop) $(recovery_resource_deps) \ $(recovery_fstab) \ $(recovery_fstab_mtd) \ $(recovery_fstab_emmc) \ $(RECOVERY_INSTALL_OTA_KEYS) $(call build-recoveryimage-target, $@)
|
1 2 3 4 5 6 7 8 9 10
| 1.MKBOOTFS, MINIGZIP, MKBOOTIMG,PC端工具软件 2.INSTALLED_RAMDISK_TARGET,标准根文件系统 ramdisk.img 3.INSTALLED_BOOTIMAGE_TARGET,即boot.img,标准内核及标准根文件系统 4. recovery_binary, Recovery可执行程序,源码位于:bootable/recovery 5. recovery_initrc,recovery模式的init.rc, 位于 bootable/recovery/etc/init.rc 6. recovery_kernel, recovery 模式的kernel, 同标准内核 7. INSTALLED_2NDBOOTLOADER_TARGET,我们不用 8. recovery_build_prop, recovery 模式的build.prop, 同标准模式 9. recovery_resource_deps, recovery 模式使用的res, 位于:recovery/custom/{product_name}/res 10. RECOVERY_INSTALL_OTA_KEYS, ota 密钥
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| recovery ├── charger -> /sbin/healthd ├── data ├── default.prop ├── dev ├── drmboot.ko ├── etc │ ├── recovery.emmc.fstab │ └── recovery.fstab ├── file_contexts ├── fstab.rk30board.bootmode.emmc ├── fstab.rk30board.bootmode.unknown ├── init ├── init.bootmode.emmc.rc ├── init.bootmode.unknown.rc ├── init.rc ├── oem ├── proc ├── property_contexts ├── res │ ├── images │ │ ├── erasing_text.png │ │ ├── error_text.png │ │ ├── font.png │ │ ├── icon_error.png │ │ ├── icon_installing.png │ │ ├── installing_text.png │ │ ├── no_command_text.png │ │ ├── progress_empty.png │ │ ├── progress_fill.png │ │ ├── stage_empty.png │ │ └── stage_fill.png │ └── keys ├── rk30xxnand_ko.ko ├── sbin │ ├── adbd │ ├── busybox │ ├── e2fsck │ ├── healthd │ ├── mkdosfs │ ├── mkfs.f2fs │ ├── recovery │ ├── resize2fs │ ├── sh │ ├── ueventd -> ../init │ └── watchdogd -> ../init ├── seapp_contexts ├── selinux_version ├── sepolicy ├── service_contexts ├── sys ├── system ├── tmp ├── ueventd.rc └── ueventd.rk30board.rc
|
recovery根文件系统
从bootloader 进入Recovery 模式后,首先也是运行Linux内核,该内核跟普通模式没有区别(减轻了BSP开发者的任务)。区别从执行文件系统开始。 Recovery 模式的细节就隐藏在其根文件系统中。
下面,我们就看看进入Recovery 根文件系统都干些啥。
init.rc
和正常启动一样,内核进入文件系统会执行/init, init 的配置文件就是 /init.rc
主要功能为:
- 设置环境变量;
- 建立 etc 链接;
- 挂载文件系统并创建文件夹目录;
- 启动 recovery主程序;
执行流程分析
在Android源码环境中,recovery的源码主要在bootable/recovery文件下,另外在device目录下,会根据各个设备定制自己的接口以及UI界面,也就是文章后半部分分析的界面定制的内容;
在bootable/recovery目录下,看Android.mk文件的源文件列表:
1 2 3 4 5 6 7 8 9 10 11 12
| 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 \
|
主要执行流程
标准错误输出重定向
将标准输出和标准错误输出重定位到”/tmp/recovery.log”,如果是eng模式,就可以通过adb pull /tmp/recovery.log, 看到当前的log信息,这为我们提供了有效的调试手段。后面还会看到,recovery模式运行完毕后,会将其拷贝到cache分区,以便后续分析。
miniui初始化
Recovery 使用了一个简单的基于framebuffer的ui系统,叫miniui,这里,进行了简单的初始化(主要是图形部分以及事件部分),并启动了一个 event 线程用于响应用户按键。
解析参数
从misc 分区以及 CACHE:recovery/command 文件中读入参数,写入到argc, argv ,并且,如果有必要,回写入misc分区。这样,如果recovery没有操作成功(比如,升级还没有结束,就拔电池),系统会一直进入recovery模式。提醒用户继续升级,直到成功。
设备定制文件初始化
device_recovery_start() 它給设备制造商提供了一个调用机会,可写入设备相关初始化代码。
根据命令参数,执行命令
根据用户提供参数,调用各项功能,比如,安装一个升级包,擦除cache分区, 擦除user data分区,install_package比较复杂。
完成指令后,等待用户后续指令
如果前面已经做了某项操作并且成功,则进入重启流程。否则,等待用户选择具体操作。而用户可选操作为: reboot, 安装update.zip,除cache分区, 擦除user data分区,如前所述,只有安装package 比较复杂,其它简单。
结束recovery
finish_recovery(send_intent); 它的功能如下:
1. 将前面定义的intent字符串写入(如果有的话):CACHE:recovery/command
2. 将 /tmp/recovery.log 复制到 "CACHE:recovery/log";
3. 清空 misc 分区,这样重启就不会进入recovery模式4)删除command 文件:CACHE:recovery/command;
核心函数 really_install_package
更新UI显示
ui->Print 更新升级时UI界面显示
确保所有分区正确挂载
ensure_path_mounted ,主要是cache分区或者SD分区
读取update.zip文件
load_keys 装载公钥
verify_file 注释很清楚,就是签名的验证;
打开升级包
mzOpenZipArchive,将相关信息存到 ZipArchive 数据结构中,便于后面处理。
调用try_update_binary继续执行
如果升级包中包含脚本文件,则解压执行;主要功能都在这个函数中完成,接下去继续分析;
复制脚本文件
将升级包内文件META-INF/com/google/android/update-binary 复制为/tmp/update_binary
创建新的进程,执行:/tmp/update_binary
主进程与子进程通过管道进行进程间通信;原进程变成一个服务进程,它提供UI更新服务;
包括:更新精度条,打印提示信息,清除缓存,清除显示等
执行update_binary
这样,我们又回到了升级包中的文件:META-INF/com/google/android/update-binary