Chinaunix首页 | 论坛 | 博客
  • 博客访问: 126945
  • 博文数量: 17
  • 博客积分: 285
  • 博客等级: 二等列兵
  • 技术积分: 200
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-30 16:27
文章分类

全部博文(17)

文章存档

2016年(1)

2015年(2)

2014年(1)

2013年(2)

2012年(11)

我的朋友

分类: Android平台

2014-07-30 09:47:22


注:部分来源于网络,如有侵犯,请联系.

  预备知识:
Android 分区
Boot boot.img, linux kernel(within normal ramdisk)
MISC bootloader message strcut
Recovery recovery.img, linux kernel(within recovery ramdisk)
System system.img 
Data userdata.img
Cache cache files


Recovery功能
先看下recovery.cpp的注释


/*
 * The recovery tool communicates with the main system through /cache files.
 *   /cache/recovery/command - INPUT - command line for tool, one arg per line
 *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
 *   /cache/recovery/intent - OUTPUT - intent that was passed in
 *
 * The arguments which may be supplied in the recovery.command file:
 *   --send_intent=anystring - write the text out to recovery.intent
 *   --update_package=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
 *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
 *   --just_exit - do nothing; exit and reboot
 *
 * After completing, we remove /cache/recovery/command and reboot.
 * Arguments may also be supplied in the bootloader control block (BCB).
 * These important scenarios must be safely restartable at any point:
 *
 * FACTORY RESET
 * 1. user selects "factory reset"
 * 2. main system writes "--wipe_data" to /cache/recovery/command
 * 3. main system reboots into recovery
 * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
 *    -- after this, rebooting will restart the erase --
 * 5. erase_volume() reformats /data
 * 6. erase_volume() reformats /cache
 * 7. finish_recovery() erases BCB
 *    -- after this, rebooting will restart the main system --
 * 8. main() calls reboot() to boot main system
 *
 * OTA INSTALL
 * 1. main system downloads OTA package to /cache/some-filename.zip
 * 2. main system writes "--update_package=/cache/some-filename.zip"
 * 3. main system reboots into recovery
 * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
 *    -- after this, rebooting will attempt to reinstall the update --
 * 5. install_package() attempts to install the update
 *    NOTE: the package install must itself be restartable from any point
 * 6. finish_recovery() erases BCB
 *    -- after this, rebooting will (try to) restart the main system --
 * 7. ** if install failed **
 *    7a. prompt_and_wait() shows an error icon and waits for the user
 *    7b; the user reboots (pulling the battery, etc) into the main system
 * 8. main() calls maybe_install_firmware_update()
 *    ** if the update contained radio/hboot firmware **:
 *    8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
 *        -- after this, rebooting will reformat cache & restart main system --
 *    8b. m_i_f_u() writes firmware image into raw cache partition
 *    8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
 *        -- after this, rebooting will attempt to reinstall firmware --
 *    8d. bootloader tries to flash firmware
 *    8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
 *        -- after this, rebooting will reformat cache & restart main system --
 *    8f. erase_volume() reformats /cache
 *    8g. finish_recovery() erases BCB
 *        -- after this, rebooting will (try to) restart the main system --
 * 9. main() calls reboot() to boot main system
 */


从上面注释基本可知recovery的基本功能:格式化、清除cache、升级、文件系统加密(没有找到入口)。


一. Recovery结构


recovery.img:(没有实际打开分析过,网上资料)
├── kernel
├── ramdisk
└── RAMDISK
    ├── advanced_meta_init.rc
    ├── data
    ├── default.prop
    ├── dev
    ├── etc
    ├── init
    ├── init.factory.rc
    ├── init.goldfish.rc
    ├── init.mt6516.rc
    ├── init.rc
    ├── meta_init.rc
    ├── proc
    ├── res
    ├── sbin
    ├── sys
    ├── system
    └── tmp




update.zip包目录结构
   |------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
   |--update-script
