新しいデバイスに仮想 A/B を実装する、またはリリース済みデバイスをレトロフィットするには、デバイス固有のコードを変更する必要があります。
ビルドフラグ
仮想 A/B を使用するデバイスは、A/B デバイスとして構成し、動的パーティションを使ってリリースする必要があります。
仮想 A/B でリリースするデバイスの場合は、仮想 A/B デバイスの基本構成を継承するように設定します。
$(call inherit-product, \
$(SRC_TARGET_DIR)/product/virtual_ab_ota.mk)
仮想 A/B でリリースするデバイスでは、BOARD_SUPER_PARTITION_SIZE
のボードサイズに必要な部分が半分になります。これは、B スロットが super パーティションからなくなったためです。つまり、BOARD_SUPER_PARTITION_SIZE
はアップデート グループのサイズ合計 + オーバーヘッド以上のサイズである必要があり、同様に、パーティションのサイズの合計 + オーバーヘッド以上のサイズである必要があります。
Android 13 以降の場合、仮想 A/B で圧縮スナップショットを有効にするには、次の基本構成を継承します。
$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_ramdisk.mk)
$(call inherit-product, \
$(SRC_TARGET_DIR)/product/virtual_ab_ota/vabc_features.mk)
これにより、no-op 圧縮方式を使用しながら、仮想 A/B でユーザー空間のスナップショットが有効になります。すると、圧縮方式を、サポートされている方式のいずれか(zstd
、lz4
、)に構成できます。Android 15 では、デバイスのニーズに合わせて圧縮をさらにカスタマイズできます。詳細については、圧縮のファインチューニングを参照してください。
PRODUCT_VIRTUAL_AB_COMPRESSION_METHOD := lz4
PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 65536
Android 12 の場合、仮想 A/B で圧縮スナップショットを有効にするには、次の基本構成を継承します。
$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_ramdisk.mk)
$(call inherit-product, \
$(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk)
XOR 圧縮
Android 13 以降にアップグレードするデバイスの場合、デフォルトでは、XOR 圧縮機能が有効になっていません。XOR 圧縮を有効にするには、デバイスの .mk
ファイルに次の行を追加します。
PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.xor.enabled=true
android_t_baseline.mk
を継承するデバイスでは、XOR 圧縮がデフォルトで有効になっています。
ユーザー空間の統合
最新バージョンの仮想 A/B(Android T 以降)では、スナップショットの統合プロセスは完全にユーザー空間で実行されます。この変更は snapuserd と dm-user によって可能になりました。Android 13 以降を搭載したデバイスでは、ユーザー空間の統合がデフォルトで有効になっています。古いデバイスをアップグレードする場合は、このプロパティを次のように設定できます。
PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true
ブート コントロール HAL
ブート コントロール HAL は、OTA クライアントがブートスロットを制御するためのインターフェースを提供します。仮想 A/B では、ブート コントロール HAL のマイナー バージョン アップグレードが必要です。これは、フラッシュや出荷時設定へのリセットの際にブートローダーを確実に保護するのに追加の API が必要となるためです。HAL 定義の最新バージョンについては、IBootControl.hal と types.hal をご覧ください。
// hardware/interfaces/boot/1.1/types.hal
enum MergeStatus : uint8_t {
NONE, UNKNOWN, SNAPSHOTTED, MERGING, CANCELLED };
// hardware/interfaces/boot/1.1/IBootControl.hal
package android.hardware.boot@1.1;
interface IBootControl extends @1.0::IBootControl {
setSnapshotMergeStatus(MergeStatus status)
generates (bool success);
getSnapshotMergeStatus()
generates (MergeStatus status);
}
// Recommended implementation
Return<bool> BootControl::setSnapshotMergeStatus(MergeStatus v) {
// Write value to persistent storage
// e.g. misc partition (using libbootloader_message)
// bootloader rejects wipe when status is SNAPSHOTTED
// or MERGING
}
fstab の変更
起動プロセスでは、メタデータ パーティションの整合性が特に重要です。特に OTA アップデートが適用された直後では重要です。したがって、first_stage_init
がマウントする前にメタデータ パーティションをチェックする必要があります。確実にこれを行うには、check
fs_mgr フラグを /metadata
のエントリに追加します。次に例を示します。
/dev/block/by-name/metadata /metadata ext4 noatime,nosuid,nodev,discard,sync wait,formattable,first_stage_mount,check
カーネルの要件
スナップショットを有効にするには、CONFIG_DM_SNAPSHOT
を true
に設定します。
F2FS を使用するデバイスの場合、f2fs: export FS_NOCOW_FL flag to user のカーネルパッチを追加して、ファイルの固定を修正します。f2fs: support aligned pinned file のカーネルパッチも含めてください。
仮想 A/B は、カーネル バージョン 4.3 で追加された機能である snapshot
と snapshot-merge
ターゲットにおけるオーバーフロー ステータス ビットに依存します。Android 9 以降を搭載したデバイスには、カーネル バージョン 4.4 以降が搭載されているはずです。
圧縮スナップショットを有効にする場合、サポートされる最小カーネル バージョンは 4.19 です。CONFIG_DM_USER=m
または CONFIG_DM_USER=y
を設定します。前者(モジュール)を使用する場合は、モジュールを第 1 ステージの RAM ディスクに読み込む必要があります。そのためには、デバイスの Makefile に次の行を追加します。
BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD := dm-user.ko
fastboot ツールの変更
Android 11 では、fastboot プロトコルに次の変更が加えられます。
getvar snapshot-update-status
- ブート コントロール HAL がブートローダーに通知した値を返します。- ステータスが
MERGING
の場合、ブートローダーはmerging
を返します。 - ステータスが
SNAPSHOTTED
の場合、ブートローダーはsnapshotted
を返します。 - それ以外の場合は、ブートローダーは
none
を返します。
- ステータスが
snapshot-update merge
- マージ処理を完了します。必要に応じてリカバリまたは fastboot を起動します。このコマンドは、snapshot-update-status
がmerging
の場合にのみ有効で、fastbootd でのみサポートされています。snapshot-update cancel
- ブート コントロール HAL のマージ ステータスをCANCELLED
に設定します。このコマンドは、デバイスがロックされている場合は無効です。erase
またはwipe
-metadata
、userdata
、またはブート コントロール HAL のマージ ステータスを保持するパーティションのerase
またはwipe
ではスナップショットのマージ ステータスを確認します。ステータスがMERGING
またはSNAPSHOTTED
の場合、デバイスはオペレーションを中止します。set_active
- アクティブ スロットを変更するset_active
コマンドは、スナップショットのマージ ステータスを確認する必要があります。ステータスがMERGING
の場合、デバイスはオペレーションを中止します。スロットはSNAPSHOTTED
状態で安全に変更できます。
これらの変更の目的は、デバイスが誤って起動不能になることを避けるためですが、自動化ツールの妨げとなる場合があります。すべてのパーティションをフラッシュするコンポーネント(fastboot flashall
の実行など)としてコマンドを使用する場合は、次のフローを使用することをおすすめします。
getvar snapshot-update-status
をクエリします。merging
またはsnapshotted
の場合、snapshot-update cancel
を発行します。- フラッシュの手順に進みます。
ストレージ要件の削減
フル A/B ストレージが super パーティションに割り当てられたものではなく、必要に応じて /data
を使用する必要があるデバイスでは、ブロック マッピング ツールを使用することを強くおすすめします。ブロック マッピング ツールは、ビルド間でブロック割り当ての一貫性を保ち、スナップショットへの不必要な書き込みを削減します。詳細については、OTA サイズの削減をご覧ください。
OTA 圧縮アルゴリズム
OTA パッケージは、さまざまなパフォーマンス指標に合わせて調整できます。Android ではいくつかの圧縮方法(lz4
、zstd
、none
)がサポートされており、それぞれにインストール時間、COW のスペース使用量、起動時間、スナップショットのマージ時間のトレードオフがあります。圧縮ありの仮想 ab でデフォルトで有効になっているオプションは lz4
compression method
です。
圧縮の微調整
圧縮アルゴリズムは、(圧縮レベル)(速度を犠牲にして達成される圧縮の量)と(圧縮係数)(圧縮可能な最大ウィンドウ サイズ)の 2 つの方法でさらにカスタマイズできます。圧縮レベルは、zstd
などの特定のアルゴリズムで使用可能であり、レベルを変更すると、速度と圧縮率の間でトレードオフが発生します。圧縮係数は、OTA インストール中に使用される最大圧縮ウィンドウ サイズを表します。デフォルトは 64k に設定されていますが、ビルド パラメータ PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR
をカスタマイズすることで上書きできます。サポートされている圧縮係数は 4k、8k、16k、32k、64k、128k、256k です。
PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 65536
Google Pixel 8 Pro の増分 OTA
postinstall フェーズなしのインストール時間 | COW のスペース使用量 | OTA 後の起動時間 | スナップショットのマージ時間 | |
---|---|---|---|---|
lz4 | 18 分 15 秒 | 2.5 GB | 32.7 秒 | 98.6 秒 |
zstd | 24 分 49 秒 | 2.05 GB | 36.3 秒 | 133.2 秒 |
なし | 16 分 42 秒 | 4.76 GB | 28.7 秒 | 76.6 秒 |
Google Pixel 8 Pro のフル OTA
postinstall フェーズなしのインストール時間 | COW のスペース使用量 | OTA 後の起動時間 | スナップショットのマージ時間 | |
---|---|---|---|---|
lz4 | 15 分 11 秒 | 4.16 GB | 17.6 秒 | 82.2 秒 |
zstd | 16 分 19 秒 | 3.46 GB | 21.0 秒 | 106.3 秒 |
なし | 13 分 33 秒 | 6.39 GB | 18.5 秒 | 92.5 秒 |