1. 目的 4
2. 适用范围 4
3. 参考文档 4
4. 缩写 4
5. 名词定义 4
6. Recovery简介 5
7. Android手机一般有三种启动模式 5
8. Recovery涉及到的其他系统及文件 5
9. Recovery Case 6
10. “apply update from sdcard”功能详解 7
11. Recovery模式流程 9
12. Recovery模式流程图 (网络上的参考流程) 12
1.Recovery简介
Android利用Recovery模式,进行恢复出厂设置,OTA升级,patch升级及firmware升级。
升级一般通过运行升级包中的META-INF/com/google/android/update-script脚本来执行自定义升级,脚本中是一组recovery系统能识别的UI控制,文件系统操作命令,例如write_raw_image(写FLASH分区),package_extract_dir(复制目录)。该包一般被下载至SDCARD和CACHE分区下。升级中还涉及到包的数字签名,签名方式和普通JAR文件签名差不多。公钥会被硬编译入recovery,编译时生成在:out/target/product/brownstone/obj/PACKAGING/ota_keys_inc_intermediates/keys.inc
Recovery功能实现主要需要的模块有:
1、framework/base/core层:Power.java、PowerManager.java和Recoverysystem.java等上层文件来管理和记录和Recovery有关的上层操作,例如FACTORY RESET和OTA升级。
2、framework jni层主要有android_os_power.cpp提供上层OTA升级和RBOOT接口函数。
3、recovery系统层:实现recovery的具体操作。
4、boot下brownstone.c通过判断按键信息和内存信息来确定启动方式,引导进入recovery模式。
2.Android手机一般有三种启动模式
启动过程中通过boot下得brownstone.c中的magic_test()函数读取按键信息和上层写入的command来确定启动模式
MAGIC KEY:
•menu + power:bootloader进入fastboot模式(也有使用camera + power来实现的)
•home + power:进入recovery模式
•正常启动
Bootloader正常启动,又有三种方式,按照BCB(Bootloader Control Block)的command分类:
•command == 'boot-recovery' → 启动recovery.img进入recovery模式
•command == 'update-firmware' → 更新firmware(bootloader)
•其他 → 启动boot.img
3.Recovery涉及到的其他系统及文件
CACHE分区文件
Recovery 工具通过NAND cache分区上的三个文件和主系统打交道。主系统(包括恢复出厂设置和OTA升级)可以写入recovery所需的命令,读出recovery过程中的LOG和intent。/cache/recovery/command: 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过程日志,由主系统读出
/cache/recovery/intent:recovery输出的intent
MISC分区内容
Bootloader Control Block (BCB) 存放recovery bootloader message。结构如下:
struct bootloader_message {
char command[32];
char status[32]; // 还未完全搞懂此变量的用途
char recovery[1024];
};
command可以有以下两个值
“boot-recovery”:标示recovery正在进行,或指示bootloader应该进入recovery mode
“update-firmware”:由Recovery写入指示bootloader更新firmware
recovery内容
“recovery\\n
其中recovery command为CACHE:/recovery/command命令 4.Recovery Case ●FACTORY RESET(恢复出厂设置) 1.用户选择“恢复出厂设置” 2.设置系统将"--wipe_data"命令写入/cache/recovery/command 3.系统重启,并进入recover模式 4.get_args() 将 "boot-recovery"和"--wipe_data"写入BCB 5.erase_root() 格式化(擦除)DATA分区 6.erase_root() 格式化(擦除)CACHE分区 7.finish_recovery() 擦除BCB 8.重启系统 ●OTA INSTALL(OTA升级) 1.升级系统下载 OTA包到/cache/update.zip 2.升级系统写入recovery命令"--update_package=CACHE:update.zip" 3.重启,并进入recovery模式 4.get_args() 将"boot-recovery" 和 "--update_package=..." 写入BCB 5.install_package() 作升级 6.finish_recovery() 擦除 BCB 7.** 如果安装包失败 ** prompt_and_wait() 等待用户操作,选择ALT+S或ALT+W 升级或恢复出厂设置 8.main() 调用 maybe_install_firmware_update() 1)如果包里有hboot/radio的firmware则继续,否则返回 2)将 "boot-recovery" 和 "--wipe_cache" 写入BCB 3)将 firmware image写入cache分区 4)将 "update-radio/hboot" 和 "--wipe_cache" 写入BCB 5)重启系统 6)bootloader自身更新firmware 7)bootloader 将 "boot-recovery" 写入BCB 8)erase_root() 擦除CACHE分区 9)清除 BCB 9.main() 调用 reboot() 重启系统 ●Recovery 模式 开机过程中按住home+power键 Boot启动过程中判断magic_test()函数读取按键信息,选择启动Recovery 进入Recovery并通过ui显示Recovery功能选项: "reboot system now //用于重启系统 "apply update from sdcard //通过sdcard卡的升级 "wipe data/factory reset //擦出用户信息恢复出厂设置 "wipe cache partition //清楚缓存 选择相应功能 系统进入recovery操作 5.“apply update from sdcard”功能详解 从ar0922e_gingerbread_beta1编译好的代码码树中提取一个update.zip所需要的所有资源, 从而最终构建一个可以在android recovery模式下刷机的update.zip包. 建立如下的update目录(用于生成update.zip): |\update\ |-- META-INF | `-- com | `-- google | `-- android | |-- update-binary | `-- updater-script |-- system update-binary: 二进制文件, 相当于一个脚本解释器, 能识别 updater-script 中描述的操作。该文件由ar0922e_gingerbread_beta1/out/target/product/brownstone/system/bin/updater重命名所得。具体用什么名字是由 ar0922e_gingerbread_beta1/bootable/recovery/install.c 文件中的宏 ASSUMED_UPDATE_BINARY_NAME 的值而定. updater-script: 该文件需要自己根据更新包需要更新的内容自行编写. 具体用什么名字是由 ar0922e_gingerbread_beta1/bootable/recovery/updater/updater.c 文件中的宏 SCRIPT_NAME 的值而定.(注意: 是updater-script, 而不是 update-script). system: 该目录下放需要更新的内容. 比如,只是需要添加几个系统软件, 则在此目录下添加一个 app 目录, 然后把待添加的系统软件copy进来即可. 如果你是要制作一个系统更新包. 那ar0922e_gingerbread_beta1/out/target/product/brownstone/system/ 中的所有文件copy到这个目录里来. ps:具体添加文件的名字和路径要和updater-script脚本相同。 •一个更新包update.zip的制作步骤 $mkdir update ps:在任意目录下,创建一个叫update的目录 $cd upadte ps: 进入该目录 $mkdir –p META-INF/com/google/android/ && mkdir system ps:在update目录下, 创建2个子目录 $cp ar0922e_gingerbread_beta1/out/target/product/brownstone/system/bin/updater META-INF/ com/google/android/update-binary ps:获取update-binary文件 $cp ar0922e_gingerbread_beta1/out/target/product/brownstone /ramdisk.img ps: 获取更新ramdisk所需要的文件 $vim META-INF/com/google/android/updater-script ps:打开updater-script脚本, 根据需要更新的内容开始编写更新过程. $zip -qr ../update.zip ./ ps: 编辑完updater-script脚本后, 把update内的所有内容打成一个update.zip包. $java –jar ar0922e_gingerbread_beta1/out/host/linux-x86/framework/signapk.jar –w ar0922e_gingerbread_beta1/build/target/product/security/testkey.x509.pem /testkey.pk8 update.zip update_signed.zip 通过这条命令, 会对update.zip包进行签名, 然后输出一个update_signed.zip的签好名update包. 以此作为最终更新包。 这条命令需要5个条件: host机需装有java环境, ubuntu上为 sun-java5-jdk DEB包 signapk.jar 文件. 在编译好的ar0922e_gingerbread_beta1/out/host/linux-x86/framework目录下 testkey.x509.pem 在源码的 ar0922e_gingerbread_beta1/build/target/product/security 目录下 testkey.pk8 在编译好的 ar0922e_gingerbread_beta1/build/target/product/security 目录下 update.zip 没有经过签名的ZIP包. PS:也可以使用签名工具进行签名。 •updater-script 脚本的编写 updater-script 的内容其实为一行一行update-binery能识别的命令序列. 文件 ar0922e_gingerbread_beta1/bootable/recovery/update/install.c 末尾描述了所有可执行命令.部分命令的使用实例: mount : eg : mount(“MTD”, “system”, “/system”); 挂在 MTD的system分区到文件系统的 /system 目录下. ui_print : eg : ui_print(“Hello word!”); 在屏幕上打印提示信息. format : eg : format(“MTD”, “system”); 格式化MTD的system分区 package_extract_dir : eg : package_extract_dir(“system”, “/system”); 把update包中system中的内容全部拷贝到文件系统/system下 特别说明:固件升级(boot和radio)和其他软件升级的脚本有所不同,具体在“11.Recovery模式流程”中详述。 6.Recovery模式流程 /init → init.rc → /sbin/recovery → main():recovery.c //main入口 ∙ui_init():ui.c [UI initialize] //ui显示初始化 ogr_init():minui/graphics.c //set tty0 to graphic mode, open fb0// oev_init():minui/events.c //open /dev/input/event*// ores_create_surface:minui/resource.c //create surfaces for all bitmaps used later, include icons, bmps// ocreate 2 threads: progress/input_thread //create progress show and input event handler thread// ∙get_args():recovery.c //读取系统资源和信息 oget_bootloader_message():bootloader.c //read mtdblock0(misc partition) 2nd page for commandline// ocheck if nand misc partition has boot message. If yes, fill argc/argv. oIf no, get arguments from /cache/recovery/command, and fill argc/argv. oset_bootloader_message():bootloader.c //set bootloader message back to mtdblock0// ∙Parser argv[] filled above ∙register_update_commands():commands.c //注册系统命令 oregisterCommand():commands.c ▪Register command with name, hook, type, cookie. ▪Commands, e.g: assert, delete, copy_dir, symlink, write_raw_image. oregisterFunction():commands.c ▪Register function with name, hook, cookie. ▪Function, e.g: get_mark, matches, getprop, file_contains ∙install_package(): //升级包安装 otranslate_root_path():roots.c //EM:lib" and turns it into a string like "/system/lib", translate the updater.zip path// omzOpenZipArchive():zip.c //open updater.zip file// ohandle_update_package():install.c ▪verify_jar_signature():verifier.c //verify signature with keys.inc key,verify manifest and zip package archive// ▪verifySignature() //verify the signature file: CERT.sf/rsa.// ▪digestEntry():verifier.c //get SHA-1 digest of CERT.sf file// ▪RSA_verify(public key:keys.inc, signature:CERT.rsa, CERT.sf's digest):libc/rsa.c //Verify a 2048 bit RSA PKCS1.5 signature against an expected SHA-1 hash. Use public key to decrypt the CERT.rsa to get original SHA digest, then compare to digest of CERT.sf// ▪verifyManifest() //Get manifest SHA1-Digest from CERT.sf. Then do digest to MANIFEST.MF. Compare them// ▪verifyArchive() //verify all the files in update.zip with digest listed in MANIFEST.MF// ▪find_update_script():install.c //find META-INF/com/google /android/updater script// ▪handle_update_script():install.c // read cmds from script file, and do parser, exec// ▪parseAmendScript():amend.c //call yyparse() to parse to command// ▪exeCommandList():install.c ▪exeCommand():execute.c //call command hook function// ∙erase DATA/CACHE partition //擦出用户DATA和CACHE ∙prompt_and_wait():recovery.c //wait for user select: 1) reboot 2) update.zip 3) wipe data/factory reset 4)wipe cach)// o1) do nothing but reboot o2) install_package('SDCARD:update.zip') o3) erase_root() → format_root_device() DATA/CACHE o4) only erase the CACHE //****************************固件升级部分详述**********************start // ∙ForUpdateFirmwareFn():\\bootable\\recovery\er\\install.c //is called by “mrvl_for_update_firmware()” command in the updater-script to stores the bootloader image to mmcblk0p11 partition, and write “update-firmware” command to MISC partition for bootloader message to let bootloader update itself after reboot ostrlcpy(boot.command, "update-firmware", sizeof(boot.command));//set the “update-firmware”bootcommand to step to obm_update() in boot// ocopy_bootfile_to_device(char* filename, char* devicename, int offset) //copy the file which used to update-firmware from \\cache\\ to the mmcblk0p11 ,a partition only used to update-firmware // oreboot(RB_AUTOBOOT); // Perform a hard reset now// //-----在与Recovery配合update-firmware 的boot函数----start// ∙obm_update():\\boot\\\board\\marvell\brownstone.c //only in the bootloader// ommc->block_dev.block_read(0, NTIM_BLK_ADDR, 3, buffer) //read the boot image from mmcblk0p11 to buffer// ommc->block_dev.block_write(0, PARTITION_BURN_ADDR, 1, buffer) //burn the boot image to the boot address in emmc from buffer// //--在与Recovery配合update-firmware 的boot函数--end// //****************************固件升级部分详述**********************end// ∙finish_recovery():recovery.c //clear the recovery command and prepare to boot a (hopefully working) system, copy our log file to cache as well (for the system to read), and record any intent we were asked to communicate back to the system. // ∙reboot() // 7.Recovery模式流程图 (网络上的参考流程) 以下流程图绘制了系统从启动加载bootloader后的行为流程。