概覽
Virtual AB系統
(1)無縫升級:virtual AB和AB系統一樣,支持無縫升級
(2)回滾:Virtual AB支持系統回滾。當系統升級失敗的時候,設備會自動回滾到舊的系統版本。
(3)省空間:在Virtual AB中,super分區采用dm-snapshot技術來進行升級,不需要分AB系統,所以virtual AB系統比AB系統節省了很多空間。
背景
Device-mapper技術

System分區的dm設備掛載棧如圖上所示:
(1)最底層是物理分區:super分區
(2)第二層是用dm-linear技術實現的動態邏輯分區:為/dev/block/mapper/system_a分區
(3)第三層是用dm-verity技術實現的校驗分區:為/dev/block/mapper/system-verity分區
(4)最后system分區掛載在/dev/block/mapper/system-verity分區上面
Dm-snapshot技術概覽

Dm-snapshot分區由四個設備組成:
(1)通常system分區被作為base設備
(2)COW設備用來保存base設備所改動的文件。
(3)Dm-snapshot設備由snapshot目標創建。往dm-snapshot設備寫文件,會寫到COW設備中。從dm-snapshot設備讀文件,則會從base設備和COW設備中讀文件,如果文件通過dm-snapshot改變過,則從COW設備中讀;否則從base設備中讀。
(4)dm-snapshot-origin設備由snapshot-origin目標創建。讀寫文件都是從base設備中進行讀寫。
BootControl服務
Misc分區中相關的數據結構。
(1)bootloader_message_ab數據結構
struct bootloader_message_ab {
struct bootloader_message message;
char slot_suffix[32]; // 為bootloader_control數據結構,相關的信息都存放於此
char update_channel[128];
char reserved[1888];
};
(2)bootloader_control 數據結構
struct bootloader_control {
// NUL terminated active slot suffix.
char slot_suffix[4]; // 存放_a或者_b
// Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
uint32_t magic; // 為0x42414342
// Version of struct being used (see BOOT_CTRL_VERSION).
uint8_t version; // 為1
// Number of slots being managed.
uint8_t nb_slot : 3; // slot的數目,一般為2(AB)
// Number of times left attempting to boot recovery.
uint8_t recovery_tries_remaining : 3;
// Status of any pending snapshot merge of dynamic partitions.
uint8_t merge_status : 3;
// Ensure 4-bytes alignment for slot_info field.
uint8_t reserved0[1];
// Per-slot information. Up to 4 slots.
struct slot_metadata slot_info[4]; // 最大支持4個slot
// Reserved for further use.
uint8_t reserved1[8];
// CRC32 of all 28 bytes preceding this field (little endian
// format).
uint32_t crc32_le;
} __attribute__((packed));
(3)slot_metadata數據結構
struct slot_metadata {
// Slot priority with 15 meaning highest priority, 1 lowest
// priority and 0 the slot is unbootable.
uint8_t priority : 4; // slot的優先級,優先級高的先啟動,最高為15,最低為1。為0時不給啟動
// Number of times left attempting to boot this slot.
uint8_t tries_remaining : 3; // 該slot能重啟的次數
// 1 if this slot has booted successfully, 0 otherwise.
uint8_t successful_boot : 1; // 當該slot成功啟動的時候,該位置1
// 1 if this slot is corrupted from a dm-verity corruption, 0
// otherwise.
uint8_t verity_corrupted : 1; // 目前無人設此值1
// Reserved for further use.
uint8_t reserved : 7;
} __attribute__((packed));
Virtual AB的分區概覽圖

除了super分區,其他分區如果需要升級,都需要分為AB分區。因為super分區不用分AB,而其他分區又比較小,所以virtual AB比AB系統要節省很多空間。
Snapshot和合並流程
升級前的分區概覽

OTA升級過程中

