Mises à jour système dynamiques

Les mises à jour système dynamiques (DSU) vous permettent de créer une image système Android que les utilisateurs peuvent télécharger sur Internet et tester sans risque de corrompre l'image système actuelle. Ce document explique comment prendre en charge le DSU.

Exigences du noyau

Voir Implémenter des partitions dynamiques pour connaître les exigences du noyau.

De plus, le DSU s'appuie sur la fonctionnalité de noyau device-mapper-verity (dm-verity) pour vérifier l'image système Android. Vous devez donc activer le noyau suivant de configuration:

  • CONFIG_DM_VERITY=y
  • CONFIG_DM_VERITY_FEC=y

Exigences concernant les partitions

À partir d'Android 11, DSU exige que la partition /data utilise le système de fichiers F2FS ou ext4. F2FS offre de meilleures performances et est recommandé, mais la différence devrait être insignifiante.

Voici quelques exemples du temps que prend une mise à jour dynamique du système avec un Pixel appareil:

  • À l'aide de F2FS :
    • 109s, utilisateur 8G, système 867M, type de système de fichiers: F2FS: chiffrement=aes-256-xts:aes-256-cts
    • 104 s, utilisateur 8G, système 867M, type de système de fichiers: F2FS: encryption=ice
  • Avec ext4: <ph type="x-smartling-placeholder">
      </ph>
    • 135s, utilisateur 8G, système 867M, type de système de fichiers: ext4: chiffrement=aes-256-xts:aes-256-cts

Si l'opération prend beaucoup plus de temps sur votre plate-forme, vérifiez si le support contient tout indicateur qui effectue une écriture "sync", ou vous pouvez spécifier un indicateur "async" l'indicateur explicite pour obtenir de meilleures performances.

La partition metadata (16 Mo ou plus) est requise pour stocker les données liées aux images installées. Il doit être installé lors du premier montage.

La partition userdata doit utiliser le système de fichiers F2FS ou ext4. Lorsque vous utilisez F2FS, incluez tous les correctifs liés à F2FS disponibles dans le kernel commun Android.

DSU a été développé et testé avec kernel/common 4.9. Nous vous recommandons d'utiliser noyau 4.9 et versions ultérieures pour cette fonctionnalité.

Comportement du HAL du fournisseur

HAL Weaver

Le HAL Weaver fournit un nombre fixe d'emplacements pour stocker les clés utilisateur. Le DSU utilise deux emplacements de clés supplémentaires. Si un OEM dispose d'un HAL Weaver, il doit disposer d'assez d'emplacements pour une image système générique (GSI) et une image hôte.

HAL Gatekeeper

Le Gatekeeper HAL doit acceptent les valeurs USER_ID élevées, car le GSI décale les UID vers le HAL en +1000000.

Vérifier le démarrage

Si vous souhaitez prendre en charge le démarrage des images GSI de développement en état LOCKED sans désactiver le démarrage validé, incluez les clés GSI de développement en ajoutant la ligne suivante au fichier device/<device_name>/device.mk :

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

Protection contre le rollback

Lorsque vous utilisez le DSU, l'image système Android téléchargée doit être plus récente que l'image système actuelle sur l'appareil. Pour cela, nous comparons le correctif de sécurité niveaux du Démarrage validé Android (AVB) Descripteur de propriété AVB des deux images système: Prop: com.android.build.system.security_patch -> '2019-04-05'.

Pour les appareils qui n'utilisent pas AVB, indiquez le niveau du correctif de sécurité du système actuel. dans la cmdline ou le bootconfig du noyau avec le bootloader: androidboot.system.security_patch=2019-04-05

Configuration matérielle requise

Lorsque vous lancez une instance DSU, deux fichiers temporaires sont alloués :

  • Une partition logique pour stocker GSI.img (1 à 1,5 G)
  • Une partition /data vide de 8 Go en tant que bac à sable pour exécuter le GSI

Nous vous recommandons de réserver au moins 10 Go d'espace libre avant de lancer une instance DSU. Le DSU prend également en charge l'allocation à partir d'une carte SD. Lorsqu'une carte SD est présente, elle a la priorité la plus élevée pour l'allocation. La prise en charge des cartes SD est critique pour les appareils à faible puissance qui pourraient manquer de mémoire de stockage interne. Si une carte SD est présente, assurez-vous qu'elle n'a pas été adoptée. DSU n'est pas compatible adopté des cartes SD.