|---android/
   |--metadata


   updata.zip包可通过make otgpackage制作
   1. boot.img 是更新boot分区所需的文件。这个boot.img主要包括kernel+ramdisk。
   2. system/目录的内容在升级后会放在系统的system分区。主要用来更新系统的一些应用或则应用会用到的一些库等等。
   3. recovery/目录中的recovery-from-boot.p是boot.img和recovery.img的补丁(patch),主要用来更新recovery分区,其中etc/目录下的install-recovery.sh是更新脚本。
   4. update-binary是一个二进制文件,相当于一个脚本解释器,能够识别updater-script中描述的操作。该文件在Android源码编译后updater.c生成,可将updater重命名为update-binary得到。  该文件在具体的更新包中的名字由源码中bootable/recovery/install.c中的宏ASSUMED_UPDATE_BINARY_NAME的值而定。
   5. updater-script:此文件是一个脚本文件,具体描述了更新过程。我们可以根据具体情况编写该脚本来适应我们的具体需求。该文件的命名由源码中bootable/recovery/updater/updater.c文件中的宏SCRIPT_NAME的值而定。
   6. metadata文件是描述设备信息及环境变量的元数据。主要包括一些编译选项,签名公钥,时间戳以及设备型号等。
   7. 我们还可以在包中添加userdata目录,来更新系统中的用户数据部分。这部分内容在更新后会存放在系统的/data目录下。
   8. MANIFEST.MF:这个manifest文件定义了与包的组成结构相关的数据。类似Android应用的mainfest.xml文件。
   9. CERT.RSA:与签名文件相关联的签名程序块文件,它存储了用于签名JAR文件的公共签名。
   10.CERT.SF:这是JAR文件的签名文件,其中前缀CERT代表签名者。






二. updata.zip制作(整包或者增量包,一般是增量包或者叫差分包)
   1. 手动:按照update.zip的目录结构手动创建我们需要的目录。然后将对应的文件拷贝到相应的目录下,比如我们向系统中新加一个应用程序。可以将新增的应用拷贝到我们新建的update/system/app/下(system目录是事先拷贝编译源码后生成的system目录),打包并签名后,拷贝到SD卡就可以使用了。这种方式在实际的tcc8800开发板中未测试成功。签名部分未通过,可能与具体的开发板相关。
   2. make otapackage




三. 系统更新updata.zip包的两种方式
  1. 离线(本地) /sdcard/.......
  2. 在线OTA /cache/.......
四. android系统启动方式
  1. magic key
  2. normal
  3. bootloader mode




五. 进入recovery的方式
  1. MainSystem: android 系统选择升级系统选项(通过SD卡或者OTA)在重启之前会向misc分区BCB块写入命令(稍后介绍BCB分区)
  2. 通过/cache/recovery/command


六. recovery 重要的通信借口
  1. Cache 分区
   /cache/recovery/command 保存由Main System发给recovery的命令(其中每一行是一条命令)
--send_intent=anystring 不太理解有什么作用
    --update_package=root:path 需要升级,在进入recovery之后会写入到BCB块,以保证在出错或者重启之后还会进入recovery模式
--wipe_data 擦除用户数据,在选择此选项之后默认连同选择--wipe_cache
--wipe_cache 擦除cache分区
   /cache/recovery/log
recovery 工作时产生的log信息
   /cache/recovery/intent recovery传递给系统的信息(可能是扩展用的)
  2. BCB(Bootloader control block)
BCB是bootloader与recovery的通信接口,其存储在MISC分区占用3个page
BCB结构:
struct bootloader_message{
char command[32];//与cache中的类似
char status[32];//
char recovery[1024];
}
其中recovery字段可被系统写入,也可被recovery更新,该文件存储的就是一个字符串,必须以recovery\n开头,否则这个字段的所有内容域会被忽略。“recovery\n”之后的部分,是/cache/recovery/command支持的命令。可以将其理解为Recovery操作过程中对命令操作的备份。Recovery对其操作的过程为:先读取BCB然后读取/cache/recovery/command,然后将二者重新写回BCB,这样在进入Main system之前,确保操作被执行。在操作之后进入Main system之前,Recovery又会清空BCB的command域和recovery域,这样确保重启后不再进入Recovery模式。




通过上面的一些信息已经对recovery有了初步的认识,接下来介绍recovery的流程。


