这篇很多部分来源于其他博客,我个人进行了总结,可以串联起来整个ota流程

1、问题来源
2、固件内容与开机流程
3、开机BCB作用
4、recovery升级交互流程

1、问题来源
查看解密流程和recovery.cpp升级流程,多次提到BCB相关
1、从system到recovery需要用到BCB
文件RecoverySystemService.java //设置BCB
public boolean setupBcb(String command) {
return setupOrClearBcb(true, command);
private boolean setupOrClearBcb(boolean isSetup, String command) {
Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear") +" bcb successfully finished.");
对应Log:RecoverySystemService: uncrypt setup bcb successfully finished.\
2、 recovery升级也需要操作BCB
//get_args方法获取misc分区的command文件
std::vector<std::string> args = get_args(argc, argv);
std::vector<char*> args_to_parse(args.size());
std::transform(args.cbegin(), args.cend(), args_to_parse.begin(),
[](const std::string& arg) { return const_cast<char*>(arg.c_str()); });
Last_log:
I:Boot command: boot-recovery
I:Got 3 arguments from boot message
3、遇到过一次升级问题,因为系统会发送多次开机广播而导致升级失败,第二次开机广播清除了command信息
所以查询了一些资料作为整理该部分涉及到bootloder Misc system recovery boot分区
在升级流程上很少有客户会改动,作为了解就可以

2、固件内容
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。

2、开机流程

3、开机BCB作用

4、Recovery模式中的两个通信接口
在Recovery服务中上述的三个实体之间的通信是必不可少的,他们相互之间又有以下两个通信接口。
(一)通过CACHE分区中的三个文件:
Recovery通过/cache/recovery/目录下的三个文件与mian system通信。具体如下
1/cache/recovery/command:
这个文件保存着Main system传给Recovery的命令行,每一行就是一条命令,支持一下几种的组合。
--send_intent=anystring //write the text out to recovery/intent
在Recovery结束时在finish_recovery函数中将定义的intent字符串作为参数传进来,并写入到/cache/recovery/intent中
--update_package=root:path //verify install an OTA package file
Main system将这条命令写入时,代表系统需要升级,在进入Recovery模式后,将该文件中的命令读取并写入BCB中,
然后进行相应的更新update.zip包的操作。
--wipe_data //erase userdata(and cache),then reboot。擦除用户数据。擦除data分区时必须要擦除cache分区。
--wipe_cache //wipe cache(butnot user data),then reboot。擦除cache分区。
2/cache/recovery/log:
Recovery模式在工作中的log打印。在recovery服务运行过程中,stdout以及stderr会重定位到/tmp/recovery.log
在recovery退出之前会将其转存到/cache/recovery/log中,供查看。
3/cache/recovery/intent:Recovery传递给Main system的信息。作用不详。

(二)通过BCB(Bootloader Control Block):
BCB是bootloader与Recovery的通信接口,也是Bootloader与Main system之间的通信接口。存储在flash中的MISC分区,
占用三个page,其本身就是一个结构体,具体成员以及各成员含义如下:
struct bootloader_message{
char command[32];
char status[32];
char recovery[1024];
};
1command成员:其可能的取值在上文已经分析过了,即当想要在重启进入Recovery模式时,会更新这个成员的值。
另外在成功更新后结束Recovery时,会清除这个成员的值,防止重启时再次进入Recovery模式。
2status:在完成相应的更新后,Bootloader会将执行结果写入到这个字段。
3recovery:可被Main System写入,也可被Recovery服务程序写入。该文件的内容格式为:
“recovery\n
<recovery command>\n
<recovery command>”
该文件存储的就是一个字符串,必须以recovery\n开头,否则这个字段的所有内容域会被忽略。“recovery\n”之后的部分,
是/cache/recovery/command支持的命令。可以将其理解为Recovery操作过程中对命令操作的备份。
Recovery对其操作的过程为:先读取BCB然后读取/cache/recovery/command,然后将二者重新写回BCB,
这样在进入Main system之前,确保操作被执行。在操作之后进入Main system之前,
Recovery又会清空BCB的command域和recovery域,这样确保重启后不再进入Recovery模式。

1、RecoverySystem类:
RecoverySystem类的源码所在文件路径为:gingerbread0919/frameworks/base/core/java/android/os/RecoverySystem.java。
我们关心的是installPackage(Context context,FilepackageFile)函数。这个函数首先根据我们传过来的包文件,获取这个包文件的绝对路径filename。
然后将其拼成arg=“--update_package=”+filename。它最终会被写入到BCB中。这个就是重启进入Recovery模式后,Recovery服务要进行的操作。
它被传递到函数bootCommand(context,arg)。
2、bootCommand():
在这个函数中才是Main System在重启前真正做的准备。主要做了以下事情,首先创建/cache/recovery/目录,
删除这个目录下的command和log(可能不存在)文件在sqlite数据库中的备份。
然后将上步中的arg命令写入到/cache/recovery/command文件中。
下一步就是真正重启了。接下来看一下在重启函数reboot中所做的事情。
3、pm.reboot():
重启之前先获得了PowerManager(电源管理)并进一步获得其系统服务。然后调用了pm.reboot(“recovery”)函数。
他就是/bionic/libc/bionic/reboot.cpp中的reboot函数。这个函数实际上是一个系统调用,
即__reboot(LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,mode,NULL);从这个函数我们可以看出前两个参数就代表了我们的组合键,
mode就是我们传过来的“recovery”。再进一步跟踪就到了汇编代码了,我们无法直接查看它的具体实现细节。
但可以肯定的是这个函数只将“recovery”参数传递过去了,之后将“boot-recovery”写入到了MISC分区的BCB数据块的command域中。
这样在重启之后Bootloader才知道要进入Recovery模式。
在这里我们无法肯定Main System在重启之前对BCB的recovery域是否进行了操作。其实在重启前是否更新BCB的recovery域是不重要的,
因为进入Recovery服务后,Recovery会自动去/cache/recovery/command中读取要进行的操作然后写入到BCB的recovery域中。
至此,Main System就开始重启并进入Recovery模式。在这之前Main System做的最实质的就是两件事,
一 、是将“boot-recovery”写入BCB的command域,
二、是将--update_package=/cache/update.zip”或则“--update_package=/sdcard/update.zip”写入/cache/recovery/command文件中。

从Bootloader开始如果没有组合键按下,
就从MISC分区读取BCB块的command域(在主系统时已经将“boot-recovery”写入)。然后就以Recovery模式开始启动。
与正常启动不同的是Recovery模式下加载的镜像是recovery.img。这个镜像同boot.img类似,也包含了标准的内核和根文件系统。
其后就与正常的启动系统类似,也是启动内核,然后启动文件系统。在进入文件系统后会执行/init,init的配置文件就是/init.rc。
这个配置文件来自/bootable/recovery/etc/init.rc。这个文件做的事情很简单:
1、设置环境变量。
2、建立etc连接。
3、新建目录,备用。
4、挂载/tmp为内存文件系统tmpfs
5、启动recovery(/sbin/recovery)服务。
6、启动adbd服务(用于调试)。
这里最重要的就是当然就recovery服务了。在Recovery服务中将要完成升级工作。

从recovery.cpp的main函数开始:
1.ui_init():Recovery服务使用了一个基于framebuffer的简单ui(miniui)系统。这个函数对其进行了简单的初始化。
在Recovery服务的过程中主要用于显示一个背景图片(正在安装或安装失败)和一个进度条(用于显示进度)。
另外还启动了两个线程,一个用于处理进度条的显示(progress_thread),另一个用于响应用户的按键(input_thread)。
2.get_arg():
(1)get_bootloader_message():主要工作是根据分区的文件格式类型(mtd或emmc)从MISC分区中读取BCB数据块到一个临时的变量中。
(2)然后开始判断Recovery服务是否有带命令行的参数(/sbin/recovery,根据现有的逻辑是没有的),若没有就从BCB中读取recovery域。
如果读取失败则从/cache/recovery/command中读取然后。这样这个BCB的临时变量中的recovery域就被更新了。
在将这个BCB的临时变量写回真实的BCB之前,又更新的这个BCB临时变量的command域为“boot-recovery”。
这样做的目的是如果在升级失败(比如升级还未结束就断电了)时,系统在重启之后还会进入Recovery模式,直到升级完成。
(3)在这个BCB临时变量的各个域都更新完成后使用set_bootloader_message()写回到真正的BCB块中。
3. parserargc/argv:解析获得参数。注册所解析的命令(register_update_command)
4. if(update_package):判断update_package是否有值,若有就表示需要升级更新包,此时就会调用install_package()
在这一步中将要完成安装实际的升级包。这是最为复杂,也是升级update.zip包最为核心的部分。
5. if(wipe_data/wipe_cache):这一步判断实际是两步,在源码中是先判断是否擦除data分区(用户数据部分)的,然后再判断是否擦除cache分区。值
得注意的是在擦除data分区的时候必须连带擦除cache分区。在只擦除cache分区的情形下可以不擦除data分区。

6.finish_recovery():
这是Recovery关闭并进入Main System的必经之路。其大体流程如下:
将intent(字符串)的内容作为参数传进finish_recovery中。如果有intent需要告知Main System,则将其写入/cache/recovery/intent中。
将内存文件系统中的Recovery服务的日志(/tmp/recovery.log)拷贝到cache(/cache/recovery/log)分区中,
以便告知重启后的Main System发生过什么。
擦除MISC分区中的BCB数据块的内容,以便系统重启后不在进入Recovery模式而是进入更新后的主系统。
删除/cache/recovery/command文件。这一步也是很重要的,因为重启后Bootloader会自动检索这个文件,如果未删除的话又会进入Recovery模式。
7. reboot():
这是一个系统调用。在这一步Recovery完成其服务重启并进入Main System。
这次重启和在主系统中重启进入Recovery模式调用的函数是一样的,但是其方向是不一样的。所以参数也就不一样。
查看源码发现,其重启模式是RB_AUTOBOOT。这是一个系统的宏。

二、Android BCB的作用相关推荐

  1. 腾讯、网易云、字节跳动面试点总结—AMS在Android起到什么作用?

    本专栏专注分享大型Bat面试知识,后续会持续更新,喜欢的话麻烦点击一个关注 面试官: AMS在Android起到什么作用,简单的分析下Android的源码 心理分析:这道题在发生在大多数场景下.面对这 ...

  2. [安卓开发笔记二]android Studio通过jni调用C++代码

    [安卓开发笔记二]android Studio通过jni调用C++代码 16/12/11 更新 此博客基于安卓android studio 1.5所写,现在已经有了android studio2.2的 ...

  3. GPS定位系统(二)——Android端

    前言 GPS系列--Android端,github项目地址 tag: gps_mine Android移动端,主要是使用高德地图定位,后台上传定位信息,然后就是想办法尽量保活. 包括两个小功能:1.上 ...

  4. android的适配器作用,适配器在Android中的作用是什么?

    适配器在Android中的作用是什么? 我想知道在Android环境中何时,何地以及如何使用适配器. 来自Android开发者文档的信息对我来说不够,我希望得到更详细的分析. 11个解决方案 39 v ...

  5. Android面试题(二)Android高级/资深面试题

    Android面试题(一)Java基础 Android面试题(二)Android基础 Android面试题(三)Java虚拟机 Android面试题(四)设计模式 Android面试题(五)数据结构/ ...

  6. android 自定义xmlns,Android xmlns 的作用及其自定义实例详解

    Android xmlns 的作用及其自定义实例详解 xmlns:Android="http://schemas.android.com/apk/res/android的作用是: 这个是xm ...

  7. URI是什么,在Android中有什么作用?

    URI是什么,在Android中有什么作用? Android中的URI即通用资源标识符,Universal Resource Identifier, 简称URI. Uri代表要操作的数据,Androi ...

  8. 为什么Android项目mainactivity中有一个变量R_【Android开发入门教程】二.Android应用程序结构分析!...

    一.新建HelloWorld项目: 1.打开Eclipse,点击"File"->"New"->"Project"-Android ...

  9. Android xmlns 的作用及其自定义

    xmlns:android="http://schemas.android.com/apk/res/android 声明xml命名空间.xmlns意思为"xml namespace ...

最新文章

  1. php 点击删除数据,使用php脚本删除数据
  2. 缓冲区溢出-基本ROP-ret2syscall
  3. 《构架之美》阅读笔记三
  4. 谈谈关于MVP模式中V-P交互问题
  5. PAT——1018. 锤子剪刀布
  6. Linux 程 序 员 失 业 警 告
  7. 狼性文化遭质疑,那我们当个佛系程序员可好?
  8. [Android Pro] 判断Uri对应的ContentProvider所操作的数据库u存在,及DownloadManager的暂停,继续...
  9. linux mysql查看数据库编码_MySQL查看和修改字符编码的实现方法
  10. 软件方法上竞赛自测题答案
  11. OpenHarmony啃论文俱乐部玩法攻略
  12. 音视频学习(三)——sip协议
  13. ITK VKT 安装-详细
  14. linux 模拟usb键盘,在Linux下模拟键盘按键
  15. 良心,是黑暗里的一盏灯
  16. 译 | 如果你惯用右手,你应该把鼠标放在左边的10个理由
  17. 51单片机流水灯现象1
  18. 计算机视觉——全景图像拼接
  19. 分享一组Rpg Marker人物行走,游戏素材图片,共20张图片
  20. Effective Java(第3版) 90条经验法则

热门文章

  1. 微信小程序|考试系统|基于微信小程序和SpringBoot+VUE的智能在线考试系统毕业设计
  2. opencv-python:16_形态学处理【二】(开操作、闭操作、形态学梯度、顶帽变换、黑帽变换,去除皮肤镜中的毛发噪音、cv2.morphologyEx())
  3. 调用方法有抛出异常的解决办法
  4. HTTPS中间人攻击实验
  5. 导航栏的使用(ToolBar、BottomNavgationView)
  6. Framework学习之旅:Zygote进程
  7. solr增量 dih deltaimport 入门
  8. 可以正常上网但ping 127.0.0.1或localhost出现请求超时的解决方法
  9. C++异常之栈解旋(unwinding)
  10. element-ui快速使用(使用element-ui做一个表格)