仮想A / Bの実装

新しいデバイスに仮想A / Bを実装する、または起動したデバイスを後付けするには、デバイス固有のコードを変更する必要があります。

ビルドフラグ

仮想A / Bを使用するデバイスは、A / Bデバイスとして構成し、動的パーティションで起動する必要があります。

仮想A / Bで起動するデバイスの場合、仮想A / Bデバイスの基本構成を継承するようにデバイスを設定します。

$(call inherit-product, \
    $(SRC_TARGET_DIR)/product/virtual_ab_ota.mk)

仮想A / Bで起動するデバイスは、Bスロットがスーパーではなくなったため、 BOARD_SUPER_PARTITION_SIZEのボードサイズの半分しか必要としません。つまり、 BOARD_SUPER_PARTITION_SIZEは、 sum(更新グループのサイズ)+オーバーヘッド以上である必要があります。これは、 sum(パーティションのサイズ)+オーバーヘッド以上である必要があります。

仮想A / Bで圧縮スナップショットを有効にするには、代わりに次の基本構成を継承します。

$(call inherit-product, \
    $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression.mk)

ブート制御HAL

ブート制御HALは、OTAクライアントがブートスロットを制御するためのインターフェイスを提供します。仮想A / Bでは、フラッシュ/ファクトリリセット中にブートローダーを確実に保護するために追加のAPIが必要になるため、ブートコントロールHALのマイナーバージョンアップグレードが必要です。 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_SNAPSHOTtrueに設定します。

F2FSを使用するデバイスの場合は、f2fsを含めます。FS_NOCOW_FLフラグをユーザーカーネルパッチにエクスポートして、ファイルの固定を修正します。 f2fsを含めます:整列された固定ファイルカーネルパッチもサポートします。

仮想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

Android11にアップグレードするデバイスに後付け

Android 11にアップグレードする場合、動的パーティションで起動したデバイスは、オプションで仮想A / Bを後付けできます。更新プロセスは、仮想A / Bで起動するデバイスの場合とほとんど同じですが、いくつかの小さな違いがあります。

  • COWファイルの場所—起動デバイスの場合、OTAクライアントは、 /data内のスペースを使用する前に、スーパーパーティション内の使用可能なすべての空のスペースを使用します。レトロフィットデバイスの場合、スーパーパーティションには常に十分なスペースがあるため、COWファイルが/dataに作成されることはありません。

  • ビルド時機能フラグ—仮想A / Bを後付けするデバイスの場合、以下に示すように、 PRODUCT_VIRTUAL_AB_OTAPRODUCT_VIRTUAL_AB_OTA_RETROFITの両方がtrueに設定されます。

    (call inherit-product, \
        (SRC_TARGET_DIR)/product/virtual_ab_ota_retrofit.mk)
    
  • スーパーパーティションサイズ—仮想A / Bで起動するデバイスは、Bスロットがスーパーパーティションにないため、 BOARD_SUPER_PARTITION_SIZEを半分に削減できます。仮想A / Bを後付けするデバイスは、古いスーパーパーティションサイズを保持するため、 BOARD_SUPER_PARTITION_SIZE2 * sum(更新グループのサイズ)+オーバーヘッド以上であり、 2 * sum(パーティションのサイズ)以上です。 +オーバーヘッド

ブートローダーの変更

更新のマージステップ中、 /dataはAndroidOSの唯一のインスタンス全体を保持します。移行が開始されると、コピーが完了するまで、ネイティブsystemvendor 、およびproductのパーティションは不完全になります。このプロセス中に、リカバリまたは[システム設定]ダイアログを介してデバイスが工場出荷時にリセットされた場合、デバイスは起動できなくなります。

/dataを消去する前に、デバイスの状態に応じて、リカバリまたはロールバックでマージを終了します。

  • 以前に新しいビルドが正常に起動した場合は、移行を終了します。
  • それ以外の場合は、古いスロットにロールバックします。
    • 動的パーティションの場合は、前の状態にロールバックします。
    • 静的パーティションの場合、アクティブスロットを古いスロットに設定します。

