为了实现u盘升级功能,但每次进入recovery升级都提示的是升级失败,最终我通过找到位于cache/recovery下的升级日志进行分析,摘取关键日志如下:
I:no boot messages
I:Got arguments from /cache/recovery/command
locale is [zh_CN]
can't open /dev/tty0: No such file or directory
framebuffer: fd 3 (480 x 854)
ioctl(): blank: Invalid argument
ioctl(): blank: Invalid argument
installing_text: zh_CN (240 x 38 @ 1818)
erasing_text: zh_CN (128 x 38 @ 1521)
no_command_text: zh_CN (92 x 38 @ 1521)
error_text: zh_CN (98 x 38 @ 1521)
Command: "/sbin/recovery" "--update_package=/storage/sdcard0/update.zip" "--locale=zh_CN"
update_package = /storage/sdcard0/update.zip
I:Finding update package...
I:Update location: /storage/sdcard0/update.zip
E:unknown volume for path [/storage/sdcard0/update.zip]
我尝试了许多次网络上所提供的方法,依旧没能解决问题,实在没办法,我就将u盘的update.zip文件通过程序拷贝到cache/recovery之后再升级,最后成功实现了本地升级功能。
过了几日再回过来看,实际没那么麻烦。下面来跟一下代码讲讲思路(不同芯片厂家实现可能会有一定差异):
./bootable/recovery/recovery.cpp
int
main(int argc, char **argv) {
//省略无关代码
load_volume_table();//这个是关键点,后面会讲
//省略无关代码
status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);
//省略无关代码
}
从recovery的入口main函数找到install_package函数,这里是实现升级的关键接口,再往下跟踪:
./bootable/recovery/install.cpp
int
install_package(const char* path, int* wipe_cache, const char* install_file)
{
//省略无关代码
result = really_install_package(path, wipe_cache);
//省略无关代码
return result;
}
最终实现升级我们的升级包的是调用了really_install_package函数,那就继续往下,我也不多废话了:
./bootable/recovery/install.cpp
static int
really_install_package(const char *path, int* wipe_cache)
{
//省略无关代码
if (ensure_path_mounted(path) != 0) {
LOGE("Can't mount %s\n", path);
return INSTALL_CORRUPT;
}
//省略无关代码
return try_update_binary(path, &zip, wipe_cache);
}
这个really_install_package函数中,我们有目的的直接找ensure_path_mounted的实现代码:
int ensure_path_mounted(const char* path) {
Volume* v = volume_for_path(path);
if (v == NULL) {
LOGE("unknown volume for path [%s]\n", path);
return -1;
}
//省略无关代码
}
可以看到日志中的问题是在这儿弹出的,表示的就是在分区表中没找到我们升级地址,那我们是不是只要在分区表中加上不就能找到了呢?特别提下,volume_for_path函数是从结构体中遍历,看分区表是否注册有这个地址,实现这里就不贴了。
我们现在再回到最开始讲的main函数中,这个load_volume_table函数就是加载分区表:
./bootable/recovery/roots.cpp
void load_volume_table() {
int i;
int ret;
if ( check_flash_type() == NAND_TYPE ) {
LOGI("Load the recovery.fstab\n");
fstab = fs_mgr_read_fstab("/etc/recovery.fstab");
} else if ( check_flash_type() == EMMC_TYPE ) {
LOGI("Load the recovery.emmc.fstab\n");
fstab = fs_mgr_read_fstab("/etc/recovery.emmc.fstab");
} else {
fstab = fs_mgr_read_fstab("/etc/recovery.fstab");
}
if (!fstab) {
LOGE("failed to read /etc/recovery.fstab\n");
return;
}
ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk", 0);
if (ret < 0 ) {
LOGE("failed to add /tmp entry to fstab\n");
fs_mgr_free_fstab(fstab);
fstab = NULL;
return;
}
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");
}
可以看到是根据flash类型加载不同的分区表,解析并存到fstab 结构体中,所以我们根据实际情况选择,在fstab文件中添加我们的u盘升级路径即可。
我这里也讲的比较简单,但差不多原理就是这样了。