實作動態分區

動態分區是透過 dm-Linear 裝置對應程式執行 與 Linux kernel 中執行模組類似super 分區包含 列出每個動態分區的名稱和區塊範圍的中繼資料 在 super內。在 init 第一階段中,這個 都會經過剖析和驗證,並透過建立虛擬區塊裝置 每個動態分區

套用 OTA 時,系統會自動建立動態分區 或視需要刪除A/B 裝置有兩個副本: 而您所做的變更只會套用到代表 目標版位。

動態分區會在使用者空間中實作,因此 就無法將系統啟動載入程式進行動態化例如 boot 系統啟動載入程式會讀取 dtbovbmeta 因此必須保持為實體分區

每個動態分區都可以隸屬於一個更新群組。這些 群組會限制該群組中分區可使用的空間上限。 舉例來說,systemvendor 可以屬於 可限制 systemvendor

在新裝置實作動態分區

本節詳細說明如何在新裝置中實作動態分區 搭載 Android 10 以上版本更新 現有裝置,請參閱:升級 Android 裝置

分區變更

如果是搭載 Android 10 的裝置,請建立 名為 super 的分區super 分區會在內部處理 A/B 運算單元,因此 A/B 裝置不需要 super_asuper_b 分區。 系統啟動載入程式未使用的所有唯讀 Android 開放原始碼計畫分區 必須是動態值,且必須從 GUID 分區表 (GPT) 中移除。 供應商專屬的分區不必動態,且可放置在 加入清單

如要預估「super」的大小,請將 卻會從 GPT 中刪除分區A/B 裝置: 應包含兩個版位的大小。圖 1 顯示 轉換為動態前後的分區表範例 多個分區

分區表格版面配置
圖 1.新的實體分區表格版面配置 轉換為動態分區
,瞭解如何調查及移除這項存取權。

支援的動態分區如下:

  • 系統
  • 供應商
  • 產品
  • 系統延伸模組
  • ODM

針對搭載 Android 10 的裝置, 核心指令列選項 androidboot.super_partition 必須為空白,這樣指令 sysprop ro.boot.super_partition 是空的,

分區對齊

如果 super 個分區未正確對齊。 「super」分區必須對齊 I/O 下限 要求大小;根據預設, 建構系統 (透過 lpmake) super 分區映像檔),此假設是透過 1 MiB 對齊 足以應付每個動態分區不過,如果供應商 確認 super 分區已正確對齊。

您可以透過下列方式決定封鎖裝置的最小要求大小: 正在檢查 sysfs。例如:

# ls -l /dev/block/by-name/super
lrwxrwxrwx 1 root root 16 1970-04-05 01:41 /dev/block/by-name/super -> /dev/block/sda17
# cat /sys/block/sda/queue/minimum_io_size
786432

您可以驗證 super 分區在 類似的方式:

# cat /sys/block/sda/sda17/alignment_offset

對齊偏移量必須為 0。

裝置設定變更

如要啟用動態分區,請在 device.mk:

PRODUCT_USE_DYNAMIC_PARTITIONS := true

主面板設定變更

您必須設定 super 分區的大小:

BOARD_SUPER_PARTITION_SIZE := <size-in-bytes>

在 A/B 裝置上,如果總大小總和,建構系統會擲回錯誤 動態分區映像檔的比例超過 super 的一半 分區大小

您可以按照下列方式設定動態分區清單。適用對象 請列出要更新群組的裝置 BOARD_SUPER_PARTITION_GROUPS 變數。每個群組名稱 則具有 BOARD_group_SIZEBOARD_group_PARTITION_LIST 變數。 如果是 A/B 裝置,群組大小上限只能涵蓋一個 因為群組名稱是內部加上運算單元的結尾。

以下裝置範例會將所有分區分組 名稱為 example_dynamic_partitions

BOARD_SUPER_PARTITION_GROUPS := example_dynamic_partitions
BOARD_EXAMPLE_DYNAMIC_PARTITIONS_SIZE := 6442450944
BOARD_EXAMPLE_DYNAMIC_PARTITIONS_PARTITION_LIST := system vendor product

以下範例裝置將系統和產品服務導入 group_foo,以及vendorproduct, 並將 odm 轉換為 group_bar

