Mises à jour système dynamiques

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

Exigences du noyau

Voir Implémentation de partitions dynamiques pour connaître la configuration requise du noyau.

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

  • CONFIG_DM_VERITY=y
  • CONFIG_DM_VERITY_FEC=y

Exigences de partition

À partir d'Android 11, DSU nécessite 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 de la durée d’une mise à jour dynamique du système avec un appareil Pixel :

  • Utilisation de F2FS :
    • 109s, utilisateur 8G, système 867M, type de système de fichiers : F2FS : chiffrement=aes-256-xts:aes-256-cts
    • 104s, utilisateur 8G, système 867M, type de système de fichiers : F2FS : chiffrement=ice
  • En utilisant ext4 :
    • 135s, utilisateur 8G, système 867M, type de système de fichiers : ext4 : chiffrement=aes-256-xts:aes-256-cts

Si cela prend beaucoup plus de temps sur votre plate-forme, vous souhaiterez peut-être vérifier si l'indicateur de montage contient un indicateur permettant d'écrire « sync », ou vous pouvez spécifier explicitement un indicateur « async » 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 monté lors du montage de la première étape.

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 noyau commun Android .

DSU a été développé et testé avec kernel/common 4.9. Il est recommandé d'utiliser le noyau 4.9 et supérieur pour cette fonctionnalité.

Comportement HAL du fournisseur

Tisserand HAL

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

Gardien d'accès HAL

Le Gatekeeper HAL doit prendre en charge de grandes valeurs USER_ID , car le GSI décale les UID du HAL de +1 000 000.

Vérifier le démarrage

Si vous souhaitez prendre en charge le démarrage des images Developer GSI dans l'état LOCKED sans désactiver le démarrage vérifié, incluez les clés Developer GSI 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 la restauration

Lors de l'utilisation de DSU, l'image système Android téléchargée doit être plus récente que l'image système actuelle sur l'appareil. Cela se fait en comparant les niveaux de correctifs de sécurité dans le descripteur de propriété AVB Android Verified Boot (AVB) des deux images système : Prop: com.android.build.system.security_patch -> '2019-04-05' .

Pour les appareils n'utilisant pas AVB, placez le niveau de correctif de sécurité de l'image système actuelle dans la ligne de commande du noyau ou dans la configuration de démarrage avec le chargeur de démarrage : androidboot.system.security_patch=2019-04-05 .

Exigences matérielles

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 comme 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. 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 essentielle pour les appareils moins puissants qui ne disposent pas de suffisamment de stockage interne. Lorsqu'une carte SD est présente, assurez-vous qu'elle n'est pas adoptée. DSU ne prend pas en charge les cartes SD adoptées .

Interfaces disponibles

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

Lancez DSU en utilisant adb

Pour lancer DSU à l'aide d'adb, entrez 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 DSU à l'aide d'une application

Le principal point d'entrée de DSU est l'API android.os.image.DynamicSystemClient.java :

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. Étant donné que DynamicSystemClient est une API système, vous ne pouvez pas créer l'application avec l'API SDK standard et vous ne pouvez pas la publier sur Google Play. Le but de cette application est :

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

    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 compressé et non fragmenté, que vous pouvez créer avec les commandes suivantes :

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

Le nom du fichier doit suivre ce format :

<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.

Lancement du chargeur DSU

Figure 1. Lancement du chargeur DSU

Lorsque le développeur clique sur le bouton DSU Loader , il récupère un descripteur DSU JSON préconfiguré sur le Web et affiche toutes les images applicables dans le menu flottant. Sélectionnez une image pour démarrer l'installation de DSU et la progression s'affiche dans la barre de notification.

Progression de l'installation de l'image DSU

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

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

Indicateur de fonctionnalité

La fonctionnalité DSU se trouve sous l’indicateur de fonctionnalité settings_dynamic_android . Avant d'utiliser DSU, assurez-vous que l'indicateur de fonctionnalité correspondant est activé.

Activation de l'indicateur de fonctionnalité.

Figure 3. Activation de l'indicateur de fonctionnalité

L’interface utilisateur de l’indicateur de fonctionnalité peut ne pas être disponible sur un appareil exécutant une build utilisateur. Dans ce cas, utilisez plutôt la commande adb :

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

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

L'un des emplacements de stockage possibles pour les images système est le compartiment Google Compute Engine (GCE). 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é ici :

Accès public au GCE

Figure 4. Accès public dans GCE

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

DSU à partitions multiples dans un fichier ZIP

À partir d’Android 11, DSU peut avoir plusieurs partitions. Par exemple, il peut contenir un product.img en plus du system.img . Lorsque le périphérique démarre, la première étape init détecte les partitions DSU installées et remplace temporairement la partition sur le périphérique, lorsque le DSU installé est activé. Le package DSU peut contenir une partition qui n'a pas de partition correspondante sur le périphérique.

Processus DSU avec plusieurs partitions

Figure 5. Processus DSU avec plusieurs partitions

DSU signé 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
}

