分區概覽
下面列出標準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) 穩定性時,請記住以下架構:
-
/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_PARTITIONS
和BOARD_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 9:
BOARD_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
在第一階段依然做爲根文件系統加載,之後執行/init
將system.img
加載到/system
上。然後執行切換根操作將裝載從/system
移動到/
,裝載完成後ramdisk 的內容將會釋放。
要將非 A/B 設備在 Android 9 上更新爲使用 system-as-root,您必須更新 boot.img
和 system.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.0
和vboot 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版本上的分區佈局。
對於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版本上的分區佈局。
動態分區
Android 10支持了動態分區,這是一種可以通過無線下載 (OTA) 更新來創建、銷燬分區或調整分區大小的用戶空間分區系統。系統爲設備分配一個 super
分區,其中的子分區可動態地調整大小。單個分區映像不再需要爲將來的 OTA 預留空間,super
中剩餘的可用空間可用於所有動態分區。
動態分區是使用 Linux 內核中的 dm-linear device-mapper 模塊實現的。super
分區中包含列出 super
內每個動態分區的名稱和塊範圍的元數據。在第一階段 init
執行期間,系統會解析和驗證這些元數據,並創建虛擬塊設備來表示每個動態分區。
應用 OTA 時,系統會根據需要自動創建/刪除動態分區,或者調整動態分區的大小。若是 A/B 設備,將存在兩個元數據副本,而更改僅會應用到表示目標槽位的副本。 由於動態分區是在用戶空間中實現的,因此引導加載程序所需的分區不能是動態的。例如,引導加載程序會讀取 boot
、dtbo
和 vbmeta
,因此這些分區必須仍保持爲物理分區。
在新設備上實現動態分區
分區更改
對於搭載 Android 10 的設備,請創建名爲 super
的分區。super
分區在內部處理 A/B 槽位,因此 A/B 設備不需要單獨的 super_a
和 super_b
分區。引導加載程序未使用的所有隻讀 AOSP 分區都必須是動態的,並且必須從 GUID 分區表 (GPT) 中移除。供應商專用分區則可以不是動態的,並且可以放在 GPT 中。
支持的動態分區包括:system、vendor、product、system、odm。下圖顯示轉換爲動態分區前後變化,
分區對齊
如果 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_SIZE
和 BOARD_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
,並將 vendor
、product
和 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
/ 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
分區,也就無法裝載 system
。 system
需要 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 時,如果設備未使用鏈式分區描述符,則不需要進行更改。但如果使用了鏈式分區,並且其中一個已驗證分區是動態分區,則需要進行更改。
下面的設備配置示例鏈接 system
和 vendor
分區所對應的 vbmeta
。
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_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
使用該配置,引導加載程序可以在 system
和 vendor
分區的末尾找到 vbmeta 頁腳。由於這兩個分區對引導加載程序不再可見(它們位於 super
中),因此需要進行兩項更改。
- 在設備的分區表中添加
vbmeta_system
和vbmeta_vendor
分區。若是 A/B 設備,請添加vbmeta_system_a
、vbmeta_system_b
、vbmeta_vendor_a
和vbmeta_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 := (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,請包含以下補丁程序:
- 818cf56740775446285466eda984acedd4baeac0 -“libavb:僅在 cmdline 需要時才查詢分區 GUID。”
- 5abd6bc2578968d24406d834471adfd995a0c2e9 -”允許不存在 system 分區”
- 9ba3b6613b4e5130fa01a11d984c6b5f0eb3af05 -“修復 AvbSlotVerifyData->cmdline 可能爲 NULL”
如果使用鏈式分區,請包含一個額外的補丁程序:
-
49936b4c0109411fdd38bd4ba3a32a01c40439a9 -“libavb:支持在分區開頭存放 vbmeta blob。”
注意:以前,在
AvbOps
中實現get_size_of_partition
是可選的。在此更改之後,則變成了必需的,並且引導加載程序也必須實現此函數。
內核命令行更改
必須在內核命令行中添加新參數 androidboot.boot_devices
。init
使用它來啓用 /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_GROUPS
、BOARD_group_SIZE
和BOARD_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 分區塊設備。例如,如果設備已經有 system
和 vendor
分區,並且您希望將它們作爲存儲動態分區區段的塊設備,則應將它們的 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.img
、vendor.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.img
和 super_vendor.img
。系統會將這些映像存放在 target_files.zip
的 OTA 文件夾中。
設備映射的存儲設備調整
動態分區可以容納許多不確定的設備映射對象。它們可能不會像預期的那樣全部實例化,因此您必須跟蹤所有mounts,並更新所有底層存儲涉筆關聯分區的Android屬性。
Init中有一個機制可以跟蹤mounts並異步更新Android屬性。但它花費的時間不能保證在特定時間內,因此必須爲所有on-property
的觸發提供足夠的時間。這些屬性爲dev.mnt.blk.<partition>
,其中<partition>
爲root
、system
、data
或vendor
。每個屬性都與存儲設備名稱關聯,如以下所示:
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
時才處於活動狀態,因此在初始啓動階段不能使用它們來處理root
、system
、或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;& 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`