Implémentation de l'A/B virtuel

Pour implémenter un A/B virtuel sur un nouvel appareil ou pour moderniser un appareil lancé, vous devez apporter des modifications au code spécifique à l'appareil.

Construire des drapeaux

Les appareils qui utilisent A/B virtuel doivent être configurés en tant qu'appareils A/B et doivent être lancés avec des partitions dynamiques .

Pour les appareils lancés avec A/B virtuel, configurez-les pour qu'ils héritent de la configuration de base de l'appareil A/B virtuel :

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

Les appareils lancés avec A/B virtuel n'ont besoin que de la moitié de la taille de la carte pour BOARD_SUPER_PARTITION_SIZE car les emplacements B ne sont plus en super. Autrement dit, BOARD_SUPER_PARTITION_SIZE doit être supérieur ou égal à sum(size of update groups) + overhead , qui, à son tour, doit être supérieur ou égal à sum(size of partitions) + overhead .

Pour activer les instantanés compressés avec Virtual A/B, héritez plutôt de la configuration de base suivante :

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

Contrôle de démarrage HAL

Le contrôle de démarrage HAL fournit une interface permettant aux clients OTA de contrôler les emplacements de démarrage. Virtual A/B nécessite une mise à niveau de version mineure de la HAL de contrôle de démarrage car des API supplémentaires sont nécessaires pour garantir que le chargeur de démarrage est protégé pendant le clignotement/la réinitialisation d'usine. Voir IBootControl.hal et types.hal pour la dernière version de la définition 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
}

Changements Fstab

L'intégrité de la partition de métadonnées est essentielle au processus de démarrage, en particulier juste après l'application d'une mise à jour OTA. Ainsi, la partition de métadonnées doit être vérifiée avant que first_stage_init ne la monte. Pour vous en assurer, ajoutez l'indicateur check fs_mgr à l'entrée pour /metadata . Ce qui suit en donne un exemple :

/dev/block/by-name/metadata /metadata ext4 noatime,nosuid,nodev,discard,sync wait,formattable,first_stage_mount,check

Exigences du noyau

Pour activer la création d'instantanés, définissez CONFIG_DM_SNAPSHOT sur true .

Pour les appareils utilisant F2FS, incluez l' indicateur f2fs: export FS_NOCOW_FL vers le correctif du noyau utilisateur pour corriger l'épinglage de fichiers. Incluez le f2fs : prend également en charge le correctif de noyau de fichier épinglé aligné .

Virtual A/B s'appuie sur des fonctionnalités ajoutées dans la version 4.3 du noyau : le bit d'état de débordement dans les cibles d' snapshot et snapshot-merge . Tous les appareils lancés avec Android 9 et versions ultérieures doivent déjà disposer de la version 4.4 ou ultérieure du noyau.

Pour activer les instantanés compressés, la version minimale du noyau prise en charge est 4.19. Définissez CONFIG_DM_USER=m ou CONFIG_DM_USER=y . Si vous utilisez le premier (un module), le module doit être chargé dans le disque virtuel du premier étage. Ceci peut être réalisé en ajoutant la ligne suivante au Makefile de l'appareil :

BOARD_GENERIC_RAMDISK_KERNEL_MODULES_LOAD := dm-user.ko

Mise à niveau sur les appareils passant à Android 11

Lors de la mise à niveau vers Android 11, les appareils lancés avec des partitions dynamiques peuvent éventuellement moderniser A/B virtuel. Le processus de mise à jour est essentiellement le même que pour les appareils lancés avec A/B virtuel, avec quelques différences mineures :

  • Emplacement des fichiers COW — Pour les périphériques de lancement, le client OTA utilise tout l'espace vide disponible dans la super partition avant d'utiliser l'espace dans /data . Pour les appareils modernisés, il y a toujours suffisamment d'espace dans la super partition pour que le fichier COW ne soit jamais créé sur /data .

  • Indicateurs de fonctionnalité au moment de la construction — Pour les appareils modernisant A/B virtuel, PRODUCT_VIRTUAL_AB_OTA et PRODUCT_VIRTUAL_AB_OTA_RETROFIT sont définis sur true , comme indiqué ci-dessous :

    (call inherit-product, \
        (SRC_TARGET_DIR)/product/virtual_ab_ota_retrofit.mk)
    
  • Taille de la super partition — Les périphériques lancés avec A/B virtuel peuvent réduire de moitié BOARD_SUPER_PARTITION_SIZE car les emplacements B ne se trouvent pas dans la super partition. Les appareils modernisant A/B virtuel conservent l'ancienne taille de super partition, donc BOARD_SUPER_PARTITION_SIZE est supérieur ou égal à 2 * sum(taille des groupes de mise à jour) + surcharge , qui à son tour est supérieure ou égale à 2 * sum(taille des partitions) + frais généraux .

Modifications du chargeur de démarrage