Interfaces disponibles

Vous pouvez lancer le DSU à l'aide de adb, d'une application OEM ou du chargeur DSU en un clic (sous Android 11 ou version ultérieure).

Lancer DSU à l'aide d'adb

Pour lancer DSU à l'aide d'adb, saisissez ces commandes:

$ simg2img out/target/product/.../system.img system.raw
$ gzip -c system.raw > system.raw.gz
$ adb push system.raw.gz /storage/emulated/0/Download
$ adb shell am start-activity \
-n com.android.dynsystem/com.android.dynsystem.VerificationActivity  \
-a android.os.image.action.START_INSTALL    \
-d file:///storage/emulated/0/Download/system.raw.gz  \
--el KEY_SYSTEM_SIZE $(du -b system.raw|cut -f1)  \
--el KEY_USERDATA_SIZE 8589934592

Lancer une DSU à l'aide d'une application

android.os.image.DynamicSystemClient.java est le principal point d'entrée de la mise à jour API:

public class DynamicSystemClient {


...
...

     /**
     * Start installing DynamicSystem from URL with default userdata size.
     *
     * @param systemUrl A network URL or a file URL to system image.
     * @param systemSize size of system image.
     */
    public void start(String systemUrl, long systemSize) {
        start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
    }

Vous devez regrouper/préinstaller cette application sur l'appareil. En effet, DynamicSystemClient étant une API système, vous ne pouvez pas créer l'application avec l'API et vous ne pouvez pas la publier sur Google Play. L'objectif de cette application est le suivant :

  1. Récupérez une liste d'images et l'URL correspondante à l'aide d'un schéma défini par le fournisseur.
  2. Faites correspondre les images de la liste à l'appareil et affichez les images compatibles pour que l'utilisateur puisse les sélectionner.
  3. Appelez DynamicSystemClient.start comme suit :

    DynamicSystemClient aot = new DynamicSystemClient(...)
       aot.start(
            ...URL of the selected image...,
            ...uncompressed size of the selected image...);
    
    

L'URL pointe vers un fichier image système non sporadique compressé en gzip, que vous pouvez créer à l'aide des commandes suivantes :

$ simg2img ${OUT}/system.img ${OUT}/system.raw
$ gzip ${OUT}/system.raw
$ ls ${OUT}/system.raw.gz

Le nom de fichier doit respecter le format suivant:

<android version>.<lunch name>.<user defined title>.raw.gz

Exemples :

