動的パーティションを使用する非 A/B デバイス向け OTA

Android 10 は、無線(OTA)アップデート時にパーティションの作成、サイズ変更、破棄を行えるユーザー空間パーティショニング システムである動的パーティションをサポートしています。

このページでは、OTA クライアントが非 A/B デバイスのアップデート時に動的パーティションのサイズを変更する方法について説明します。

非 A/B デバイスの場合、動的パーティションの OTA アップデートは、アップデート パッケージ内の updater を使用して適用されます。

リリース デバイスをアップデートする

このセクションは、動的パーティションをサポートする状態でリリースされた非 A/B デバイスに適用されます。そうしたデバイスは、Android 10 から、それより上位のリリースにアップグレードされます。

アップデート パッケージを生成する

OTA アップデート パッケージは、build/make/tools/releasetools にある ota_from_target_files スクリプトによって生成されます。デフォルトでは、このスクリプトは system パーティションと vendor パーティションをアップデートするパッケージを生成します。productproduct_servicesodm などの追加の動的パーティションが存在する場合、それらのアップデートはデバイス固有のコードで生成する必要があります。

アップデートを生成するには、拡張 Python モジュールで、FullOTA_GetBlockDifferences()IncrementalOTA_GetBlockDifferences() を実装します。これら 2 つの関数は、BlockDifference オブジェクトのリストを返します。各オブジェクトには、パーティションに適用されるアップデート パッチが記述されています。これら 2 つの関数から返されるパーティションは、手動で変更しないでください。また、*_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_partitionsop_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 アップデートでは、次のロジックが使用されます。

  1. パーティションを縮小する / パーティションを削除する / グループからパーティションを移動する(グループを縮小するのに十分な空き容量を確保するため)
  2. グループを縮小する(グループを拡張するのに十分な空き領域を確保するため)
  3. グループを拡張する(パーティションを拡張または追加するのに十分な空き容量を確保するため)
  4. パーティションを拡張する / パーティションを追加する / 新しいグループにパーティションを移動する

具体的には、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_partitionsop_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 アップデートでは、次のロジックが使用されます。

  1. 既存のグループとパーティションをすべて削除する
  2. グループを追加する
  3. パーティションを追加する

具体的には、update-script は次のロジックで生成されます。

update_dynamic_partitions(op_list)

for each adding partition:
    block_image_update(map_partition(name), …)

update_dynamic_partitionsop_list ファイルは次のロジックで生成されます。

remove_all_groups
for each adding group:
    add_group
for each adding partition:
    add
for each adding partition:
    resize