BOARD_SUPER_PARTITION_GROUPS := group_foo group_bar
BOARD_GROUP_FOO_SIZE := 4831838208
BOARD_GROUP_FOO_PARTITION_LIST := system product_services
BOARD_GROUP_BAR_SIZE := 1610612736
BOARD_GROUP_BAR_PARTITION_LIST := vendor product odm
,瞭解如何調查及移除這項存取權。
  • 針對虛擬 A/B 啟動裝置,所有群組的大小上限總和 最多:
    BOARD_SUPER_PARTITION_SIZE - 負擔
    請參閱「實作虛擬 A/B」一節。
  • 對於 A/B 上市裝置,所有群組的大小上限總和 為:
    BOARD_SUPER_PARTITION_SIZE / 2 - 經常性費用
  • 如果是非 A/B 裝置和翻新 A/B 裝置,則 所有群組的大小都必須是:
    BOARD_SUPER_PARTITION_SIZE - 經常使用
  • 在建構期間,每個分區的映像檔大小總和 更新群組中的不得超過群組大小上限。
  • 系統在計算過程中需要負擔,才能將中繼資料納入考量, 等對齊方式合理的負擔為 4 MiB,不過您 可以依據裝置需求 增加更多的負荷

大小動態分區的大小

在動態分區之前,分區大小會過度分配到 確保他們有足夠的空間,可容納日後的更新內容。實際大小 且大部分唯讀分區都提供一些免費 空間在動態分區中,可用空間是 並可用於 OTA 期間增加分區。 請務必確保分區不會浪費空間 分配到最小可用大小

如果是唯讀 ext4 映像檔,建構系統會自動分配 未指定硬式編碼分區大小時的最小大小。 建構系統能配合映像檔 未使用的空間這可確保裝置不會浪費 可用於網路旅行社

此外,只要啟用 block- 簡化或簡化作業如要啟用這項功能,請使用下列設定:

BOARD_EXT4_SHARE_DUP_BLOCKS := true

如果您不想自動分配分區大小下限 控管分區大小的方法有兩種您可以指定 最低可用空間 BOARD_partitionIMAGE_PARTITION_RESERVED_SIZE, 也可以指定 BOARD_partitionIMAGE_PARTITION_SIZE即可強制 達到特定大小的動態分區這些都不是 建議上傳

例如:

BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE := 52428800

這會強制 product.img 中的檔案系統 未使用 50 MiB 的空間。

對系統進行變更

搭載 Android 10 的裝置不得 我們要使用系統權限

具有動態分區的裝置 (無論是使用或翻新的裝置) 動態分區) 不得使用系統式根層級。Linux kernel 無法 會解讀 super 分區,因此無法掛接 system 本身。system 目前掛接於 第一階段 init,位於 ramdisk 中。

不要設定 BOARD_BUILD_SYSTEM_ROOT_IMAGE。於 Android 10、 BOARD_BUILD_SYSTEM_ROOT_IMAGE 標記只會用於 區分系統是由核心或 第一階段 init 進入 ramdisk 中。

BOARD_BUILD_SYSTEM_ROOT_IMAGE設為true 會導致建構錯誤 PRODUCT_USE_DYNAMIC_PARTITIONS也是true

BOARD_USES_RECOVERY_AS_BOOT 設為 true 時, 復原映像檔為 boot.img,內含復原作業的 ramdisk。系統先前使用 skip_initramfs 核心 決定要啟動的模式適用對象 Android 10 裝置,「不得」通過系統啟動載入程式 skip_initramfs 傳送至核心指令列。而是系統啟動載入程式 應通過 androidboot.force_normal_boot=1,略過復原程序 並啟動一般的 Android搭載 Android 12 的裝置 或之後的版本必須使用 bootconfig 傳遞 androidboot.force_normal_boot=1

AVB 設定變更

使用 Android 時 驗證開機程序 2.0 (如果裝置未使用鏈結分區) 描述元,則無須變更。如果使用鏈結 但其中一個已驗證分區是動態的 必須做出變更

以下為鏈結的裝置設定範例 針對 systemvbmeta vendor 個分區。

BOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048
BOARD_AVB_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 1

