Android10 動態分區介紹

分區概覽

下面列出標準Android系統中存在的分區,供應商可能對分區進行增減和更改。需要注意的是,Android 9開始對分區結構增加新的技術(A/B 設備、system-as-root等),它們對分區作用有很大影響。

  • boot:包含通過 mkbootimg 組合在一起的kernel鏡像和 ramdisk。使能system-as-root後,該分區僅存放kernel鏡像。
  • system:主要包含 Android 框架。使能system-as-root後,該分區會包含原始 system.img 和 ramdisk.img 的合併內容。
  • recovery:用於存儲在 OTA 升級時使用的recovery系統。如果設備支持A/B更新,OTA升級可以通過ramdisk執行,該分區可以不需要。
  • cache:用於存儲臨時數據,OTA升級包也會下載到這個分區。如果設備使用 A/B 更新,則可以不要此分區。
  • misc:分區供recovery使用,存儲空間不能小於 4KB。
  • userdata:存儲用戶安裝的應用和數據。
  • metadata:如果設備被加密,則需要使用該分區,分區的存儲空間不能小於 16MB。
  • vendor:包含所有不可分發給 Android 開源項目 (AOSP) 的二進制文件。如果沒有專有信息,可以不要該分區。
  • radio:包含無線裝置映像。只有包含無線裝置的設備才需要此分區。
  • tos:用於存儲 Trusty 操作系統的二進制映像文件,僅在設備包含 Trusty 時使用。
  • product:用於存放產品專用的配置和應用,以便OEM定製自己的系統。Android 9 及更高版本支持該分區。product分區是對system分區的擴展,必須同時升級這兩個分區。
  • odm:用於ODM自定義自己的板級支持包。Android 10 開始支持該分區。odm分區是對vendor分區的擴展,必須同時升級這兩個分區。

Product分區

許多 OEM 會自定義 AOSP 系統映像,以實現自己的功能並滿足運營商的要求。不過,如果進行這類自定義,則無法針對多個軟件 SKU 使用單個系統映像。映像必須各不相同,才能支持不同的語言區域、運營商等自定義。如果使用單獨的 /product 分區來包含自定義項,則可以針對多個軟件 SKU 使用單個系統映像。

