入口函数: main (sbin/recovery)
load_volume_table(); //创建分区表
ensure_path_mounted(LAST_LOG_FILE); //挂载cache分区
ui->Init();
ev_init(input_callback, NULL); //初始化input_event事件
pthread_create(&input_t, NULL, input_thread, NULL); //监听input_event事件
if (show_text) ui->ShowText(true); //如果show_text不为真,则recovery界面不能显示文字
if (update_package) { //如果不为null,获取升级包的位置
if (strncmp(update_package, "CACHE:", 6) == 0) {
int status = INSTALL_SUCCESS; //初始化status的默认状态为成功,如果后面的操作失败,重新更改status的状态
if (update_package != NULL) {
struct bootloader_message boot;
memset(&boot, 0, sizeof(boot));
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
strlcat(boot.recovery, "--update_package=", sizeof(boot.recovery));
strlcat(boot.recovery, update_package, sizeof(boot.recovery));
if (stage) {
snprintf(boot.stage, sizeof(boot.stage), "%s", stage);
}
set_bootloader_message(&boot); //往misc分区写指令,表明当前的状态,万一升级一半突然掉电,重新启动还可以重新进入升级模式
sync();
status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE, true);
if (status == INSTALL_SUCCESS) { //升级成功的话,往data分区写个文件表明当前状态。
update_modem_property();
}
prompt_error_message(status); //在屏幕打印安装信息
if (status != INSTALL_SUCCESS) {
ui->Print("Installation aborted.\n");
// If this is an eng or userdebug build, then automatically
// turn the text display on if the script fails so the error
// message is visible.
char buffer[PROPERTY_VALUE_MAX+1];
property_get("ro.build.fingerprint", buffer, "");
if (strstr(buffer, ":userdebug/") || strstr(buffer, ":eng/")) {
ui->ShowText(true); //为true后屏幕才能显示文字
}
}
else if (wipe_data) { //清空data分区
........
else if (wipe_cache){ //清空cache分区
.......
else if (fota_delta_path) { //ota升级
......
else if (restore_data_path) { //备份用户数据到sd卡
......
else if (!just_exit) { //如果没发来什mecommand命令,执行这个else
status = INSTALL_NONE; // No command specified
ui->SetBackground(RecoveryUI::NO_COMMAND);
if (update_package
write_result_file(MOTA_RESULT_FILE, status);
remove_mota_file(update_package); //删除本地升级包
remove_mota_file(DEFAULT_MOTA_FILE); //删除默认升级包
if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { //如果升级失败或者没有什么操作命令或者之前ui->ShowText设置为true,进入prompt_and_wait
Device::BuiltinAction temp = prompt_and_wait(device, status); //进入用户手动操作界面,等待键盘按键事件
if (temp != Device::NO_ACTION) after = temp;
}
if (update_package != NULL) { //如果升级成功,才会接下来跑到这里,不然会阻塞在之前的prompt_and_wait函数。
perform_update = 1;
}
finish_recovery(send_intent); //关键函数,主要是擦除 misc分区,拷贝log到cache分区。如果recovery中没执行过这个函数,开机检测misc有
//进入recovery的命令,就会再次进入recovery,并执行相关操作
switch (after) { //最后根据after的值进行退出recovery的操作
case Device::SHUTDOWN:
.......
case Device::REBOOT_BOOTLOADER:
.......
default: //reboot
.......
}
sleep(5); // should reboot before this finishes
return EXIT_SUCCESS;
调试过程关键点:
1.cat /etc/recovery.fstab //mtk并没有使用这个配置文件,而是在recovery中写死
boot /boot emmc defaults defaults
/dev/block/mmcblk0p2 /cache ext4 defaults defaults
/dev/block/mmcblk0p3 /data ext4 defaults defaults
misc /misc emmc defaults defaults
recovery /recovery emmc defaults defaults
/dev/block/mmcblk0p4 /sdcard vfat defaults defaults
/dev/block/mmcblk0p6 /system ext4 defaults defaults
/dev/block/mmcblk0p12 /custom ext4 defaults defaults
2.当时所用代码中recovery写死的分区表为 //可以通过/tmp/recovery.log查看
0 /tmp ramdisk (null) (null) 0
1 /boot emmc /dev/block/platform/mtk-msdc.0/by-name/boot (null) 0
2 /cache ext4 /dev/block/platform/mtk-msdc.0/by-name/cache (null) 0
3 /data ext4 /dev/block/platform/mtk-msdc.0/by-name/userdata (null) 0
4 /misc emmc /dev/block/platform/mtk-msdc.0/by-name/para (null) 0
5 /recovery emmc /dev/block/platform/mtk-msdc.0/by-name/recovery (null) 0
6 /sdcard vfat /dev/block/mmcblk1p1 /dev/block/mmcblk1 0
7 /system ext4 /dev/block/platform/mtk-msdc.0/by-name/system (null) 0
8 /nvdata ext4 /dev/block/platform/mtk-msdc.0/by-name/nvdata (null) 0
这个写死的分区表不是每个都会mount上,在recover的main函数中,调用ensure_path_mounted来实现实际上需要挂载的分区,但是如果要挂载的分区不在这个分区表,调用ensure_path_mounted函数会报错
3.recovery实现adb shell和串口控制台的最简单的方式
a.mount安卓的system分区到recovery上,这样就可以实现adb shell;
b.在recovery/etc/init.rc实现这样的语句
service console /system/bin/sh (实现方式是照抄kernel的init.rc文件,recovery的kernel跟android的kernel的差别其实就是文件系统的差别,用的内核是一样的)
console
disabled
user shell
group shell log
seclabel u:r:shell:s0
start console
这样就可以实现串口控制台
4.关键函数
set_bootloader_message //设置bootloader的启动信息,在finish_recovery函数调用这个函数,重新设置boot.mode为正常启动模式而不是recovery模式set_bootloader_message,其实就是往misc分区写东西
5.对于recovery这个进程的selinux策略一律放行。修改external/sepolicy/recovery.te 里面 recovery_only(`这一句后面加一条 permissive recovery; (