四、install_package详细流程
本篇将介绍install_package的流程,将重点分析install.cpp和roots.cpp,大致分为
1、UI设置
2、升级包路径挂载
3、分配内存空间
4、校验升级包
5、校验compatibility
6、try_update_binary(下个篇幅单独讲解,核心内容)
install.cpp
//返回status int类型
int install_package(const std::string& path, bool* wipe_cache, const std::string& install_file,
bool needs_mount, int retry_count) {
//传进来的参数中,needs_mount = true retry_count = 0 path = update_package
CHECK(!path.empty());
CHECK(!install_file.empty());
CHECK(wipe_cache != nullptr);
modified_flash = true;
//std::chrono::system_clock 它表示当前的系统时钟,系统中运行的所有进程使用now()得到的时间是一致的。now() 当前时间time_point
auto start = std::chrono::system_clock::now();
int start_temperature = GetMaxValueFromThermalZone();
int max_temperature = start_temperature;
int result;
std::vector<std::string> log_buffer;
//追踪判断roots.cpp
if (setup_install_mounts() != 0) {
LOG(ERROR) << "failed to set up expected mounts for install; aborting";
result = INSTALL_ERROR;
} else {
//主要install流程
result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count,
&max_temperature);
}
setup_install_mounts() != 0方法分析
roots.cpp
int setup_install_mounts() {
//判断static struct fstab* fstab是否为空
if (fstab == nullptr) {
LOG(ERROR) << "can't set up install mounts: no fstab loaded";
return -1;
}
printf("\n====roots.cpp setup_install_mounts====\n");
for (int i = 0; i < fstab->num_entries; ++i) {
const Volume* v = fstab->recs + i;
//这里打印出来的,其实跟我们之前加载分区表得到的时相同的,不过只是再次确认下mount_point的信息 char* mount_point
printf(" %d %s %s %s %lld\n", i, v->mount_point, v->fs_type, v->blk_device, v->length);
// We don't want to do anything with "/".这里通过log其实可以看到挂载点为/ 的只有system
if (strcmp(v->mount_point, "/") == 0) {
continue;
}
//如果mount_point中有tmp 和cache,判断是不是可以挂载成功
if (strcmp(v->mount_point, "/tmp") == 0 || strcmp(v->mount_point, "/cache") == 0) {
if (ensure_path_mounted(v->mount_point) != 0) {
LOG(ERROR) << "Failed to mount " << v->mount_point;
return -1;
}
} else {
//如果不是tmp和cache,判断是不是可以不挂载
if (ensure_path_unmounted(v->mount_point) != 0) {
LOG(ERROR) << "Failed to unmount " << v->mount_point;
return -1;
}
}
}
return 0;
}
ensure_path_mounted方法 看这个方法之前,我们先把其中一些调用方法整理清楚
Volume* v = volume_for_path(path)
roots.cpp
//传入mount_point 具体为/tmp 和 /cache
static Volume* volume_for_path(const char* path) {
//如果路径为空,或者路径第一个字节为空,返回nullptr
if (path == nullptr || path[0] == '\0') return nullptr;
//将路径转换为string格式
std::string str(path);
while (true) {
//传入获取的静态fstab,和路径,获取到对应的fstab_rec结构体
Volume* result = fs_mgr_get_entry_for_mount_point(fstab, str);
if (result != nullptr || str == "/") {
return result;
}
//为了使自己的程序有很好的移植性,c++程序员应该尽量使用size_t和size_type而不是int, unsigned
//size_t是全局定义的类型;size_type是STL类中定义的类型属性,用以保存任意string和vector类对象的长度
size_t slash = str.find_last_of('/');
if (slash == std::string::npos) return nullptr;
if (slash == 0) {
str = "/";
} else {
str = str.substr(0, slash);
}
}
return nullptr;
}
fs_mgr_get_entry_for_mount_point
system/core/fs_mgr/fs_mgr_fstab.cpp
/*
* Returns the fstab_rec* whose mount_point is path. 返回fstab_rec结构体
* Returns nullptr if not found.
*/
struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path) {
if (!fstab) {
return nullptr;
}
//如果当前路径在recs的挂载点中存在,返回该挂载点的fstab_rec结构体,之前我们已经知道了结构体fstab和fstab_rec的内容
//fstab包含了分区表中条目的个数,recs,分区表的名称,fstab_rec包含了详细的条目信息,这里返回的就是包含当前挂载点的条目信息
for (int i = 0; i < fstab->num_entries; i++) {
//如果当前条目mount_point不为空,而且等于我们传进来的mount_point,返回这个条目
if (fstab->recs[i].mount_point && path == fstab->recs[i].mount_point) {
return &fstab->recs[i];
}
}
return nullptr;
}
!scan_mounted_volumes()
bootable/recovery/mounts.cpp
struct MountedVolume {
std::string device;
std::string mount_point;
std::string filesystem;
std::string flags;
};
std::vector<MountedVolume*> g_mounts_state;
bool scan_mounted_volumes() {
for (size_t i = 0; i < g_mounts_state.size(); ++i) {
delete g_mounts_state[i];
}
//执行之前先清除了容器中的内容,其实按目前的代码看,本身就是没有赋值的,
g_mounts_state.clear();
// Open and read mount table entries.
//setmntent,getmument,endmnment获取文件信息,得到mnment结构体,拿到文件中的详细信息
//介绍非常详细https://blog.csdn.net/lixiaogang_theanswer/article/details/79431318
FILE* fp = setmntent("/proc/mounts", "re");
if (fp == NULL) {
return false;
}
mntent* e;
//遍历出mntent中的参数,赋值给g_mounts_state
while ((e = getmntent(fp)) != NULL) {
MountedVolume* v = new MountedVolume;
v->device = e->mnt_fsname;
v->mount_point = e->mnt_dir;
v->filesystem = e->mnt_type;
v->flags = e->mnt_opts;
//将组装好的结构体放入g_mounts_state容器中
g_mounts_state.push_back(v);
}
endmntent(fp);
printf("\n==========mounts.cpp scan_mounted_volumes g_mounts_state==========\n");
for (size_t i = 0; i < g_mounts_state.size(); ++i) {
printf(" %s %s %s %s\n", g_mounts_state[i]->mount_point.c_str(), g_mounts_state[i]->device.c_str(), g_mounts_state[i]->filesystem.c_str(), g_mounts_state[i]->flags.c_str());
}
printf("\n==================================================================\n");
return true;
}
find_mounted_volume_by_mount_point
bootable/recovery/mounts.cpp
MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) {
printf("\n==========mounts.cpp find_mounted_volume_by_mount_point before for==========\n");
for (size_t i = 0; i < g_mounts_state.size(); ++i) {
//如果g_mounts_state[i]的挂载点存在传入的mount_point,那么就返回当前位置在的MountedVolumes结构体
if (g_mounts_state[i]->mount_point == mount_point){
printf("\n==========mounts.cpp find_mounted_volume_by_mount_point after for==========\n");
printf(" %s %s %s %s\n", g_mounts_state[i]->mount_point.c_str(), g_mounts_state[i]->device.c_str(), g_mounts_state[i]->filesystem.c_str(), g_mounts_state[i]->flags.c_str());
return g_mounts_state[i];
}
}
return nullptr;
}
ensure_path_mounted这个方法在下面really_install_package中也有走到,上述几个方法是加在该方法中的一些条件,我加了很多的打印,具体看了下,下载到data和sdcard中的一些区别,以下代码中会做一些说明
roots.cpp
//注意参数这里传入的是mount_point
int ensure_path_mounted(const char* path) {
// Mount at the default mount point.
return ensure_path_mounted_at(path, nullptr);
}
//传进来的mount_point为空
int ensure_path_mounted_at(const char* path, const char* mount_point) {
Volume* v = volume_for_path(path);
printf("\n==========ensure_path_mounted_at volume_for_path==========\n");
printf(" %s %s %s\n",v->mount_point, v->fs_type, v->blk_device);
//adupsfota start
#ifdef ADUPS_FOTA_SUPPORT
if (strncmp(path, "/fotaupdate/", 12) == 0) {
return 0;
}
#endif
//adupsfota end
//如果得到的fstab_rec为空,则打印错误unknown volume for path
if (v == nullptr) {
LOG(ERROR) << "unknown volume for path [" << path << "]";
return -1;
}
//如果得到的fstab_rec中fs_type为 ramdisk 则返回0 无需执行挂载操作
if (strcmp(v->fs_type, "ramdisk") == 0) {
// The ramdisk is always mounted.
return 0;
}
//这个判断条件中是从mounts文件中读取数据,在机器中路径为/proc/mounts,pull出来可以看到,具体打印如下
//[ 6.937196] ==========mounts.cpp scan_mounted_volumes g_mounts_state==========
//[ 6.937230] / rootfs rootfs rw,seclabel
//[ 6.937263] /dev tmpfs tmpfs rw,seclabel,nosuid,relatime,mode=755
//[ 6.937295] /dev/pts devpts devpts rw,seclabel,relatime,mode=600
//[ 6.937326] /proc proc proc rw,relatime,gid=3009,hidepid=2
//[ 6.937357] /sys sysfs sysfs rw,seclabel,relatime
//[ 6.937389] /sys/fs/selinux selinuxfs selinuxfs rw,relatime
//[ 6.937421] /mnt tmpfs tmpfs rw,seclabel,nosuid,nodev,noexec,relatime,mode=755,gid=1000
//[ 6.937452] /acct none cgroup rw,relatime,cpuacct
//[ 6.937483] /tmp tmpfs tmpfs rw,seclabel,relatime
//[ 6.937514] /config none configfs rw,relatime
//[ 6.937545] /dev/usb-ffs/adb adb functionfs rw,relatime
//[ 6.937577] /cache /dev/block/platform/bootdevice/by-name/cache ext4 rw,seclabel,nosuid,nodev,noatime,discard,noauto_da_alloc,data=ordered
if (!scan_mounted_volumes()) {
LOG(ERROR) << "Failed to scan mounted volumes";
return -1;
}
//如果mount_point为空,把fstab_rec中的值给到mount_point
if (!mount_point) {
mount_point = v->mount_point;
}
//从g_mounts_state中读取数据,看是否有对应的挂载点
//根据我们上个方法拿到的数据不难发现,g_mount_state中并没有sdcard的条目
//所以 路径为data时,mv不为空,直接return 0,路径为sdcard时 mv为空,需要执行后续的mount方法
//data打印结果
//[ 7.032122] ==========roots.cpp find_mounted_volume_by_mount_point before==========
//[ 7.032179] ==========mounts.cpp find_mounted_volume_by_mount_point before for==========
//[ 7.032237] ==========mounts.cpp find_mounted_volume_by_mount_point after for==========
//[ 7.032269] /cache /dev/block/platform/bootdevice/by-name/cache ext4 rw,seclabel,nosuid,nodev,noatime,discard,noauto_da_alloc,data=ordered
//[ 7.032326] ==========roots.cpp find_mounted_volume_by_mount_point after==========
//sdcard打印结果
//[ 7.022144] ==========roots.cpp find_mounted_volume_by_mount_point before==========
//[ 7.022393] ==========mounts.cpp find_mounted_volume_by_mount_point before for==========
//[ 7.022647] ==========roots.cpp find_mounted_volume_by_mount_point after==========
printf("\n==========roots.cpp find_mounted_volume_by_mount_point before==========\n");
const MountedVolume* mv = find_mounted_volume_by_mount_point(mount_point);
printf("\n==========roots.cpp find_mounted_volume_by_mount_point after==========\n");
if (mv != nullptr) {
printf("\n==========roots.cpp mv != nullptr==========\n");
return 0;
}
mkdir(mount_point, 0755); // in case it doesn't already exist
if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "squashfs") == 0 ||
strcmp(v->fs_type, "vfat") == 0) {
int result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options);
if (result == -1 && fs_mgr_is_formattable(v)) {
PLOG(ERROR) << "Failed to mount " << mount_point << "; formatting";
bool crypt_footer = fs_mgr_is_encryptable(v) && !strcmp(v->key_loc, "footer");
if (fs_mgr_do_format(v, crypt_footer) == 0) {
result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options);
} else {
PLOG(ERROR) << "Failed to format " << mount_point;
return -1;
}
}
if (result == -1) {
PLOG(ERROR) << "Failed to mount " << mount_point;
return -1;
}
return 0;
}
LOG(ERROR) << "unknown fs_type \"" << v->fs_type << "\" for " << mount_point;
return -1;
}
really_install_package
install.cpp
//还是先说明传进来的参数 needs_mount = true retry_count = 0 path = update_package
static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
std::vector<std::string>* log_buffer, int retry_count,
int* max_temperature) {
//ui部分的处理设置背景和进度条 TODO 后续有时间再处理UI部分
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->Print("Finding update package...\n");
// Give verification half the progress bar...
ui->SetProgressType(RecoveryUI::DETERMINATE);
ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
LOG(INFO) << "Update location: " << path;
// Map the update package into memory.
ui->Print("Opening update package...\n");
//needs_mount =true,所以进入判断
if (needs_mount) {
//如果path的开始位置为@,也就是内置存储转换之后的路径,那么取去除第一个位置之后的string 也就是cache/recovery/block.map
//ensure_path_mounted,以上已经做过分析,这里不做描述
if (path[0] == '@') {
ensure_path_mounted(path.substr(1).c_str());
} else {
ensure_path_mounted(path.c_str());
}
}
//映射内存空间
MemMapping map;
if (!map.MapFile(path)) {
LOG(ERROR) << "failed to map file";
log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
return INSTALL_CORRUPT;
}
// Verify package.校验升级包签名
if (!verify_package(map.addr, map.length)) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
return INSTALL_CORRUPT;
}
// Try to open the package.从内存中打开升级包
ZipArchiveHandle zip;
int err = OpenArchiveFromMemory(map.addr, map.length, path.c_str(), &zip);
if (err != 0) {
LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);
log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
CloseArchive(zip);
return INSTALL_CORRUPT;
}
// Check partition size between source and target
// 校验前后版本的分区
#ifndef AB_OTA_UPDATER
int ret=INSTALL_SUCCESS;
if (mt_really_install_package_check_part_size(ret, path.c_str(), zip)) {
CloseArchive(zip);
return ret;
}
#endif
// Additionally verify the compatibility of the package.
// 校验升级包中的compatibility.zip
// 关于compatibility.zip的介绍https://blog.csdn.net/csdn66_2016/article/details/81704720
if (!verify_package_compatibility(zip)) {
log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
CloseArchive(zip);
return INSTALL_CORRUPT;
}
// Verify and install the contents of the package.
ui->Print("Installing update...\n");
if (retry_count > 0) {
ui->Print("Retry attempt: %d\n", retry_count);
}
ui->SetEnableReboot(false);
//调用脚本解释器执行升级脚本
int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
ui->SetEnableReboot(true);
ui->Print("\n");
CloseArchive(zip);
return result;
}
map.MapFile(path)方法,虽然对于路径也有方法的区分,但是大致的流程都需要把升级包的数据映射到内存空间,后续校验方法传入的也是内存映射地址
bootable/recovery/otautil/SysUtil.cpp
bool MemMapping::MapFile(const std::string& fn) {
if (fn.empty()) {
LOG(ERROR) << "Empty filename";
return false;
}
//这里分为了两种情况,走了不同的内存地址映射的方法,data区和其他路径
if (fn[0] == '@') {
// Block map file "@/cache/recovery/block.map".
if (!MapBlockFile(fn.substr(1))) {
LOG(ERROR) << "Map of '" << fn << "' failed";
return false;
}
} else {
// This is a regular file.
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY)));
if (fd == -1) {
PLOG(ERROR) << "Unable to open '" << fn << "'";
return false;
}
if (!MapFD(fd)) {
LOG(ERROR) << "Map of '" << fn << "' failed";
return false;
}
}
return true;
}
针对cache/recovery/block.map的MapBlockFile
bootable/recovery/otautil/SysUtil.cpp
bool MemMapping::MapBlockFile(const std::string& filename) {
std::string content;
//以是否可以读取出字符串判断,如果block.map为空,将会报错failed to read
if (!android::base::ReadFileToString(filename, &content)) {
PLOG(ERROR) << "Failed to read " << filename;
return false;
}
//以换行分割block.map到string类型的容器中
std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
//加了打印用于输出lines容器中的内容,输出结果:
//[ 7.034186] =================MapBlockFile block.map data
"/dev/block/platform/bootdevice/by-name/userdata"
//[ 7.034245] "19485612 4096"
//[ 7.034276] "2"
//[ 7.034307] "556032 557056"
//[ 7.034337] "583680 587414"
printf("\n=================MapBlockFile block.map data====================\n");
for (const auto& line : lines) {
printf(" \"%s\"\n", line.c_str());
}
if (lines.size() < 4) {
LOG(ERROR) << "Block map file is too short: " << lines.size();
return false;
}
size_t size;
size_t blksize;
// %d输出int型。 %zu输出size_t型
// int sscanf() 从字符串读取格式化输入 返回输入时几个参数,这个读取的第二行的两个参数,如果返回的不是2 那说明block.map缺失数据
// sscanf介绍:https://www.cnblogs.com/wwjyt/p/3182892.html
if (sscanf(lines[1].c_str(), "%zu %zu", &size, &blksize) != 2) {
LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
return false;
}
size_t range_count;
//判断第三行有没有block区间的总数
if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
LOG(ERROR) << "Failed to parse block map header: " << lines[2];
return false;
}
size_t blocks;
//如果blksize不为空,那么计算出blocks的数量
if (blksize != 0) {
blocks = ((size - 1) / blksize) + 1;
}
//再次确认各项数值有无异常和空值
//这里的SIZE_MAX 是./bionic/libc/include/stdint.h中宏定义的数值 #define SIZE_MAX UINT64_MAX
if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 ||
lines.size() != 3 + range_count) {
LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize
<< ", range_count " << range_count << ", lines " << lines.size();
return false;
}
// Reserve enough contiguous address space for the whole file.
//使用mmap用来在进程虚拟地址空间中分配创建一片虚拟内存地址映射,返回值:成功返回映射的虚拟内存地址的起始地址,失败返回MAP_FAILED
//内存映射:https://blog.csdn.net/qq_33611327/article/details/81738195 http://blog.itpub.net/7728585/viewspace-2142411/
void* reserve = mmap(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (reserve == MAP_FAILED) {
PLOG(ERROR) << "failed to reserve address space";
return false;
}
///dev/block/platform/msm_sdcc.1/by-name/userdata
//o_rdonly read only 只读 o_wronly write only 只写 o_rdwr read write 可读可写,fd为文件描述符
const std::string& block_dev = lines[0];
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY)));
if (fd == -1) {
PLOG(ERROR) << "failed to open block device " << block_dev;
munmap(reserve, blocks * blksize);
return false;
}
//ranges_ 在sysutil.h中定义 ,是每个元素为结构体MappedRange的容器,std::vector<MappedRange> ranges_;
// struct MappedRange {
// void* addr;
// size_t length;
//};
//装入之前清除容器中内容
ranges_.clear();
//static_cast相当于传统的C语言里的强制转换,将reserve强制转换为char类型
unsigned char* next = static_cast<unsigned char*>(reserve);
//blocks = ((size - 1) / blksize) + 1;
size_t remaining_size = blocks * blksize;
bool success = true;
for (size_t i = 0; i < range_count; ++i) {
const std::string& line = lines[i + 3];
size_t start, end;//1000 1008 从第四行开始处理
if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) {
LOG(ERROR) << "failed to parse range " << i << ": " << line;
success = false;
break;
}
//区间的大小为区间长度×块大小
size_t range_size = (end - start) * blksize;
if (end <= start || (end - start) > SIZE_MAX / blksize || range_size > remaining_size) {
LOG(ERROR) << "Invalid range: " << start << " " << end;
success = false;
break;
}
//将第一个block range放入到我们分配的内存地址中,偏移量为第一个区间的起始位置
void* range_start = mmap(next, range_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd,
static_cast<off_t>(start) * blksize);
if (range_start == MAP_FAILED) {
PLOG(ERROR) << "failed to map range " << i << ": " << line;
success = false;
break;
}
//将第一个区间的MappedRange结构体放入到ranges容器中
ranges_.emplace_back(MappedRange{ range_start, range_size });
//第一个区间放入后,将起始位置加上之前的range_size
next += range_size;
//而总的大小减去第一个ranges,这两个数据的变动是为了第二个区间的放入
remaining_size -= range_size;
}
if (success && remaining_size != 0) {
LOG(ERROR) << "Invalid ranges: remaining_size " << remaining_size;
success = false;
}
if (!success) {
munmap(reserve, blocks * blksize);
return false;
}
//数据全部放入后,拿到的addr内存映射地址就是我们升级包的数据
addr = static_cast<unsigned char*>(reserve);
length = size;
LOG(INFO) << "mmapped " << range_count << " ranges";
return true;
}
针对sdcard的MapFD方法
bootable/recovery/otautil/SysUtil.cpp
bool MemMapping::MapFD(int fd) {
struct stat sb;
if (fstat(fd, &sb) == -1) {
PLOG(ERROR) << "fstat(" << fd << ") failed";
return false;
}
//分配内存地址映射
void* memPtr = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (memPtr == MAP_FAILED) {
PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed";
return false;
}
addr = static_cast<unsigned char*>(memPtr);
length = sb.st_size;
ranges_.clear();
ranges_.emplace_back(MappedRange{ memPtr, static_cast<size_t>(sb.st_size) });
return true;
}
verify_package校验签名
bootable/recovery/install.cpp TODO 这里可以作为了解,看下里面的打印信息,知道大概流程
bool verify_package(const unsigned char* package_data, size_t package_size) {
static constexpr const char* PUBLIC_KEYS_FILE = "/res/keys";
std::vector<Certificate> loadedKeys;
if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) {
LOG(ERROR) << "Failed to load keys";
return false;
}
LOG(INFO) << loadedKeys.size() << " key(s) loaded from " << PUBLIC_KEYS_FILE;
// Verify package.
ui->Print("Verifying update package...\n");
auto t0 = std::chrono::system_clock::now();
int err = verify_file(package_data, package_size, loadedKeys,
std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
if (err != VERIFY_SUCCESS) {
LOG(ERROR) << "Signature verification failed";
LOG(ERROR) << "error: " << kZipVerificationFailure;
return false;
}
return true;
}
try_update_binary将作为单独讲解,recovery使用update_binary,解析update-scrypt进行具体升级流程
来源:https://blog.csdn.net/Android_2016/article/details/98938550