前言

这篇水文中我已经提到了动态分区和Virtual A/B大概是啥,这篇文章主要记录一下Virtual A/B大概是怎么工作的

谷歌对此的解释完全令人迷惑
https://source.android.com/devices/tech/ota/virtual_ab

秋大对此的说法比谷歌更加形象,但其实也漏掉了许多重要的点(也许是为了便于一般人理解)

p1

正文

分阶段讲一讲系统都干了些什么 (主要是基于谷歌的文档和秋大的说法进行梳理,再加上一些自己的简单测试与日志,没有牵扯到源码分析)

系统内安装更新时

主要是创建Δ分区(或文件)(谷歌称之为copy-on-write (COW) device
这个东西的作用是存储新系统与老系统的拆分包(新系统-老系统=Δ) (但是似乎并不是新老系统diff一下,把差异存在里面,而是根据payload里更新了哪些区块,有更新的都会往里面塞,无论内容是否相同)

为什么说是Δ分区(或文件)呢?
谷歌写道

Location of COW files — For launch devices, the OTA client uses all available empty space in the super partition before using space in /data. For retrofit devices, there's always enough space in the super partition so that the COW file is never created on /data.

也就是说,Δ内容默认会在super分区中创建临时的逻辑分区来进行存储,而在super分区空间不够用时,才会在/data下创建

而按照谷歌的描述,Virtual A/B 实际上有两种制式,一种是出厂安卓11的原生VAB,这种制式下super分区只预留了足以装下一组逻辑分区的空间;另一种是由安卓10动态分区升级改装而来的VAB,这种制式下super分区的容量足够装下两组逻辑分区。
因此,对于原生VAB,需要data分区的空间补偿来保存Δ内容,对于第二种,super分区本身已经足够。

那么,新建的用来存储Δ内容的逻辑分区和存储Δ内容的文件长什么样呢?(来自fastbootd)

(bootloader) is-logical:system_a-cow:yes
(bootloader) is-logical:odm_a-cow:yes
(bootloader) is-logical:product_a-cow:yes
(bootloader) is-logical:system_ext_a-cow:yes

那么,文件呢?
位于
/data/gsi/ota
是类似于这样的东西

# ls /data/gsi/ota
system_ext_a-cow-img.img  system_ext_a-cow-img.img.0000  vendor_a-cow-img.img  vendor_a-cow-img.img.0000

嗯,一部分分区的Δ位于data,一部分位于super,看空间安排了。

在Δ内容全部生成好后,系统将会把boot control HAL的merge status设置为SNAPSHOTTED,并切换分区(我猜这里的切换仅仅只是重命名)

这里有个很有趣的点,比如当前你在a分区,当前system分区名为system_a,那么生成的Δ分区名称就为system_a-cow,而在切换分区这一操作完成后,这两个分区中的a都会变成b(笑 不愧是《Virtual》

安装完开机

此时boot control HAL的merge statusSNAPSHOTTED,新系统的每个动态分区都由两部分组成——老系统的分区和新旧系统拆分的Δ内容。系统在开机时需要将分区的原始内容(即上一版的系统)与Δ内容(新旧系统的拆分包)进行动态的合并,并使用这合并后的内容启动系统 (此时仅仅只是动态的合并并加载到内存中,并没有物理上的合并)

如果开机失败,那很简单,分区切回去,再次开机时不加载Δ内容,也就是使用老系统开机

如果开机成功,系统将会设置boot control HAL的merge statusMERGING,并在后台默默将Δ内容物理的合并进老系统的分区中,形成真正的新系统

合并完成,系统将会设置boot control HAL的merge statusNONE

再次重新启动

此时,boot control HAL的merge statusNONE,纯粹由包含完整新系统的分区进行启动,没有Δ内容参与
至此,Virtual A/B更新正式完成

merge status 是干啥的?

嗯。这个很有意思,简单来说,不同的状态决定了系统要如何启动,也决定了系统的一些特性能否正常工作。
当merge status处于SNAPSHOTTED状态时,系统通过合并Δ内容和老系统分区 来形成 新系统分区 进行启动
当merge status处于MERGING状态时,这时很有意思,系统分区处于新老系统交替的状态,甚至根本就不是完整的文件系统,此时系统的启动相当于是通过 分区中的已合并内容 + Δ中的未合并内容 来完成的,也就是说,MERGING过程中随便重启也是没有关系的,谷歌早就帮你考虑好了(笑
当merge status处于NONE状态时,仅通过系统分区启动,不加载Δ内容。

有人可能会说,那我在MERGING的时候双清会怎么样,不是有一部分Δ内容在data里么?
谷歌早就帮你想好了,这样会砖的(因为系统分区不完整)
所以,人家就,不让你双清🤣

If the merge status is MERGING, or if the merge status is SNAPSHOTTED and the slot has changed to the newly updated slot, then requests to wipe userdata, metadata, or the partition storing the merge status must be rejected in the bootloader.

所以,bootloader,fastbootd,recovery和系统,要同时都能获得merge status的内容,否则,它们之中的随便一个都可以双清系统....

这里就出现了一个奇观

Implement the boot control HAL so that the bootloader can read the value set by the setSnapshotMergeStatus() method.

bootloader这种木乃伊要活起来从HAL里读内容🤣

嗯,只是大概弄清了一些,但是还有许多谜团没有解开
比如

  • data分区在这个过程中是怎么挂载的?难道不是要早于system分区的加载?
  • merge status 存储在哪里?data里?那bootloader会挂载data?
  • bootloader这种僵尸是怎么和HAL通信的?

有待进一步探索