本节继续分析一下升级过程。Android中sideload的安装方式比较少见,并且是比较复杂的,我们通过分析这种升级方式来了解Recovery的升级过程。
1、sideload安装方式
在上一节prompt_and_wait函数中处理菜单命令时,如果选择的命令是APPLY_ADB_SIDELOAD,将启动ADBD,让用户通过adb连接来执行sideload命令上传,更新文件到/tmp/update.zip,然后再执行更新操作。在prompt_and_wait函数中的处理如下:
case Device::APPLY_ADB_SIDELOAD:
status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);//入口函数,/tmp/last_install
if (status >= 0) {
if (status != INSTALL_SUCCESS) {
ui->SetBackground(RecoveryUI::ERROR);
ui->Print("Installation aborted.\n");
copy_logs();
} else if (!ui->IsTextVisible()) {
return Device::NO_ACTION; // reboot if logs aren't visible
} else {
ui->Print("\nInstall from ADB complete.\n");
}
}
break;
sideload安装方式的入口是apply_from_adb()函数,位于bootable/recovery/Adb_install.cpp中,代码如下:
int
apply_from_adb(RecoveryUI* ui_, int* wipe_cache, const char* install_file) {
ui = ui_;
stop_adbd();//先停止ADBD
set_usb_driver(true);//启动USB连接
//打印使用sideload命令的提示
ui->Print("\n\nNow send the package you want to apply\n"
"to the device with \"adb sideload <filename>\"...\n");
pid_t child;
if ((child = fork()) == 0) {//fork一个子进程,然后在子进程中执行recovery
execl("/sbin/recovery", "recovery", "--adbd", NULL);
_exit(-1);
}
// FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host
// connects and starts serving a package. Poll for its
// appearance. (Note that inotify doesn't work with FUSE.)
int result;
int status;
bool waited = false;
struct stat st;
for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
if (waitpid(child, &status, WNOHANG) != 0) {//等待子进程ADBD结束
result = INSTALL_ERROR;
waited = true;
break;
}
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {//获取/sideload/package.zip文件信息,并保存在stat结构体中
if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) {//文件不存在
sleep(1);
continue;
} else {//超时
ui->Print("\nTimed out waiting for package.\n\n", strerror(errno));
result = INSTALL_ERROR;
kill(child, SIGKILL);
break;
}
}
result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false);//文件存在,调用install_package函数开始安装
break;
}
if (!waited) {
// Calling stat() on this magic filename signals the minadbd
// subprocess to shut down.
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
// TODO(dougz): there should be a way to cancel waiting for a
// package (by pushing some button combo on the device). For now
// you just have to 'adb sideload' a file that's not a valid
// package, like "/dev/null".
waitpid(child, &sta