デバイスのロックが解除されている場合、ブートローダーとfastbootdの両方が/dataパーティションを消去できます。 fastbootdは移行を強制的に完了することができますが、ブートローダーはできません。ブートローダーは、マージが進行中であるかどうか、または/data内のどのブロックがOSパーティションを構成しているかを知りません。デバイスは、次の手順を実行して、ユーザーが無意識のうちにデバイスを操作不能(ブリック)にしないようにする必要があります。

  1. ブートローダーがsetSnapshotMergeStatus()メソッドによって設定された値を読み取れるように、ブート制御HALを実装します。
  2. マージステータスがMERGINGの場合、またはマージステータスがSNAPSHOTTEDであり、スロットが新しく更新されたスロットに変更された場合、ユーザーuserdatametadata 、またはマージステータスを格納するパーティションをワイプする要求はブートローダーで拒否する必要があります。
  3. fastboot snapshot-update cancelコマンドを実装して、ユーザーがこの保護メカニズムをバイパスすることをブートローダーに通知できるようにします。
  4. カスタムフラッシュツールまたはスクリプトを変更して、デバイス全体をフラッシュするときにfastboot snapshot-update cancelを発行します。デバイス全体をフラッシュするとOTAが削除されるため、これは安全に発行できます。ツールは、 fastboot getvar snapshot-update-statusを実装することにより、実行時にこのコマンドを検出できます。このコマンドは、エラー状態を区別するのに役立ちます。

struct VirtualAbState {
    uint8_t StructVersion;
    uint8_t MergeStatus;
    uint8_t SourceSlot;
};

bool ShouldPreventUserdataWipe() {
    VirtualAbState state;
    if (!ReadVirtualAbState(&state)) ...
    return state.MergeStatus == MergeStatus::MERGING ||
           (state.MergeStatus == MergeStatus::SNAPSHOTTED &&
            state.SourceSlot != CurrentSlot()));
}

Fastbootツールの変更

Android 11は、fastbootプロトコルに次の変更を加えます。

  • getvar snapshot-update-status —ブートコントロールHALがブートローダーに通信した値を返します。
    • 状態がMERGINGの場合、ブートローダーはmergingを返す必要があります。
    • 状態がSNAPSHOTTEDの場合、ブートローダーはsnapshottedを返す必要があります。
    • それ以外の場合、ブートローダーはnoneを返す必要があります。
  • snapshot-update merge —マージ操作を完了し、必要に応じてrecovery / fastbootdで起動します。このコマンドは、 snapshot-update-statusmergingされている場合にのみ有効であり、fastbootdでのみサポートされます。
  • snapshot-update cancel —ブートコントロールHALのマージステータスをCANCELLEDに設定します。デバイスがロックされている場合、このコマンドは無効です。
  • eraseまたはwipemetadataデータ、ユーザーuserdata 、またはブートコントロールHALのマージステータスを保持しているパーティションのeraseまたはwipeは、スナップショットのマージステータスを確認する必要があります。ステータスがMERGINGまたはSNAPSHOTTEDの場合、デバイスは操作を中止する必要があります。
  • set_active —アクティブスロットを変更するset_activeコマンドは、スナップショットマージステータスをチェックする必要があります。ステータスがMERGINGの場合、デバイスは操作を中止する必要があります。 SNAPSHOTTED状態でスロットを安全に交換できます。

これらの変更は、誤ってデバイスを起動できなくすることを防ぐように設計されていますが、自動化されたツールを混乱させる可能性があります。 fastboot flashallの実行など、コマンドをすべてのパーティションのフラッシュのコンポーネントとして使用する場合は、次のフローを使用することをお勧めします。

  1. getvar snapshot-update-status照会します。
  2. mergingまたはsnapshottedを作成する場合は、 snapshot-update cancel updatecancelを発行します。
  3. 点滅する手順に進みます。

ストレージ要件の削減

スーパーでフルA / Bストレージが割り当てられておらず、必要に応じて/dataを使用することが予想されるデバイスでは、ブロックマッピングツールを使用することを強くお勧めします。ブロックマッピングツールは、ビルド間でブロック割り当ての一貫性を維持し、スナップショットへの不要な書き込みを減らします。これは、 OTAサイズの削減に記載されています。