Implémenter des mises à jour A/B

Les OEM et les fournisseurs de SoC qui souhaitent implémenter des mises à jour du système A/B doivent s'assurer que leur bootloader implémente la HAL boot_control et transmet les paramètres appropriés au noyau.

Implémenter la HAL de contrôle du démarrage

Les bootloaders compatibles A/B doivent implémenter la boot_control HAL à hardware/libhardware/include/hardware/boot_control.h. Vous pouvez tester les implémentations à l'aide de l' system/extras/bootctl utilitaire et system/extras/tests/bootloader/.

Vous devez également implémenter la machine à états illustrée ci-dessous :

Figure 1. Machine à états du bootloader

Configurer le noyau

Pour implémenter les mises à jour du système A/B :

  1. Sélectionnez les séries de correctifs de noyau suivantes (si nécessaire) :
  2. Assurez-vous que les arguments de ligne de commande du noyau contiennent les arguments supplémentaires suivants :
    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>"
    ... où la valeur <public-key-id> correspond à l'ID de la clé publique utilisée pour valider la signature de la table de vérification (pour en savoir plus, consultez dm-verity).
  3. Ajoutez le certificat .X509 contenant la clé publique au trousseau de clés du système :
    1. Copiez le certificat .X509 au format .der à la racine du kernel répertoire. Si le certificat .X509 est au format .pem file, utilisez la commande openssl suivante pour le convertir du format .pem au format .der :
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. Créez le zImage pour inclure le certificat dans le trousseau de clés du système. Pour vérifier,consultez l'entrée procfs (vous devez activer 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
      L'inclusion réussie du certificat .X509 indique la présence de la clé publique dans le trousseau de clés du système (la mise en surbrillance indique l'ID de la clé publique).
    3. Remplacez l'espace par # et transmettez-le en tant que <public-key-id> dans la ligne de commande du noyau. Par exemple, transmettez Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f à la place de <public-key-id>.

Définir des variables de compilation

Les bootloaders compatibles A/B doivent répondre aux critères de variables de compilation suivants :

Doit définir pour la cible A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
      boot \
      system \
      vendor
    et autres partitions mises à jour via update_engine (radio, bootloader, etc.)
  • PRODUCT_PACKAGES += \
      update_engine \
      update_verifier
Pour obtenir un exemple, consultez /device/google/marlin/+/android-7.1.0_r1/device-common.mk. Vous pouvez éventuellement effectuer l'étape dex2oat post-installation (mais avant le redémarrage) décrite dans Compilation.
Fortement recommandé pour la cible A/B
  • Définissez TARGET_NO_RECOVERY := true
  • Définissez BOARD_USES_RECOVERY_AS_BOOT := true
  • Ne définissez pas BOARD_RECOVERYIMAGE_PARTITION_SIZE
Impossible de définir pour la cible A/B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
Facultatif pour les compilations de débogage PRODUCT_PACKAGES_DEBUG += update_engine_client

Définir des partitions (emplacements)

Les appareils A/B n'ont pas besoin de partition de récupération ni de partition de cache, car Android n'utilise plus ces partitions. La partition de données est désormais utilisée pour le package OTA téléchargé, et le code de l'image de restauration se trouve sur la partition de démarrage. Toutes les partitions A/B doivent être nommées comme suit (les emplacements sont toujours nommés a, b, etc.) : boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.

Cache