BOARD_AVB_VENDOR_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
BOARD_AVB_VENDOR_ALGORITHM := SHA256_RSA2048
BOARD_AVB_VENDOR_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
BOARD_AVB_VENDOR_ROLLBACK_INDEX_LOCATION := 1

使用此設定時,系統啟動載入程式預期會找到 vbmeta system結尾和 vendor 個分區。由於這些分區 系統啟動載入程式向 (位於 super 中) 顯示,其中兩個 需要修改

  • 新增 vbmeta_systemvbmeta_vendor 分割為裝置分區表。如果是 A/B 裝置,請新增 vbmeta_system_avbmeta_system_bvbmeta_vendor_avbmeta_vendor_b。如果 新增一或多個分區的大小 做為 vbmeta 分區。
  • 透過新增 VBMETA_ 和 指定鏈結延伸至哪些分區:
    BOARD_AVB_VBMETA_SYSTEM := system
    BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
    BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA2048
    BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
    BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION := 1
    
    BOARD_AVB_VBMETA_VENDOR := vendor
    BOARD_AVB_VBMETA_VENDOR_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
    BOARD_AVB_VBMETA_VENDOR_ALGORITHM := SHA256_RSA2048
    BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
    BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION := 1
    

裝置可能正在使用其中一個、兩個分區,或全都不使用。變更內容 只有在鏈結至邏輯分區時才需要使用

AVB 系統啟動載入程式變更

如果系統啟動載入程式已嵌入 libavb, 包含下列修補程式:

如果使用鏈結分區,請加入其他修補程式:

  • 49936b4c0109411fdd38bd4ba3a32a01c40439a9 — "libavb:支援分區開頭的 vbmeta blob。"

核心指令列變更

必須加入新參數「androidboot.boot_devices」 加入核心指令列init 會使用這項資訊: 啟用 /dev/block/by-name 符號連結。這應該是 裝置路徑元件連結至 ueventd,那是 /dev/block/platform/device-path/by-name/partition-name。 搭載 Android 12 以上版本的裝置必須使用 Bootconfig 將 androidboot.boot_devices 傳遞至 init

舉例來說,如果超級分區的依名稱符號連結 /dev/block/platform/soc/100000.ufshc/by-name/super, 只要在 BoardConfig.mk 檔案中新增指令列參數, 如下:

BOARD_KERNEL_CMDLINE += androidboot.boot_devices=soc/100000.ufshc
您可以在 BoardConfig.mk 檔案中新增 bootconfig 參數,如下所示:
BOARD_BOOTCONFIG += androidboot.boot_devices=soc/100000.ufshc

Fstab 變更

裝置樹狀結構和裝置樹狀結構疊加層不得包含 fstab 項目。請使用屬於 ramdisk 的 fstab 檔案。

必須對 fstab 檔案進行邏輯分區的變更:

  • fs_mgr 旗標欄位必須包含 logical 旗標 和 first_stage_mount 旗標, Android 10,表示分區設定為 都會掛接到第一個階段
  • 分區可以指定 以「avb=vbmeta partition name」表示 fs_mgr 標記,然後是指定的 vbmeta 系統會先從第一個階段 init 初始化分區 再嘗試掛接任何裝置
  • dev 欄位必須是分區名稱。

下列 fstab 項目會將系統、供應商和產品設為邏輯 都會在兩個可用區中運作

#<dev>  <mnt_point> <type>  <mnt_flags options> <fs_mgr_flags>
system   /system     ext4    ro,barrier=1        wait,slotselect,avb=vbmeta,logical,first_stage_mount
vendor   /vendor     ext4    ro,barrier=1        wait,slotselect,avb,logical,first_stage_mount
product  /product    ext4    ro,barrier=1        wait,slotselect,avb,logical,first_stage_mount

將 fstab 檔案複製到第一個階段 ramdisk。

SELinux 變更

超級分區區塊裝置必須標有標籤 super_block_device。舉例來說,如果超級分區的依名稱符號連結 /dev/block/platform/soc/100000.ufshc/by-name/super, 在 file_contexts 中新增下列程式碼:

/dev/block/platform/soc/10000\.ufshc/by-name/super   u:object_r:super_block_device:s0

Fastbootd

