Implémenter les mises à jour A/B

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

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

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

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

Figure 1. Machine à états du chargeur de démarrage

Configurer le noyau

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

  1. Cherrypick la série de correctifs de noyau suivante (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> est l'ID de la clé publique utilisée pour vérifier la signature de la table de vérité (pour plus de détails, voir dm-verity ) .
  3. Ajoutez le certificat .X509 contenant la clé publique au trousseau de clés système :
    1. Copiez le certificat .X509 formaté au format .der à la racine du répertoire du kernel . Si le certificat .X509 est formaté en tant que fichier .pem , utilisez la commande openssl suivante pour 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 jeu de clés système. Pour vérifier, vérifiez l'entrée procfs (nécessite l'activation de 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 jeu de clés du système (la surbrillance indique l'ID de la clé publique).
    3. Remplacez l'espace par # et transmettez-le comme <public-key-id> dans la ligne de commande du noyau. Par exemple, transmettez Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f à la place de <public-key-id> .

Définir les variables de construction

Les chargeurs de démarrage compatibles A/B doivent répondre aux critères de variables de build 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 un exemple, reportez-vous à /device/google/marlin/+/android-7.1.0_r1/device-common.mk . Vous pouvez éventuellement effectuer l'étape dex2oat post-installation (mais avant redémarrage) décrite dans Compilation .
Fortement recommandé pour la cible A/B
  • Définir TARGET_NO_RECOVERY := true
  • Définir BOARD_USES_RECOVERY_AS_BOOT := true
  • Ne pas définir 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 versions 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 ou 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 récupération se trouve sur la partition de démarrage. Toutes les partitions A/B-ed 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. Le pire des cas serait une partition de cache 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 sur une partition qui n'est pas actuellement utilisée) 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 chargeur de démarrage 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 exécuté dans l'image système démarrée standard. Il existe toujours un mode de récupération utilisé pour implémenter la réinitialisation des données d'usine et le chargement latéral des packages de mise à jour (d'où vient le nom « récupération »). Le code et les données pour le mode de récupération sont stockés dans la partition de démarrage standard d'un disque virtuel ; pour démarrer dans l'image système, le chargeur de démarrage demande au noyau d'ignorer le disque virtuel (sinon, le périphérique démarre en mode de récupération. Le mode de récupération est petit (et une grande partie était déjà sur la partition de démarrage), donc la partition de démarrage n'augmente pas en taille.

Fstab

L'argument slotselect doit être sur la ligne pour les partitions A/B-ed. Par 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 point de montage /vendor .

Arguments d'emplacement du noyau

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

Par défaut, fastboot fait clignoter l'emplacement actuel sur un périphérique A/B. Si le package de mise à jour contient également des images pour l'autre emplacement non actuel, fastboot fait également clignoter ces images. Les options disponibles incluent :

  • --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é, alors l'emplacement actuel est défini comme actif.
  • fastboot --help . Obtenez des détails sur les commandes.

Si le chargeur de démarrage implémente fastboot, il doit prendre en charge la commande set_active <slot> qui définit l'emplacement actif actuel sur l'emplacement donné (cela doit également effacer l'indicateur de démarrage impossible pour cet emplacement et réinitialiser le nombre de tentatives aux valeurs par défaut). Le chargeur de démarrage doit également prendre en charge les variables suivantes :

  • has-slot:<partition-base-name-without-suffix> . Renvoie « oui » si la partition donnée prend en charge les emplacements, « non » sinon.
  • current-slot . Renvoie le suffixe de l'emplacement qui sera démarré ensuite.
  • slot-count . Renvoie un entier représentant le nombre d'emplacements disponibles. Actuellement, deux emplacements sont pris en charge, cette valeur est donc 2 .
  • slot-successful:<slot-suffix> . Renvoie "oui" si l'emplacement donné a été marqué comme démarrant avec succès, "non" sinon.
  • slot-unbootable:<slot-suffix> . Renvoie « oui » si l'emplacement donné est marqué comme ne pouvant pas démarrer, « non » sinon.
  • slot-retry-count . 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 du 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 build pour la cible A/B. Les outils de package OTA identifient et génèrent automatiquement des packages au format pour le programme 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 les partitions

Le update_engine peut mettre à jour n'importe quelle paire de partitions A/B définies sur le même disque. Une paire de partitions a 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 make AB_OTA_PARTITIONS .

Par exemple, si une paire de partitions bootloader_a et booloader_b sont incluses ( _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 dans le nouvel emplacement. Toute modification peut entraîner l'échec de la vérification des nouvelles données d'emplacement pendant le 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 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). De plus, spécifiez le type de système de fichiers à transmettre à l'appel système mount(2) . 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 build pour garantir 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 de manière à ce qu'il s'exécute comme une étape 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éoptés dans la deuxième partition système inutilisée, reportez-vous à Installation au premier démarrage des fichiers DEX_PREOPT .