Pour les mises à jour non A/B, la partition de cache était utilisée pour stocker les packages OTA téléchargés et pour stocker temporairement les blocs lors de l'application des mises à jour. Il n'y a jamais eu de bonne façon de dimensionner la partition de cache : sa taille dépendait des mises à jour que vous souhaitiez appliquer. Dans le pire des cas, une partition de cache était aussi grande que l'image système. Avec les mises à jour A/B, il n'est pas nécessaire de stocker des blocs (car vous écrivez toujours dans une partition qui n'est pas utilisée actuellement). Et avec le streaming A/B, il n'est pas nécessaire de télécharger l'intégralité du package OTA avant de l'appliquer.

Récupération

Le disque RAM de récupération est désormais contenu dans le fichier boot.img. Lors de la récupération, le bootloader ne peut pas placer l'option skip_initramfs sur la ligne de commande du noyau.

Pour les mises à jour non A/B, la partition de récupération contient le code utilisé pour appliquer les mises à jour. Les mises à jour A/B sont appliquées par update_engine s'exécutant dans l'image système démarrée normale. Un mode de récupération est toujours utilisé pour implémenter la réinitialisation des données d'usine et le chargement latéral des packages de mise à jour (d'où le nom "récupération"). Le code et les données du mode de récupération sont stockés dans la partition de démarrage normale dans un ramdisk. Pour démarrer dans l'image système, le bootloader indique au noyau d'ignorer le ramdisk (sinon, l'appareil démarre en mode de récupération mode). Le mode Recovery [Recovery mode] est petit (et une grande partie se trouvait déjà sur la partition de démarrage), de sorte que la taille de la partition de démarrage n'augmente pas.

Fstab

L'argument slotselect doit figurer sur la ligne des partitions A/B. Exemple :

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

Aucune partition ne doit être nommée vendor. Au lieu de cela, la partition vendor_a ou vendor_b sera sélectionnée et montée sur le /vendor point d'installation.

Arguments d'emplacement du noyau

Le suffixe d'emplacement actuel doit être transmis via un nœud d'arborescence de périphériques (DT) spécifique (/firmware/android/slot_suffix) ou via la androidboot.slot_suffix ligne de commande du kernel ou l'argument bootconfig.

Par défaut, fastboot flashe l'emplacement actuel sur un appareil A/B. Si le package de mise à jour contient également des images pour l'autre emplacement non actuel, fastboot flashe également ces images. Les options disponibles sont les suivantes :

  • --slot SLOT. Remplacez le comportement par défaut et invitez fastboot à flasher l'emplacement transmis en tant qu' argument.
  • --set-active [SLOT]. Définissez l'emplacement comme actif. Si aucun argument facultatif n'est spécifié, l'emplacement actuel est défini comme actif.
  • fastboot --help. Obtenez des informations sur les commandes.

Si le bootloader implémente fastboot, il doit être compatible avec la commande set_active <slot> qui définit l'emplacement actif actuel sur l'emplacement donné (cela doit également effacer l'indicateur non démarrable pour cet emplacement et réinitialiser le nombre de tentatives sur les valeurs par défaut ). Le bootloader doit également être compatible avec les variables suivantes :

  • has-slot:<partition-base-name-without-suffix>. Renvoie "yes" si la partition donnée est compatible avec les emplacements, "no" sinon.
  • current-slot. Renvoie le suffixe d'emplacement à partir duquel le démarrage sera effectué ensuite.
  • slot-count. Renvoie un entier représentant le nombre d'emplacements disponibles. Actuellement, deux emplacements sont compatibles, de sorte que cette valeur est 2.
  • slot-successful:<slot-suffix>. Renvoie "yes" si l'emplacement donné a été marqué comme ayant démarré correctement, "no" sinon.
  • slot-unbootable:<slot-suffix>. Renvoie "yes" si l'emplacement donné est marqué comme non démarrable, "no" sinon.
  • slot-retry-count:<slot-suffix>. Nombre de tentatives restantes pour tenter de démarrer l'emplacement donné.

Pour afficher toutes les variables, exécutez fastboot getvar all.

Générer des packages OTA

Les outils de package OTA suivent les mêmes commandes que les commandes des appareils non A/B. Le fichier target_files.zip doit être généré en définissant les variables de compilation pour la cible A/B. Les outils de package OTA identifient automatiquement et génèrent des packages au format de l'outil de mise à jour A/B.

Exemples :

  • Pour générer un OTA complet :
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • Pour générer un OTA incrémentiel :
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        dist_output/tardis-target_files.zip \
        incremental_ota_update.zip
    

Configurer des partitions

update_engine peut mettre à jour n'importe quelle paire de partitions A/B définies sur le même disque. Une paire de partitions possède un préfixe commun (tel que system ou boot) et un suffixe par emplacement (tel que _a). La liste des partitions pour lesquelles le générateur de charge utile définit une mise à jour est configurée par la variable de création AB_OTA_PARTITIONS.

Par exemple, si une paire de partitions bootloader_a et booloader_b est incluse (_a et _b sont les suffixes d'emplacement), vous pouvez mettre à jour ces partitions en spécifiant les éléments suivants dans la configuration du produit ou de la carte :

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

Toutes les partitions mises à jour par update_engine ne doivent pas être modifiées par le reste du système. Lors des mises à jour incrémentielles ou delta les données binaires de l'emplacement actuel sont utilisées pour générer les données du nouvel emplacement. Toute modification peut entraîner l'échec de la validation des nouvelles données d'emplacement lors du processus de mise à jour, et donc l'échec de la mise à jour.

Configurer la post-installation

Vous pouvez configurer l'étape de post-installation différemment pour chaque partition mise à jour à l'aide d'un ensemble de paires clé-valeur. Pour exécuter un programme situé dans /system/usr/bin/postinst dans une nouvelle image, spécifiez le chemin d'accès relatif à la racine du système de fichiers dans la partition système.

Par exemple, usr/bin/postinst est system/usr/bin/postinst (si vous n'utilisez pas de disque RAM). Spécifiez également le type de système de fichiers à transmettre à l' mount(2) appel système. Ajoutez les éléments suivants aux fichiers .mk du produit ou de l'appareil (le cas échéant) :

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

Compiler des applications

Les applications peuvent être compilées en arrière-plan avant le redémarrage avec la nouvelle image système. Pour compiler des applications en arrière-plan, ajoutez les éléments suivants à la configuration de l'appareil du produit (dans le fichier device.mk du produit) :

  1. Incluez les composants natifs dans la compilation pour vous assurer que le script de compilation et les binaires sont compilés et inclus dans l'image système.
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. Connectez le script de compilation à update_engine afin qu'il s'exécute en tant qu'étape de post-installation.
      # 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
    

Pour obtenir de l'aide sur l'installation des fichiers préoptimisés dans la deuxième partition système inutilisée, consultez Installation au premier démarrage des fichiers DEX_PREOPT.