Format de fichier APEX

Android Pony EXpress (APEX) est un format de conteneur introduit dans Android 10 qui est utilisé dans le flux d'installation pour les modules système de niveau inférieur. Ce format facilite les mises à jour des composants du système qui ne correspondent pas au modèle d'application Android standard. Certains composants sont par exemple des services natifs et des bibliothèques, des couches d'abstraction matérielle ( HAL ), exécution ( ART ), et les bibliothèques de classes.

Le terme "APEX" peut également faire référence à un fichier APEX.

Fond

Bien qu'Android prenne en charge les mises à jour des modules qui correspondent au modèle d'application standard (par exemple, les services, les activités) via les applications d'installation de packages (telles que l'application Google Play Store), l'utilisation d'un modèle similaire pour les composants de système d'exploitation de niveau inférieur présente les inconvénients suivants :

  • Les modules basés sur APK ne peuvent pas être utilisés au début de la séquence de démarrage. Le gestionnaire de packages est le référentiel central d'informations sur les applications et ne peut être démarré qu'à partir du gestionnaire d'activités, qui devient prêt à une étape ultérieure de la procédure de démarrage.
  • Le format APK (en particulier le manifeste) est conçu pour les applications Android et les modules système ne conviennent pas toujours.

Concevoir

Cette section décrit la conception de haut niveau du format de fichier APEX et du gestionnaire APEX, qui est un service qui gère les fichiers APEX.

Pour plus d' informations sur les raisons de cette conception pour APEX a été sélectionné, voir Autres solutions envisagées lors de l' élaboration APEX .

Format APEX

C'est le format d'un fichier APEX.

Format de fichier APEX

Figure 1. Format de fichier APEX

Au niveau supérieur, un fichier APEX est un fichier zip dans lequel les fichiers sont stockés non compressés et situés aux limites de 4 Ko.

Les quatre fichiers d'un fichier APEX sont :

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

Le apex_manifest.json fichier contient le nom du package et la version, qui identifient un fichier APEX.

Le AndroidManifest.xml fichier permet au fichier APEX d'utiliser des outils liés à la APK et des infrastructures telles que la BAD, PackageManager et applications d'installation de package (comme Play Store). Par exemple, le fichier APEX peut utiliser un outil existant tel que aapt pour inspecter les métadonnées de base à partir du fichier. Le fichier contient le nom du package et les informations de version. Cette information est généralement disponible en apex_manifest.json .

apex_manifest.json est recommandé sur AndroidManifest.xml pour le nouveau code et les systèmes qui traitent de l' APEX. AndroidManifest.xml peut contenir des informations de ciblage supplémentaire qui peut être utilisé par les outils de publication d'applications existantes.

apex_payload.img est une image de système de fichiers ext4 soutenu par dm-verity. L'image est montée au moment de l'exécution via un périphérique de bouclage. Plus précisément, l'arbre de hachage et le bloc de métadonnées sont créés à l'aide de libavb. La charge utile du système de fichiers n'est pas analysée (car l'image doit pouvoir être montée sur place). Les fichiers normaux sont inclus dans le apex_payload.img fichier.

apex_pubkey est la clé publique utilisée pour signer l'image du système de fichiers. Au moment de l'exécution, cette clé garantit que l'APEX téléchargé est signé avec la même entité qui signe le même APEX dans les partitions intégrées.

Responsable APEX

Le directeur APEX (ou apexd ) est un processus natif autonome responsable de la vérification, l' installation et la désinstallation des fichiers APEX. Ce processus est lancé et est prêt au début de la séquence de démarrage. Fichiers APEX sont pré-installés normalement sur l'appareil sous /system/apex . Le gestionnaire APEX utilise par défaut ces packages si aucune mise à jour n'est disponible.

