游戏迷提供最新游戏下载和手游攻略!

Android系统恢复功能更新机制解析

发布时间:2024-09-19浏览:19

各位老铁们好,相信很多人对Android系统恢复功能更新机制解析都不是特别的了解,因此呢,今天就来为大家分享下关于Android系统恢复功能更新机制解析以及的问题知识,还望可以帮助大家,解决大家的一些困惑,下面一起来看看吧!

Recovery模式指的是一种可以对安卓机内部的数据或系统进行修改的模式(类似于windows PE或DOS)。也可以称之为安卓的恢复模式,在这个所谓的恢复模式下,我们可以刷入新的安卓系统,或者对已有的系统进行备份或升级,也可以在此恢复出厂设置(格式化数据和缓存)。

1. Recovery相关概念

Recovery: Recovery模式指的是一种可以对安卓机内部的数据或系统进行修改的模式,也指Android的Recovery分区

OTA: Over-the-Air Technology,即空中下载技术,是 Android 系统提供的标准软件升级方式。 它功能强大,提供了完全升级、增量升级模式,可以通过 SD 卡升级,也可以通过网络升级。不管是哪种方式,都有几个过程:生成升级包、下载升级包、安装升级包。

RecoverySystem:Android系统内部实现的一个工具类,Android应用层操作Recovery模式的一个重要途径,它提供了几个重要的API,用于实现OTA包校验、升级以及恢复出厂设置(格式化数据和缓存)。

Main System:主系统模式,即Android正常开机所进入的Android系统

Bootloader:Bootloader是嵌入式系统在加电后执行的第一段代码,在它完成CPU和相关硬件的初始化之后,再将操作系统映像或固化的嵌入式应用程序装在到内存中然后跳转到操作系统所在的空间,启动操作系统运行。

BCB:Bootloader Control Block,启动控制信息块,位于misc分区,从代码上看,就是一个结构体。

2. Android系统的启动模式

2.1 Android 各个分区介绍

一般来说,安卓手机和平板一般包括以下标准内部分区:

Boot:包含Linux内核和一个最小的root文件系统(装载到ramdisk中),用于挂载系统和其他的分区,并开始Runtime。正如名字所代表的意思(注:boot的意思是启动),这个分区使Android设备可以启动。如果没有这个分区,Android设备通常无法启动到Android系统。

System:这个分区几乎包含了除kerner和ramdisk之外的整个android操作系统,包括了用户界面、和所有预装的系统应用程序和库文件(AOSP中可以获取到源代码)。在运行的过程中,这个分区是read-only的。当然,一些Android设备,也允许在remount的情况下,对system分区进行读写。 擦除这个分区,相当于删除整个安卓系统,会导致不能进入Main System, 但不会影响到Recovery。因此,可以通过进入Recovery程序或者bootloader程序中,升级安装一个新ROM。

Userdata:用户数据区,用户安装的应用程序会把数据保存在这里,包含了用户的数据:联系人、短信、设置、用户安装的程序。擦除这个分区,本质上等同于手机恢复出厂设置,也就是手机系统第一次启动时的状态,或者是最后一次安装官方或第三方ROM后的状态。在Recovery程序中进行的“data/factory reset ”操作就是在擦除这个分区。正常情况下OTA是不会清除这里的数据的,指定要删除数据的除外。

Cache:系统缓存区,临时的保存应用数据(要把数据保存在这里,需要特地的app permission), OTA的升级包也可以保存在这里。OTA升级过程可能会清楚这个分区的数据。一般来讲,Android差分包升级也需要依赖此分区存放一些中间文件。

Recovery:包括了一个完整Linux内核和一些特殊的recovery binary,可以读取升级文件用这些文件来更新其他的分区。

Misc:一个非常小的分区,4 MB左右。recovery用这个分区来保存一些关于升级的信息,应对升级过程中的设备掉电重启的状况,Bootloader启动的时候,会读取这个分区里面的信息,以决定系统是否进Recovery System 或 Main System。

以上几个分区是Google官方的标准,对于第三方Android设备厂商来讲,分区的情况可能稍微不一样,比如Rockchip平台,还增加了user分区、kernel分区和backup分区。其中:

kernel:顾名思义,是存放kernel.img镜像的。在boot分区里面的kernel内核镜像损坏的情况下(比如flash损坏),bootloader会尝试加载kerner分区里面的内核镜像。

backup:存放整个系统镜像(update.img), 可用于恢复设备到出厂ROM。

user: 用户分区,也就是平时我们所说的内置sdcard。另外还有外置的sdcard分区,用于存放用户相片、视频、文档、ROM安装包等。

2.2 Android的启动模式

一般来讲,Android有三种启动模式:Fastboot模式,Recovery System 以及Main System。

Fastboot:在这种模式下,可以修改手机的硬件,并且允许我们发送一些命令给Bootloader。如使用电脑刷机,则需要进入fastboot模式,通过电脑执行命令将系统镜像刷到通过USB刷到Android设备中中。

Recovery:Recovery是一个小型的操作系统,并且会加载部分文件系统,这样才能从sdcard中读取升级包。

Main System: 即我们平时正常开机后所使用的手机操作系统模式

首先说一下,正常启动和进入Recovery的区别,一图以概之:

2.3 如何进入Recovery模式

一般来讲,进入recovery有两种方式,一种是通过组合键进入recovery,按键指引的方式,各个Android平台都不一样,比如三星的手机是在关机状态下同时按住【音量上】、【HOME键】、【电源键】,等待屏幕亮起后即可放开,进入Recovery模式。而Rockchip的机顶盒,则是使用按【Reset键】加【电源键】开机的方式,形式不一。

另一种,则是使用系统命令启动到Recovery模式的,这对绝大部分Android设备是通用的:

reboot recovery

3. Recovery升级原理

3.1 应用层升级流程

在Android应用层部分,OTA系统升级流程。大概的流程图如下所示:

以上部分,只介绍了应用层层面的 ota升级包的下载、校验以及最后的发起安装过程。在这里,重要讲解进入Recovery模式后,OTA包的升级过程。

首先,在应用层下载升级包后,会调用RecoverySystem.installPackage(Context context, File packageFile)函数来发起安装过程,这个过程主要的原理,实际上只是往 /cache/recovery/command 写入ota升级包存放路径,然后重启到recovery模式,仅此而已。