七.简单的流程:


recovery->main->log相关的操作->解析main函数参数(好像这个用到的不太多)->get_args->arg的解析->UI的相关操作->判断需要升级还是wipe①








-     Wipe data (if wipe data must wipe cache )②
     


①install->install_package->really_install_package->openupdate.zip->try_update_binary->创建进程子进程负责升级,父进度条等等。③


wipe cache④


|→②
|→③子进程调用execv执行binary解析update_script  →  finish_recovry
|→④
 




简单介绍:(下面会详细介绍各个功能)


1. 打开临时日志文件TEMPORARY_LOG_FILE此文件里的信息最终会被写入到日志文件LAST_LOG_FILE,LAST_LOG_FILE的定义如下:static const char *LAST_LOG_FILE = "/cache/recovery/last_log";可以获得系统升级时详细的LOG信息。
2. 首先获得recovery模式下系统分区的信息。(recovery.fstab)
3. main函数参数解析过程:如果查入参数有erase_boot_flag或者有read_boot_flag相关的命令则把信息写入BCB并重启或者打印出read的结果
4.get_args()函数:
args获取的方式有(1)从BCB块区读取  (2)从/cache分区获取
读取BCB块中的信息如果从BCB读取失败则从/cache/recovery/command中读取,读取完成之后要更新BCB区,存储读取到的命令,以便在重启或者其他意外情况重启之后仍然可以进入recovery模式。
5.对args的解析
  如果是install操作可以通过case 'u': update_package = optarg; break; 获取升级包的目录。
  根据args不同的指令进行不同的操作如升级,清除/dataf分区,清除/cache分区等。


八.UI的相关操作
主要用到的函数:
ui->SetBackground();
ui->Print();
ui->SetProgressType();
ui->ShowProgress();
LoadBitmap();




九.update:(install)
  调用install_package函数,install_package函数调用真正的install函数really_install_package,really_install_package函数主要是确保安装文件目录已经mount,从/res/keys加载公钥,加载公钥之后通过verify_file对安装包进行校验。
mzOpenZipArchive():打开升级包,并将相关的信息拷贝到一个临时的ZipArchinve变量中。这一步并未对我们的update.zip包解压。
try_update_binary():这个函数才是update.zip升级的地方。这个函数一开始先根据我们上一步获得的zip包信息,以及升级包的绝对路径将update_binary文件拷贝到内存文件系统的/tmp/update_binary中。以便后面使用。
之后创建管道pipe(2);用来下面父子进程沟通。
fork();创建子进程,其子进程主要是执行binary脚本。父进程有两个作用。一是通过管道接受子进程发送的命令来更新UI显示。二是等待子进程退出并返回INSTALL SUCCESS。其中子进程在解析执行安装脚本的同时所发送的命令有以下几种:
    progress   :根据第二个参数secs(秒)来设置进度条。
    set_progress  :直接设置进度条,frac取值在0.0到0.1之间。
firmware <”hboot”|”radio”>:升级firmware时使用,在APIV3中不再使用。
    ui_print :在屏幕上显示字符串,即打印更新过程。




之前跟到这就迷糊了不知道具体是怎么更新的,而且网上信息基本到这也就没了,现在把之前的update.zip拿过来分析一下里面的成分,里面有两个脚本一个是update-binary这个脚本是由update.c生成一般理解为脚本解释器,另一个是binary_script我的理解是在生成update.zip差分包的时候生成的脚本,故此脚本里面有差分包的信息。用update-binary来解释执行binary_script,然后进行文件或者文件夹的拷贝。不知道理解的对不对。


十.wipe_data 在选择wipe_data的同时也会默认选择wipe_cache


erase_volume("/data")->format_volume()->Volume* v = volume_for_path();->mtd_scan_partitions(按照我的理解是获取分区信息)->mtd_find_partitions_by_name()(找到要erase的分区,填充MtdPartitons结构体 注①)->mrd_write_partition->填充MtdWriteContext结构体(注②)->mtd_erase_blocks(write, -1)->通过封装过的ioctl函数进行擦除数据。