/product 分區包含以下組件:

  • 產品專用的系統屬性 (/product/build.prop)
  • 產品專用的 RRO (/product/overlay/*.apk)
  • 產品專用的應用 (/product/app/*.apk)
  • 產品專用的特權應用 (/product/priv-app/*.apk)
  • 產品專用的內容庫 (/product/lib/*)
  • 產品專用的 Java 庫 (/product/framework/*.jar)
  • 產品專用的 Android 框架系統配置(/product/etc/sysconfig/*/product/etc/permissions/*
  • 產品專用的媒體文件 (/product/media/audio/*)
  • 產品專用的 bootanimation 文件

Android 9 中的 /product 分區是 /system 分區的擴展。由於 /product/system 分區之間的 ABI 穩定性較弱,因此必須同時升級這兩者,而且 ABI 應基於系統 SDK。如果系統 SDK 不涵蓋 /product/system 之間的所有 API 表面,則 OEM 必須在這兩個分區之間維護自己的 ABI。

/product 分區和 /system 分區可以相互依賴。不過,在沒有 /product 分區的情況下,對通用系統映像 (GSI) 的測試必須能夠正常運行。/product 分區不能對 /vendor 分區有任何依賴。/product/vendor 分區之間不允許有任何直接交互。(這一規則將由 SEpolicy 強制執行。)

爲了實現新的Product分區,請添加以下的編譯標記:

  • BOARD_USES_PRODUCTIMAGE
  • BOARD_PRODUCTIMAGE_PARTITION_SIZE
  • BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE
  • PRODUCT_PRODUCT_PROPERTIES for /product/build.prop。這些必須在 $(call inherit-product path/to/device.mk) 內,例如 PRODUCT_PRODUCT_PROPERTIES += product.abc=ok

product 分區中安裝模塊時,請添加以下編譯標記:

  • Android.bp 中的 product_specific: true
  • Android.mk 中的 LOCAL_PRODUCT_MODULE := true

爲防止 /product 分區被惡意軟件篡改,您應該爲該分區啓用 Android 啓動時驗證 (AVB)(就像爲 /vendor/ 和 /system 分區啓用一樣)。要啓用 AVB,請添加以下編譯標記:

  • BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS

ODM 分區

原始設計製造商 (ODM)可能需要實現自己的內核模塊,或 替換或自定義 SoC 組件。 這就需要自定義SoC 供應商的板級支持包 (BSP)。之前 Android 版本,自定義BSP需要更改vendor映像,這就會阻止相同 SoC設備使用同一vendor映像。在 Android 10 中,系統構建了單獨的 /odm 分區,自定義行爲存放在/odm分區中,因而能夠針對多個硬件 SKU 使用單個供應商映像。

/odm 分區包含以下 ODM 專用組件(類似於 /vendor 分區),如下表所示。

ODM 專用組件 位置
可加載內核模塊 (LKM) /odm/lib/modules/*.ko
原生庫 /odm/lib[64]
HAL /odm/lib[64]/hw
SEPolicy /odm/etc/selinux
VINTF 對象數據 /odm/etc/vintf
init.rc 文件 /odm/etc/init
系統屬性 /odm/build.prop
運行時資源疊加層 (RRO) /odm/overlay/*.apk
應用 /odm/app/*.apk
特權應用 /odm/priv-app/*.apk
Java 庫 /odm/framework/*.jar
Android 框架系統配置 /odm/etc/sysconfig/*/odm/etc/permissions/*

odm 分區是 /vendor 分區的擴展。在考慮應用二進制接口 (ABI) 穩定性時,請記住以下架構:

build_sys_to_prod_odm_partitions.png

  • /odm/vendor 分區之間不具有 ABI 穩定性。必須同時升級這兩個分區。
  • /odm/vendor 分區可以相互依賴,但是在沒有 /odm 分區的情況下,/vendor 分區必須運行。
  • /odm/system 之間的 ABI 與 /vendor/system 之間的 ABI 相同。

/product 分區與 /vendor/odm 分區之間不允許有任何直接交互。(這一規則將由 SEpolicy 強制執行。)

要設置 /odm 分區,請添加以下構建標記:

  • BOARD_ODMIMAGE_PARTITION_SIZE(適用於固定分區大小)
  • PRODUCT_USE_DYNAMIC_PARTITIONSBOARD_ODMIMAGE_PARTITION_RESERVED_SIZE(適用於動態分區大小)
  • BOARD_ODMIMAGE_FILE_SYSTEM_TYPE 文件系統類型(用於 ODM 映像)
  • PRODUCT_ODM_PROPERTIES(適用於 /odm/build.prop
    $(call inherit-product path/to/device.mk) 中使用該標記,例如 PRODUCT_ODM_PROPERTIES += product.abc=ok

使用以下構建標記向 /odm 分區中安裝模塊:

  • Android.bp 中的 device_specific: true
  • Android.mk 中的 LOCAL_ODM_MODULE := true

要防止惡意軟件篡改 /odm 分區,請爲這些分區啓用 Android 啓動時驗證 (AVB)(就像爲 /vendor/system 分區啓用一樣)。要啓用 AVB,請添加構建標記 BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS

/product 分區的支持,讓您可以通過使用不同 product.img 映像來提供的多個軟件 SKU,並保證它們使用單個system映像。/odm 分區讓你可以在多個硬件SKU中使用同一vendor映像。這樣,整個系統變得很有層次。使用 /system 分區來託管通用代碼(這類代碼在許多軟件 SKU 之間共享),使用 /vendor 分區來託管 SoC 專屬 BSP 代碼(這類代碼基於指定 SoC 在多臺設備之間共享)。

分區佈局的變化

system-as-root

system-as-root的含義是使用system分區做爲根目錄。Android 9默認使用 system-as-root,但Android 10在啓動是依然會使用ramdisk,之後會將system分區切換爲root目錄。

  • Android 9BOARD_BUILD_SYSTEM_ROOT_IMAGE 設爲 true時,會強制編譯將根文件系統合併到 system.img 中,然後將 system.img 作爲根文件系統 (rootfs) 進行裝載。此配置對於搭載 Android 9 的設備是強制性的,但對於升級到 Android 9 及搭載較低 Android 版本的設備是可選的。
  • Android 10:編譯始終將 $TARGET_SYSTEM_OUT$TARGET_ROOT_OUT 合併到 system.img 中;此配置是搭載 Android 10 的所有設備的默認行爲。boot.img在第一階段依然做爲根文件系統加載,之後執行/initsystem.img加載到/system上。然後執行切換根操作將裝載從 /system 移動到 /,裝載完成後ramdisk 的內容將會釋放。

要將非 A/B 設備在 Android 9 上更新爲使用 system-as-root,您必須更新 boot.imgsystem.img 的分區架構、設置 dm-verity,並移除特定於設備的根文件夾中的任何啓動依賴項。

  • 更新分區boot.img中將原有的ramdisk.img去除,僅包含正常啓動內核。 system.img 中增加ramdisk.img的內容,就是說rootfs會合併到 system.img 中。
  • 設置 dm-verity:在 system-as-root 中,內核必須使用 dm-verity 在 /(裝載點)下裝載 system.img。AOSP 支持 vboot 1.0vboot 2.0 的dm-verity 實現。
  • 不使用設備特定的根文件夾:使用 system-as-root 時,在設備上刷寫常規系統映像 (GSI) 之後(以及在運行供應商測試套件測試之前),任何通過 BOARD_ROOT_EXTRA_FOLDERS 添加的特定於設備的根文件夾都會消失,因爲整個根目錄內容已被 system-as-root GSI 取代。爲避免此問題,請勿使用 BOARD_ROOT_EXTRA_FOLDERS 來添加特定於設備的根文件夾。如果需要指定設備特定的裝載點,請使用 /mnt/vendor/<mount point>

A/B 設備

Android 9開始,系統支持A/B設備。因此可以在分區上將設備分爲兩種,

  • A/B設備:系統在磁盤空間上劃分爲兩個Slot:Slot A和Slot B。需要獨立升級的分區(boot、recovery、system、radio、vendor等)在每個Slot上都會存在一個分區。例如boot會存在boot_a和boot_b兩個分區,一個做爲當前工作分區,另一個做爲備份分區。OTA升級時,會在備份的Slot上更新,更新成功後將當前工作Slot進行切換,類似一種乒乓工作模式。
  • 非A/B設備:之前Android系統的分區方式。OTA升級時在recovery模式下通過cache分區進行更新。存在的問題是不夠靈活且無法保證安全。

對於非A/B設備,分區佈局在各Android版本間的佈局也是不同的。Android 9中使用system-as-root,沒有了ramdisk。Android 10使用類似 ramdisk + system-as-root 的方式,ramdisk中包含第一階段 Init 和 fstab 。第一階段 Init 是位於 /init 的靜態可執行文件,Init會將 system.img 作爲 /system 進行裝載,然後執行切換根操作將裝載從 /system 移動到 /。裝載完成後,ramdisk 的內容將會釋放。

下表總結了非A/B設備在各Android版本上的分區佈局。

partition_noAB.png

對於A/B設備,在Android 9之前時不支持的,Android 9和Android 10間的主要區別是boot分區中ramdisk的差別。Android 10上的第一階段Init使用了recovery ramdisk 中的 /init。設備首先將根切換到 /first_stage_ramdisk,以便從環境中移除recovery組件,然後裝載system.img 和釋放ramdisk。如果內核命令行中存在 androidboot.force_normal_boot=1,則設備會正常啓動(啓動到 Android)而不是啓動到恢復模式。

下表總結了A/B設備在各Android版本上的分區佈局。

partition_AB.png

動態分區

Android 10支持了動態分區,這是一種可以通過無線下載 (OTA) 更新來創建、銷燬分區或調整分區大小的用戶空間分區系統。系統爲設備分配一個 super 分區,其中的子分區可動態地調整大小。單個分區映像不再需要爲將來的 OTA 預留空間,super 中剩餘的可用空間可用於所有動態分區。

動態分區是使用 Linux 內核中的 dm-linear device-mapper 模塊實現的。super 分區中包含列出 super 內每個動態分區的名稱和塊範圍的元數據。在第一階段 init 執行期間,系統會解析和驗證這些元數據,並創建虛擬塊設備來表示每個動態分區。

應用 OTA 時,系統會根據需要自動創建/刪除動態分區,或者調整動態分區的大小。若是 A/B 設備,將存在兩個元數據副本,而更改僅會應用到表示目標槽位的副本。 由於動態分區是在用戶空間中實現的,因此引導加載程序所需的分區不能是動態的。例如,引導加載程序會讀取 bootdtbovbmeta,因此這些分區必須仍保持爲物理分區。

在新設備上實現動態分區

分區更改

對於搭載 Android 10 的設備,請創建名爲 super 的分區。super 分區在內部處理 A/B 槽位,因此 A/B 設備不需要單獨的 super_asuper_b 分區。引導加載程序未使用的所有隻讀 AOSP 分區都必須是動態的,並且必須從 GUID 分區表 (GPT) 中移除。供應商專用分區則可以不是動態的,並且可以放在 GPT 中。

支持的動態分區包括:system、vendor、product、system、odm。下圖顯示轉換爲動態分區前後變化,

dynamic_partitions_table.png

分區對齊

如果 super 分區未正確對齊,device-mapper 模塊的運行效率可能會降低。super 分區必須與最小 I/O 請求大小保持一致,該大小由塊層決定。默認情況下,編譯系統(通過生成 super 分區映像的 lpmake)認爲對每個動態分區應用 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,並將 vendorproductodm 放入 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 / 2 - 開銷
  • 對於非 A/B 設備和改造的 A/B 設備,所有組的大小上限總和必須爲:
    BOARD_SUPER_PARTITION_SIZE - 開銷
  • 在編譯時,更新組中每個分區的映像大小總和不得超過組的大小上限。
  • 在計算時需要扣除開銷,因爲要考慮元數據、對齊等。合理的開銷是 4 MiB,但您可以根據設備的需要選擇更大的開銷。

調整動態分區的大小

在採用動態分區之前,會爲分區分配富餘的空間,以確保它們有足夠的空間滿足將來的更新。使用動態分區後,你但可以在 OTA 期間增加分區容量。確保分區在合理使用的條件下,分區儘可能減少可用空間。對於只讀的 ext4 映像,如果未指定分區大小,則編譯系統會自動分配最小的空間。編譯系統會適配映像,以儘可能減少文件系統中的未使用空間。這樣可以確保設備不會浪費可用於 OTA 的空間。

此外,通過啓用塊級重複信息刪除,可以進一步壓縮 ext4 映像。要啓用此功能,請使用以下配置:

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 的未使用空間。

System-as-root 更改

具有動態分區的設備(無論是搭載動態分區還是改造動態分區)不得使用純粹 system-as-root。因爲Linux 內核無法解讀 super 分區,也就無法裝載 systemsystem 需要 ramdisk 中的第一階段 init 裝載。

請勿設置 BOARD_BUILD_SYSTEM_ROOT_IMAGE。在 Android 10 中,BOARD_BUILD_SYSTEM_ROOT_IMAGE 標記僅用於區分系統是由內核裝載,還是由 ramdisk 中的第一階段 init 裝載。如果將 BOARD_BUILD_SYSTEM_ROOT_IMAGE 設置爲 true,則當 PRODUCT_USES_DYNAMIC_PARTITIONS 也爲 true 時,就會導致編譯錯誤。

BOARD_USES_RECOVERY_AS_BOOT 設置爲 true 時,recovery映像將被編譯爲 boot.img,其中包含recovery的 ramdisk。以前,引導加載程序使用 skip_initramfs 內核命令行參數來決定啓動到哪種模式。而對於 Android 10 設備,引導加載程序不能向內核命令行傳遞 skip_initramfs,而應傳遞 androidboot.force_normal_boot=1 來跳過recovery並正常啓動 Android。

AVB 配置更改

使用 Android 啓動時驗證 2.0 時,如果設備未使用鏈式分區描述符,則不需要進行更改。但如果使用了鏈式分區,並且其中一個已驗證分區是動態分區,則需要進行更改。

下面的設備配置示例鏈接 systemvendor 分區所對應的 vbmeta

BOARD_AVB_VENDOR_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
BOARD_AVB_VENDOR_ALGORITHM := SHA256_RSA2048
BOARD_AVB_VENDOR_ROLLBACK_INDEX := (PLATFORMSECURITYPATCHTIMESTAMP)BOARDAVBVENDORROLLBACKINDEXLOCATION:=1"title=""dataoriginaltitle=""></span></div></div><preclass="makefilehljs"><codeclass="makefile">BOARDAVBSYSTEMKEYPATH:=external/avb/test/data/testkeyrsa2048.pemBOARDAVBSYSTEMALGORITHM:=SHA256RSA2048BOARDAVBSYSTEMROLLBACKINDEX:=<spanclass="hljsvariable">(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_AVB_VENDOR_ROLLBACK_INDEX_LOCATION := 1" title="" data-original-title="複製"></span> </div> </div><pre class="makefile hljs"><code class="makefile">BOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048 BOARD_AVB_SYSTEM_ROLLBACK_INDEX := <span class="hljs-variable">(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

使用該配置,引導加載程序可以在 systemvendor 分區的末尾找到 vbmeta 頁腳。由於這兩個分區對引導加載程序不再可見(它們位於 super 中),因此需要進行兩項更改。

  • 在設備的分區表中添加 vbmeta_systemvbmeta_vendor 分區。若是 A/B 設備,請添加 vbmeta_system_avbmeta_system_bvbmeta_vendor_avbmeta_vendor_b。如果添加上述一個或多個分區,則它們的大小應與 vbmeta 分區相同。
  • 通過添加 VBMETA_來重命名配置標記,並指定鏈接擴展到的分區:

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 := (PLATFORMSECURITYPATCHTIMESTAMP)BOARDAVBVBMETAVENDORROLLBACKINDEXLOCATION:=1"title=""dataoriginaltitle=""></span></div></div><preclass="makefilehljs"><codeclass="makefile">BOARDAVBVBMETASYSTEM:=systemBOARDAVBVBMETASYSTEMKEYPATH:=external/avb/test/data/testkeyrsa2048.pemBOARDAVBVBMETASYSTEMALGORITHM:=SHA256RSA2048BOARDAVBVBMETASYSTEMROLLBACKINDEX:=<spanclass="hljsvariable">(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION := 1" title="" data-original-title="複製"></span> </div> </div><pre class="makefile hljs"><code class="makefile">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 := <span class="hljs-variable">(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。”

    注意:以前,在 AvbOps 中實現 get_size_of_partition 是可選的。在此更改之後,則變成了必需的,並且引導加載程序也必須實現此函數。

內核命令行更改

必須在內核命令行中添加新參數 androidboot.boot_devicesinit 使用它來啓用 /dev/block/by-name 符號鏈接。該參數應該是由 ueventd 創建的底層 by-name 符號鏈接(即 /dev/block/platform/device-path/by-name/partition-name)中的設備路徑部分。

例如,如果 super 分區的 by-name 符號鏈接是 /dev/block/platform/soc/100000.ufshc/by-name/super,則您可以在 BoardConfig.mk 文件中按以下方式添加該命令行參數:

BOARD_KERNEL_CMDLINE += androidboot.boot_devices=soc/100000.ufshc

fstab 更改

設備樹和設備樹疊加層不得包含 fstab 條目。使用將成爲 ramdisk 一部分的 fstab 文件。

必須對邏輯分區的 fstab 文件進行以下更改:

  • fs_mgr 標記字段中必須包含 logical 標記和 first_stage_mount 標記。first_stage_mount 標記在 Android 10 中引入,指示將在第一階段裝載分區。
  • 分區可以將 avb=vbmeta partition name 作爲 fs_mgr 標記進行指定,隨後該指定的 vbmeta 分區將先由第一階段 init 初始化,然後再嘗試裝載設備。
  • dev 字段必須是分區名稱。

以下 fstab 條目按照上述規則設置 system、vendor 和 product 邏輯分區。

#<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
注意:對於非 A/B 設備,請勿包含 slotselect

將 fstab 文件複製到第一階段 ramdisk。

SELinux 更改

必須使用 super_block_device 標籤標記 super 分區塊設備。例如,如果 super 分區的 by-name 符號鏈接是 /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

引導加載程序(或任何非用戶空間刷寫工具)無法理解動態分區,因此無法對其進行刷寫。爲解決此問題,設備必須使用 fastboot 協議的用戶空間實現,稱爲 fastbootd。

adb remount

對於使用 eng 或 userdebug 編譯的開發者,adb remount 對於實現快速迭代非常有用。動態分區給 adb remount 造成了問題,因爲每個文件系統中都不再有空閒空間。爲解決此問題,設備可以啓用 overlayfs。只要 super 分區中有空閒空間,adb remount 就會自動創建臨時的動態分區,並使用 overlayfs 進行寫入。該臨時分區的名稱爲 scratch,因此請勿將此名稱用於其他分區。

升級 Android 設備

如果您想將設備升級到 Android 10,並且希望在 OTA 中包含動態分區支持,則不需要更改內置分區表。需要進行一些額外的配置。

設備配置更改

要改造動態分區,請在 device.mk 中添加以下標記:

PRODUCT_USE_DYNAMIC_PARTITIONS := true
PRODUCT_RETROFIT_DYNAMIC_PARTITIONS := true

板級配置更改

您需要設置以下板級變量:

  • BOARD_SUPER_PARTITION_BLOCK_DEVICES 設置爲用於存儲動態分區區段的塊設備的列表。這是設備上現有物理分區的名稱列表。
  • 通過 BOARD_SUPER_PARTITION_partition_DEVICE_SIZE 分別設置 BOARD_SUPER_PARTITION_BLOCK_DEVICES 中的每個塊設備的大小。這是設備上現有物理分區的大小列表。在現有的板級配置中,這通常爲 BOARD_partitionIMAGE_PARTITION_SIZE
  • BOARD_SUPER_PARTITION_BLOCK_DEVICES 中的所有分區取消現有的 BOARD_partitionIMAGE_PARTITION_SIZE 設置。
  • 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

例如,如果設備已經有 system 和 vendor 分區,並且您希望在更新期間將它們轉換爲動態分區並添加新的 product 分區,請設置以下板級配置:

Rename BOARD_SYSTEMIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE.

BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE :=

Rename BOARD_VENDORIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE

BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE :=

This is BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE + BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE

BOARD_SUPER_PARTITION_SIZE :=

Configuration for dynamic partitions. For example:

BOARD_SUPER_PARTITION_GROUPS := group_foo
BOARD_GROUP_FOO_SIZE :=
BOARD_GROUP_FOO_PARTITION_LIST := system vendor product" title="" data-original-title=“複製”>

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 屬性標記 super 分區塊設備。例如,如果設備已經有 systemvendor 分區,並且您希望將它們作爲存儲動態分區區段的塊設備,則應將它們的 by-name 符號鏈接標記爲 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;

出廠映像

對於搭載動態分區支持的設備,請勿使用用戶空間 fastboot 來刷寫出廠映像,因爲啓動到用戶空間比其他刷寫方法慢。

爲了解決此問題,make dist 現在會編譯一個額外的 super.img 映像,該映像可以直接刷寫到 super 分區。它會自動捆綁邏輯分區的內容,這意味着除了 super 分區元數據外,它還包含 system.imgvendor.img 等。此映像可以直接刷寫到 super 分區,無需進行任何其他加工,也無需使用 fastbootd。編譯之後,super.img 會存放在 ${ANDROID_PRODUCT_OUT} 中。

對於搭載動態分區的 A/B 設備,super.img 包含 A 槽位中的映像。直接刷寫 super 映像後,在重啓設備之前將槽位 A 標記爲可啓動。

對於改造設備,make dist 會編譯一組可以直接刷寫到相應物理分區的 super_*.img 映像。例如,當 BOARD_SUPER_PARTITION_BLOCK_DEVICES 是“system vendor”時,make dist 會編譯 super_system.imgsuper_vendor.img。系統會將這些映像存放在 target_files.zip 的 OTA 文件夾中。

設備映射的存儲設備調整

動態分區可以容納許多不確定的設備映射對象。它們可能不會像預期的那樣全部實例化,因此您必須跟蹤所有mounts,並更新所有底層存儲涉筆關聯分區的Android屬性。

Init中有一個機制可以跟蹤mounts並異步更新Android屬性。但它花費的時間不能保證在特定時間內,因此必須爲所有on-property的觸發提供足夠的時間。這些屬性爲dev.mnt.blk.<partition>,其中<partition>rootsystemdatavendor。每個屬性都與存儲設備名稱關聯,如以下所示:

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]" title="" data-original-title=“複製”>

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將變爲活動狀態,並且屬性值將開始更新。但是,由於屬性觸發直到late-init時才處於活動狀態,因此在初始啓動階段不能使用它們來處理rootsystem、或vendor。可能在init.rc腳本中early-fs階段(當各種守護進程和工具啓動時)可以修改read-ahead-kb之前,內核默認的read-ahead-kb已經足夠。因此,Google建議使用on-property特性和init.rc-controlled屬性(如sys.read_ahead_kb)來處理時序操作和防止競爭,如以下示例所示:

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 KaTeX parse error: Expected 'EOF', got '&' at position 193: …mnt.blk.root=* &̲amp;&amp; prope…{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}z`

參考文檔

深入理解overlayfs(二):使用與原理分析
Android 分區
Android 實現動態分區

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章