La séquence de mise à jour d'une APEX utilise la classe PackageManager et se présente comme suit.

  1. Un fichier APEX est téléchargé via une application d'installation de package, ADB ou une autre source.
  2. Le gestionnaire de paquets démarre la procédure d'installation. Lorsqu'il reconnaît que le fichier est un APEX, le gestionnaire de packages transfère le contrôle au gestionnaire APEX.
  3. Le gestionnaire APEX vérifie le fichier APEX.
  4. Si le fichier APEX est vérifié, la base de données interne du gestionnaire APEX est mise à jour pour indiquer que le fichier APEX sera activé au prochain démarrage.
  5. Le demandeur de l'installation reçoit une diffusion lors de la vérification réussie du package.
  6. Pour continuer l'installation, le système doit être redémarré.
  7. Au prochain démarrage, le gestionnaire APEX démarre, lit la base de données interne et effectue les opérations suivantes pour chaque fichier APEX répertorié :

    1. Vérifie le fichier APEX.
    2. Crée un périphérique de bouclage à partir du fichier APEX.
    3. Crée un périphérique de bloc de mappeur de périphérique au-dessus du périphérique de bouclage.
    4. Montures le dispositif de bloc dispositif de mapper sur une trajectoire unique (par exemple, /apex/ name @ ver ).

Lorsque tous les fichiers APEX répertoriés dans la base de données interne sont montés, le gestionnaire APEX fournit un service de classeur pour les autres composants du système afin de demander des informations sur les fichiers APEX installés. Par exemple, les autres composants du système peuvent interroger la liste des fichiers APEX installés dans l'appareil ou interroger le chemin exact où un APEX spécifique est monté, afin que les fichiers soient accessibles.

Les fichiers APEX sont des fichiers APK

Fichiers APEX sont valides fichiers APK parce qu'ils sont signés zip archives ( en utilisant le schéma de signature APK) contenant un AndroidManifest.xml fichier. Cela permet aux fichiers APEX d'utiliser l'infrastructure pour les fichiers APK, telle qu'une application d'installation de packages, l'utilitaire de signature et le gestionnaire de packages.

Le AndroidManifest.xml fichier à l' intérieur d' un fichier APEX est minime, composé du paquet name , versionCode , et en option targetSdkVersion , minSdkVersion et maxSdkVersion pour grains fins de ciblage. Ces informations permettent aux fichiers APEX d'être livrés via des canaux existants tels que les applications d'installation de packages et ADB.

Types de fichiers pris en charge

Le format APEX prend en charge les types de fichiers suivants :

  • Bibliothèques partagées natives
  • Exécutables natifs
  • fichiers JAR
  • Fichiers de données
  • Fichiers de configuration

Cela ne signifie pas qu'APEX peut mettre à jour tous ces types de fichiers. La mise à jour d'un type de fichier dépend de la plate-forme et de la stabilité des interfaces définies pour les types de fichiers.

Signature

Les fichiers APEX sont signés de deux manières. Tout d' abord, le apex_payload.img ( plus précisément, le descripteur de vbmeta ajouté à apex_payload.img ) fichier est signé avec une clé. Ensuite, est signé l'ensemble de l' APEX en utilisant le schéma de signature APK v3 . Deux clés différentes sont utilisées dans ce processus.

Côté appareil, une clé publique correspondant à la clé privée utilisée pour signer le descripteur vbmeta est installée. Le gestionnaire APEX utilise la clé publique pour vérifier les APEX dont l'installation est demandée. Chaque APEX doit être signé avec des clés différentes et est appliqué à la fois au moment de la génération et à l'exécution.

APEX dans les partitions intégrées

Les fichiers APEX peuvent être situés dans intégré des partitions telles que /system . La partition est déjà sur dm-verity, donc les fichiers APEX sont montés directement sur le périphérique de bouclage.