public static void installPackage(Context context, File packageFile) throws IOException { String filename = packageFile.getCanonicalPath(); Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); final String filenameArg = "--update_package=" + filename; final String localeArg = "--locale=" + Locale.getDefault().toString(); bootCommand(context, filenameArg, localeArg); } private static void bootCommand(Context context, String... args) throws IOException { RECOVERY_DIR.mkdirs(); // In case we need it COMMAND_FILE.delete(); // In case it's not writable LOG_FILE.delete(); FileWriter command = new FileWriter(COMMAND_FILE); try { for (String arg : args) { if (!TextUtils.isEmpty(arg)) { command.write(arg); command.write("\n"); } } } finally { command.close(); } // Having written the command file, go ahead and reboot PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); pm.reboot(PowerManager.REBOOT_RECOVERY); throw new IOException("Reboot failed (no permissions?)"); }

因此,实质上等同于以下命令:

echo -e "--update_package=/mnt/sdcard/ota/update.zip" >/cache/recovery/commandreboot recovery

3.2 OTA升级包的目录结构

OTA升级包的目录结构大致如下所示:

|----boot.img|----system/|----recovery/ |----recovery-from-boot.p |----etc/ `|----install-recovery.sh|---META-INF/ |CERT.RSA |CERT.SF |MANIFEST.MF |----com/ |----google/ |----android/ |----update-binary |----updater-script |----android/ |----metadata

其中:

boot.img 是更新boot分区所需要的镜像文件。这个boot.img主要包括kernel、ramdisk。

system/目录的内容在升级后会放在系统的system分区,主要是系统app,library和binary二进制文件

update-binary是一个二进制文件,相当于一个脚本解释器,能够识别updater-script中描述的操作。

metadata文件是描述设备信息及环境变量的元数据。主要包括一些编译选项,签名公钥,时间戳以及设备型号等。

我们还可以在包中添加userdata目录,来更新系统中的用户数据部分。这部分内容在更新后会存放在系统的/data目录下。

update.zip包的签名:update.zip更新包在制作完成后需要对其签名,否则在升级时会出现认证失败的错误提示。而且签名要使用和目标板一致的加密公钥。默认的加密公钥及加密需要的三个文件在Android源码编译后生成的具体路径为:

out/host/linux-x86/framework/signapk.jar

build/target/product/security/testkey.x509.pem

build/target/product/security/testkey.pk8

MANIFEST.MF:这个manifest文件定义了与包的组成结构相关的数据。类似Android应用的mainfest.xml文件。

CERT.RSA:与签名文件相关联的签名程序块文件,它存储了用于签名JAR文件的公共签名。

CERT.SF:这是JAR文件的签名文件,其中前缀CERT代表签名者。

3.3 Recovery模式下的OTA升级流程

进入Recovery模式之后,便开始对下载的升级包进行升级,整体的流程图如下所示:

BCB(Bootloader与Recovery通过BCB(Bootloader Control Block)通信)

这里,详解介绍一下升级流程中的各个模块。

1. get_args(&argc, &argv)

get_args的原理流程图如下所示:

接着,会把启动参数的信息通过set_bootloader_message()函数又保存到了BCB块中。这样做的目的是防止一旦升级或擦除数据的过程中发生崩溃或不正常断电,下次重启,Bootloader会依据BCB的指示,引导进入Recovery模式,从/misc分区中读取更新的命令,继续进行更新操作。因此,可以说是一种掉电保护机制。

get_args具体的流程如下图所示:

get_args函数核心代码如下:

static void get_args(int *argc, char ***argv) { struct bootloader_message boot; memset(&boot, 0, sizeof(boot)); //解析BCB模块 get_bootloader_message(&boot); // this may fail, leaving a zeroed structure ...... // --- if that doesn't work, try the command file if (*argc<= 1) { FILE *fp = fopen_path(COMMAND_FILE, "r");//COMMAND_FILE指/cache/recovery/command if (fp != NULL) { char *argv0 = (*argv)[0]; *argv = (char **) malloc(sizeof(char *) * MAX_ARGS); (*argv)[0] = argv0; // use the same program name char buf[MAX_ARG_LENGTH]; for (*argc = 1; *argc< MAX_ARGS; ++*argc) { if (!fgets(buf, sizeof(buf), fp)) break; (*argv)[*argc] = strdup(strtok(buf, "\r\n")); // Strip newline. } check_and_fclose(fp, COMMAND_FILE); LOGI("Got arguments from %s\n", COMMAND_FILE); } }...... set_bootloader_message(&boot); //回写BCB

这里需要说一下“BCB”,即bootloader control block, 中文可以呼之为“启动控制模信息块”**,位于/misc分区,从代码上看,就是一个struct 结构体 :

struct bootloader_message {

char command[32];

char status[32];

char recovery[1024];

};

bootloader_message 结构体包含三个字段,具体含义如下:

command 字段中存储的是命令,它有以下几个可能值:

boot-recovery:系统将启动进入Recovery模式

update-radia 或者 update-hboot:系统将启动进入更新firmware的模式,这个更新过程由bootloader完成

NULL:空值,系统将启动进入Main System主系统,正常启动。

status 字段存储的是更新的结果。更新结束后,由Recovery或者Bootloader将更新结果写入到这个字段中。

recovery 字段存放的是recovry模块的启动参数,一般包括升级包路径。其存储结构如下:第一行存放字符串“recovery”,第二行存放路径信息“–update_package=/mnt/sdcard/update.zip”等。 因此,参数之间是以“\n”分割的。

2. update_package

ota升级包的存放路径,从BCB或者/cache/recovery/command里面解析得到的,升级包一般下载后存放在cache或sdcard分区,当然,也有一些是存放到U盘之类的外接存储设备中的。一般赋值格式如下:

--update_package=/mnt/sdcard/update.zip 或 --update_package=CACHE:update.zip

3. int install_package (const char path, int wipe_cache, const char* install_file)

int install_package(const char* path, int* wipe_cache, const char* install_file){//install_file 为 /cache/recovery/last_install FILE* install_log = fopen_path(install_file, "w"); if (install_log) { fputs(path, install_log); fputc('\n', install_log); } else { LOGE("failed to open last_install: %s\n", strerror(errno)); } int result = really_install_package(path, wipe_cache); if (install_log) { fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log); fputc('\n', install_log); fclose(install_log); } return result;}

4. static int really_install_package(const char path, int wipe_cache)

really_install_package函数在install_package函数中被调用,函数的主要作用是调用ensure_path_mounted确保升级包所在的分区已经挂载,另外,还会对升级包进行一系列的校验,在具体升级时,对update.zip包检查时大致会分三步:

检验SF文件与RSA文件是否匹配;

检验MANIFEST.MF与签名文件中的digest是否一致;

检验包中的文件与MANIFEST中所描述的是否一致

通过校验后,调用try_update_binary函数去实现真正的升级。

5. static int try_update_binary(const char path, ZipArchive zip, int wipe_cache)

try_update_binary是真正实现对升级包进行升级的函数:

static int try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { const ZipEntry* binary_entry = mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);...... const char* binary = "/tmp/update_binary"; unlink(binary); int fd = creat(binary, 0755); .....//将升级包里面的update_binary解压到/tmp/update_binary bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); close(fd); mzCloseZipArchive(zip);...... int pipefd[2]; pipe(pipefd); const char** args = (const char**)malloc(sizeof(char*) * 5); args[0] = binary; //update_binary存放路径 args[1] = EXPAND(RECOVERY_API_VERSION); // Recovery版本号 char* temp = (char*)malloc(10); sprintf(temp, "%d", pipefd[1]); args[2] = temp; args[3] = (char*)path; //升级包存放路径 args[4] = NULL; pid_t pid = fork();//fork一个子进程 if (pid == 0) { close(pipefd[0]); //子进程调用update-binary执行升级操作 execv(binary, (char* const*)args); fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); _exit(-1); }...... int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { //安装失败,返回INSTALL_ERROR return INSTALL_ERROR; } //安装成功,返回INSTALL_SUCCESS return INSTALL_SUCCESS;}

总的来说,try_update_binary主要做了以下几个操作:

(1)mzOpenZipArchive():打开升级包,并将相关的信息拷贝到一个临时的ZipArchinve变量中。注意这一步并未对我们的update.zip包解压。

(2)mzExtractZipEntryToFile(): 解压升级包特定文件,将升级包里面的META-INF/com/google/android/update-binary 解压到内存文件系统的/tmp/update_binary中。

(3)fork创建一个子进程 , 使用系统调用函数execv( ) 去执行/tmp/update-binary程序,

(4)update-binary: 这个是Recovery OTA升级的核心程序,是一个二进制文件,实现代码位于系统源码bootable/recovery/updater。其实质是相当于一个脚本解释器,能够识别updater-script中描述的操作并执行。

assert(getprop("ro.product.device") == "rk31sdk" || getprop("ro.build.product") == "rk301dk");show_progress(0.500000, 0);format("ext4", "EMMC", "/dev/block/mtd/by-name/system", "0", "/system");mount("ext4", "EMMC", "/dev/block/mtd/by-name/system", "/system");package_extract_dir("recovery", "/system");package_extract_dir("system", "/system");symlink("Roboto-Bold.ttf", "/system/fonts/DroidSans-Bold.ttf");symlink("mksh", "/system/bin/sh");......set_perm_recursive(0, 0, 0755, 0644, "/system");set_perm_recursive(0, 2000, 0755, 0755, "/system/bin");......set_perm(0, 0, 06755, "/system/xbin/su");set_perm(0, 0, 06755, "/system/xbin/tcpdump");show_progress(0.200000, 0);show_progress(0.200000, 10);write_raw_image(package_extract_file("boot.img"), "boot");show_progress(0.100000, 0);clear_misc_command();unmount("/system");

update_script常用的命令如下:

因此,根据上面的升级脚本,可以知道,升级包的大致升级流程如下:

判断是不是升级包是否适用于该设备,如果不适用,则停止升级,否则继续。

显示进度条

格式化system分区

挂载system分区

将ota升级包里面的system、recovery目录解压到system分区

建立一些软链接,升级过程需要用到

设置部分文件权限

将升级包里面的boot.img写入到/boot分区

清空misc分区,即BCB块置为NULL

卸载system分区

6. wipe data/cache

main函数,在执行完install_package后,会根据传入的wipe_data/wipe_cache,决定是否执行/data和/cache分区的清空操作。

7. prompt_and_wait

这个函数的作用就是一直在等待用户输入,是一个不断的循环,可以选择Recovery模式下的一些选项进行操作,包括恢复出厂设置和重启等。如果升级失败, prompt_and_wait会显示错误,并等待用户响应。

8. finish_recovery

OTA升级成功,清空misc分区(BCB置零),并将保存到内存系统的升级日志/tmp/recovery.log保存到/cache/recovery/last_log。重启设备进入Main System,升级完成。

9. install-recovery.sh

从上面的流程中,可以知道,Recovery模式下的OTA升级成功,只是更新了/system和/boot两个最核心的分区,而本身用来升级的Recovery自身并没有在那个时候得到更新。Recovery分区的更新,是在重启进入主系统的时候,由install-recovery.sh来更新的。这样可以保证,即使升级失败,Recovery模式也不会受到影响,仍然可以手动进入Recovery模式执行升级或擦除数据操作。

在Recovery升级的时候,有一句:

package_extract_dir("recovery", "/system");

这条命令就是将升级包里面的recovery目录的内容,解压到/system分区

recovery目录下的文件,主要有install-recovery.sh和 recovery-from-boot.p,目录结构如下所示:

├── bin│ └── install-recovery.sh└── recovery-from-boot.p

其中:

recovery-from-boot.p 是boot.img和recovery.img的补丁(patch)

install-recovery.sh 则是来用安装recovery-from-boot.p的升级脚本, 主要是利用android系统的 applypatch 工具来打补丁。

至此,一个完整的OTA包升级就正式完成了!

4. Bootloader、BCB、Recovery与Main System之间的交互

首先,通过前面的介绍,可以知道, Recovery System与Main System的交互,主要是通过/cache分区下的文件进行信息交互的。具体如下:

其中,command的值一般有以下一个或多个:

其次,Bootloader与Recovery和Main System之间也是存在交互的: Bootloader会通过解析BCB模块,决定启动系统到Recovery或Main System。而Recovery或Main System也能够操作BCB,进而影响到Bootloader的行为。

当Main System系统关键进程崩溃太多次的时候,系统还会自发启动进入到Recovery模式。

另外,部分平台的Android设备,在Recovery模式下,也能够对Bootloader进行升级。

用户评论

醉婉笙歌

终于找到靠谱的解释了!一直想弄懂这个 Android Recovery 的更新机制,原来是通过 fastboot 刷入固件镜像来实现的。以前也试过在手机里更新,感觉慢吞吞而且过程比较复杂。这篇文章讲解得很清晰,用通俗易懂的语言描述了整个原理,受益匪浅!

    有13位网友表示赞同!

玩味

看了文章之后终于明白为什么有人说 Recovery 模式刷机会损伤手机硬件了。原来是升级过程中需要进入低级模式操作,对芯片和存储器会产生一定的影响。看来自己以后还是乖乖地用官方更新方式吧。

    有7位网友表示赞同!

漫长の人生

关于 Android 固件的打包与加载流程真的太详细了,我之前只知道简单的刷机步骤,却从未深入了解过其中的原理。文章解析得非常透彻,甚至还提及到了分区管理和系统版本识别等细节,让人受益匪浅!不过对于新手来说,可能还是比较难理解这种复杂的机制吧。

    有9位网友表示赞同!

肆忌

其实更新 Android 系统的时候很多人都不会去特意操作 Recovery 模式吧?直接使用手机菜单里的升级功能就足够了!这篇文章写的蛮具体的,但我感觉用在我身上是有点鸡肋的...毕竟不是每个人都会对这些技术细节感兴趣吧?

    有8位网友表示赞同!

一样剩余

这个文章真是太棒了!以前每次刷机都充满了茫然和不安,现在终于明白了整个过程是怎么进行的。感谢作者的耐心解释!以后要是遇到手机系统问题的话,自己也能尝试解决一下啦!

    有8位网友表示赞同!

■□丶一切都无所谓

不过这篇文章里没有提到用电脑连接手机进行升级的情况啊?我的手机经常会卡在那个更新进度界面,如果能通过电脑强制刷机的话,那岂不美滋滋!

    有16位网友表示赞同!

炙年

文章描述的 Recovery 模式的启动流程非常清楚,但是对于不同设备型号的操作步骤差异并没有细致说明。希望作者能够补充一些针对不同机型的具体步骤,这样更加实用。

    有16位网友表示赞同!

你的眸中有星辰

我一直认为 Recovery 模式是手机系统中最神秘的功能之一!现在终于明白了它的原理,感觉整个智能手机的内部结构都变得更有意思了。不过我想知道,为什么 Android 系统还需要这么一个独立的运行模式呢?

    有14位网友表示赞同!

遗憾最汹涌

我尝试着按照文章描述进行了一些操作测试,发现确实能够通过 Recovery 模式刷入新的固件镜像!这个方法确实比官方更新方式更快更便捷!但需要注意的是,如果操作不当容易导致手机无法正常启动,所以一定要谨慎操作。

    有7位网友表示赞同!

墨染殇雪

这篇博文真是把 Android Recovery 的原理讲得清清楚楚!它介绍了如何进入 Recovery 模式,以及如何在Recovery 模式内对固件进行操作的过程。对于想要了解 Android 系统内部机制,或者想学习刷机技巧的读者来说,这篇文章绝对是一篇不可错过的好资源!

    有17位网友表示赞同!

熟悉看不清

我以前一直以为手机升级就是手机自己会自动下载并安装更新软件就可以了,现在才知道原来还有那么复杂的功能设定在后端?文章讲解得很详细,让我对 Android 系统有了更深的理解,尤其是在刷机和固件方面。

    有18位网友表示赞同!

浮世繁华

关于系统分区管理的部分感觉讲得还是比较清晰的,例如 Bootloader、System Partition 和 Userdata 等重要分区。对于想学习Android软件开发的朋友来说,这部分内容非常有用。建议作者可以多加一些图片或图形化的解释,更直观地展示这些分区的布局和功能。

    有16位网友表示赞同!

有你,很幸福

我个人不太同意文章里说的“Recovery 模式刷机会损伤手机硬件”的观点。我认为随着技术的进步,即使进入低级模式操作,也不会对存储器或芯片造成太大的伤害。只要选择正规渠道下载固件镜像,并且操作步骤严格按照指引执行,就不会出现问题。

    有9位网友表示赞同!

冷青裳

文章内容很棒,能够让我更深入了解 Android Recovery 的原理。不过我觉得对于一些初学者来说,可能过于专业,可以考虑用更通俗易懂的语言解释一些概念,例如“Flashing” 和 “Partition” 这些词语,这样更容易理解。

    有6位网友表示赞同!

滴在键盘上的泪

作为一名Android手机用户,我经常会遇到系统卡顿、软件崩溃等问题。以前只能无奈地将手机送去维修点。现在学习了 Recovery 模式刷机的方法,可以自己解决一部分问题,省去了很多麻烦!感谢作者分享这篇文章,帮助我掌握了这个宝贵的技能!

    有18位网友表示赞同!

陌颜幽梦

文章介绍的 Android Recovery 升级原理非常清楚,让我对整个过程有了更全面的认识。也让我更加了解到手机系统软件的复杂性和巧妙性。如果能有关于不同机型固件回厂工具下载及使用方法的详细讲解,那就更好了!

    有17位网友表示赞同!

青衫故人

总的来说,这篇文章是一篇非常好的 Android Recovery 升级原理介绍文章,内容全面清晰易懂。建议作者可以定期更新文章,添加最新的技术知识和实践经验分享,以便更好地帮助读者掌握最新刷机技巧。

    有18位网友表示赞同!

热点资讯