  • o.aosp_taimen-userdebug.2018dev.raw.gz
  • p.aosp_taimen-userdebug.2018dev.raw.gz

Chargeur DSU en un clic

Android 11 introduit le chargeur DSU en un clic, qui est une interface dans les paramètres du développeur.

Lancer le chargeur de DSU

Figure 1 : Lancer le chargeur de DSU

Lorsque le développeur clique sur le bouton Chargeur DSU, celui-ci récupère un fichier de description JSON du DSU provenant du Web et affiche toutes les images applicables dans le fichier menu flottant. Sélectionnez une image pour lancer l'installation de la DSU, puis l'état s'affiche dans la barre de notification.

Progression de l&#39;installation de l&#39;image DSU

Figure 2. Progression de l'installation de l'image DSU

Par défaut, le chargeur DSU charge un descripteur JSON contenant les images GSI. Les sections suivantes expliquent comment créer des packages DSU signés par l'OEM et charger à partir du chargeur de DSU.

Indicateur de fonctionnalité

La fonctionnalité DSU se trouve sous le flag de fonctionnalité settings_dynamic_android. Avant avec le DSU, assurez-vous que le flag de fonctionnalité correspondant est activé.

Activer le flag de fonctionnalité.

Figure 3. Activer le flag de fonctionnalité

L'interface utilisateur des flags de fonctionnalités peut être indisponible sur un appareil exécutant un build utilisateur. Dans ce cas, utilisez plutôt la commande adb :

$ adb shell setprop persist.sys.fflag.override.settings_dynamic_system 1

Images système hôte du fournisseur sur GCE (facultatif)

Le bucket Google Compute Engine (GCE) est l'un des emplacements de stockage possibles des images système. L'administrateur de la version utilise la console de stockage GCP pour ajouter/supprimer/modifier l'image système publiée.

Les images doivent être accessibles au public, comme indiqué ci-dessous :

Accès public dans GCE

Figure 4. Accès public dans GCE

La procédure à suivre pour rendre un élément public est disponible dans la documentation Google Cloud.

DSU à plusieurs partitions dans le fichier ZIP

À partir d'Android 11, le DSU peut comporter plusieurs partitions. Par exemple, il peut contenir un product.img en plus du system.img Lorsque l'appareil démarre, le premier niveau init détecte les partitions DSU installées et remplace temporairement la partition sur l'appareil lorsque le DSU installé est activé. Le package DSU peut contenir une partition qui ne avoir une partition correspondante sur l'appareil.

Processus DSU avec plusieurs partitions

Figure 5. Processus DSU avec plusieurs partitions

DSU signée par l'OEM

Pour vous assurer que toutes les images exécutées sur l'appareil sont autorisées par le fabricant de l'appareil, toutes les images d'un package DSU doivent être signées. Par exemple, supposons qu'il existe un package DSU contenant deux images de partition comme ci-dessous :

dsu.zip {
    - system.img
    - product.img
}

Les champs system.img et product.img doivent tous deux être signés par la clé OEM avant de pouvoir sont placés dans le fichier ZIP. Il est courant d'utiliser un algorithme asymétrique, par exemple RSA, où la clé secrète est utilisée pour signer le package et la clé publique pour le valider. Le disque RAM de la première étape doit inclure l'association clé publique, par exemple /avb/*.avbpubkey. Si l'appareil a déjà adopté AVB, la procédure de signature existante suffit. Les sections suivantes illustrent le processus de signature et mettent en évidence l'emplacement de la clé publique AVB utilisée pour valider les images du package DSU.

Descripteur JSON de la DSU

Le descripteur JSON DSU décrit les packages DSU. Il accepte deux primitives. Tout d'abord, la primitive include inclut des descripteurs JSON supplémentaires ou redirige le chargeur DSU vers un nouvel emplacement. Exemple :

{
    "include": ["https://.../gsi-release/gsi-src.json"]
}

Deuxièmement, la primitive image sert à décrire les packages DSU publiés. La primitive d'image contient plusieurs attributs :

  • Les attributs name et details sont des chaînes affichées sur dans la boîte de dialogue que l'utilisateur peut sélectionner.

  • Les attributs cpu_api, vndk et os_version sont utilisés pour les vérifications de compatibilité, qui sont décrites dans la section suivante.

  • L'attribut facultatif pubkey décrit le champ d'application associée à la clé secrète utilisée pour signer le package DSU. Lorsque cette valeur est spécifiée, le service DSU peut vérifier si l'appareil dispose de la clé pour vérifier le package DSU. Cela évite d'installer un package DSU non reconnu, par exemple d'installer un DSU signé par OEM-A sur un appareil fabriqué par OEM-B.

  • L'attribut facultatif tos pointe vers un fichier texte qui décrit les conditions d'utilisation du package de DSU correspondant. Lorsqu'un développeur sélectionne un package DSU avec l'attribut "terms of service" [conditions d'utilisation] spécifié, illustrée à la figure 6 s'ouvre, dans laquelle le développeur est invité à accepter les conditions d'utilisation. avant d'installer le package DSU.

    Boîte de dialogue des conditions d&#39;utilisation

    Figure 6. Boîte de dialogue "Conditions d'utilisation"

Pour référence, voici un descripteur JSON DSU pour le GSI:

{
   "images":[
      {
         "name":"GSI+GMS x86",
         "os_version":"10",
         "cpu_abi": "x86",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_x86-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI+GMS ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI x86_64",
         "os_version":"10",
         "cpu_abi": "x86_64",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_x86_64-exp-QP1A.190711.020.C4-5928301.zip"
      }
   ]
}

Gestion de la compatibilité

Plusieurs attributs sont utilisés pour spécifier la compatibilité entre un package DSU et l'appareil local :

  • cpu_api est une chaîne qui décrit l'architecture de l'appareil. Cet attribut est obligatoire et est comparée à la propriété système ro.product.cpu.abi. Leurs valeurs doivent correspondre exactement.

  • os_version est un entier facultatif qui spécifie une version Android. Pour par exemple, pour Android 10, os_version est 10 et pour Android 11, os_version est 11. Lorsque cet attribut est spécifié, il doit être supérieur ou égal à la propriété système ro.system.build.version.release. Cette vérification permet d'empêcher le démarrage d'une image GSI Android 10 sur une instance Android 11 appareil du fournisseur, qui n'est actuellement pas pris en charge. Le démarrage d'une image GSI Android 11 sur un appareil Android 10 est autorisé.

  • vndk est un tableau facultatif qui spécifie tous les VNDK inclus dans le package DSU. Lorsqu'il est spécifié, le chargeur DSU vérifie si le nombre extrait de la propriété système ro.vndk.version est inclus.

Révoquer les clés DSU pour des raisons de sécurité

Dans le cas extrêmement rare où la paire de clés RSA utilisée pour signer les images DSU est compromise, le ramdisk doit être mis à jour dès que possible pour supprimer la clé compromise. En plus de mettre à jour la partition de démarrage, vous pouvez bloquer les clés compromises à l'aide d'une liste de révocation de clés DSU (liste noire de clés) à partir d'une URL HTTPS.

La liste de révocation de clés DSU contient une liste de clés publiques AVB révoquées. Lors de l'installation du DSU, les clés publiques des images du DSU sont validées avec la liste de révocation. S'il s'avère que les images contiennent un élément le processus d'installation des DSU s'interrompt.

L'URL de la liste de révocation de clés doit être une URL HTTPS pour garantir la sécurité. Elle est spécifiée dans une chaîne de ressources :

frameworks/base/packages/DynamicSystemInstallationService/res/values/strings.xml@key_revocation_list_url

La valeur de la chaîne est https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json, qui est une liste de révocation pour les clés GSI publiées par Google. Cette chaîne de ressource peut être en superposition et en personnalisation. Ainsi, les OEM qui adoptent la fonctionnalité DSU peuvent fournir leur propre liste noire de clés. Cela permet à l'OEM de bloquer certaines clés publiques sans mettre à jour l'image ramdisk de l'appareil.

Le format de la liste de révocation est le suivant :

{
   "entries":[
      {
         "public_key":"bf14e439d1acf231095c4109f94f00fc473148e6",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      },
      {
         "public_key":"d199b2f29f3dc224cca778a7544ea89470cbef46",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      }
   ]
}
  • public_key est le récapitulatif SHA-1 de la clé révoquée, au format décrit dans la section Générer la clé publique AVB.
  • status indique l'état de révocation de la clé. Actuellement, la seule valeur acceptée est REVOKED.
  • reason est une chaîne facultative décrivant le motif de révocation.

Procédures DSU

Cette section explique comment effectuer plusieurs procédures de configuration de DSU.

Générer une nouvelle paire de clés

Utilisez la commande openssl pour générer une paire de clés publique/privée RSA dans .pem (par exemple, avec une taille de 2 048 bits):

$ openssl genrsa -out oem_cert_pri.pem 2048
$ openssl rsa -in oem_cert_pri.pem -pubout -out oem_cert_pub.pem

La clé privée peut ne pas être accessible et n'est conservée que dans un module de sécurité matérielle (HSM). Dans ce cas, un certificat de clé publique X509 peut être disponible après la génération de la clé. Consultez la section Ajouter la clé publique d'association au disque RAM. pour savoir comment générer la clé publique AVB à partir d'un certificat x509.

Pour convertir un certificat x509 au format PEM :

$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem

Ignorez cette étape si le certificat est déjà un fichier PEM.

Ajouter la clé de publication associée au disque RAM

oem_cert.avbpubkey doit être placé sous /avb/*.avbpubkey pour vérifier un package DSU signé. Commencez par convertir la clé publique au format PEM en clé publique AVB. format de clé:

$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey

Incluez ensuite la clé publique dans le ramdisk de la première étape en procédant comme suit :

  1. Ajoutez un module prédéfini pour copier le avbpubkey. Par exemple, ajoutez device/<company>/<board>/oem_cert.avbpubkey et device/<company>/<board>/avb/Android.mk avec un contenu comme celui-ci :

    include $(CLEAR_VARS)
    
    LOCAL_MODULE := oem_cert.avbpubkey
    LOCAL_MODULE_CLASS := ETC
    LOCAL_SRC_FILES := $(LOCAL_MODULE)
    ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
    LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
    else
    LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
    endif
    
    include $(BUILD_PREBUILT)
    
  2. Faites en sorte que la cible droidcore dépende de l'oem_cert.avbpubkey ajoutée :

    droidcore: oem_cert.avbpubkey
    

Générer l'attribut de clé publique AVB dans le descripteur JSON

oem_cert.avbpubkey est au format binaire de clé publique AVB. Utilisez SHA-1 pour Rendez-la lisible avant de la placer dans le descripteur JSON:

$ sha1sum oem_cert.avbpubkey | cut -f1 -d ' '
3e62f2be9d9d813ef5........866ac72a51fd20

Correspond au contenu de l'attribut pubkey du descripteur JSON.

   "images":[
      {
         ...
         "pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
         ...
      },

Signer un package DSU

Utilisez l'une des méthodes suivantes pour signer un package de DSU:

  • Méthode 1 : Réutilisez l'artefact créé par le processus de signature AVB d'origine pour créer un package DSU. Une autre approche consiste à extraire les sous-titres déjà signés du package et utilisez les images extraites pour créer le fichier ZIP directement.

  • Méthode 2 : Utilisez les commandes suivantes pour signer les partitions DSU si la clé privée est disponible. Chaque img d'un package DSU (fichier ZIP) est signé séparément :

    $ key_len=$(openssl rsa -in oem_cert_pri.pem -text | grep Private-Key | sed -e 's/.*(\(.*\) bit.*/\1/')
    $ for partition in system product; do
        avbtool add_hashtree_footer \
            --image ${OUT}/${partition}.img \
            --partition_name ${partition} \
            --algorithm SHA256_RSA${key_len} \
            --key oem_cert_pri.pem
    done
    

Pour en savoir plus sur l'ajout de add_hashtree_footer à l'aide de avbtool, consultez la section Utiliser avbtool.

Vérifier le package DSU localement

Il est recommandé de vérifier toutes les images locales par rapport à la clé publique d'association ces commandes:


for partition in system product; do
    avbtool verify_image --image ${OUT}/${partition}.img  --key oem_cert_pub.pem
done

Le résultat attendu ressemble à ceci :

Verifying image dsu/system.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/system.img
: Successfully verified sha1 hashtree of dsu/system.img for image of 898494464 bytes

Verifying image dsu/product.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/product.img
: Successfully verified sha1 hashtree of dsu/product.img for image of 905830400 bytes

Créer un package DSU

L'exemple suivant crée un package DSU contenant un system.img et un product.img :

dsu.zip {
    - system.img
    - product.img
}

Une fois les deux images signées, utilisez la commande suivante pour créer le fichier ZIP :

$ mkdir -p dsu
$ cp ${OUT}/system.img dsu
$ cp ${OUT}/product.img dsu
$ cd dsu && zip ../dsu.zip *.img && cd -

Personnaliser la DSU en un clic

Par défaut, le chargeur de DSU pointe vers des métadonnées d'images GSI qui sont https://...google.com/.../gsi-src.json

Les OEM peuvent écraser la liste en définissant persist.sys.fflag.override.settings_dynamic_system.list pointant vers son propre descripteur JSON. Par exemple, un OEM peut fournir des métadonnées JSON qui incluent des GSI ainsi que des images propriétaires de l'OEM, comme suit :

{
    "include": ["https://dl.google.com/.../gsi-src.JSON"]
    "images":[
      {
         "name":"OEM image",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"...",
         "vndk":[
            27,
            28,
            29
         ],
         "spl":"...",
         "pubkey":"",
         "uri":"https://.../....zip"
      },

}

Un OEM peut enchaîner des métadonnées DSU publiées, comme illustré dans la figure 7.

Relier des métadonnées de DSU publiées

Figure 7. Relier des métadonnées de DSU publiées