系統啟動載入程式 (或任何非使用者空間刷新工具) 無法辨識 因而無法刷新這些區域為瞭解決這個問題,裝置 必須使用快速啟動通訊協定的使用者空間實作 (稱為 Fastbootd。

如要進一步瞭解如何實作 Fastbootd,請參閱將 Fastboot 移至使用者空間

ADB 儲值

針對使用 eng 或 userdebug 版本的開發人員,adb remount 非常適合快速疊代動態分區具有 由於您已經停止免費,因此發生 adb remount 的問題 空間如要解決這個問題,裝置可以啟用 疊加層只要超級分區中有足夠的空間 adb remount 會自動建立臨時動態 也會使用 Overlayfs 進行寫入暫時性分區為 名為 scratch,因此請勿將這個名稱用於其他產品 多個分區

如要進一步瞭解如何啟用疊加層,請參閱疊加層 Android 開放原始碼計畫中的 README 檔案。

升級 Android 裝置

如果您將裝置升級至 Android 10 想要在 OTA 中加入動態分區支援,就不需要 變更內建分區資料表。一些額外設定 這通常代表交易 不會十分要求關聯語意

裝置設定變更

如要調整動態分區,請在 device.mk:

PRODUCT_USE_DYNAMIC_PARTITIONS := true
PRODUCT_RETROFIT_DYNAMIC_PARTITIONS := true

主面板設定變更

您必須設定下列 Jamboard 變數:

  • BOARD_SUPER_PARTITION_BLOCK_DEVICES 設為使用的封鎖裝置清單 儲存動態分區的範圍這是現有實體 多個分區
  • BOARD_SUPER_PARTITION_partition_DEVICE_SIZE 設為大小 每個區塊裝置的 BOARD_SUPER_PARTITION_BLOCK_DEVICES 權限。 以下是裝置上現有實體分區的大小清單。這通常 BOARD_partitionIMAGE_PARTITION_SIZE在現有主面板中 儲存空間設定
  • 為所有使用者取消設定現有BOARD_partitionIMAGE_PARTITION_SIZEBOARD_SUPER_PARTITION_BLOCK_DEVICES 中使用多個分區。
  • BOARD_SUPER_PARTITION_SIZE 設為 BOARD_SUPER_PARTITION_partition_DEVICE_SIZE
  • BOARD_SUPER_PARTITION_METADATA_DEVICE 設為符合以下條件的封鎖裝置: 儲存動態分區中繼資料。必須是以下任一個值: BOARD_SUPER_PARTITION_BLOCK_DEVICES。通常這個參數會設為 system
  • 設定 BOARD_SUPER_PARTITION_GROUPSBOARD_group_SIZEBOARD_group_PARTITION_LIST。詳情請見 在新裝置上變更主面板設定

舉例來說,如果裝置已有系統和廠商分區,而您想要轉換 到動態分區中新增產品分區,並在更新期間新增產品分區,請採用下列設定:

BOARD_SUPER_PARTITION_BLOCK_DEVICES := system vendor
BOARD_SUPER_PARTITION_METADATA_DEVICE := system

# Rename BOARD_SYSTEMIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE.
BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE := <size-in-bytes>

# Rename BOARD_VENDORIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE
BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE := <size-in-bytes>

# This is BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE + BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE
BOARD_SUPER_PARTITION_SIZE := <size-in-bytes>

# Configuration for dynamic partitions. For example:
BOARD_SUPER_PARTITION_GROUPS := group_foo
BOARD_GROUP_FOO_SIZE := <size-in-bytes>
BOARD_GROUP_FOO_PARTITION_LIST := system vendor product

SELinux 變更

超級分區區塊裝置必須標上屬性 super_block_device_type。舉例來說,如果裝置已 systemvendor 分區,您想要將其做為區塊使用 裝置儲存動態分區的範圍,且依名稱的符號連結會標示為 system_block_device:

/dev/block/platform/soc/10000\.ufshc/by-name/system   u:object_r:system_block_device:s0
/dev/block/platform/soc/10000\.ufshc/by-name/vendor   u:object_r:system_block_device:s0

接著在 device.te 中新增下列程式碼:

typeattribute system_block_device super_block_device_type;

如需其他設定的資訊,請參閱實作 動態分區

如要進一步瞭解 Retrofit 更新,請參閱 適用於無動態 A/B 裝置裝置的 OTA 分區

原廠映像檔

如果是支援動態分區功能的裝置,請避免使用 啟動使用者空間時,使用者空間 Fastboot 連線至原廠映像檔 速度較其他快閃方法慢

為解決這個問題,make dist 現在建構了 super.img 圖片可直接刷新至超級 自動封裝邏輯內容 也就是包含 system.img vendor.img 等等,除了 super 以外 分區中繼資料。此圖像可直接刷新至 super 個分區,無須任何額外工具或使用 Fastbootd。建構之後,super.img 會放入 ${ANDROID_PRODUCT_OUT}

針對推出內含動態分區的 A/B 裝置 super.img 包含位置中的圖片。刷新 請直接在超映像檔中將版位 A 標示為可開機,再重新啟動 裝置。

對於 Retrofit 裝置,make dist 會建構一組 super_*.img 圖像可直接刷新至 對應的實體分區例如:make dist 版本 super_system.imgsuper_vendor.img 系統是 BOARD_SUPER_PARTITION_BLOCK_DEVICES 時 供應商。這些映像檔會存放在 target_files.zip

裝置對應裝置儲存空間裝置調整

動態分區支援多種非確定性的裝置對應工具 如需儲存大量結構化物件 建議使用 Cloud Bigtable這些方法不一定都會如預期地例項化,因此您必須追蹤所有 並在掛接時更新所有相關聯分區的 Android 屬性 妥善管理基礎儲存裝置

init 中的機制可追蹤掛接,並以非同步方式追蹤。 會更新 Android 屬性。時間量不保證會達到 。 ,讓所有 on property 觸發事件做出回應。這些屬性 dev.mnt.blk.<partition>,其中 <partition>rootsystemdata 或 例如 vendor。每項資源都會與 基本 Storage 裝置名稱,如以下範例所示:

taimen:/ % getprop | grep dev.mnt.blk
[dev.mnt.blk.data]: [sda]
[dev.mnt.blk.firmware]: [sde]
[dev.mnt.blk.metadata]: [sde]
[dev.mnt.blk.persist]: [sda]
[dev.mnt.blk.root]: [dm-0]
[dev.mnt.blk.vendor]: [dm-1]

blueline:/ $ getprop | grep dev.mnt.blk
[dev.mnt.blk.data]: [dm-4]
[dev.mnt.blk.metadata]: [sda]
[dev.mnt.blk.mnt.scratch]: [sda]
[dev.mnt.blk.mnt.vendor.persist]: [sdf]
[dev.mnt.blk.product]: [dm-2]
[dev.mnt.blk.root]: [dm-0]
[dev.mnt.blk.system_ext]: [dm-3]
[dev.mnt.blk.vendor]: [dm-1]
[dev.mnt.blk.vendor.firmware_mnt]: [sda]

init.rc 語言可讓 Android 屬性 將裝置納入規則,且儲存空間裝置可由平台調整 使用以下指令:

write /sys/block/${dev.mnt.blk.root}/queue/read_ahead_kb 128
write /sys/block/${dev.mnt.blk.data}/queue/read_ahead_kb 128

指令在第二階段 init 開始處理後, epoll loop 會生效,並開始更新這些值。不過 因為屬性觸發條件要到 init 後才會啟用, 無法用於初始啟動階段處理 rootsystemvendor。您可能會想 保留核心預設值 read_ahead_kb,直到有 early-fs 中的 init.rc 指令碼可覆寫 包括各種 Daemon 和設施因此,Google 建議 您是使用 on property 功能,再加上 由 init.rc 控制的資源,例如 sys.read_ahead_kb, 處理作業時間並防止競爭狀況,就像這些 範例:

on property:dev.mnt.blk.root=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.root}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.system=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.system}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.vendor=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.vendor}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.product=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.system_ext}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.oem=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.oem}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.data=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.data}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on early-fs:
    setprop sys.read_ahead_kb ${ro.read_ahead_kb.boot:-2048}

on property:sys.boot_completed=1
   setprop sys.read_ahead_kb ${ro.read_ahead_kb.bootcomplete:-128}