Si un APEX est présent dans une partition intégrée, l'APEX peut être mis à jour en fournissant un package APEX avec le même nom de package et un code de version supérieur ou égal. Le nouveau APEX est stocké dans /data et, semblable à APK, les ombres de nouvelle version installée la version déjà présentes dans la partition intégrée. Mais contrairement aux APK, la version nouvellement installée de l'APEX n'est activée qu'après le redémarrage.

Exigences du noyau

Pour prendre en charge les modules principaux APEX sur un appareil Android, les fonctionnalités suivantes du noyau Linux sont requises : le pilote de bouclage et dm-verity. Le pilote de bouclage monte l'image du système de fichiers dans un module APEX et dm-verity vérifie le module APEX.

Les performances du pilote de bouclage et de dm-verity sont importantes pour obtenir de bonnes performances système lors de l'utilisation de modules APEX.

Versions de noyau prises en charge

Les modules principaux APEX sont pris en charge sur les appareils utilisant les versions de noyau 4.4 ou supérieures. Les nouveaux appareils lancés avec Android 10 ou supérieur doivent utiliser la version 4.9 ou supérieure du noyau pour prendre en charge les modules APEX.

Correctifs de noyau requis

Les correctifs de noyau requis pour la prise en charge des modules APEX sont inclus dans l'arborescence commune Android. Pour que les correctifs prennent en charge APEX, utilisez la dernière version de l'arborescence commune Android.

Noyau version 4.4

Cette version n'est prise en charge que pour les appareils mis à niveau d'Android 9 vers Android 10 et qui souhaitent prendre en charge les modules APEX. Pour obtenir les correctifs nécessaires, un bas-fusion de l' android-4.4 branche est fortement recommandée. Ce qui suit est une liste des correctifs individuels requis pour la version 4.4 du noyau.

  • AMONT: boucle: ajouter ioctl pour changer la taille de bloc logique ( 4,4 )
  • Backport: bloc / boucle: set hw_sectors ( 4,4 )
  • AMONT: boucle: Ajouter à LOOP_SET_BLOCK_SIZE compat ioctl ( 4.4 )
  • ANDROID: mnt: Fix next_descendent ( 4.4 )
  • ANDROID: mnt: remount devrait se propager aux esclaves d'esclaves ( 4.4 )
  • ANDROID: mnt: Propager correctement remount ( 4.4 )
  • Revert "ANDROID: dm Verity: ajouter la taille minimum prefetch" ( 4.4 )
  • AMONT: boucle: caches de chute si offset ou block_size sont modifiés ( 4.4 )

Versions du noyau 4.9/4.14/4.19

Pour obtenir les correctifs requis pour les versions du noyau 4.9 / 4.14 / 4.19, en baisse de fusion de l' android-common branche.

Options de configuration du noyau requises

La liste suivante montre les exigences de configuration de base pour la prise en charge des modules APEX qui ont été introduits dans Android 10. Les éléments avec un astérisque (*) sont des exigences existantes à partir d'Android 9 et versions antérieures.

(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support

Exigences relatives aux paramètres de ligne de commande du noyau

Pour prendre en charge APEX, assurez-vous que les paramètres de ligne de commande du noyau répondent aux exigences suivantes.

  • loop.max_loop ne doit pas être ensemble
  • loop.max_part doit être <= 8

Construire un APEX

Cette section décrit comment créer un APEX à l'aide du système de génération Android. Voici un exemple de Android.bp pour un APEX nommé apex.test .

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    // libc.so and libcutils.so are included in the apex
    native_shared_libs: ["libc", "libcutils"],
    binaries: ["vold"],
    java_libs: ["core-all"],
    prebuilts: ["my_prebuilt"],
    compile_multilib: "both",
    key: "apex.test.key",
    certificate: "platform",
}

apex_manifest.json exemple:

{
  "name": "com.android.example.apex",
  "version": 1
}

file_contexts exemple:

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

Types de fichiers et emplacements dans APEX

