转 Android Recovery 工作流程 recovery ui 会话流程
By codingguy on 2011 年 12 月 14 日
找到了以前写的老文档,09年初写的,不管内容怎样,贴出来晒晒~
—————————————————————-
Android Recovery
Android Recovery: 功能简介
Android支持Recovery模式。在某些操作之后,系统会自动重启并进入到Recovery模式,用户按组合键开机(HOME+POWER),也可进入Recovery模式。该模式提供如下功能:
1、擦除用户数据
恢复系统到出厂模式,即擦除用户数据和缓存数据。
2、系统升级
系统升级的概念比较广,包括系统文件的升级、恢复损害的系统数据、firmware的升级,以及应用软件的维护,甚至影音文件的下载。系统升级需要使用特定的升级包,Android使用OTA[1]升级包,其初衷在于可以发挥广域无线通信链路的优势,如3G。
升级方式有两种:
1、在线升级
利用无线通信网络,系统自动连接更新源,查看有无升级包、下载OTA升级包,然后给出提示,发起升级过程,如下左图。感觉有点类似Windows XP的系统更新,只不过升级的时候,Android系统会重启系统进入Recovery模式。另外Android的升级内容很广泛,比如可以通过这种方式安装应用程序。T-Mobile已经提供了这种服务,如升级服务器以OTA无线方式向G1终端发送Android平台RC33升级包,传输媒介可以是3G网络、Wi-Fi或GPRS。
2、离线升级
可以将下载到的OTA包放在SD卡里,通过离线方式升级,如下右图所示。这种升级方式比较灵活,不用花费无线流量。这样一来,使用自己制作的OTA进行升级也成为可能。事实上,G1就是用这种方式进行刷机的,比如更新radio firmware以支持某个频段。
Android: 分区结构
在分析Recovery工作流程之前,我们先了解一下Android文件系统的分区结构。下表是android/bootable/recovery/root.c中提得到的结构:
NameDevicePartition nameMount pointFile system
BOOTg_mtd_deviceBootNULLg_raw
CACHEg_mtd_deviceCache/cacheyaffs2
DATAg_mtd_deviceUserdata/datayaffs2
MISCg_mtd_deviceMiscNULLg_raw
PACKAGENULLNULLNULLg_package_file
RECOVERYg_mtd_deviceRecovery/g_raw
SDCARD/dev/block/mmcblk0p1NULL/sdcardVfat
SYSTEMg_mtd_deviceSystem/systemyaffs2
TMPNULLNULL/tmpNULL
Root file system layout
模拟器环境下adb shell里的mount输出:
# mount
……
/dev/block/mtdblock0 /system yaffs2 ro 0 0
/dev/block/mtdblock1 /data yaffs2 rw,nosuid,nodev 0 0
/dev/block/mtdblock2 /cache yaffs2 rw,nosuid,nodev 0 0
综上,MTD中有如下分区:
BOOT: boot.img,Linux kernel (within normal ramdisk)
MISC: bootloader message struct
RECOVERY: recovery.img,Linux kernel (within recovery ramdisk)
SYSTEM: system.img
DATA: userdata.img
CACHE: some cache files
有几点说明:
1、一般来讲,主板上还有用于存储bootloader的可擦写存储设备。若具备通信能力,还要存储radio firmware,这两部分的更新由Recovery协助Bootloader完成,没有代码证明一定存在NAND flash上。
2、RECOVERY分区无文件系统,存放二进制image。
3、SYSTEM中有recovery.img的备份:/system/recovery.img,initrc中有如下代码:
service flash_recovery /system/bin/flash_image recovery system/recovery.img
oneshot
每次启动,flash_image程序,会检查recovery分区中image的header,如果与备份的recovery.img不符,就会把备份写到RECOVERY分区。这样做是为了应对RECOVERY分区遭到破坏。当然,我们也可以更换这个备份,这样也会将其写到RECOVERY。事实上,处于安全及版权考虑,OTA是有签名的(其实就是JAR包),Recovery对签名有要求,所以只能进行被允许的升级,此时的破解思路就是更换一个不检查签名的Recovery程序,方法就是设法更换/system/recovery.img。
Android Recovery: 三个部分、两个接口
Recovery的工作需要整个软件平台的配合,从架构角度看,有三个部分:
1、Main system:用boot.img启动的Linux系统,Android的正常工作模式。
2、Recovery:用recovery.img启动的Linux系统,主要是运行Recovery程序。
3、Bootloader:除了加载、启动系统,还会通过读取flash的MISC分区获得来自Main system和Recovery的消息,并以此决定做何种操作。
在Recovery的工作流程中,上述三个实体的通信必不可少。通信的接口有以下两个:
l CACHE分区中的三个文件:/cache/recovery/…
Recovery通过/cache/recovery里的文件与main system通信,有三个文件:
/cache/recovery/command
Main system传给Recovery的命令行,每一行有一个命令,支持以下几种:
–send_intent=anystring write the text out to recovery/intent
–update_package=root:path verify install an OTA package file
–wipe_data erase user data (and cache), then reboot
–wipe_cache wipe cache (but not user data), then reboot
/cache/recovery/log
Recovery的log输出,在recovery运行过程中,stdout及stderr会重定位到/tmp/recovery.log文件,Recovery退出之前会将其转储到/cache/recovery/log中,也就是cache分区的recovery/log。
/cache/recovery/intent
Recovery传给Main system的信息
l BCB (bootloader control block)
struct bootloader_message {
char command[32];
char status[32];
char recovery[1024];
};
BCB是Bootloader与Recovery的通信接口,也是Bootloader与Main system的通信接口,存储在flash中的MISC分区,占用三个page,各成员意义如下:
command:
当想要重启进入recovery模式,或升级radio/bootloader firmware时,会更新这个域。当firmware更新完毕,为了启动后进入recovery做最终的清除,bootloader还会修改它。
status:
update-radio或update-hboot完成后,bootloader会写入相应的信息,一般是一些状态或执行结果。
recovery:
仅被Main system写入,用于向Recovery发送消息,必须以“recoveryn”开头,否则这个域的所有内容会被忽略。这一项的内容中“recovery/n”以后的部分,是/cache/recovery/command支持的命令,可以认为这是在Recovery操作过程中,对命令操作的备份。Recovery也会更新这个域的信息,执行某操作前把该操作命令写到recovery域,并更新command域,操作完成后再清空recovery域及command域,这样在进入Main system之前,就能确保操作被执行。
如图所示,Main system、Recovery与Bootloader通过上述接口通信,通信逻辑依不同的目的而不同,在后面介绍具体工作流程中还会详细介绍。
从Main system进入Recovery的方法
我们提到,从Main system进入到Recovery,要修改MISC分区的数据并重启,从而告诉Bootloader是用boot.img还是用recovery.img启动。
init.c里的wait_for_one_process函数中有如下代码:
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, “recovery”);
一些关键的进程运行异常,会重启进入recovery模式,这里用__reboot函数进入recovery。跟踪这个函数,由系统调用处理函数,到kernel_restart(char *cmd),最终调用machine_restart使用体系结构相关的代码完成重启。
Android中没有给出如何处理“recovery”重启。不过可以断定,在重启之前会向BCB中写入信息,以告知bootloader如何启动,具体操作是这样的:
向command域中写入“boot-recovery” // 此操作必做
向recovery域写入“recoveryn” // 此操作也可不做
这些操作很可能在kernel_restart(char *cmd)中完成,因为这一部分与体系结构无关,如果要实现完整的Recovery,这部分工作是必须做的。
Bootloader得到进入Recovery模式的指示,用recovery.img启动,进入Recovery模式,init.rc (bootable/recovery/etc/init.rc)的内容比Main system的要短的多,最重要的是把recovery程序作为服务启动:
service recovery /sbin/recovery
Android Recovery: 总体流程
根据Recovery的initrc,kernel启动完成后,启动recovery服务,这是一个C程序,入口在/bootable/recovery/recovery.c中,main函数结构清晰,主要流程如图:
l get_args:首先调用get_args获取参数,主要流程如下:
get_args不仅传回获取到的参数,还会将其写入BCB,这样,一旦升级或擦除数据的过程中出现错误,重启之后依然进入Recovery并做相同操作。
l register_update_command,这是为update做准备工作,负责注册update用的command & function,正是这些command & function组成了update用到的update_script:
先用commandInit(android/bootable/recovery/amend/command.c)初始化command symbol table,然后多次调用registerCommand及registerFunction注册command及function。command相关的源代码都在amend目录中,语法的构建及解析使用Android已经包含的Bison(Yacc)。
这里的command有15个,见下表:
Command NameArgument TypeCommand Handler
assertCMD_ARGS_BOOLEANcmd_assert
deleteCMD_ARGS_WORDScmd_delete
delete_recursiveCMD_ARGS_WORDScmd_delete
copy_dirCMD_ARGS_WORDScmd_copy_dir
run_programCMD_ARGS_WORDScmd_run_program
set_permCMD_ARGS_WORDScmd_set_perm
set_perm_recursiveCMD_ARGS_WORDScmd_set_perm
show_progressCMD_ARGS_WORDScmd_show_progress
symlinkCMD_ARGS_WORDScmd_symlink
formatCMD_ARGS_WORDScmd_format
write_radio_imageCMD_ARGS_WORDScmd_write_firmware_image
write_hboot_imageCMD_ARGS_WORDScmd_write_firmware_image
write_raw_imageCMD_ARGS_WORDScmd_write_raw_image
markCMD_ARGS_WORDScmd_mark
doneCMD_ARGS_WORDScmd_done
CMD_ARGS_BOOLEAN表示该command后面接的参数是boolean值,即true或false,解析脚本时计算参数的逻辑值,然后传给command handler,目前只有“assert”这个command用此类型的参数。
CMD_ARGS_WORDS表示该command后面接的参数是字符,形如C程序启动时加的参数,解析脚本时把参数直接传递给command handler,比如“format BOOT:”,“BOOT:”会传给cmd_format。
Function NameFunction Handler
compatible_withfn_compatible_with
update_forcedfn_update_forced
get_markfn_get_mark
hash_dirfn_hash_dir
matchesfn_matches
concatfn_concat
getpropfn_getprop
file_containsfn_file_contains
function与command用同样的处理框架,只不过function会产生返回值,目前见到的用法一般都是与assert一起使用,例如下面脚本:
assert getprop(“ro.bootloader”) == “0.95.0000″
先用getprop从properties中取得bootloader版本,然后再将比较后的boolean值传给assert。
l prompt_and_wait:等待用户输入
首先打印文本信息。然后执行finish_recovery(NULL),这个函数后面介绍。然后进入ui_wait_key等待用户输入,按下不同的组合键会有不同的动作。对于键盘输入,先到达input_thread函数(android/bootable/recovery/ui.c),在那里处理两种组合键,其余才交给ui_wait_key处理:
KEYFuncionHandler
Home + Backreboot system nowui_wait_key
Alt + Sapply sdcard:update.zipui_wait_key
Alt + Wwipe data/factory resetui_wait_key
Alt + Ltoggle log text displayinput_thread
Green + Menu + Redreboot immediatelyinput_thread
Home + Back:退出prompt_and_wait。
Alt + W或Alt + S,执行完install_package或erase_root后,若没有激活log text display,那么,就会退出prompt_and_wait,否则继续等待输入。
Green + Menu + Red:立刻重启,一般这样还会进入Recovery,因为BCB还没有来得及清空。
l finish_recovery:离开Recovery进入Main system的必经之路,流程如下:
intent内容作为参数传进来,如果有intent需要告知Main system,将其写入/cache/recovery/intent;
将所有log信息转储到/cache/recovery/log文件,以供Main system读取;
清除BCB,也就是告知Bootloader启动进入Main system;
删除/cache/recovery/command;
以上是整体流程中的几个函数,关于安装升级包、升级firmware等操作将在具体流程中介绍。
Android Recovery: Factory data reset流程
如果系统不稳定,可以尝试恢复出厂设置,该操作会擦除DATA分区及CACHE分区,有两种恢复方式,下面分别介绍:
l 通过Setting程序发起Facory data reset:
屏幕显示如上图,结合着下面的通信图,列出工作流程:
1、在应用程序Setting中选择factory data reset
2、Main system向/cache/recovery/command写入”–wipe_data”
3、Main system重启进入recovery模式(方法:修改BCB)
4、Recovery向BCB写入”boot-recevory”和”recoveryn–wipe_datan”
5、擦除DATA分区,里面是用户数据,擦除CACHE分区
6、finish_recovery函数
7、重启,回到Main system
第四个步骤,Recovery向BCB写入boot-recovery和–wipe_data,这是为了保证后面几个步骤的完整执行。如,在擦除DATA分区或CACHE分区过程中,如果发生了重启、关机等操作,导致没有擦除成功,那么再次用常规方式开机后,Bootloader会依据BCB的指示,引导进入Recovery,并重新擦除这两个分区。擦完DATA分区与CACHE分区后,调用finish_recovery,做返回Main system前最后的工作,最终要的是擦除BCB,即MISC分区。此后,用常规方式重新开机,系统会进入Main system。
阅读Android的代码,发现Setting通过RPC调用Checkin Service的masterClear()启动这个过程,然而在Android中并没有找到masterClear()的实现,相关代码需要在产品化的过程中加入。从ICheckinService.aidl的注释可以了解到这个函数的作用:
/** Reboot into the recovery system and wipe all user data. */
代码位置:
/packages/apps/Settings/src/com/android/settings/MasterClear.java
/frameworks/base/core/java/android/os/ICheckinService.aidl
l 通过HOME+POWER组合键进入Recovery,再按ALT+W启动Factory data reset
过程比较简单,而且与上一种方式类似,结合总体流程,步骤如下:
1、捕获按键Alt + W。
2、擦除DATA分区、擦除CACHE分区。
3a、若激活了log显示(ALT+L:toggle log text display),调用finish_recovery函数重启,回到Main system。
3b、若没有激活log显示,继续接收按键,可用HOME+BACK重启回到Main system。
Android Recovery Update: 流程
l update.zip
update操作需要升级包,该升级包是文件名是*.zip,但观察包内结构会发现其实就是JAR包,JAR包是具有特定目录和文件结构的ZIP压缩包,因此可以作为ZIP包解开:
MANIFEST.MF:这个manifest文件定义了与包相关数据。
XXX.SF:这是JAR文件的签名文件,占位符xxx标识签名者,如CERT。
XXX.DSA:与签名文件相关联的签名程序块文件,它存储了用于签名JAR文件的公共签名。
在META-INF/com/google/android目录下有update_script文件,内容就是update要做的操作,也就是前面提到过的command序列。
出于安全性及版本控制的考虑,JAR包要求必须有完整性以及合法性签名。可以看出这是Android确保安全的策略。JAR相关内容参见http://www.ibm.com/developerworks/cn/java/j-jar/,这里就不再详细介绍。
l Main system部分
通过Android系统下载升级包并启动升级操作,需要上层应用Updater的支持,它是Java程序,代码位置android/packages/apps/Updater。大致流程:
系统启动后,如果存在网络连接,则检查是否存在升级包;
如果存在升级包,则下载至/cache目录;
调用Updater程序来提示是否升级;
如果Updater程序进程不存在,则自动启动此程序;
没有在代码中找到开始升级后执行哪些操作。不过由recovery.c的注释部分可以肯定一定需要重启进入Recovery,重启前要更新/cache/recovery/command,以告知Recovery进行升级:
–update_package=root:path
l update流程
update有两种方式,第一种是上面提到的由Android启动的自动update过程,升级包在cache/下,升级包的名字在/cache/recovery/command文件中指定。第二种是手动进入Recovery模式,然后输入Alt + S,安装/sdcard/update.zip升级包。两种方式不同的只是安装包的位置以及传递参数给Recovery的方法,update过程都是一样的,工作流程如下图所示:
install_package @ android/bootable/recovery/install.c
得到安装包信息,如“–update_package=CACHE:update.zip”,进入install_package函数,流程如下左图。mount安装包所在的分区,然后打开zip压缩包,进入handle_update_package开始升级:
handle_update_package中,先对包进行校验,校验过程分三步:
verifySignature: 检验SF文件与RSA文件的匹配
verifyManifest: 检验/META-INF/MANIFEST.MF与签名文件中的digest是否一致
verifyArchive: 检验包中的文件与MANIFEST是否一致
接着find_update_script从MANIFEST.MF找到update_script的位置,然后handle_update_script,如下图,把内容读到buffer后,对其进行解析,分解成各个command(包括function)放在一个list中依次执行。
maybe_install_firmware_update @ android/bootable/recovery/firmware.c
install_package成功后,调用maybe_install_firmware_update,这个函数处理firmware的更新。update firmware脚本是这样的:
write_radio_image PACKAGE:radio.img
cmd_write_firmware_image处理write_radio_image这个命令,将image从压缩包加载到RAM中,并调用remember_firmware_update更新update_type、update_data及update_length。这三个变量对于maybe_install_firmware_update是可见的,并由它们来判断是否要安装firmware。下面是主要流程:
如果升级涉及radio / hboot firmware (radio:基带处理相关,hboot:bootloader)
1、向BCB写入”boot-recovery”和”–wipe_cache”
……此后重启系统,将进入recovery并擦除CACHE分区
2、write_update_for_bootloader向raw CACHE分区写入image,CACHE分区的内容将被破坏。
3、向BCB写入”update-radio/hboot”和”–wipe_cache”
4、重启,由Bootloader更新firmware
5、Bootloader向BCB写入”boot-recovery”,并保留BCB中recovery里的”–wipe_cache”
6、重启,再次进入Recovery,调用erase_root()擦除CACHE分区
7、finish_recovery()清除BCB
8、重启,进入main system
l Bootloader
每次启动,Bootloader都会读取位于MISC分区的bootloader_message,并检查command区域以 结尾,还要考虑flash存在坏块的情况。然后根据读取的命令,启动系统或者更新firmware。工作流程如下:
升级之后,无论升级成功是否,Bootloader都会进入recovery完成最后的收尾工作,并带着status以告知是否成功。如果更新hboot(尚不知道为什么叫这个名字,不过可以确定它就是bootloader firmware),一旦失败,若原有的bootloader遭到破坏,那么系统将不能boot。
为实现Android Recovery,还需要做什么?
实现Setting中Factory data reset
查看Updater工作流程,找到发起update的方法
实现__reboot(…..”recovery”)函数,连接Main system与Recovery
升级包的打包方法,以及JAR包签名机制
实现Bootloader与Recovery及Main System的通信;
实现Bootloader的启动逻辑、firmware升级;
[1] OTA:Over The Air,一种手机等终端应用的“空中下载”技术,利用这种技术用户可以通过下载来修补终端的漏洞或升级某些功能
更多阅读
会计实操——出纳的基本工作流程 出纳会计实操
会计实操——出纳的基本工作流程——简介出纳的定义(结合出纳票据来讲解)出纳,作为会计名词,运用在不同场合有着不同涵义。从这个角度讲,出纳一词至少有出纳工作、出纳人员两种涵义。出纳工作,顾名思义,出即支出,纳即收入。出纳工作是管理
会计的工作流程 会计的工作内容
财会人员应该了解工作流程,更应该了解相关的财务软件,稍有规模或管理水平高一点的企业均采信息化管理,应该知道如何使用软件和如何设置,只要凭证制作正确,其余一切由计算机完成:凭证-汇总-明细账-总账-各种报表等。首先来了解财务流程是非
企业财务管理工作流程 财务预算
一、出纳岗工作流程(一)现金收付1、收现根据会计岗开具的收据(销售会计开具的发票)收款——→检查收据开具的金额正确、大小写一致、有经手人签名——→在收据(发票)上签字并加盖财务结算章——→将收据第②联(或发票联)给交款人——→凭记
客房服务员工作流程 客房部服务员工作流程
客房服务员工作流程22:45: 1.换好制服,到管家部办公室签到,22:55: 2.阅读楼层服务员和领班的交班23:00: 1.领取钥匙和通迅工具2. 打印次日团队离店单23:00-6:30: 1.(23:00, 2:00, 5:00) 巡楼,推门,每晚三次,时间分别是23:00,2:00,5:002.
会计工作流程 会计工作内容及流程
一、出纳岗工作流程(一)现金收付1、收现根据会计岗岗位开具的收据(销售会计开具的发票)收款-检查收据开具的金额正确、大小写是否一致、必须有经手人签名-在收据(发票)上签字并加盖财务结算章-将收据第2联或发票联给交款人-凭记账联登记现