system.img et product.img doivent être signés par la clé OEM avant d'être placés dans le fichier ZIP. La pratique courante consiste à utiliser un algorithme asymétrique, par exemple RSA, dans lequel la clé secrète est utilisée pour signer le package et la clé publique est utilisée pour le vérifier. Le disque virtuel de première étape doit inclure la clé publique d'association, par exemple /avb/*.avbpubkey . Si l'appareil a déjà adopté AVB, la procédure de signature existante suffira. Les sections suivantes illustrent le processus de signature et mettent en évidence l'emplacement de la clé publique AVB utilisée pour vérifier les images dans le package DSU.

Descripteur JSON DSU

Le descripteur DSU JSON décrit les packages DSU. Il prend en charge deux primitives. Premièrement, la primitive include inclut des descripteurs JSON supplémentaires ou redirige le chargeur DSU vers un nouvel emplacement. Par exemple:

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

Deuxièmement, la primitive image est utilisée pour décrire les packages DSU publiés. À l'intérieur de la primitive d'image, il y a plusieurs attributs :

  • Les attributs name et details sont des chaînes affichées 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 contrôles de compatibilité, qui sont décrits dans la section suivante.

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

  • L'attribut tos facultatif pointe vers un fichier texte qui décrit les conditions de service du package DSU correspondant. Lorsqu'un développeur sélectionne un package DSU avec l'attribut de conditions de service spécifié, la boîte de dialogue illustrée dans la figure 6 s'ouvre, demandant au développeur d'accepter les conditions de service avant d'installer le package DSU.

    Boîte de dialogue Conditions d'utilisation

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

Pour référence, voici un descripteur DSU JSON 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 le périphérique local :

  • cpu_api est une chaîne qui décrit l'architecture du périphérique. Cet attribut est obligatoire et est comparé à 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 d'Android. 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 égal ou supérieur à la propriété système ro.system.build.version.release . Cette vérification est utilisée pour empêcher le démarrage d’une image GSI Android 10 sur un appareil du fournisseur Android 11, 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 numéro 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 disque virtuel 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 des clés DSU contient une liste des clés publiques AVB révoquées. Lors de l'installation de DSU, les clés publiques contenues dans les images DSU sont validées avec la liste de révocation. Si les images contiennent une clé publique révoquée, le processus d'installation de DSU s'arrête.

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

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 ressources peut être superposée et personnalisée, afin que les OEM qui adoptent la fonctionnalité DSU puissent fournir et maintenir leur propre liste noire de clés. Cela permet à l'OEM de bloquer certaines clés publiques sans mettre à jour l'image du disque virtuel de l'appareil.

Le format de la liste de révocation est :

{
   "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ésumé SHA-1 de la clé révoquée, dans le format décrit dans la section génération de la clé publique AVB .
  • status indique l’état de révocation de la clé. Actuellement, la seule valeur prise en charge est REVOKED .
  • reason est une chaîne facultative décrivant le motif de la révocation.

Procédures du Mémorandum d'accord

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

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

Utilisez la commande openssl pour générer une paire de clés privée/publique RSA au format .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ériel (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 Ajout de la clé publique de couplage au disque virtuel pour obtenir des instructions sur la génération de 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.

Ajoutez la clé publique d'appariement au disque virtuel

Le oem_cert.avbpubkey doit être placé sous /avb/*.avbpubkey pour vérifier le package DSU signé. Tout d’abord, convertissez la clé publique au format PEM au format de clé publique AVB :

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

Incluez ensuite la clé publique dans le disque virtuel de première étape en suivant les étapes suivantes.

  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 dépendre la cible droidcore de la oem_cert.avbpubkey ajoutée :

    droidcore: oem_cert.avbpubkey
    

Générer l'attribut pubkey AVB dans le descripteur JSON

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

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

Ce sera le contenu de l'attribut pubkey du descripteur JSON.

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

Signer un package DSU

Utilisez l’une de ces méthodes pour signer un package DSU :

  • Méthode 1 : réutilisez l’artefact créé par le processus de signature AVB d’origine pour créer un package DSU. Une approche alternative consiste à extraire les images déjà signées du package de version et à utiliser les images extraites pour créer directement le fichier ZIP.

  • 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 (le fichier ZIP) est signée 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 plus d'informations sur l'ajout add_hashtree_footer à l'aide avbtool , consultez Utilisation de avbtool .

Vérifier le package DSU localement

Il est recommandé de vérifier toutes les images locales par rapport à la clé publique d'appariement avec 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 qui contient 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 -

Personnalisez le DSU en un clic

Par défaut, le chargeur DSU pointe vers une métadonnée d'images GSI qui est https://...google.com/.../gsi-src.json .

Les OEM peuvent écraser la liste en définissant la propriété persist.sys.fflag.override.settings_dynamic_system.list qui pointe vers leur propre descripteur JSON. Par exemple, un OEM peut fournir des métadonnées JSON qui incluent GSI ainsi que des images propriétaires OEM comme celle-ci :

{
    "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"
      },

}

Il est possible pour un OEM de chaîner les métadonnées DSU publiées, comme le montre la figure 7.

Chaînage des métadonnées DSU publiées

Figure 7. Chaînage des métadonnées DSU publiées