當update_engine開始寫一個新的system分區的時候,它會創建以下設備:
(1)基於system_a設備創建system_b_base設備(dm-5設備)
(2)在/data/gsi/ota/目錄下創建system_b-cow-img.img.0000文件,然后通過/dev/block/loop0設備進行掛載。然后創建dm-6設備將其映射到system_cow上
(3)創建dm-7(dm-snapshot)設備,將起映射為system_b
(4)打開dm-snapshot設備,將OTA升級包中的system.img寫進去,此時寫的內容,就寫到了/data/gsi/ota/system_b-cow-img.img.0000文件中。
(5)升級成功之后,調用boot_ctrl中的SetActiveBootSlot函數:將_a(目前slot)的priority值設小(如果最大就減1)。然后將_b(要升級的slot) 的priority值設為15(最大),以及tries_remaining的值設為6(升級失敗,最多可以重啟6次)。
//在升級過程中創建設備:
[INFO:dynamic_partition_control_android.cc(319)] Loaded metadata from slot B in /dev/block/by-name/super
update_engine: Successfully unmapped snapshot system_b
vold : Disk at 253:5 changed
update_engine: [libfs_mgr]Created logical partition system_b-base on device /dev/block/dm-5
vold : Disk at 7:1 changed
gsid : Created loop device /dev/block/loop1 for file /data/gsi/ota/system_b-cow-img.img.0000
update_engine: Mapped system_b-cow-img to /dev/block/loop1
update_engine: Calling GetMappedImageDevice with local image manager; device /dev/block/loop1may not be available in first stage init!
vold : Disk at 253:6 changed
update_engine: Mapped COW device for system_b at /dev/block/dm-6
vold : Disk at 253:7 changed
update_engine: Mapped system_b as snapshot device at /dev/block/dm-7
[INFO:dynamic_partition_control_android.cc(173)] Succesfully mapped system_b to device mapper (force_writable = 1); device path at /dev/block/dm-7
//升級成功之后,調用boot_ctrl中的SetActiveBootSlot函數:
[INFO:postinstall_runner_action.cc(376)] All post-install commands succeeded
OTA升級完成后,重啟,uboot端slot切換流程。
(1)遍歷所有slot,如果該slot的tries_remaining為0則跳過(不從該slot啟動,當_b系統無法啟動,則在這里切換回_a系統)。然后選擇priority最高的slot進行啟動(升級后,_b最高)
(2)如果當前slot的successful_boot為0(OTA升級重啟成功后,會將此值設為1),則tries_remaining的值減1
(3)將slot_suffix的值改為_b
OTA升級完成后,重啟過程中

(1)創建dm-0設備,作為system_b_base設備(映射super分區中的system分區)
(2)創建dm-1設備,作為system_b-cow-img設備(映射data分區中的system_b-cow-img.img.0000文件)
(3)創建dm-2設備,映射為system_cow設備
(4)創建dm-3設備,映射為dm-snapshot設備(system_b分區)
(5)將system分區掛載在dm-3上(dm-snapshot設備)
(6)然后系統啟動讀system分區的時候,如果讀到的文件,在system_cow中記錄到有改變,則直接讀system_cow中的文件。
// 在first stage init過程中創建dm設備:
init: Creating logical partitions with snapshots as needed
random: init: uninitialized urandom read (16 bytes read)
init: [libfs_mgr]Created logical partition system_b-base on device /dev/block/dm-0
random: init: uninitialized urandom read (16 bytes read)
init: [libfs_mgr]Created logical partition system_b-cow-img on device /dev/block/dm-1
init: Mapped system_b-cow-img to 253:1
init: Mapped COW device for system_b at /dev/block/dm-2
init: Mapped system_b as snapshot device at /dev/block/dm-3
重啟完成后,開始snapshot-merge

(1)重啟成功后,調用BootControl的MarkBootSuccessful函數,將successful_boot和tries_remaining設為1。
(2)將dm-snapshot設備設為merge狀態,將cow設備和base設備的內容進行合並。
(3)umaped相關的設備
(4)合並成功之后,刪除/data/gsi/ota目錄下的img文件以及/metadata/ota目錄下的相關文件。
// 1. 啟動完成之后,調用BootControl的MarkBootSuccessful函數
[INFO:cleanup_previous_update_action.cc(137)] Boot completed, waiting on markBootSuccessful()
// 2. 進行合並,大概30秒左右
17:34:51.739365 [INFO:cleanup_previous_update_action.cc(338)] Attempting to initiate merge.
設置dm-snapshot為merge:
update_engine: Successfully switched snapshot device to a merge target: system_b
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 8%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 20%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 27%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 33%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 40%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 48%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 55%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 63%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 70%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 78%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 85%.
[INFO:cleanup_previous_update_action.cc(302)] Waiting for merge to complete: 93%.
// 3. unmap設備,然后清除data目錄下的img文件
update_engine: Removing all update state.
// 4. merge完成
17:35:21.018875 [INFO:cleanup_previous_update_action.cc(262)] Merge finished with state MergeCompleted.
Snapshot-merge完成后

異常處理
Update_verifier程序
Update_verifier程序:如果沒有配置checkpoint,則會調用boot_ctrl的module->markBootSuccessful,導致android如果升級失敗,會無法回退。
解決辦法:配置checkpoint。