Type de fichier Emplacement dans l'APEX
Bibliothèques partagées /lib et /lib64 ( /lib/arm pour le bras traduit en x86)
Exécutables /bin
Bibliothèques Java /javalib
Préconstruits /etc

Dépendances transitives

Les fichiers APEX incluent automatiquement les dépendances transitives des bibliothèques ou des exécutables partagés natifs. Par exemple, si libFoo dépend de libBar , les deux libs sont inclus lorsque seulement libFoo est listé dans la native_shared_libs propriété.

Gestion de plusieurs ABI

Installer la native_shared_libs propriété pour les deux interfaces binaires de l' application primaire et secondaire (Abis) du dispositif. Si un APEX cible des appareils avec une seule ABI (c'est-à-dire 32 bits uniquement ou 64 bits uniquement), seules les bibliothèques avec l'ABI correspondante sont installées.

Installer la binaries propriété uniquement pour l'ABI primaire du dispositif tel que décrit ci - dessous:

  • Si le périphérique est en 32 bits uniquement, seule la variante 32 bits du binaire est installée.
  • Si le périphérique est en 64 bits uniquement, seule la variante 64 bits du binaire est installée.

Pour ajouter un contrôle granulaire fin sur les ABIs des bibliothèques et des binaires natifs, utilisez le multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries] propriétés.

  • first : Matchs de l'ABI primaire du dispositif. C'est la valeur par défaut pour les binaires.
  • lib32 : Matchs 32 bits ABI de l'appareil, si pris en charge.
  • lib64 : Résultats de la 64-bit ABI du dispositif, elle appuie.
  • prefer32 : Matchs 32 bits ABI de l'appareil, si pris en charge. Si l'ABI 32 bits n'est pas prise en charge, correspond à l'ABI 64 bits.
  • à la both : les deux matchs ABIs. Ceci est la valeur par défaut pour native_shared_libraries .

Les java , les libraries et prebuilts propriétés sont ABI-agnostique.

Cet exemple concerne un appareil qui prend en charge le 32/64 et ne préfère pas le 32 :

apex {
    // other properties are omitted
    native_shared_libs: ["libFoo"], // installed for 32 and 64
    binaries: ["exec1"], // installed for 64, but not for 32
    multilib: {
        first: {
            native_shared_libs: ["libBar"], // installed for 64, but not for 32
            binaries: ["exec2"], // same as binaries without multilib.first
        },
        both: {
            native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
            binaries: ["exec3"], // installed for 32 and 64
        },
        prefer32: {
            native_shared_libs: ["libX"], // installed for 32, but not for 64
        },
        lib64: {
            native_shared_libs: ["libY"], // installed for 64, but not for 32
        },
    },
}

signature vbmeta

Signez chaque APEX avec des clés différentes. Lorsqu'une nouvelle clé est nécessaire, créer une clé privée ou publique , et faire un apex_key module. Utilisez la key propriété pour signer l'APEX en utilisant la clé. La clé publique est automatiquement incluse dans l'APEX avec le nom avb_pubkey .

# create an rsa key pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_key {
    name: "apex.test.key",
    public_key: "foo.avbpubkey",
    private_key: "foo.pem",
}

Dans l'exemple ci - dessus, le nom de la clé publique ( foo ) devient l'ID de la clé. L'ID de la clé utilisée pour signer un APEX est écrit dans l'APEX. Lors de l' exécution, apexd vérifie l'APEX en utilisant une clé publique avec le même ID dans l'appareil.

Signature ZIP

Signez les APEX de la même manière que les APK. Inscrivez -vous APEXS deux fois, une fois pour le mini - système de fichiers ( apex_payload.img de fichier) et une fois pour l'ensemble du dossier.

