動的パーティションのない A/B デバイス向け OTA

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

このページでは、動的パーティションをサポートせずにリリースされた A/B デバイスの更新時に OTA クライアントが動的パーティションのサイズを変更する方法と、OTA クライアントを Android 10 にアップグレードする方法について説明します。

背景

動的パーティションをサポートする A/B デバイスの更新中は、デバイスの GUID パーティション テーブル(GPT)が保持されるので、デバイスに super パーティションはありません。メタデータは system_asystem_b に保存され、BOARD_SUPER_PARTITION_METADATA_DEVICE を変更することでカスタマイズできます。

各ブロック デバイスには、2 つのメタデータ スロットがあります。各ブロック デバイスのメタデータ スロットは 1 つのみが使用されます。たとえば、system_a のメタデータ 0 と system_b のメタデータ 1 は、それぞれ A スロットと B スロットのパーティションに対応します。ランタイムには、どちらのスロットが更新中かは関係ありません。

このページでは、メタデータ スロットはメタデータ S(ソース)とメタデータ T(ターゲット)と呼ばれます。同様に、パーティションは system_svendor_t などと呼びます。

ビルドシステム設定の詳細については、デバイスのアップグレードをご覧ください。

パーティションがアップデート グループに属する方法については、新しいデバイスのボード設定の変更をご覧ください。

デバイス上のメタデータの例を次に示します。

  • 物理ブロック デバイス system_a
    • メタデータ 0
      • グループ foo_a
        • 論理(動的)パーティション system_a
        • 論理(動的)パーティション product_services_a
        • Foo によって更新されたその他のパーティション
      • グループ bar_a
        • 論理(動的)パーティション vendor_a
        • 論理(動的)パーティション product_a
        • Bar によって更新されたその他のパーティション
    • メタデータ 1(使用しない)
  • 物理ブロック デバイス system_b
    • メタデータ 0(使用しない)
    • メタデータ 1
      • グループ foo_b
        • 論理(動的)パーティション system_b
        • 論理(動的)パーティション product_services_b
        • Foo によって更新されたその他のパーティション
      • グループ bar_b
        • 論理(動的)パーティション vendor_b
        • 論理(動的)パーティション product_b
        • Bar によって更新されたその他のパーティション

system/extras/partition_tools にある lpdump ツールを使用してデバイスにメタデータをダンプできます。次に例を示します。

lpdump --slot 0 /dev/block/by-name/system_a
lpdump --slot 1 /dev/block/by-name/system_b

アップデートのレトロフィット

Android 9 以前を搭載しているデバイスでは、デバイスの OTA クライアントは更新前の動的パーティションのマッピングをサポートしていません。マッピングを既存の物理パーティションに直接適用できるように、追加のパッチセットが作成されます。

OTA ジェネレータは、すべての動的パーティションのコンテンツを含んだ最終的な super.img ファイルをビルドします。その後そのイメージ ファイルを、システムやベンダーなどに対応した物理ブロック デバイスのサイズに一致する複数のイメージに分割します。これらのイメージの名前は super_system.imgsuper_vendor.img などです。 OTA クライアントは、イメージを論理(動的)パーティションではなく、物理パーティションに適用します。

OTA クライアントは動的パーティションのマッピング方法を認識していないため、アップデート パッケージが生成されると、動的パーティションに対するインストール後の手順はすべて自動的に無効になります。詳しくはインストール後の設定をご覧ください。

更新フローは Android 9 と同じです。

アップデート前:

ro.boot.dynamic_partitions=
ro.boot.dynamic_partitions_retrofit=

アップデート後:

ro.boot.dynamic_partitions=true
ro.boot.dynamic_partitions_retrofit=true

レトロフィット後のアップデート

アップデートのレトロフィットを行うと、OTA クライアントは動的パーティションを使用できるように更新されます。ソース パーティションの範囲は、ターゲットの物理パーティションにまたがりません。

定期アップデート パッケージを使用した更新フロー

  1. super パーティション メタデータを初期化します。
    1. メタデータ S(ソース メタデータ)から新しいメタデータ M を作成します。 たとえば、メタデータ S が [system_svendor_sproduct_s] をブロック デバイスとして使用する場合、新しいメタデータ M は [system_tvendor_tproduct_t] をブロック デバイスとして使用します。すべてのグループとパーティションは M で破棄されます。
    2. アップデート マニフェストの dynamic_partition_metadata フィールドに従ってターゲット グループとパーティションを追加します。各パーティションのサイズは、new_partition_info にあります。
    3. M をメタデータ T に書き込みます。
    4. デバイス マッパー上に追加されたパーティションを書き込み可能としてマッピングします。
  2. ブロック デバイスにアップデートを適用します。
    1. 必要に応じて、デバイス マッパー上のソース パーティションを読み取り専用としてマッピングします。更新前にはソース パーティションがマッピングされていないため、サイドローディングにはこの処理が必要です。
    2. ターゲット スロットのすべてのブロック デバイスにフル アップデートまたは差分アップデートを適用します。
    3. パーティションをマウントしてインストール後のスクリプトを実行し、その後パーティションをマウント解除します。
  3. ターゲット パーティションのマッピングを解除します。

