A/B アップデートの実装

A/B システム アップデートを実装しようとする OEM や SoC ベンダーは、ブートローダーが boot_control HAL を実装し、正しいパラメータをカーネルに渡すようにします。

ブート コントロール HAL の実装

A/B 対応ブートローダーは、boot_control HAL を hardware/libhardware/include/hardware/boot_control.h に実装する必要があります。実装をテストするには、system/extras/bootctl ユーティリティおよび system/extras/tests/bootloader/ を使用します。

次の状態マシンも実装する必要があります。

図 1. ブートローダーの状態マシン

カーネルのセットアップ

A/B システム アップデートを実装するには、次の手順を行います。

  1. 次のカーネルパッチ シリーズから選び出します(必要な場合のみ)。
  2. カーネル コマンドライン引数に次の追加の引数が含まれていることを確認します。
    skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"
    この引数の <public-key-id> 値は、verity テーブルの署名の検証に使用する公開鍵の ID です。詳細は、dm-verity をご覧ください。
  3. 公開鍵を含む .X509 証明書をシステム キーリングに追加します。
    1. X509 証明書を .der の形式で kernel ディレクトリのルートにコピーします。X509 証明書が .pem ファイル形式の場合、次のように openssl コマンドを使用して .pem 形式から .der 形式に変換します。
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. zImage をビルドし、システム キーリングの一部としてこの証明書を含めます。検証するには、procfs エントリを確認します。KEYS_CONFIG_DEBUG_PROC_KEYS を有効にする必要があります。
      angler:/# cat /proc/keys
      
      1c8a217e I------     1 perm 1f010000     0     0 asymmetri
      Android: 7e4333f9bba00adfe0ede979e28ed1920492b40f: X509.RSA 0492b40f []
      2d454e3e I------     1 perm 1f030000     0     0 keyring
      .system_keyring: 1/4
      X509 証明書が正常に登録されると、システム キーリングに公開鍵が存在することを示します。ハイライトは公開鍵 ID を示します。
    3. スペースを # に置き換えて、これをカーネル コマンドラインで <public-key-id> として渡します。たとえば、<public-key-id> の代わりに Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f を渡します。

ビルド変数の設定

A/B 対応ブートローダーは、次のビルド変数基準を満たす必要があります。

A/B ターゲットでの定義が必要
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
      boot \
      system \
      vendor
    および update_engine(無線、ブートローダーなど)を介して更新されたその他のパーティション
  • PRODUCT_PACKAGES += \
      update_engine \
      update_verifier
たとえば、/device/google/marlin/+/android-7.1.0_r1/device-common.mk を参照します。コンパイルで説明したインストール後(ただし再起動前)の dex2oat 手順を任意で実行できます。
A/B ターゲットに強く推奨
  • TARGET_NO_RECOVERY := true を定義する
  • BOARD_USES_RECOVERY_AS_BOOT := true を定義する
  • BOARD_RECOVERYIMAGE_PARTITION_SIZE を定義しない
A/B ターゲットでの定義不可
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
デバッグビルドの場合は任意 PRODUCT_PACKAGES_DEBUG += update_engine_client

パーティション(スロット)の設定

A/B デバイスでは、リカバリ パーティションやキャッシュ パーティションは不要です。Android ではこれらのパーティションを使用しなくなりました。データ パーティションはダウンロードされた OTA パッケージで使用され、リカバリ イメージコードが起動パーティションに追加されました。A/B 化されたパーティションにはすべて、次のような名前を付ける必要があります(スロットには常に ab などを付ける)。boot_aboot_bsystem_asystem_bvendor_avendor_b

キャッシュ

非 A/B アップデートでは、ダウンロードされた OTA パッケージを保存し、アップデートの適用中にブロックを一時的に保管するためにキャッシュ パーティションを使用していました。キャッシュ パーティションのサイズを決めるのに適した方法はありませんでした。適用するアップデートの種類によって必要なサイズが変わるからです。最悪のケースでは、キャッシュ パーティションがシステム イメージと同じくらいのサイズになります。A/B アップデートでは、ブロックを保存する必要はありません。必ず現在は使用していないパーティションに書き込むからです。また、A/B ストリーミングを行うことで適用前に OTA パッケージ全体をダウンロードする必要はありません。

リカバリ

リカバリ RAM ディスクは、boot.img ファイルに含まれています。リカバリ中は、ブートローダーはカーネル コマンドラインに skip_initramfs オプションを使用できません

非 A/B アップロードの場合、リカバリ パーティションにはアップデートを適用するためのコードが含まれます。A/B アップロードは、通常の起動システム イメージで実行される update_engine によって適用されます。データの初期化とアップデート パッケージのサイドローディングを行う、「リカバリ」の名前の由来になったリカバリモード(Recovery mode)も残っています。リカバリモード(Recovery mode)のコードとデータは、ramdisk にある通常の起動パーティションに保存されます。システム イメージに起動するため、ブートローダーがカーネルに ramdisk をスキップするように指示します。そうしないと、デバイスはリカバリモード(Recovery mode)で起動します。リカバリモード(Recovery mode)のサイズは小さく、大部分はすでに起動パーティションにあるため、起動パーティションのサイズは大きくなりません。

Fstab

A/B パーティションでは slotselect 引数を指定する必要があります。次に例を示します。

<path-to-block-device>/vendor  /vendor  ext4  ro
wait,verify=<path-to-block-device>/metadata,slotselect