Pour signer un APEX au niveau fichier, définissez le certificate propriété dans l' une de ces trois façons:

  • Non défini: Si aucune valeur est définie, l'APEX est signé avec le certificat situé à PRODUCT_DEFAULT_DEV_CERTIFICATE . Si aucun indicateur est défini, les paramètres par défaut de chemin de build/target/product/security/testkey .
  • <name> : L'APEX est signé avec le <name> certificat dans le même répertoire que PRODUCT_DEFAULT_DEV_CERTIFICATE .
  • :<name> : L'APEX est signé avec le certificat qui est défini par le module Soong nommé <name> . Le module de certificat peut être défini comme suit.
android_app_certificate {
    name: "my_key_name",
    certificate: "dir/cert",
    // this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}

Installation d'un APEX

Pour installer un APEX, utilisez ADB.

adb install apex_file_name
adb reboot

Utiliser un APEX

Après le redémarrage, l'APEX est monté au /apex/<apex_name>@<version> répertoire. Plusieurs versions du même APEX peuvent être montées en même temps. Parmi les chemins de montage, celui qui correspond à la dernière version est bind monté à /apex/<apex_name> .

Les clients peuvent utiliser le chemin monté par liaison pour lire ou exécuter des fichiers à partir d'APEX.

Les APEX sont généralement utilisés comme suit :

  1. Un OEM ou ODM précharges un sommet sous /system/apex lorsque l'appareil est expédié.
  2. Les fichiers de l'APEX sont accessibles via le /apex/<apex_name>/ chemin.
  3. Lorsqu'une version mise à jour de l'APEX est installé dans /data/apex , les points de chemin vers le nouveau APEX après le redémarrage.

Mettre à jour un service avec un APEX

Pour mettre à jour un service à l'aide d'un APEX :

  1. Marquez le service dans la partition système comme pouvant être mis à jour. Ajouter l'option updatable à la définition du service.

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. Créer un nouveau .rc fichier pour le service mis à jour. Utilisez l' override option pour redéfinir le service existant.

    /apex/my.apex@1/etc/init.rc:
    
    service myservice /apex/my.apex@1/bin/myservice
        class core
        user system
        ...
        override
    

Les définitions des services ne peuvent être définis dans le .rc fichier d'un APEX. Les déclencheurs d'action ne sont pas pris en charge dans les APEX.

Si un service marqué comme pouvant être mis à jour démarre avant l'activation des APEX, le démarrage est retardé jusqu'à ce que l'activation des APEX soit terminée.

Configuration du système pour prendre en charge les mises à jour APEX

Définissez la propriété système suivante à true pour soutenir les mises à jour de fichiers APEX.

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

ou juste

<device.mk>:

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

APEX aplati

Pour les périphériques hérités, il est parfois impossible ou irréalisable de mettre à jour l'ancien noyau pour prendre pleinement en charge APEX. Par exemple, le noyau pourrait avoir été construit sans CONFIG_BLK_DEV_LOOP=Y , ce qui est crucial pour le montage de l'image du système de fichiers à l' intérieur d' un APEX.

L'APEX aplati est un APEX spécialement conçu qui peut être activé sur les appareils dotés d'un noyau hérité. Les fichiers d'un APEX aplati sont directement installés dans un répertoire sous la partition intégrée. Par exemple, lib/libFoo.so dans un flattend APEX my.apex est installé à /system/apex/my.apex/lib/libFoo.so .

L'activation d'un APEX aplati n'implique pas le périphérique de boucle. Le répertoire entier /system/apex/my.apex est bind monté directement à /apex/name@ver .

Les APEX aplatis ne peuvent pas être mis à jour en téléchargeant des versions mises à jour des APEX à partir du réseau, car les APEX téléchargés ne peuvent pas être aplatis. Les APEX aplatis ne peuvent être mis à jour que via une OTA standard.

L'APEX aplati est la configuration par défaut. Cela signifie que tous les APEX sont aplatis par défaut, sauf si vous configurez explicitement votre appareil pour créer des APEX non aplatis pour prendre en charge les mises à jour APEX (comme expliqué ci-dessus).

Le mélange d'APEX aplatis et non aplatis dans un périphérique n'est PAS pris en charge. Les APEX d'un appareil doivent être soit tous non aplatis, soit tous aplatis. Ceci est particulièrement important lors de l'expédition de préconfigurations APEX pré-signées pour des projets tels que Mainline. Les APEX qui ne sont pas pré-signés (c'est-à-dire construits à partir de la source) doivent également être non aplatis et signés avec les clés appropriées. L'appareil doit hériter de updatable_apex.mk comme expliqué dans la mise à jour d' un service avec un APEX .

Alternatives envisagées lors du développement de l'APEX

Voici quelques options que nous avons prises en compte lors de la conception du format de fichier APEX, et pourquoi nous les avons incluses ou exclues.

Systèmes de gestion de colis réguliers

Distributions Linux ont des systèmes de gestion de paquets comme dpkg et rpm , qui sont puissants, matures, robustes. Cependant, ils n'ont pas été adoptés pour APEX car ils ne peuvent pas protéger les packages après l'installation. La vérification n'est effectuée que lors de l'installation des packages. Les attaquants peuvent briser l'intégrité des packages installés sans se faire remarquer. Il s'agit d'une régression pour Android où tous les composants du système étaient stockés dans des systèmes de fichiers en lecture seule dont l'intégrité est protégée par dm-verity pour chaque E/S. Toute altération des composants du système doit être interdite ou être détectable afin que l'appareil puisse refuser de démarrer s'il est compromis.

dm-crypt pour l'intégrité

Les fichiers contenus dans un récipient APEX sont de partitions intégré (par exemple, le /system partition) qui sont protégés par dm-Verity, où toute modification des fichiers sont interdits , même après les cloisons sont montées. Pour fournir le même niveau de sécurité aux fichiers, tous les fichiers d'un APEX sont stockés dans une image de système de fichiers associée à un arbre de hachage et à un descripteur vbmeta. Sans dm-Verity, un APEX dans les /data partition est vulnérable à des modifications non intentionnelles effectuées après qu'il est vérifié et installé.

En fait, les /data partition est également protégé par des couches de cryptage telles que dm-crypt. Bien que cela offre un certain niveau de protection contre la falsification, son objectif principal est la confidentialité et non l'intégrité. Quand un accès aux gains attaquant à la /data les /system /data partition, il n'y a aucune protection supplémentaire, ce qui est à nouveau une régression par rapport à tous les composants du système étant dans le /system partition. L'arbre de hachage à l'intérieur d'un fichier APEX avec dm-verity offre le même niveau de protection du contenu.

Redirigeant chemins de /system à /apex

Fichiers de composants système emballés dans un APEX sont accessibles par l' intermédiaire de nouvelles voies comme /apex/<name>/lib/libfoo.so . Lorsque les fichiers faisaient partie du /system partition, ils étaient accessibles par des chemins tels que /system/lib/libfoo.so . Un client d'un fichier APEX (autres fichiers APEX ou la plate-forme) doit utiliser les nouveaux chemins. Cette modification des chemins peut nécessiter des mises à jour du code existant.

Une façon d'éviter le changement de chemin est de superposer le contenu du fichier dans un fichier APEX sur le /system partition. Cependant, nous avons décidé de ne pas les fichiers sur le recouvrement /system partition parce que nous croyions cela aurait un effet négatif sur les performances que le nombre de fichiers superposée (peut - être même empilés les uns après les autres) augmente.

Une autre option est de détourner les fonctions d'accès aux fichiers tels que open , stat , et readlink , de sorte que les chemins qui commencent par /system sont redirigés vers leurs sous chemins correspondants /apex . Nous avons rejeté cette option car il est pratiquement impossible de modifier toutes les fonctions qui acceptent les chemins. Par exemple, certaines applications relient statiquement Bionic, qui implémente les fonctions. Dans ce cas, la redirection ne se produira pas pour l'application.