Lors de l'étape de fusion d'une mise à jour, /data contient la seule instance complète du système d'exploitation Android. Une fois la migration démarrée, les partitions natives du system , du vendor et product sont incomplètes tant que la copie n'est pas terminée. Si l'appareil est réinitialisé aux paramètres d'usine pendant ce processus, soit par récupération, soit via la boîte de dialogue Paramètres système, l'appareil ne pourra pas démarrer.

Avant d'effacer /data , terminez la fusion dans recovery ou rollback selon l'état de l'appareil :

  • Si la nouvelle version a démarré avec succès avant, terminez la migration.
  • Sinon, revenez à l'ancien emplacement :
    • Pour les partitions dynamiques, revenez à l'état précédent.
    • Pour les partitions statiques, définissez l'emplacement actif sur l'ancien emplacement.

Le bootloader et le fastbootd peuvent effacer la partition /data si le périphérique est déverrouillé. Alors que fastbootd peut forcer la migration à se terminer, le chargeur de démarrage ne le peut pas. Le chargeur de démarrage ne sait pas si une fusion est en cours ou non, ni quels blocs dans /data constituent les partitions du système d'exploitation. Les appareils doivent empêcher l'utilisateur de rendre l'appareil inutilisable (briquetage) sans le savoir en procédant comme suit :

  1. Implémentez le contrôle de démarrage HAL afin que le chargeur de démarrage puisse lire la valeur définie par la méthode setSnapshotMergeStatus() .
  2. Si l'état de la fusion est MERGING , ou si l'état de la fusion est SNAPSHOTTED et que l'emplacement a été remplacé par l'emplacement nouvellement mis à jour, les demandes d'effacement des données userdata , metadata ou de la partition stockant l'état de la fusion doivent être rejetées dans le chargeur de démarrage.
  3. Implémentez la commande fastboot snapshot-update cancel afin que les utilisateurs puissent signaler au chargeur de démarrage qu'ils souhaitent contourner ce mécanisme de protection.
  4. Modifiez les outils ou les scripts de flashage personnalisés pour émettre fastboot snapshot-update cancel démarrage rapide lors du flashage de l'ensemble du périphérique. Ceci est sûr à émettre car le fait de clignoter l'ensemble de l'appareil supprime l'OTA. Les outils peuvent détecter cette commande au moment de l'exécution en implémentant fastboot getvar snapshot-update-status . Cette commande permet de différencier les conditions d'erreur.

Exemple

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()));
}

Modifications de l'outillage Fastboot

Android 11 apporte les modifications suivantes au protocole fastboot :

  • getvar snapshot-update-status — Renvoie la valeur que la HAL de contrôle de démarrage a communiquée au chargeur de démarrage :
    • Si l'état est MERGING , le bootloader doit retourner merging .
    • Si l'état est SNAPSHOTTED , le chargeur de démarrage doit renvoyer snapshotted .
    • Sinon, le chargeur de démarrage doit renvoyer none .
  • snapshot-update merge — Termine une opération de fusion, en démarrant sur recovery/fastbootd si nécessaire. Cette commande n'est valide que si snapshot-update-status merging et n'est prise en charge que dans fastbootd.
  • snapshot-update cancel — Définit l'état de fusion de HAL de contrôle de démarrage sur CANCELLED . Cette commande n'est pas valide lorsque l'appareil est verrouillé.
  • erase ou wipe — Un erase ou un wipe des metadata , des données userdata ou d'une partition contenant l'état de fusion pour le contrôle de démarrage HAL doit vérifier l'état de fusion de l'instantané. Si le statut est MERGING ou SNAPSHOTTED , l'appareil doit abandonner l'opération.
  • set_active — Une commande set_active qui modifie l'emplacement actif doit vérifier l'état de fusion de l'instantané. Si l'état est MERGING , l'appareil doit abandonner l'opération. L'emplacement peut être modifié en toute sécurité dans l'état SNAPSHOTTED .

Ces modifications sont conçues pour éviter de rendre accidentellement un appareil impossible à démarrer, mais elles peuvent perturber les outils automatisés. Lorsque les commandes sont utilisées comme composant de flashage de toutes les partitions, comme l'exécution de fastboot flashall , il est recommandé d'utiliser le flux suivant :

  1. getvar snapshot-update-status .
  2. S'il s'agit d'une merging ou d'un snapshotted , émettez snapshot-update cancel .
  3. Procéder aux étapes de clignotement.

Réduction des besoins de stockage

Il est fortement recommandé d'utiliser l'outil de mappage de blocs pour les appareils qui ne disposent pas d'un stockage A/B complet alloué dans super et qui prévoient d'utiliser /data si nécessaire. L'outil de mappage de blocs assure la cohérence de l'allocation des blocs entre les builds, réduisant ainsi les écritures inutiles sur l'instantané. Ceci est documenté sous Réduction de la taille OTA .