注①
struck MtdPartitions {
int device_index;
unsigned int size;
unsigned int ertase_size;
char *name;
};
/* Parse the contents of the file, which looks like:
     *
     *     # cat /proc/mtd
     *     dev:    size   erasesize  name
     *     mtd0: 00080000 00020000 "bootloader"
     *     mtd1: 00400000 00020000 "mfg_and_gsm"
     *     mtd2: 00400000 00020000 "0000000c"
     *     mtd3: 00200000 00020000 "0000000d"
     *     mtd4: 04000000 00020000 "system"
     *     mtd5: 03280000 00020000 "userdata"
     */
MtdPartitions结构体实际为cat /proc/mtd的列表信息


注②
struct MtdWriteContext {
    const MtdPartition *partition;
    char *buffer;
    size_t stored;
    int fd;


    off_t* bad_block_offsets;
    int bad_block_alloc;
    int bad_block_count;
};






十一. Wipe_cache
同wipe_data


十二.prompt_and_wait():
这个函数是在一个判断中被调用的。其意义是如果安装失败(update.zip包错误或验证签名失败),则等待用户的输入处理(如通过组合键reboot等)。


十三. Finish_recovery
① 将intent(字符串)的内容作为参数传进finish_recovery中。如果有intent需要告知Main System,则将其写入/cache/recovery/intent中。这个intent的作用尚不知有何用。
② 将内存文件系统中的Recovery服务的日志(/tmp/recovery.log)拷贝到cache(/cache/recovery/log)分区中,以便告知重启后的Main System发生过什么。
③ 擦除MISC分区中的BCB数据块的内容,以便系统重启后不在进入Recovery模式而是进入更新后的主系统。
④ 删除/cache/recovery/command文件。这一步也是很重要的,因为重启后Bootloader会自动检索这个文件,如果未删除的话又会进入Recovery模式。












下面简单介绍update.c文件
①函数参数以及版本的检查:当前updater binary API所支持的版本号有1,2,3这三个。
②获取管道并打开:在执行此程序的过程中向该管道写入命令,用于通知其父进程根据命令去更新UI显示。
③读取updater-script脚本:从update.zip包中将updater-script脚本读到一块动态内存中,供后面执行。
④Configure edify’s functions:注册脚本中的语句处理函数,即识别脚本中命令的函数。主要有以下几类(edify脚本没有了解过)
RegisterBuiltins():注册程序中控制流程的语句,如ifelse、assert、abort、stdout等。
RegisterInstallFunctions():实际安装过程中安装所需的功能函数,比如mount、format、set_progress、set_perm等等。
RegisterDeviceExtensions():与设备相关的额外添加項,在源码中并没有任何实现。 FinishRegistration():结束注册。
⑤Parsethe script:调用yy*库函数解析脚本,并将解析后的内容存放到一个Expr类型的python类中。主要函数是yy_scan_string()和yyparse()。(yy_scan_string()和yyparse()不太理解)
 ⑥执行脚本:核心函数是Evaluate(),它会调用其他的callback函数,而这些callback函数又会去调用Evaluate去解析不同的脚本片段,从而实现一个简单的脚本解释器。
⑦错误信息提示:最后就是根据Evaluate()执行后的返回值,给出一些打印信息。






EMMC=NAND Flas + 控制芯片 + 标准封装接口


函数:
getopt_long支持长选项的命令行解析,使用man getopt_long
原型:int getopt_long(int argc, char * const argv[],const char *optstring, const struct option *longopts,int *longindex);
字符串optstring可以下列元素:
1.单个字符,表示选项,
2.单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
3 单个字符后跟两个冒号,表示该选项后可以有参数也可以没有参数。如果有参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张)。




PNG:(UI部分使用)
文件头是由固定的8个字节组成:
十进制数 137 80 78 71 13 10 26 10


十六进制数
89 50 4E 47 0D 0A 1A 0A
其文件结构是:
PNG文件标志
PNG数据块 …… PNG数据块
PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。

阅读(2201) | 评论(0) | 转发(0) |
0

上一篇:网络部分。

下一篇:usb 2.0

给主人留下些什么吧!~~