レトロフィット アップデート パッケージを使用した更新フロー

動的パーティションをすでに有効にしているデバイスにレトロフィット アップデート パッケージが適用されている場合、OTA クライアントは分割された super.img ファイルをブロック デバイスに直接適用します。更新フローはレトロフィット アップデートと同様です。詳しくはアップデートのレトロフィットをご覧ください。

たとえば、次のような状況を考えます。

  • スロット A はアクティブ スロットです。
  • system_a にはスロット 0 のアクティブなメタデータを含みます。
  • system_avendor_aproduct_a はブロック デバイスとして使用されます。

レトロフィット アップデート パッケージを受け取った OTA クライアントは、super_system.img を物理 system_b に、super_vendor.img を物理 vendor_b に、super_product.img を物理 product_b に適用します。 物理ブロック デバイス system_b には、起動時に論理 system_bvendor_bproduct_b にマッピングを行うための正しいメタデータが含まれています。

アップデート パッケージの生成

増分 OTA

レトロフィット デバイスの増分 OTA を生成する場合、アップデートはベースビルドで PRODUCT_USE_DYNAMIC_PARTITIONS および PRODUCT_RETROFIT_DYNAMIC_PARTITIONS を定義しているかどうかによって異なります。

  • ベースビルドが変数を定義していない場合、これはレトロフィット アップデートです。アップデート パッケージには分割された super.img ファイルが含まれ、インストール後の手順が無効になります。
  • ベースビルドで変数を定義している場合は、動的パーティションの通常の更新と同じです。アップデート パッケージには、論理(動的)パーティションのイメージが含まれます。インストール後の手順を有効にできます。

フル OTA

2 つのフル OTA パッケージが、レトロフィット デバイス用に生成されます。

  • $(PRODUCT)-ota-retrofit-$(TAG).zip には常に分割された super.img が含まれ、レトロフィット アップデートのためのインストール後の手順は無効になります。
    • ota_from_target_files スクリプトに追加の引数 --retrofit_dynamic_partitions を指定して生成されます。
    • すべてのビルドに適用できます。
  • $(PRODUCT)-ota-$(TAG).zip には将来のアップデート用の論理イメージが含まれています。
    • 動的パーティションが有効になっているビルドにのみ適用します。適用の詳細については、下記をご覧ください。

従来のビルドに対する非レトロフィット アップデートの拒否

通常のフル OTA パッケージは、動的パーティションを有効にしたビルドにのみ適用します。OTA サーバーが正しく設定されておらず、これらのパッケージが Android 9 以前のデバイスにプッシュされた場合、デバイスは起動できません。Android 9 以前の OTA クライアントは、レトロフィット OTA パッケージと通常のフル OTA パッケージの違いを認識できないため、クライアントはフル OTA パッケージを拒否しません。

デバイスがフル OTA パッケージを受け入れないようにするには、インストール後の手順で既存のデバイス設定の確認を必須にします。次に例を示します。

device/device_name/dynamic_partitions/check_dynamic_partitions

#!/system/bin/sh
DP_PROPERTY_NAME="ro.boot.dynamic_partitions"
DP_RETROFIT_PROPERTY_NAME="ro.boot.dynamic_partitions_retrofit"

DP_PROPERTY=$(getprop ${DP_PROPERTY_NAME})
DP_RETROFIT_PROPERTY=$(getprop ${DP_RETROFIT_PROPERTY_NAME})

if [ "${DP_PROPERTY}" != "true" ] || [ "${DP_RETROFIT_PROPERTY}" != "true" ] ; then
    echo "Error: applied non-retrofit update on build without dynamic" \
         "partitions."
    echo "${DP_PROPERTY_NAME}=${DP_PROPERTY}"
    echo "${DP_RETROFIT_PROPERTY_NAME}=${DP_RETROFIT_PROPERTY}"
    exit 1
fi

device/device_name/dynamic_partitions/Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= check_dynamic_partitions
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_SRC_FILES := check_dynamic_partitions
LOCAL_PRODUCT_MODULE := true
include $(BUILD_PREBUILT)

device/device_name/device.mk

PRODUCT_PACKAGES += check_dynamic_partitions

# OPTIONAL=false so that the error in check_dynamic_partitions will be
# propagated to OTA client.
AB_OTA_POSTINSTALL_CONFIG += \
    RUN_POSTINSTALL_product=true \
    POSTINSTALL_PATH_product=bin/check_dynamic_partitions \
    FILESYSTEM_TYPE_product=ext4 \
    POSTINSTALL_OPTIONAL_product=false \

動的パーティションが有効になっていないデバイスで通常の OTA パッケージが適用されると、OTA クライアントはインストール後の手順として check_dynamic_partitions を実行し、アップデートを拒否します。