パーティションに vendor という名前を付けないでください。代わりに、パーティション vendor_a または vendor_b という名前を選び、/vendor マウント ポイントにマウントします。

カーネル スロットの引数

現在のスロットの接尾辞は、特定のデバイスツリー(DT)ノード(/firmware/android/slot_suffix)を介して、あるいは androidboot.slot_suffix カーネル コマンドラインまたは bootconfig 引数を介して渡す必要があります。

デフォルトでは、fastboot は A/B デバイスに現在のスロットを書き込みます。更新パッケージに現在のスロットではない他のスロットのイメージも含まれている場合、fastboot はそれらのイメージも同様に書き込みます。次のオプションが利用可能です。

  • --slot SLOT。デフォルトの動作をオーバーライドし、引数として渡されたスロットを書き込むよう fastboot にプロンプトします。
  • --set-active [SLOT]。スロットをアクティブに設定します。オプションの引数が指定されていない場合、現在のスロットがアクティブとして設定されます。
  • fastboot --help。コマンドの詳細を確認します。

ブートローダーが fastboot を実装している場合、指定したスロットを現在のアクティブ スロットに設定する set_active <slot> コマンドをサポートしている必要があります。これによりスロットの起動不能フラグもクリアされ、再試行回数をデフォルト値にリセットします。ブートローダーは、次の変数もサポートしている必要があります。

  • has-slot:<partition-base-name-without-suffix>。指定されたパーティションがスロットをサポートしている場合は「yes」、そうでない場合は「no」を返します。
  • current-slot。次回から起動するスロットの接尾辞を返します。
  • slot-count。使用可能なスロット数を表す整数を返します。現在、2 つのスロットがサポートされているため、この値は 2 です。
  • slot-successful:<slot-suffix>。指定されたスロットが正常に起動した場合は「yes」を返し、それ以外の場合は「no」を返します。
  • slot-unbootable:<slot-suffix>。指定されたスロットが起動不可とマークされた場合は「yes」を返し、それ以外の場合は「no」を返します。
  • slot-retry-count:<slot-suffix>。あと何回、指定スロットの起動を再試行するかを示します。

すべての変数を表示するには、fastboot getvar all を実行します。

OTA パッケージの生成

OTA パッケージ ツールは、非 A/B デバイスのコマンドと同じコマンドが使用できます。target_files.zip ファイルは A/B ターゲットのビルド変数を定義して生成する必要があります。OTA パッケージ ツールは、A/B アップデータの形式でパッケージを自動的に識別して生成します。

例:

  • フル OTA を生成するには、次のようにします。
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • 増分 OTA を生成するには、次のようにします。
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        dist_output/tardis-target_files.zip \
        incremental_ota_update.zip
    

パーティションの設定

update_engine を使用して同じディスク内に定義された A/B パーティションのペアを更新できます。パーティションのペアには、共通の接頭辞(systemboot など)とスロットごとの接尾辞(_a など)が付いています。ペイロード ジェネレータがアップデートを定義するパーティションのリストは、AB_OTA_PARTITIONS make 変数により設定されます。

たとえば、パーティション bootloader_abooloader_b のペアが含まれる場合(_a_b はスロットの接尾辞)、これらのパーティションを更新するには、サービスまたはボードの設定で次のように指定します。

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

update_engine により更新されたすべてのパーティションは、システムの他の部分からは変更しないでください。増分アップデートまたは差分アップデートの間、現在のスロットからのバイナリデータは新しいスロットのデータの生成に使用されます。更新処理中に変更を行うと、新しいスロットデータが確認できず、更新が失敗することがあります。

インストール後手順の設定

更新されたパーティションごとに、一連の Key-Value ペアを使用して、インストール後の手順を別々に設定できます。新しいイメージ内の /system/usr/bin/postinst にあるプログラムを実行するには、システム パーティション内のファイル システムのルートからの相対パスを指定します。

たとえば、usr/bin/postinstsystem/usr/bin/postinst を表します(RAM ディスクを使用していない場合)。さらに、ファイル システム タイプを指定して、mount(2) システムコールに渡します。サービスまたはデバイスの .mk ファイルに次を追加します(該当する場合)。

AB_OTA_POSTINSTALL_CONFIG += \
  RUN_POSTINSTALL_system=true \
  POSTINSTALL_PATH_system=usr/bin/postinst \
  FILESYSTEM_TYPE_system=ext4

アプリのコンパイル

アプリをバックグラウンドでコンパイルして新しいシステム イメージに再起動できます。バックグラウンドでアプリをコンパイルするには、サービスの device.mk にあるデバイス設定に次の行を追加します。

  1. コンパイル スクリプトとバイナリがコンパイルされ、システム イメージに含まれるように、ビルドにネイティブ コンポーネントを含めます。
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. コンパイル スクリプトを、インストール後の手順として実行する update_engine とひも付けします。
      # A/B OTA dexopt update_engine hookup
      AB_OTA_POSTINSTALL_CONFIG += \
        RUN_POSTINSTALL_system=true \
        POSTINSTALL_PATH_system=system/bin/otapreopt_script \
        FILESYSTEM_TYPE_system=ext4 \
        POSTINSTALL_OPTIONAL_system=true
    

未使用の 2 番目のシステム パーティションに事前に選択したファイルをインストールする方法については、DEX_PREOPT ファイルの最初の起動インストールをご覧ください。