自 2025 年 3 月 27 日起,我們建議您使用 android-latest-release
而非 aosp-main
建構及貢獻 AOSP。詳情請參閱「Android 開放原始碼計畫變更」。
適用於動態分區的非 A/B 裝置的 OTA
透過集合功能整理內容
你可以依據偏好儲存及分類內容。
Android 10 支援動態分割區,這是一種使用者空間分割系統,可在無線 (OTA) 更新期間建立、調整大小及刪除分割區。
本頁說明 OTA 用戶端如何在非 A/B 裝置更新期間調整動態分區大小。
對於非 A/B 裝置,系統會使用更新套件中的 updater
套用動態分區的 OTA 更新。
更新啟動裝置
本節適用於啟動時支援動態分割區的非 A/B 裝置,這些裝置會從 Android 10 升級至更高版本。
產生更新套件
OTA 更新套件是由 build/make/tools/releasetools
底下的 ota_from_target_files
指令碼產生。根據預設,指令碼會產生套件,用於更新 system
和 vendor
分區。如果有其他動態區隔,例如 product
、product_services
或 odm
,則必須在裝置專屬程式碼中產生更新。
如要產生更新,請在擴充的 Python 模組中實作 FullOTA_GetBlockDifferences()
和 IncrementalOTA_GetBlockDifferences()
。這兩個函式會傳回 BlockDifference
物件清單,每個物件都會描述要套用至分區的更新修補程式。由這兩個函式傳回的分區不應在其他地方手動修改或驗證,例如在 *_InstallBegin()
或 *_InstallEnd()
中。
更新產生範例:
# device/yoyodyne/tardis/releasetools.py
import os
from common import BlockDifference, EmptyImage, GetUserImage
# The joined list of user image partitions of source and target builds.
# - Items should be added to the list if new dynamic partitions are added.
# - Items should not be removed from the list even if dynamic partitions are
# deleted. When generating an incremental OTA package, this script needs to
# know that an image is present in source build but not in target build.
USERIMAGE_PARTITIONS = [
"product",
"odm",
]
def GetUserImages(input_tmp, input_zip):
return {partition: GetUserImage(partition, input_tmp, input_zip)
for partition in USERIMAGE_PARTITIONS
if os.path.exists(os.path.join(input_tmp,
"IMAGES", partition + ".img"))}
def FullOTA_GetBlockDifferences(info):
images = GetUserImages(info.input_tmp, info.input_zip)
return [BlockDifference(partition, image)
for partition, image in images.items()]
def IncrementalOTA_GetBlockDifferences(info):
source_images = GetUserImages(info.source_tmp, info.source_zip)
target_images = GetUserImages(info.target_tmp, info.target_zip)
# Use EmptyImage() as a placeholder for partitions that will be deleted.
for partition in source_images:
target_images.setdefault(partition, EmptyImage())
# Use source_images.get() because new partitions are not in source_images.
return [BlockDifference(partition, target_image, source_images.get(partition))
for partition, target_image in target_images.items()]
更新流程
在幕後,系統會將下列函式新增至 edify 指令碼:
unmap_partition(name)
- 如果已對分割區進行對應,則取消對應,否則不執行任何操作。
- 成功時傳回字串
t
,失敗時傳回空字串。
map_partition(name)
- 如果尚未對應分割區,請對應分割區。
-
在成功時傳回已對應的區塊裝置的絕對路徑,或在失敗時傳回空字串。
update_dynamic_partitions(op_list)
-
在動態分區中繼資料上套用指定的作業清單,並視需要取消對分區的對應。
-
成功時傳回
t
,失敗時傳回空白字串。
update_dynamic_partitions
的 op_list
引數會指向更新套件中的檔案。檔案中的每一行都會指定一項作業。如果任何作業失敗,update_dynamic_partitions
會立即傳回空字串。這些作業如下:
resize partition-name size
- 取消對分區的對應,然後將分區大小調整為 size。
remove partition_name
add partition-name group-name
- 在指定群組中新增分區。
-
如果群組不存在或分區已存在,則中止。
move partition-name group-name
- 將分區移至指定群組。
-
如果群組或分區不存在,則中止。
-
add_group group-name maximum-size
- 新增群組,並指定名稱和最大大小。
- 如果群組已存在,則中止。
-
maximum_size 為 0 表示群組中的分區沒有大小限制。您必須進行額外測試,確保群組中的分區不會超過裝置的可用空間。
-
resize_group group-name maximum-size
- 將群組大小調整為指定的最大值。
- 如果群組不存在,則中止。
-
maximum_size 為 0 表示群組中的分區沒有大小限制。您必須進行額外測試,確保群組中的分區不會超過裝置的可用空間。
remove_group group-name
remove_all_groups
- 從裝置對應器中取消對應所有分區。
- 移除所有分區和群組。
增量 OTA
漸進式 OTA 更新會使用以下邏輯:
-
縮小分割區/刪除分割區/將分割區移出群組 (以便有足夠空間縮小群組)
-
縮小群組 (以便有足夠空間擴充群組)
-
擴充群組 (以便有足夠空間可擴充/新增分區)
-
擴充分區/新增分區/將分區移至新群組
具體來說,update-script
是根據以下邏輯產生:
for each shrinking partition:
block_image_update(map_partition(name), …)
update_dynamic_partitions(op_list)
for each growing / adding partition:
block_image_update(map_partition(name), …)
update_dynamic_partitions
的 op_list
檔案會使用以下邏輯產生:
for each deleting partition:
remove
for each partition that changes groups:
move to "default"
for each shrinking partition:
resize
for each shrinking / removing group:
resize_group / remove_group
for each growing / adding group:
resize_group / add_group
for each adding partition:
add
for each growing / adding partition:
resize
for each partition that changes groups:
move to target group
完整 OTA
完整 OTA 更新會使用以下邏輯:
- 刪除所有現有群組和分割區
- 新增群組
- 新增分區
具體來說,update-script
是根據以下邏輯產生:
update_dynamic_partitions(op_list)
for each adding partition:
block_image_update(map_partition(name), …)
update_dynamic_partitions
的 op_list
檔案會使用以下邏輯產生:
remove_all_groups
for each adding group:
add_group
for each adding partition:
add
for each adding partition:
resize
這個頁面中的內容和程式碼範例均受《內容授權》中的授權所規範。Java 與 OpenJDK 是 Oracle 和/或其關係企業的商標或註冊商標。
上次更新時間:2025-07-27 (世界標準時間)。
[[["容易理解","easyToUnderstand","thumb-up"],["確實解決了我的問題","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["缺少我需要的資訊","missingTheInformationINeed","thumb-down"],["過於複雜/步驟過多","tooComplicatedTooManySteps","thumb-down"],["過時","outOfDate","thumb-down"],["翻譯問題","translationIssue","thumb-down"],["示例/程式碼問題","samplesCodeIssue","thumb-down"],["其他","otherDown","thumb-down"]],["上次更新時間:2025-07-27 (世界標準時間)。"],[],[],null,["# OTA for non-A/B devices with dynamic partitions\n\nAndroid 10 supports\n[dynamic partitions](/docs/core/ota/dynamic_partitions), a userspace partitioning\nsystem that can create, resize, and destroy partitions during over-the-air (OTA) updates.\n\nThis page describes how OTA clients resize dynamic partitions during an update for\nnon-A/B devices.\n\n\nFor non-A/B devices, the OTA update for dynamic partitions is applied\nusing the `updater` inside the update package.\n\nUpdate launch devices\n---------------------\n\n\nThis section applies to non-A/B devices that launch with dynamic\npartitions support; these devices upgrade from Android\n10 to higher releases.\n\n### Generate update packages\n\n\nOTA update packages are generated by the\n`ota_from_target_files` script, located under\n`build/make/tools/releasetools`. By default, the script\ngenerates a package that updates the `system` and\n`vendor` partitions. If there are additional dynamic\npartitions, such as `product`,\n`product_services`, or `odm`, their\nupdates must be generated in\n[device-specific\ncode](/docs/core/ota/nonab/device_code#ota-package-generation).\n\n\nTo generate updates, in the extended Python module, implement\n`FullOTA_GetBlockDifferences()` and\n`IncrementalOTA_GetBlockDifferences()`. These two\nfunctions return a list of `BlockDifference` objects,\neach describing the update patch that would be applied on a\npartition. Partitions returned by these two functions shouldn't be\nmodified manually or verified elsewhere, for example in\n`*_InstallBegin()` or `*_InstallEnd()`.\n\n\nExample of an update generation: \n\n```python\n# device/yoyodyne/tardis/releasetools.py\n\nimport os\nfrom common import BlockDifference, EmptyImage, GetUserImage\n\n# The joined list of user image partitions of source and target builds.\n# - Items should be added to the list if new dynamic partitions are added.\n# - Items should not be removed from the list even if dynamic partitions are\n# deleted. When generating an incremental OTA package, this script needs to\n# know that an image is present in source build but not in target build.\nUSERIMAGE_PARTITIONS = [\n \"product\",\n \"odm\",\n]\n\ndef GetUserImages(input_tmp, input_zip):\n return {partition: GetUserImage(partition, input_tmp, input_zip)\n for partition in USERIMAGE_PARTITIONS\n if os.path.exists(os.path.join(input_tmp,\n \"IMAGES\", partition + \".img\"))}\n\ndef FullOTA_GetBlockDifferences(info):\n images = GetUserImages(info.input_tmp, info.input_zip)\n return [BlockDifference(partition, image)\n for partition, image in images.items()]\n\ndef IncrementalOTA_GetBlockDifferences(info):\n source_images = GetUserImages(info.source_tmp, info.source_zip)\n target_images = GetUserImages(info.target_tmp, info.target_zip)\n\n # Use EmptyImage() as a placeholder for partitions that will be deleted.\n for partition in source_images:\n target_images.setdefault(partition, EmptyImage())\n\n # Use source_images.get() because new partitions are not in source_images.\n return [BlockDifference(partition, target_image, source_images.get(partition))\n for partition, target_image in target_images.items()]\n```\n\n### Update flow\n\n\nBehind the scenes, the following functions are added to the edify\nscript:\n\n- `unmap_partition(name)`\n - Unmap the partition if mapped, otherwise do nothing.\n - Return the string `t` on success, or an empty string on failure.\n- `map_partition(name)`\n - Map the partition if not already mapped.\n - Return the absolute path of the mapped block device on success, or an empty string on failure.\n- `update_dynamic_partitions(op_list)`\n - Apply the given operation list on dynamic partition metadata, unmapping partitions if necessary.\n - Return `t` on success, or an empty string on failure.\n\n\nThe `op_list` argument to\n`update_dynamic_partitions` points to a file in the\nupdate package. Each line in the file specifies an operation. If any\noperation fails, `update_dynamic_partitions` immediately\nreturns an empty string. The operations are:\n\n- `resize `\u003cvar translate=\"no\"\u003epartition-name\u003c/var\u003e` `\u003cvar translate=\"no\"\u003esize\u003c/var\u003e\n - Unmap the partition, then resize it to \u003cvar translate=\"no\"\u003esize\u003c/var\u003e.\n- `remove `\u003cvar translate=\"no\"\u003epartition_name\u003c/var\u003e\n - Unmap the partition, then remove it.\n- `add `\u003cvar translate=\"no\"\u003epartition-name\u003c/var\u003e` `\u003cvar translate=\"no\"\u003egroup-name\u003c/var\u003e\n - Add a new partition to the specified group.\n - Abort if the group doesn't exist or if the partition already exists.\n- `move `\u003cvar translate=\"no\"\u003epartition-name\u003c/var\u003e` `\u003cvar translate=\"no\"\u003egroup-name\u003c/var\u003e\n - Move the partition to the specified group.\n - Abort if the group doesn't exist or partition doesn't exist.\n- `add_group `\u003cvar translate=\"no\"\u003egroup-name\u003c/var\u003e` `\u003cvar translate=\"no\"\u003emaximum-size\u003c/var\u003e\n - Add a group with the given name and maximum size.\n - Abort if the group already exists.\n - A \u003cvar translate=\"no\"\u003emaximum_size\u003c/var\u003e of 0 means there are no size limits on partitions in the group. Additional testing is required to ensure that partitions in the group don't exceed the available space on the device.\n- `resize_group `\u003cvar translate=\"no\"\u003egroup-name\u003c/var\u003e` `\u003cvar translate=\"no\"\u003emaximum-size\u003c/var\u003e\n - Resize the group to the given maximum size.\n - Abort if the group doesn't exist.\n - A \u003cvar translate=\"no\"\u003emaximum_size\u003c/var\u003e of 0 means there are no size limits on partitions in the group. Additional testing is required to ensure that partitions in the group don't exceed the available space on the device.\n- `remove_group `\u003cvar translate=\"no\"\u003egroup-name\u003c/var\u003e\n - Remove a group.\n - Abort if there are partitions in the group.\n- `remove_all_groups`\n - Unmap all partitions from the device mapper.\n - Remove all partitions and groups.\n\n#### Incremental OTA\n\n\nIncremental OTA updates use the following logic:\n\n1. Shrink partitions/delete partitions/move partitions out of group (so that there's enough space to shrink groups)\n2. Shrink groups (so that there's enough space to grow groups)\n3. Grow groups (so that we have enough space to grow/add partitions)\n4. Grow partitions/add partitions/move partitions to new group\n\n\nIn detail, `update-script` is generated with this\nlogic: \n\n```scdoc\nfor each shrinking partition:\n block_image_update(map_partition(name), …)\n\nupdate_dynamic_partitions(op_list)\n\nfor each growing / adding partition:\n block_image_update(map_partition(name), …)\n```\n\n\nThe `op_list` file for\n`update_dynamic_partitions` is generated with this\nlogic: \n\n```scdoc\nfor each deleting partition:\n remove\nfor each partition that changes groups:\n move to \"default\"\nfor each shrinking partition:\n resize\nfor each shrinking / removing group:\n resize_group / remove_group\nfor each growing / adding group:\n resize_group / add_group\nfor each adding partition:\n add\nfor each growing / adding partition:\n resize\nfor each partition that changes groups:\n move to target group\n```\n\n#### Full OTA\n\n\nFull OTA updates use the following logic:\n\n1. Delete all existing groups and partitions\n2. Add groups\n3. Add partitions\n\n\nIn detail, `update-script` is generated with this\nlogic: \n\n```scdoc\nupdate_dynamic_partitions(op_list)\n\nfor each adding partition:\n block_image_update(map_partition(name), …)\n```\n\n\nThe `op_list` file for\n`update_dynamic_partitions` is generated with this\nlogic: \n\n```scdoc\nremove_all_groups\nfor each adding group:\n add_group\nfor each adding partition:\n add\nfor each adding partition:\n resize\n```"]]