目录
概述
之前博客里一篇文章讲解了OTA包的生成原理,这篇文章主要是从Recovery源码的角度介绍一下Recovery是如何使用OTA包进行系统升级的。
为了防止泄密,本文源码都是基于Android4.4.2_r1版本进行分析。
Recovery源码解析
Recovery源码的入口位置为:bootable/recovery/recovery.cpp文件。下面我就来分析一下Recovery的源码。
static const char *CACHE_LOG_DIR = "/cache/recovery";
static const char *COMMAND_FILE = "/cache/recovery/command";
static const char *INTENT_FILE = "/cache/recovery/intent";
static const char *LOG_FILE = "/cache/recovery/log";
注释里英文写的很清楚:
The recovery tool communicates with the main system through /cache files.
/cache/recovery/command - INPUT - command line for tool, one arg per line
/cache/recovery/log - OUTPUT - combined log file from recovery run(s)
/cache/recovery/intent - OUTPUT - intent that was passed in
同时,代码里还有一段对Recovery识别命令的注释描述:
The arguments which may be supplied in the command file:
1. –send_intent=anystring - write the text out to recovery.intent
2. –update_package=path - verify install an OTA package file
3. –wipe_data - erase user data (and cache), then reboot
4. –wipe_cache - wipe cache (but not user data), then reboot
main函数
接下面,我们分析一下recovery.c的入口main函数。
输出重定向
static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"
int main(int argc, char **argv)
{
time_t start = time(NULL);
freopen(TEMPORARY_LOG_FILE, "a", stdout);
setbuf(stdout, NULL);
freopen(TEMPORARY_LOG_FILE, "a", stderr);
setbuf(stderr, NULL);
// 打印启动recovery的时间
printf("Starting recovery on %s", ctime(&start));
}
该部分的主要作用是:将标准输出和错误输出重定向到/tmp/recovery.log文件中。
填充fstab结构体
struct fstab {
int num_entries;
struct fstab_rec *recs;
char *fstab_filename;
};
struct fstab_rec {
char *blk_device;
char *mount_point;
char *fs_type;
unsigned long flags;
char *fs_options;
int fs_mgr_flags;
char *key_loc;
char *verity_loc;
long long length;
char *label;
int partnum;
int swap_prio;
unsigned int zram_size;
};
typdef struct fstab_rec Volume;
void load_volume_table()
{
int i;
int ret;
// 解析/etc/recovery.fstab配置文件,填充fstab结构体
fstab = fs_mgr_read_fstab("/etc/recovery.fstab");
if (!fstab) {
LOGE("failed to read /etc/recovery.fstab\n");
return;
}
// 在fstab结构体中增加/tmp分区
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");
}
int main(int argc, char **argv)
{
load_volume_table();
}
该部分代码的主要作用是用recovery根目录下的/etc/recovery.fstab中的分区内容和/tmp分区内容来填充了fstab结构体,并没有真正的进行分区挂载。
挂载cache分区
源码分析如下:
typedef struct fstab_rec Volume;
#define LAST_LOG_FILE "/cache/recovery/last_log"
int ensure_path_mounted(const char* path)
{
// 这里是在fstab结构体中找到挂载点为/cache的fstab_recs
Volume* v = volume_for_path(path);
if (v == NULL) {
LOGE("unknown volume for path [%s]\n", path);
return -1;
}
// ramdisk类型的分区是一直挂载的。
if (strcmp(v->fs_type, "ramdisk") == 0) {
return 0;
}
int result;
result = scan_mounted_volumes();
const MountedVolume* mv = find_mounted_volume_by_mount_point(v->mount_point);
if (mv) {
// 说明当前分区已经被挂载了
return 0;
}
// 下面是具体的挂载过程
mkdir(v->mount_point, 0755);
if (strcmp(v->fs_type, "yaffs2") == 0) {
// .......不用管这个yaffs2分区类型了,目前基本是ext4的。
} else if (strcmp(v->fs_type, "ext4"