Format de fichier APEX

Le format de conteneur Android Pony EXpress (APEX) a été introduit dans Android 10 et 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 rentrent pas dans le modèle d'application Android standard. Certains exemples de composants sont les services et bibliothèques natifs, les couches d'abstraction matérielle ( HAL ), le runtime ( ART ) et les bibliothèques de classes.

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

Arrière-plan

Bien qu'Android prenne en charge les mises à jour de modules qui correspondent au modèle d'application standard (par exemple, services, activités) via des applications d'installation de packages (telles que l'application Google Play Store), l'utilisation d'un modèle similaire pour les composants du 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 sera 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.

Conception

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 pour lesquelles cette conception pour APEX a été sélectionnée, voir Alternatives envisagées lors du développement d'APEX .

Format APEX

Il s'agit du 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 dans des limites de 4 Ko.

Les quatre fichiers d'un fichier APEX sont :

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

Le fichier apex_manifest.json contient le nom et la version du package, qui identifient un fichier APEX. Il s'agit d'un tampon de protocole ApexManifest au format JSON.

Le fichier AndroidManifest.xml permet au fichier APEX d'utiliser des outils et une infrastructure liés à l'APK tels qu'ADB, PackageManager et des applications d'installation de packages (telles que Play Store). Par exemple, le fichier APEX peut utiliser un outil existant tel que aapt pour inspecter les métadonnées de base du fichier. Le fichier contient le nom du package et les informations de version. Ces informations sont généralement également disponibles dans apex_manifest.json .

apex_manifest.json est recommandé par rapport à AndroidManifest.xml pour les nouveaux codes et systèmes traitant d'APEX. AndroidManifest.xml peut contenir des informations de ciblage supplémentaires pouvant être utilisées par les outils de publication d'applications existants.

apex_payload.img est une image du système de fichiers ext4 soutenue 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 la bibliothèque 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 fichier apex_payload.img .

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.

Directives de dénomination APEX

Pour éviter les conflits de noms entre les nouveaux APEX à mesure que la plateforme progresse, utilisez les directives de dénomination suivantes :

  • com.android.*
    • Réservé aux AOSP APEX. Ce n’est pas propre à une entreprise ou à un appareil.
  • com.<companyname>.*
    • Réservé à une entreprise. Potentiellement utilisé par plusieurs appareils de cette entreprise.
  • com.<companyname>.<devicename>.*
    • Réservé aux APEX uniques à un périphérique spécifique (ou sous-ensemble de périphériques).

Responsable APEX

Le gestionnaire APEX (ou apexd ) est un processus natif autonome chargé de vérifier, d'installer et de désinstaller les fichiers APEX. Ce processus est lancé et est prêt au début de la séquence de démarrage. Les fichiers APEX sont normalement préinstallés 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'un APEX utilise la classe PackageManager et est la suivante.

  1. Un fichier APEX est téléchargé via une application d'installation de package, ADB ou une autre source.
  2. Le gestionnaire de packages démarre la procédure d'installation. Après avoir reconnu 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 est activé au prochain démarrage.
  5. Le demandeur d'installation reçoit une diffusion une fois la vérification réussie du package.
  6. Pour poursuivre 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 mappage de périphérique au-dessus du périphérique de bouclage.
    4. Monte le périphérique de bloc du mappeur de périphérique sur un chemin 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 liaison permettant aux autres composants du système d'interroger 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 sur le périphérique 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

Les fichiers APEX sont des fichiers APK valides car ce sont des archives zip signées (en utilisant le schéma de signature APK) contenant un fichier AndroidManifest.xml . Cela permet aux fichiers APEX d'utiliser l'infrastructure des fichiers APK, telle qu'une application d'installation de packages, l'utilitaire de signature et le gestionnaire de packages.

Le fichier AndroidManifest.xml à l'intérieur d'un fichier APEX est minimal et comprend le name du package , versionCode et facultatifs targetSdkVersion , minSdkVersion et maxSdkVersion pour un ciblage précis. Ces informations permettent aux fichiers APEX d'être livrés via les canaux existants tels que les applications d'installation de packages et ADB.

Types de fichiers pris en charge

Le format APEX prend en charge ces types de fichiers :

  • 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 possibilité de mettre à jour un type de fichier dépend de la plate-forme et de la stabilité des définitions des interfaces pour les types de fichiers.

Options de signature

Les fichiers APEX sont signés de deux manières. Tout d'abord, le fichier apex_payload.img (en particulier, le descripteur vbmeta ajouté à apex_payload.img ) est signé avec une clé. Ensuite, l'intégralité de l'APEX est signée à l'aide du 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 construction et au moment de l'exécution.

APEX dans les partitions intégrées

Les fichiers APEX peuvent être situés dans des partitions intégrées 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 nouvel APEX est stocké dans /data et, comme pour les APK, la version nouvellement installée masque la version déjà présente 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 du système lors de l'utilisation de modules APEX.

Versions du 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 obtenir les correctifs prenant en charge APEX, utilisez la dernière version de l’arborescence commune Android.

Version du noyau 4.4

Cette version n'est prise en charge que pour les appareils mis à niveau d'Android 9 vers Android 10 et souhaitant prendre en charge les modules APEX. Pour obtenir les correctifs requis, une fusion descendante depuis la branche android-4.4 est fortement recommandée. Voici une liste des correctifs individuels requis pour la version 4.4 du noyau.

  • UPSTREAM : boucle : ajoutez ioctl pour modifier la taille du bloc logique ( 4.4 )
  • BACKPORT : bloc/boucle : définir hw_sectors ( 4.4 )
  • UPSTREAM : boucle : ajoutez LOOP_SET_BLOCK_SIZE dans compat ioctl ( 4.4 )
  • ANDROID : mnt : correction de next_descendent ( 4.4 )
  • ANDROID : mnt : le remontage doit se propager aux esclaves des esclaves ( 4.4 )
  • ANDROID : mnt : propager correctement le remontage ( 4.4 )
  • Rétablissez "ANDROID : dm verity : ajoutez la taille minimale de prélecture" ( 4.4 )
  • UPSTREAM : boucle : supprimez les caches 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, effectuez une fusion descendante à partir de la branche android-common .

Options de configuration du noyau requises

La liste suivante présente les exigences de configuration de base pour la prise en charge des modules APEX introduits dans Android 10. Les éléments marqués d'un astérisque (*) correspondent aux exigences existantes 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 défini
  • loop.max_part doit être <= 8

Construire un APEX

Cette section décrit comment créer un APEX à l'aide du système de construction 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",
}

Exemple apex_manifest.json :

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

Exemple file_contexts :

(/.*)?           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 Localisation dans l'APEX
Bibliothèques partagées /lib et /lib64 ( /lib/arm pour arm 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 partagées ou exécutables natifs. Par exemple, si libFoo dépend de libBar , les deux bibliothèques sont incluses lorsque seule libFoo est répertoriée dans la propriété native_shared_libs .

Gérer plusieurs ABI

Installez la propriété native_shared_libs pour les interfaces binaires d'application (ABI) principales et secondaires de l'appareil. Si un APEX cible des périphériques avec un seul ABI (c'est-à-dire 32 bits uniquement ou 64 bits uniquement), seules les bibliothèques avec l'ABI correspondant sont installées.

Installez la propriété binaries uniquement pour l'ABI principal de l'appareil, comme décrit ci-dessous :

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

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

  • first : correspond à l'ABI principal de l'appareil. C'est la valeur par défaut pour les binaires.
  • lib32 : correspond à l'ABI 32 bits de l'appareil, s'il est pris en charge.
  • lib64 : correspond à l'ABI 64 bits de l'appareil pris en charge.
  • prefer32 : correspond à l'ABI 32 bits de l'appareil, s'il est pris en charge. Si l'ABI 32 bits n'est pas pris en charge, correspond à l'ABI 64 bits.
  • both : correspond aux deux ABI. Il s'agit de la valeur par défaut pour native_shared_libraries .

Les propriétés java , libraries et prebuilts sont indépendantes de l'ABI.

Cet exemple concerne un appareil prenant en charge le 32/64 et ne préférant 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 requise, créez une paire de clés publique-privée et créez un module apex_key . Utilisez la propriété key pour signer l'APEX à l'aide de la clé. La clé publique est automatiquement incluse dans l'APEX sous 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. Au moment de l'exécution, apexd vérifie l'APEX à l'aide d'une clé publique avec le même ID dans l'appareil.

Signature de l'APEX

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

Pour signer un APEX au niveau du fichier, définissez la propriété certificate de l'une des trois manières suivantes :

  • Non défini : si aucune valeur n'est définie, l'APEX est signé avec le certificat situé dans PRODUCT_DEFAULT_DEV_CERTIFICATE . Si aucun indicateur n'est défini, le chemin par défaut est build/target/product/security/testkey .
  • <name> : L'APEX est signé avec le certificat <name> dans le même répertoire que PRODUCT_DEFAULT_DEV_CERTIFICATE .
  • :<name> : L'APEX est signé avec le certificat 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)
}

Installer un APEX

Pour installer un APEX, utilisez ADB.

adb install apex_file_name
adb reboot

Si supportsRebootlessUpdate est défini sur true dans apex_manifest.json et que l'APEX actuellement installé n'est pas utilisé (par exemple, tous les services qu'il contient ont été arrêtés), alors un nouvel APEX peut être installé sans redémarrage avec l'indicateur --force-non-staged .

adb install --force-non-staged apex_file_name

Utiliser un APEX

Après le redémarrage, l'APEX est monté dans le répertoire /apex/<apex_name>@<version> . 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 monté en liaison sur /apex/<apex_name> .

Les clients peuvent utiliser le chemin monté par liaison pour lire ou exécuter des fichiers depuis APEX.

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

  1. Un OEM ou un ODM précharge un APEX sous /system/apex lorsque l'appareil est expédié.
  2. Les fichiers de l'APEX sont accessibles via le chemin /apex/<apex_name>/ .
  3. Lorsqu'une version mise à jour de l'APEX est installée dans /data/apex , le chemin pointe vers le nouvel 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. Ajoutez 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éez un nouveau fichier .rc pour le service mis à jour. Utilisez l'option override pour redéfinir le service existant.

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

Les définitions de service ne peuvent être définies que dans le fichier .rc 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.

Configurer le système pour prendre en charge les mises à jour APEX

Définissez la propriété système suivante sur true pour prendre en charge les mises à jour des 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 appareils existants, il est parfois impossible, voire irréalisable, de mettre à jour l'ancien noyau pour prendre entièrement en charge APEX. Par exemple, le noyau peut avoir été construit sans CONFIG_BLK_DEV_LOOP=Y , ce qui est crucial pour monter l'image du système de fichiers dans 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 APEX aplati my.apex est installé sur /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 directement monté en liaison sur /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 un OTA standard.

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 afin de 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éconstruits APEX pré-signés 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. Le périphérique doit hériter de updatable_apex.mk comme expliqué dans Mise à jour d'un service avec un APEX .

APEX compressés

Android 12 et versions ultérieures proposent la compression APEX pour réduire l'impact sur le stockage des packages APEX pouvant être mis à jour. Après l'installation d'une mise à jour d'un APEX, même si sa version préinstallée n'est plus utilisée, elle occupe toujours la même quantité d'espace. Cet espace occupé reste indisponible.

La compression APEX minimise cet impact sur le stockage en utilisant un ensemble hautement compressé de fichiers APEX sur des partitions en lecture seule (telles que la partition /system ). Android 12 et versions ultérieures utilisent un algorithme de compression zip DEFLATE.

La compression ne fournit pas d'optimisation pour les éléments suivants :

  • Bootstrap APEX qui doivent être montés très tôt dans la séquence de démarrage.

  • APEX non actualisables. La compression n'est bénéfique que si une version mise à jour d'un APEX est installée sur la partition /data . Une liste complète des APEX pouvant être mis à jour est disponible sur la page Composants du système modulaire .

  • Bibliothèques partagées dynamiques APEX. Étant donné apexd active toujours les deux versions de ces APEX (préinstallées et mises à niveau), leur compression n'ajoute aucune valeur.

Format de fichier APEX compressé

Il s'agit du format d'un fichier APEX compressé.

Diagram shows the format of a compressed APEX file

Figure 2. Format de fichier APEX compressé

Au niveau supérieur, un fichier APEX compressé est un fichier zip contenant le fichier apex d'origine sous forme dégonflée avec un niveau de compression de 9 et avec d'autres fichiers stockés non compressés.

Quatre fichiers composent un fichier APEX :

  • original_apex : dégonflé avec un niveau de compression de 9. Il s'agit du fichier APEX original non compressé.
  • apex_manifest.pb : stocké uniquement
  • AndroidManifest.xml : stocké uniquement
  • apex_pubkey : stocké uniquement

Les fichiers apex_manifest.pb , AndroidManifest.xml et apex_pubkey sont des copies de leurs fichiers correspondants dans original_apex .

Créer un APEX compressé

L'APEX compressé peut être construit à l'aide de l'outil apex_compression_tool.py situé dans system/apex/tools .

Plusieurs paramètres liés à la compression APEX sont disponibles dans le système de build.

Dans Android.bp , la compressibilité d'un fichier APEX est contrôlée par la propriété compressible :

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

Un indicateur de produit PRODUCT_COMPRESSED_APEX contrôle si une image système créée à partir de la source doit contenir des fichiers APEX compressés.

Pour une expérimentation locale, vous pouvez forcer une build à compresser les APEX en définissant OVERRIDE_PRODUCT_COMPRESSED_APEX= sur true .

Les fichiers APEX compressés générés par le système de build portent l'extension .capex . L'extension facilite la distinction entre les versions compressées et non compressées d'un fichier APEX.

Algorithmes de compression pris en charge

Android 12 ne prend en charge que la compression dégonflée-zip.

Activer un fichier APEX compressé lors du démarrage

Avant qu'un APEX compressé puisse être activé, le fichier original_apex qu'il contient est décompressé dans le répertoire /data/apex/decompressed . Le fichier APEX décompressé résultant est lié en dur au répertoire /data/apex/active .

Considérez l’exemple suivant comme illustration du processus décrit ci-dessus.

Considérez /system/apex/com.android.foo.capex comme un APEX compressé en cours d'activation, avec le code de version 37.

  1. Le fichier original_apex à l'intérieur /system/apex/com.android.foo.capex est décompressé en /data/apex/decompressed/com.android.foo@37.apex .
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex est effectué pour vérifier qu'il possède une étiquette SELinux correcte.
  3. Des contrôles de vérification sont effectués sur /data/apex/decompressed/com.android.foo@37.apex pour garantir sa validité : apexd vérifie la clé publique fournie dans /data/apex/decompressed/com.android.foo@37.apex pour vérifiez qu'il est égal à celui fourni dans /system/apex/com.android.foo.capex .
  4. Le fichier /data/apex/decompressed/com.android.foo@37.apex est lié en dur au répertoire /data/apex/active/com.android.foo@37.apex .
  5. La logique d'activation normale pour les fichiers APEX non compressés est effectuée sur /data/apex/active/com.android.foo@37.apex .

Interaction avec l'OTA

Les fichiers APEX compressés ont des implications sur la livraison et l'application OTA. Étant donné qu'une mise à jour OTA peut contenir un fichier APEX compressé avec un niveau de version supérieur à celui qui est actif sur un appareil, une certaine quantité d'espace libre doit être réservée avant le redémarrage d'un appareil pour appliquer une mise à jour OTA.

Pour prendre en charge le système OTA, apexd expose ces deux API de classeur :

  • calculateSizeForCompressedApex - calcule la taille requise pour décompresser les fichiers APEX dans un package OTA. Cela peut être utilisé pour vérifier qu'un appareil dispose de suffisamment d'espace avant qu'un OTA ne soit téléchargé.
  • reserveSpaceForCompressedApex - réserve de l'espace sur le disque pour une utilisation future par apexd pour décompresser les fichiers APEX compressés dans le package OTA.

Dans le cas d'une mise à jour A/B OTA, apexd tente une décompression en arrière-plan dans le cadre de la routine OTA post-installation. Si la décompression échoue, apexd effectue la décompression lors du démarrage qui applique la mise à jour OTA.

Alternatives envisagées lors du développement d’APEX

Voici quelques options prises en compte par l'AOSP lors de la conception du format de fichier APEX et pourquoi elles ont été incluses ou exclues.

Systèmes de gestion de paquets réguliers

Les distributions Linux disposent de systèmes de gestion de packages tels que dpkg et rpm , qui sont puissants, matures et 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 est effectuée uniquement lorsque les packages sont en cours d'installation. Les attaquants peuvent briser l’intégrité des packages installés sans s’en apercevoir. 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 soit interdite, soit détectable afin que le périphérique puisse refuser de démarrer s'il est compromis.

dm-crypt pour l'intégrité

Les fichiers d'un conteneur APEX proviennent de partitions intégrées (par exemple, la partition /system ) protégées par dm-verity, où toute modification des fichiers est interdite même après le montage des partitions. Pour fournir le même niveau de sécurité aux fichiers, tous les fichiers d'un APEX sont stockés dans une image du système de fichiers associée à une arborescence de hachage et à un descripteur vbmeta. Sans dm-verity, un APEX dans la partition /data est vulnérable aux modifications involontaires apportées après sa vérification et son installation.

En fait, la partition /data est également protégée par des couches de chiffrement 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é. Lorsqu'un attaquant accède à la partition /data , il ne peut y avoir aucune protection supplémentaire, et il s'agit là encore d'une régression par rapport au fait que chaque composant système se trouve dans la partition /system . L'arborescence de hachage à l'intérieur d'un fichier APEX, associée à dm-verity, offre le même niveau de protection du contenu.

Rediriger les chemins de /system vers /apex

Les fichiers de composants système regroupés dans un APEX sont accessibles via de nouveaux chemins comme /apex/<name>/lib/libfoo.so . Lorsque les fichiers faisaient partie de la partition /system , ils étaient accessibles via des chemins tels que /system/lib/libfoo.so . Un client d'un fichier APEX (autres fichiers APEX ou la plateforme) doit utiliser les nouveaux chemins. Vous devrez peut-être mettre à jour le code existant à la suite du changement de chemin.

Bien qu'un moyen d'éviter le changement de chemin consiste à superposer le contenu d'un fichier APEX sur la partition /system , l'équipe Android a décidé de ne pas superposer les fichiers sur la partition /system car cela pourrait avoir un impact sur les performances en raison du nombre de fichiers superposés ( peut-être même empilés les uns après les autres) ont augmenté.

Une autre option consistait à détourner les fonctions d'accès aux fichiers telles que open , stat et readlink , de sorte que les chemins commençant par /system soient redirigés vers leurs chemins correspondants sous /apex . L'équipe Android a abandonné cette option car il est impossible de modifier toutes les fonctions qui acceptent les chemins. Par exemple, certaines applications lient statiquement Bionic, qui implémente les fonctions. Dans de tels cas, ces applications ne sont pas redirigées.

,

Le format de conteneur Android Pony EXpress (APEX) a été introduit dans Android 10 et 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 rentrent pas dans le modèle d'application Android standard. Certains exemples de composants sont les services et bibliothèques natifs, les couches d'abstraction matérielle ( HAL ), le runtime ( ART ) et les bibliothèques de classes.

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

Arrière-plan

Bien qu'Android prenne en charge les mises à jour de modules qui correspondent au modèle d'application standard (par exemple, services, activités) via des applications d'installation de packages (telles que l'application Google Play Store), l'utilisation d'un modèle similaire pour les composants du 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 sera 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.

Conception

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 pour lesquelles cette conception pour APEX a été sélectionnée, voir Alternatives envisagées lors du développement d'APEX .

Format APEX

Il s'agit du 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 dans des limites de 4 Ko.

Les quatre fichiers d'un fichier APEX sont :

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

Le fichier apex_manifest.json contient le nom et la version du package, qui identifient un fichier APEX. Il s'agit d'un tampon de protocole ApexManifest au format JSON.

Le fichier AndroidManifest.xml permet au fichier APEX d'utiliser des outils et une infrastructure liés à l'APK tels qu'ADB, PackageManager et des applications d'installation de packages (telles que Play Store). Par exemple, le fichier APEX peut utiliser un outil existant tel que aapt pour inspecter les métadonnées de base du fichier. Le fichier contient le nom du package et les informations de version. Ces informations sont généralement également disponibles dans apex_manifest.json .

apex_manifest.json est recommandé par rapport à AndroidManifest.xml pour les nouveaux codes et systèmes traitant d'APEX. AndroidManifest.xml peut contenir des informations de ciblage supplémentaires pouvant être utilisées par les outils de publication d'applications existants.

apex_payload.img est une image du système de fichiers ext4 soutenue 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 la bibliothèque 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 fichier apex_payload.img .

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.

Directives de dénomination APEX

Pour éviter les conflits de noms entre les nouveaux APEX à mesure que la plateforme progresse, utilisez les directives de dénomination suivantes :

  • com.android.*
    • Réservé aux AOSP APEX. Ce n’est pas propre à une entreprise ou à un appareil.
  • com.<companyname>.*
    • Réservé à une entreprise. Potentiellement utilisé par plusieurs appareils de cette entreprise.
  • com.<companyname>.<devicename>.*
    • Réservé aux APEX uniques à un périphérique spécifique (ou sous-ensemble de périphériques).

Responsable APEX

Le gestionnaire APEX (ou apexd ) est un processus natif autonome chargé de vérifier, d'installer et de désinstaller les fichiers APEX. Ce processus est lancé et est prêt au début de la séquence de démarrage. Les fichiers APEX sont normalement préinstallés 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'un APEX utilise la classe PackageManager et est la suivante.

  1. Un fichier APEX est téléchargé via une application d'installation de package, ADB ou une autre source.
  2. Le gestionnaire de packages démarre la procédure d'installation. Après avoir reconnu 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 est activé au prochain démarrage.
  5. Le demandeur d'installation reçoit une diffusion une fois la vérification réussie du package.
  6. Pour poursuivre 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 mappage de périphérique au-dessus du périphérique de bouclage.
    4. Monte le périphérique de bloc du mappeur de périphérique sur un chemin 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 liaison permettant aux autres composants du système d'interroger 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 sur le périphérique 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

Les fichiers APEX sont des fichiers APK valides car ce sont des archives zip signées (en utilisant le schéma de signature APK) contenant un fichier AndroidManifest.xml . Cela permet aux fichiers APEX d'utiliser l'infrastructure des fichiers APK, telle qu'une application d'installation de packages, l'utilitaire de signature et le gestionnaire de packages.

Le fichier AndroidManifest.xml à l'intérieur d'un fichier APEX est minimal et comprend le name du package , versionCode et facultatifs targetSdkVersion , minSdkVersion et maxSdkVersion pour un ciblage précis. Ces informations permettent aux fichiers APEX d'être livrés via les canaux existants tels que les applications d'installation de packages et ADB.

Types de fichiers pris en charge

Le format APEX prend en charge ces types de fichiers :

  • 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 possibilité de mettre à jour un type de fichier dépend de la plate-forme et de la stabilité des définitions des interfaces pour les types de fichiers.

Options de signature

Les fichiers APEX sont signés de deux manières. Tout d'abord, le fichier apex_payload.img (en particulier, le descripteur vbmeta ajouté à apex_payload.img ) est signé avec une clé. Ensuite, l'intégralité de l'APEX est signée à l'aide du 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 construction et au moment de l'exécution.

APEX dans les partitions intégrées

Les fichiers APEX peuvent être situés dans des partitions intégrées 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 nouvel APEX est stocké dans /data et, comme pour les APK, la version nouvellement installée masque la version déjà présente 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 de ligne principale Apex sur un périphérique Android, les fonctionnalités du noyau Linux suivantes sont nécessaires: le pilote de bouclage et la Verrité DM. 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 la Verrité DM sont importantes pour atteindre de bonnes performances du système lors de l'utilisation de modules APEX.

Versions du noyau prise en charge

Les modules de ligne principale Apex sont pris en charge sur les périphériques à l'aide des versions du noyau 4.4 ou supérieures. Les nouveaux appareils lancés avec Android 10 ou supérieur doivent utiliser le noyau version 4.9 ou plus pour prendre en charge les modules APEX.

Patchs de noyau requis

Les patchs de noyau requis pour prendre en charge les modules APEX sont inclus dans l'arborescence commune Android. Pour obtenir les correctifs pour prendre en charge Apex, utilisez la dernière version de l'Android Common Tree.

Version 4.4 du noyau

Cette version est uniquement prise en charge pour les appareils qui sont passés d'Android 9 à Android 10 et souhaitent prendre en charge les modules APEX. Pour obtenir les correctifs requis, une baisse de la branche android-4.4 est fortement recommandée. Ce qui suit est une liste des correctifs individuels requis pour la version 4.4 du noyau.

  • En amont: boucle: ajouter ioctl pour changer la taille du bloc logique ( 4.4 )
  • Backport: Block / Loop: Définissez HW_SECTOR ( 4.4 )
  • Upstream: Loop: Ajouter LOOP_SET_BLOCK_SIZE dans Compat IOCTL ( 4.4 )
  • Android: MNT: Correction de Next_DesCective ( 4.4 )
  • Android: MNT: Remount devrait se propager aux esclaves d'esclaves ( 4.4 )
  • Android: Mnt: propager correctement le remontage ( 4.4 )
  • Retour "Android: DM Verity: Ajouter une taille minimale de préfetch" ( 4.4 )
  • Upstream: Loop: Drop cache 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 bas de la branche android-common .

Options de configuration du noyau requises

La liste suivante montre les exigences de configuration de base pour prendre en charge les modules APEX qui ont été introduits dans Android 10. Les éléments avec un astérisque (*) sont des exigences existantes d'Android 9 et infé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 des 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 réglé
  • loop.max_part doit être <= 8

Construisez un sommet

Cette section décrit comment construire un sommet à l'aide du système de construction Android. Ce qui suit est un exemple d' 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 en apex

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

Dépendances transitives

Les fichiers APEX incluent automatiquement les dépendances transitives des LIB ou exécutables partagés natifs. Par exemple, si libFoo dépend de libBar , les deux LIB sont incluses lorsque seul libFoo est répertorié dans la propriété native_shared_libs .

Gérer plusieurs abis

Installez la propriété native_shared_libs pour les interfaces binaires d'application primaire et secondaire (ABIS) de l'appareil. Si un APEX cible les appareils avec un seul ABI (c'est-à-dire 32 bits uniquement ou 64 bits uniquement), seules les bibliothèques avec ABI correspondantes sont installées.

Installez la propriété binaries uniquement pour l'ABI primaire de l'appareil comme décrit ci-dessous:

  • Si l'appareil est 32 bits uniquement, seule la variante 32 bits du binaire est installée.
  • Si l'appareil est 64 bits uniquement, seule la variante 64 bits du binaire est installée.

Pour ajouter un contrôle à grains fins sur les ABI des bibliothèques et binaires natifs, utilisez le multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]

  • first : correspond à l'ABI primaire de l'appareil. C'est la valeur par défaut pour les binaires.
  • lib32 : correspond à l'ABI 32 bits de l'appareil, s'il est pris en charge.
  • lib64 : correspond à l'ABI 64 bits de l'appareil, il a pris en charge.
  • prefer32 : correspond à l'ABI 32 bits de l'appareil, s'il est pris en charge. Si l'ABI 32 bits n'est pas pris en charge, correspond à l'ABI 64 bits.
  • both : correspond à les deux Abis. Il s'agit de la valeur par défaut pour native_shared_libraries .

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

Cet exemple est pour un appareil qui prend en charge 32/64 et ne préfère pas 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
        },
    },
}

VBMETA SIGNAGE

Signez chaque sommet avec différentes clés. Lorsqu'une nouvelle clé est requise, créez une paire de clés public-privé et créez un module apex_key . Utilisez la propriété key pour signer l'apex à l'aide de 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 sommet est écrit dans l'apex. Au moment de l'exécution, apexd vérifie l'apex en utilisant une clé publique avec le même ID dans l'appareil.

Signette Apex

Signez les apex de la même manière que vous signez des APK. Signez les apexes deux fois; une fois pour le système de fichiers mini (fichier apex_payload.img ) et une fois pour l'ensemble du fichier.

Pour signer un sommet au niveau du fichier, définissez la propriété certificate de ces trois manières:

  • Non défini: si aucune valeur n'est définie, l'apex est signé avec le certificat situé sur PRODUCT_DEFAULT_DEV_CERTIFICATE . Si aucun indicateur n'est défini, le chemin est par défaut pour build/target/product/security/testkey .
  • <name> : L'apex est signé avec le certificat <name> dans le même répertoire que PRODUCT_DEFAULT_DEV_CERTIFICATE .
  • :<name> : L'apex est signé avec le certificat 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)
}

Installer un sommet

Pour installer un apex, utilisez la BAD.

adb install apex_file_name
adb reboot

Si supportsRebootlessUpdate est défini sur true dans apex_manifest.json et que l'apex actuellement installé n'est pas utilisé (par exemple, tous les services qu'il contient ont été arrêtés), alors un nouvel apex peut être installé sans redémarrer avec le drapeau --force-non-staged .

adb install --force-non-staged apex_file_name

Utilisez un apex

Après le redémarrage, l'apex est monté au répertoire /apex/<apex_name>@<version> . 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 monté sur /apex/<apex_name> .

Les clients peuvent utiliser le chemin d'accès à la liaison pour lire ou exécuter des fichiers à partir d'Apex.

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

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

Mettre à jour un service avec un sommet

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

  1. Marquez le service dans la partition système comme mise à jour. Ajoutez 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éez un nouveau fichier .rc pour le service mis à jour. Utilisez l'option override pour redéfinir le service existant.

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

Les définitions de service ne peuvent être définies que dans le fichier .rc d'un apex. Les déclencheurs d'action ne sont pas pris en charge dans les sommets.

Si un service marqué à la mise à jour commence avant l'activation des apex, le début est retardé jusqu'à ce que l'activation des apex est terminée.

Configurer le système pour prendre en charge les mises à jour Apex

Définissez la propriété système suivante sur true pour prendre en charge les mises à jour du fichier 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 appareils hérités, il est parfois impossible ou irréalisable de mettre à jour l'ancien noyau pour prendre en charge pleinement Apex. Par exemple, le noyau peut avoir été construit sans CONFIG_BLK_DEV_LOOP=Y , ce qui est crucial pour le montage de l'image du système de fichiers dans un apex.

L'apex aplati est un apex spécialement construit qui peut être activé sur des appareils avec un noyau hérité. Les fichiers dans un apex aplati sont directement installés dans un répertoire sous la partition intégrée. Par exemple, lib/libFoo.so dans un apex aplaté my.apex est installé sur /system/apex/my.apex/lib/libFoo.so .

L'activation d'un apex aplati n'implique pas le dispositif de boucle. L'ensemble du répertoire /system/apex/my.apex est directement lié à /apex/name@ver .

Les sommets aplatis ne peuvent pas être mis à jour en téléchargeant des versions mises à jour des apexs du réseau car les apex téléchargés ne peuvent pas être aplatis. Les sommets aplatis ne peuvent être mis à jour que via une OTA ordinaire.

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

Mélanger les apex aplatis et non fractés dans un appareil n'est pas pris en charge. Les sommets dans un appareil doivent être tous non flacés ou tous aplatis. Ceci est particulièrement important lors de l'expédition des pré-préparations pré-signées APEX pour des projets tels que Mainline. Les sommets qui ne sont pas présignés (c'est-à-dire construits à partir de la source) doivent également être non enfoncés et signés avec des 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 .

Apex comprimés

Android 12 et ultérieurement compression APEX pour réduire l'impact de stockage des packages APEX à jour. Une fois une mise à jour d'un APEX installé, bien que sa version préinstallée ne soit plus utilisée, elle occupe toujours la même quantité d'espace. Cet espace occupé reste indisponible.

La compression Apex minimise cet impact de stockage en utilisant un ensemble hautement compressé de fichiers APEX sur les partitions en lecture seule (telles que la partition /system ). Android 12 et utilisez ultérieurement un algorithme de compression de zip dégonflé.

La compression ne protège pas les éléments suivants:

  • Apex bootstrap qui doivent être montés très tôt dans la séquence de démarrage.

  • Apex non updatables. La compression n'est bénéfique que si une version mise à jour d'un APEX est installée sur la partition /data . Une liste complète des apex à jour est disponible sur la page des composants du système modulaire .

  • Apex Libs partagés dynamiques. Étant donné apexd active toujours les deux versions de ces apex (préinstallées et mises à niveau), les comprimer n'ajoute pas de valeur.

Format de fichier APEX compressé

Il s'agit du format d'un fichier APEX compressé.

Diagram shows the format of a compressed APEX file

Figure 2. Format de fichier APEX compressé

Au niveau supérieur, un fichier APEX compressé est un fichier zip contenant le fichier APEX d'origine sous forme dégonflée avec un niveau de compression de 9, et avec d'autres fichiers stockés non compressés.

Quatre fichiers comprennent un fichier APEX:

  • original_apex : dégonflé avec le niveau de compression de 9 Il s'agit du fichier APEX original non compressé.
  • apex_manifest.pb : stocké uniquement
  • AndroidManifest.xml : stocké uniquement
  • apex_pubkey : stocké uniquement

Les fichiers apex_manifest.pb , AndroidManifest.xml et apex_pubkey sont des copies de leurs fichiers correspondants dans original_apex .

Construire un apex comprimé

APEX compressé peut être construit à l'aide de l'outil apex_compression_tool.py situé sur system/apex/tools .

Plusieurs paramètres liés à la compression APEX sont disponibles dans le système de construction.

Dans Android.bp , si un fichier APEX est compressible est contrôlé par la propriété compressible :

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

Un indicateur de produit PRODUCT_COMPRESSED_APEX contrôle si une image système construite à partir de la source doit contenir des fichiers APEX compressés.

Pour l'expérimentation locale, vous pouvez forcer une version à compresser les apex en définissant OVERRIDE_PRODUCT_COMPRESSED_APEX= TO true .

Les fichiers APEX compressés générés par le système de construction ont l'extension .capex . L'extension facilite la distinction entre les versions compressées et non compressées d'un fichier APEX.

Algorithmes de compression pris en charge

Android 12 ne prend en charge que la compression de dégagement de zip.

Activer un fichier Apex compressé pendant le démarrage

Avant qu'un apex compressé ne puisse être activé, le fichier original_apex à l'intérieur est décompressé dans le répertoire /data/apex/decompressed . Le fichier APEX décompressé résultant est lié à la répertoire /data/apex/active .

Considérez l'exemple suivant comme une illustration du processus décrit ci-dessus.

Considérez /system/apex/com.android.foo.capex en tant qu'apex compressé en cours d'activation, avec VersionCode 37.

  1. Le fichier original_apex à l'intérieur /system/apex/com.android.foo.capex est décompressé en /data/apex/decompressed/com.android.foo@37.apex .
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex est effectué pour vérifier qu'il a une étiquette Selinux correcte.
  3. Les vérifications de vérification sont effectuées sur /data/apex/decompressed/com.android.foo@37.apex /data/apex/decompressed/com.android.foo@37.apex apexd Vérifiez qu'il est égal à celui regroupé dans /system/apex/com.android.foo.capex .
  4. Le fichier /data/apex/decompressed/com.android.foo@37.apex est durement lié au répertoire /data/apex/active/com.android.foo@37.apex .
  5. La logique d'activation régulière pour les fichiers APEX non compressée est effectuée sur /data/apex/active/com.android.foo@37.apex .

Interaction avec OTA

Les fichiers APEX compressés ont des implications sur la livraison et l'application OTA. Étant donné qu'une mise à jour OTA peut contenir un fichier APEX compressé avec un niveau de version plus élevé que ce qui est actif sur un appareil, une certaine quantité d'espace libre doit être réservée avant qu'un appareil ne soit redémarré pour appliquer une mise à jour OTA.

Pour prendre en charge le système OTA, apexd expose ces deux API de liant:

  • calculateSizeForCompressedApex - calcule la taille requise pour décompresser les fichiers APEX dans un package OTA. Cela peut être utilisé pour vérifier qu'un appareil a suffisamment d'espace avant le téléchargement d'une OTA.
  • reserveSpaceForCompressedApex - réserve l'espace sur le disque pour une utilisation future par apexd pour décompresser les fichiers APEX compressés dans le package OTA.

Dans le cas d'une mise à jour A / B OTA, apexd tente la décompression en arrière-plan dans le cadre de la routine Postinstall OTA. Si la décompression échoue, apexd effectue la décompression pendant le démarrage qui applique la mise à jour OTA.

Alternatives considérées lors du développement de l'apex

Voici quelques options qu'AOSP a considérées lors de la conception du format de fichier APEX, et pourquoi ils ont été inclus ou exclus.

Systèmes de gestion des emballages réguliers

Les distributions Linux ont des systèmes de gestion des packages comme dpkg et rpm , qui sont puissants, matures et 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 lorsque des packages sont installés. Les attaquants peuvent briser l'intégrité des packages installés, inaperçus. Il s'agit d'une régression pour Android où tous les composants du système ont été stockés dans des systèmes de fichiers en lecture seule dont l'intégrité est protégée par la valeur DM pour chaque E / S. Toute altération des composants du système doit être interdite ou détectable afin que l'appareil puisse refuser de démarrer s'il est compromis.

DM-Crypt pour l'intégrité

Les fichiers d'un conteneur Apex proviennent de partitions intégrées (par exemple, la partition /system ) qui sont protégées par DM-Verrity, où toute modification des fichiers est interdite même après le montage des partitions. 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 qui est associée à une arborescence de hachage et à un descripteur VBMETA. Sans DM-Verrity, un sommet de la partition /data est vulnérable aux modifications involontaires qui sont apportées après la vérification et l'installation.

En fait, la partition /data est également protégée par des couches de chiffrement telles que DM-Crypt. Bien que cela offre un certain niveau de protection contre la falsification, son objectif principal est la confidentialité, pas l'intégrité. Lorsqu'un attaquant a accès à la partition /data , il ne peut y avoir de protection supplémentaire, et c'est encore une régression par rapport à chaque composant système dans la partition /system . L'arbre de hachage à l'intérieur d'un fichier APEX avec DM-Verrity offre le même niveau de protection de contenu.

Rediriger les chemins de / système vers / apex

Les fichiers de composants système emballés dans un APEX sont accessibles via de nouveaux chemins comme /apex/<name>/lib/libfoo.so . Lorsque les fichiers faisaient partie de la partition /system , ils étaient accessibles via 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. Vous devrez peut-être mettre à jour le code existant à la suite du changement de chemin.

Bien qu'une façon d'éviter la modification du chemin est de superposer le contenu des fichiers dans un fichier APEX sur la partition /system , l'équipe Android a décidé de ne pas superposer les fichiers sur la partition /system car cela pourrait avoir un impact sur les performances car le nombre de fichiers étant superposés ( Peut-être même empilé l'un après l'autre) augmentait.

Une autre option consistait à détourner des fonctions d'accès de fichiers telles que open , stat et readlink , de sorte que les chemins commençant par /system ont été redirigés vers leurs chemins correspondants sous /apex . L'équipe Android a rejeté cette option car il est impossible de modifier toutes les fonctions qui acceptent les chemins. Par exemple, certaines applications lient statiquement Bionic, qui implémente les fonctions. Dans de tels cas, ces applications ne sont pas redirigées.

,

Le format de conteneur Android Pony Express (APEX) a été introduit dans Android 10 et il est utilisé dans le flux d'installation pour les modules système de niveau inférieur. Ce format facilite les mises à jour des composants système qui ne s'intègrent pas dans le modèle d'application Android standard. Certains exemples de composants sont les services et bibliothèques natifs, les couches d'abstraction matérielle ( HALS ), l'exécution ( ART ) et les bibliothèques de classe.

Le terme "apex" peut également se référer à un fichier APEX.

Arrière-plan

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

  • Les modules basés sur APK ne peuvent pas être utilisés tôt dans 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é que du gestionnaire d'activité, 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 sont pas toujours un bon ajustement.

Conception

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 pour lesquelles cette conception pour Apex a été sélectionnée, voir les alternatives prises en compte lors du développement de l'apex .

Format apex

Ceci 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 à des limites de 4 Ko.

Les quatre fichiers d'un fichier APEX sont:

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

Le fichier apex_manifest.json contient le nom et la version du package, qui identifient un fichier APEX. Il s'agit d'un tampon de protocole ApexManifest au format JSON.

Le fichier AndroidManifest.xml permet au fichier APEX d'utiliser des outils et des infrastructures liés à l'APK tels que les applications ADB, PackageManager et les installateurs de package (tels que 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 des informations sur le nom et la version du package. Ces informations sont généralement également disponibles dans apex_manifest.json .

apex_manifest.json est recommandé sur AndroidManifest.xml pour de nouveaux code et systèmes qui traitent avec Apex. AndroidManifest.xml peut contenir des informations de ciblage supplémentaires qui peuvent être utilisées par les outils de publication d'applications existants.

apex_payload.img est une image du 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 la bibliothèque libavb . La charge utile du système de fichiers n'est pas analysée (car l'image doit être montable en place). Les fichiers réguliers sont inclus dans le fichier apex_payload.img .

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.

Directives de dénomination Apex

Pour aider à prévenir les conflits de dénomination entre les nouveaux sommets à mesure que la plate-forme avance, utilisez les directives de dénomination suivantes:

  • com.android.*
    • Réservé aux apexs AOSP. Pas unique à une entreprise ou un appareil.
  • com.<companyname>.*
    • Réservé à une entreprise. Potentiellement utilisé par plusieurs appareils de cette entreprise.
  • com.<companyname>.<devicename>.*
    • Réservé pour les sommets uniques à un appareil spécifique (ou sous-ensemble de dispositifs).

Apex Manager

L'Apex Manager (ou apexd ) est un processus natif autonome responsable de la vérification, de l'installation et de la désinstallation des fichiers APEX. Ce processus est lancé et est prêt au début de la séquence de démarrage. Les fichiers APEX sont normalement préinstallés sur le périphérique sous /system/apex . Le gestionnaire Apex est par défaut en utilisant ces packages si aucune mise à jour n'est disponible.

La séquence de mise à jour d'un Apex utilise la classe PackageManager et est la suivante.

  1. Un fichier APEX est téléchargé via une application d'installation de package, une ADB ou une autre source.
  2. Le gestionnaire de package démarre la procédure d'installation. Après avoir reconnu que le fichier est un APEX, le gestionnaire de package 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 refléter que le fichier APEX est activé au démarrage suivant.
  5. Le demandeur d'installation reçoit une diffusion sur une vérification réussie du package.
  6. Pour continuer l'installation, le système doit être redémarré.
  7. Au démarrage suivant, le gestionnaire Apex démarre, lit la base de données interne et fait ce qui suit 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 sur le dessus du périphérique de bouclage.
    4. Monte le périphérique de bloc de mappeur de périphérique sur un chemin 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 liant pour que d'autres composants système interrogent 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é, de sorte que les fichiers sont accessibles.

Les fichiers Apex sont des fichiers APK

Les fichiers APEX sont des fichiers APK valides car ce sont des archives zip signées (à l'aide du schéma de signature APK) contenant un fichier AndroidManifest.xml . Cela permet aux fichiers APEX d'utiliser l'infrastructure pour les fichiers APK, tels qu'une application d'installation de package, l'utilitaire de signature et le gestionnaire de packages.

Le fichier AndroidManifest.xml dans un fichier APEX est minime, composé du name du package, versionCode et targetSdkVersion en option, minSdkVersion et maxSdkVersion pour un ciblage à grain fin. Ces informations permettent de livrer les fichiers APEX via des canaux existants tels que les applications d'installation de packages et la BAD.

Types de fichiers pris en charge

Le format Apex prend en charge ces types de fichiers:

  • Libs partagés indigènes
  • Executiles natifs
  • Fichiers de pot
  • Fichiers de données
  • Files de configuration

Cela ne signifie pas qu'Apex peut mettre à jour tous ces types de fichiers. La question de savoir si un type de fichier peut être mis à jour dépend de la plate-forme et de la stable que les définitions des interfaces pour les types de fichiers sont.

Options de signature

Les fichiers APEX sont signés de deux manières. Tout d'abord, le fichier de descripteur apex_payload.img a annoncé au fichier apex_payload.img ). Ensuite, l'ensemble de l'apex est signé à l'aide du schéma de signature APK V3 . Deux clés différentes sont utilisées dans ce processus.

Du côté de l'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 sommets qui sont demandés pour être installés. Chaque apex doit être signé avec différentes clés et est appliqué à la fois au moment de la construction et au moment de l'exécution.

Apex dans les partitions intégrées

Les fichiers APEX peuvent être situés dans des partitions intégrées telles que /system . La partition est déjà supérieure à DM-Verrity, 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 à la version. Le nouvel Apex est stocké en /data et, similaire à APKS, la version nouvellement installée ombarde la version déjà présente 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 de ligne principale Apex sur un périphérique Android, les fonctionnalités du noyau Linux suivantes sont nécessaires: le pilote de bouclage et la Verrité DM. 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 la Verrité DM sont importantes pour atteindre de bonnes performances du système lors de l'utilisation de modules APEX.

Versions du noyau prise en charge

Les modules de ligne principale Apex sont pris en charge sur les périphériques à l'aide des versions du noyau 4.4 ou supérieures. Les nouveaux appareils lancés avec Android 10 ou supérieur doivent utiliser le noyau version 4.9 ou plus pour prendre en charge les modules APEX.

Patchs de noyau requis

Les patchs de noyau requis pour prendre en charge les modules APEX sont inclus dans l'arborescence commune Android. Pour obtenir les correctifs pour prendre en charge Apex, utilisez la dernière version de l'Android Common Tree.

Version 4.4 du noyau

Cette version est uniquement prise en charge pour les appareils qui sont passés d'Android 9 à Android 10 et souhaitent prendre en charge les modules APEX. Pour obtenir les correctifs requis, une baisse de la branche android-4.4 est fortement recommandée. Ce qui suit est une liste des correctifs individuels requis pour la version 4.4 du noyau.

  • En amont: boucle: ajouter ioctl pour changer la taille du bloc logique ( 4.4 )
  • Backport: Block / Loop: Définissez HW_SECTOR ( 4.4 )
  • Upstream: Loop: Ajouter LOOP_SET_BLOCK_SIZE dans Compat IOCTL ( 4.4 )
  • Android: MNT: Correction de Next_DesCective ( 4.4 )
  • Android: MNT: Remount devrait se propager aux esclaves d'esclaves ( 4.4 )
  • Android: Mnt: propager correctement le remontage ( 4.4 )
  • Retour "Android: DM Verity: Ajouter une taille minimale de préfetch" ( 4.4 )
  • Upstream: Loop: Drop cache 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 bas de la branche android-common .

Options de configuration du noyau requises

La liste suivante montre les exigences de configuration de base pour prendre en charge les modules APEX qui ont été introduits dans Android 10. Les éléments avec un astérisque (*) sont des exigences existantes d'Android 9 et infé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 des 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 réglé
  • loop.max_part doit être <= 8

Construisez un sommet

Cette section décrit comment construire un sommet à l'aide du système de construction Android. Ce qui suit est un exemple d' 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 en apex

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

Dépendances transitives

Les fichiers APEX incluent automatiquement les dépendances transitives des LIB ou exécutables partagés natifs. Par exemple, si libFoo dépend de libBar , les deux LIB sont incluses lorsque seul libFoo est répertorié dans la propriété native_shared_libs .

Gérer plusieurs abis

Installez la propriété native_shared_libs pour les interfaces binaires d'application primaire et secondaire (ABIS) de l'appareil. Si un APEX cible les appareils avec un seul ABI (c'est-à-dire 32 bits uniquement ou 64 bits uniquement), seules les bibliothèques avec ABI correspondantes sont installées.

Installez la propriété binaries uniquement pour l'ABI primaire de l'appareil comme décrit ci-dessous:

  • Si l'appareil est 32 bits uniquement, seule la variante 32 bits du binaire est installée.
  • Si l'appareil est 64 bits uniquement, seule la variante 64 bits du binaire est installée.

Pour ajouter un contrôle à grains fins sur les ABI des bibliothèques et binaires natifs, utilisez le multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]

  • first : correspond à l'ABI primaire de l'appareil. C'est la valeur par défaut pour les binaires.
  • lib32 : correspond à l'ABI 32 bits de l'appareil, s'il est pris en charge.
  • lib64 : correspond à l'ABI 64 bits de l'appareil, il a pris en charge.
  • prefer32 : correspond à l'ABI 32 bits de l'appareil, s'il est pris en charge. Si l'ABI 32 bits n'est pas pris en charge, correspond à l'ABI 64 bits.
  • both : correspond à les deux Abis. Il s'agit de la valeur par défaut pour native_shared_libraries .

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

Cet exemple est pour un appareil qui prend en charge 32/64 et ne préfère pas 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
        },
    },
}

VBMETA SIGNAGE

Signez chaque sommet avec différentes clés. Lorsqu'une nouvelle clé est requise, créez une paire de clés public-privé et créez un module apex_key . Utilisez la propriété key pour signer l'apex à l'aide de 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 sommet est écrit dans l'apex. Au moment de l'exécution, apexd vérifie l'apex en utilisant une clé publique avec le même ID dans l'appareil.

Signette Apex

Signez les apex de la même manière que vous signez des APK. Signez les apexes deux fois; une fois pour le système de fichiers mini (fichier apex_payload.img ) et une fois pour l'ensemble du fichier.

Pour signer un sommet au niveau du fichier, définissez la propriété certificate de ces trois manières:

  • Non défini: si aucune valeur n'est définie, l'apex est signé avec le certificat situé sur PRODUCT_DEFAULT_DEV_CERTIFICATE . Si aucun indicateur n'est défini, le chemin est par défaut pour build/target/product/security/testkey .
  • <name> : L'apex est signé avec le certificat <name> dans le même répertoire que PRODUCT_DEFAULT_DEV_CERTIFICATE .
  • :<name> : L'apex est signé avec le certificat 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)
}

Installer un sommet

Pour installer un apex, utilisez la BAD.

adb install apex_file_name
adb reboot

Si supportsRebootlessUpdate est défini sur true dans apex_manifest.json et que l'apex actuellement installé n'est pas utilisé (par exemple, tous les services qu'il contient ont été arrêtés), alors un nouvel apex peut être installé sans redémarrer avec le drapeau --force-non-staged .

adb install --force-non-staged apex_file_name

Utilisez un apex

Après le redémarrage, l'apex est monté au répertoire /apex/<apex_name>@<version> . 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 monté sur /apex/<apex_name> .

Les clients peuvent utiliser le chemin d'accès à la liaison pour lire ou exécuter des fichiers à partir d'Apex.

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

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

Mettre à jour un service avec un sommet

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

  1. Marquez le service dans la partition système comme mise à jour. Ajoutez 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éez un nouveau fichier .rc pour le service mis à jour. Utilisez l'option override pour redéfinir le service existant.

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

Les définitions de service ne peuvent être définies que dans le fichier .rc d'un apex. Les déclencheurs d'action ne sont pas pris en charge dans les sommets.

Si un service marqué à la mise à jour commence avant l'activation des apex, le début est retardé jusqu'à ce que l'activation des apex est terminée.

Configurer le système pour prendre en charge les mises à jour Apex

Set the following system property to true to support APEX file updates.

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

Flattened APEX

For legacy devices, it's sometimes impossible or infeasible to update the old kernel to fully support APEX. For example, the kernel might have been built without CONFIG_BLK_DEV_LOOP=Y , which is crucial for mounting the file system image inside an APEX.

Flattened APEX is a specially built APEX that can be activated on devices with a legacy kernel. Files in a flattened APEX are directly installed to a directory under the built-in partition. For example, lib/libFoo.so in a flattened APEX my.apex is installed to /system/apex/my.apex/lib/libFoo.so .

Activating a flattened APEX doesn't involve the loop device. The entire directory /system/apex/my.apex is directly bind-mounted to /apex/name@ver .

Flattened APEXes can't be updated by downloading updated versions of the APEXes from network because the downloaded APEXes can't be flattened. Flattened APEXes can be updated only via a regular OTA.

Flattened APEX is the default configuration. This means that all APEXes are by default flattened unless you explicitly configure your device to build non-flattened APEXes to support APEX updates (as explained above).

Mixing flattened and non-flattened APEXes in a device is NOT supported. APEXes in a device must either be all non-flattened or all flattened. This is especially important when shipping pre-signed APEX prebuilts for projects such as Mainline. APEXes that aren't presigned (that is, built from the source) should also be non-flattened and signed with proper keys. The device should inherit from updatable_apex.mk as explained in Updating a service with an APEX .

Compressed APEXes

Android 12 and later feature APEX compression for reducing the storage impact of updatable APEX packages. After an update to an APEX is installed, although its pre-installed version isn't used anymore, it still occupies the same amount of space. That occupied space remains unavailable.

APEX compression minimizes this storage impact by using a highly compressed set of APEX files on read-only partitions (such as the /system partition). Android 12 and later use a DEFLATE zip compression algorithm.

Compression doesn't provide optimization to the following:

  • Bootstrap APEXes that are required to be mounted very early in the boot sequence.

  • Nonupdatable APEXes. Compression is only beneficial if an updated version of an APEX is installed on the /data partition. A full list of updatable APEXes is available on the Modular System Components page.

  • Dynamic shared libs APEXes. Since apexd always activates both versions of such APEXes (pre-installed and upgraded), compressing them doesn't add value.

Compressed APEX file format

This is the format of a compressed APEX file.

Diagram shows the format of a compressed APEX file

Figure 2. Compressed APEX file format

At the top level, a compressed APEX file is a zip file containing the original apex file in deflated form with a compression level of 9, and with other files stored uncompressed.

Four files comprise an APEX file:

  • original_apex : deflated with compression level of 9 This is the original, uncompressed APEX file .
  • apex_manifest.pb : stored only
  • AndroidManifest.xml : stored only
  • apex_pubkey : stored only

The apex_manifest.pb , AndroidManifest.xml , and apex_pubkey files are copies of their corresponding files in original_apex .

Build compressed APEX

Compressed APEX can be built using the apex_compression_tool.py tool located at system/apex/tools .

Several parameters related to APEX compression are available in the build system.

In Android.bp whether an APEX file is compressible is controlled by the compressible property:

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

A PRODUCT_COMPRESSED_APEX product flag controls whether a system image built from source must contain compressed APEX files.

For local experimentation you can force a build to compress APEXes by setting OVERRIDE_PRODUCT_COMPRESSED_APEX= to true .

Compressed APEX files generated by the build system have the .capex extension. The extension makes it easier to distinguish between compressed and uncompressed versions of an APEX file.

Supported compression algorithms

Android 12 only supports deflate-zip compression.

Activate a compressed APEX file during boot

Before a compressed APEX can be activated, the original_apex file inside it's decompressed into the /data/apex/decompressed directory. The resulting decompressed APEX file is hard-linked to the /data/apex/active directory.

Consider the following example as an illustration of the process described above.

Consider /system/apex/com.android.foo.capex as a compressed APEX being activated, with versionCode 37.

  1. The original_apex file inside /system/apex/com.android.foo.capex is decompressed into /data/apex/decompressed/com.android.foo@37.apex .
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex is performed to verify that it has a correct SELinux label.
  3. Verification checks are performed on /data/apex/decompressed/com.android.foo@37.apex to ensure its validity: apexd checks the public key bundled in /data/apex/decompressed/com.android.foo@37.apex to verify that it's equal to the one bundled in /system/apex/com.android.foo.capex .
  4. The /data/apex/decompressed/com.android.foo@37.apex file is hard-linked to the /data/apex/active/com.android.foo@37.apex directory.
  5. The regular activation logic for uncompressed APEX files is performed on /data/apex/active/com.android.foo@37.apex .

Interaction with OTA

Compressed APEX files have implications on OTA delivery and application. Since an OTA update might contain a compressed APEX file with a higher version level than what's active on a device, a certain amount of free space must be reserved before a device is rebooted to apply an OTA update.

To support the OTA system, apexd exposes these two binder APIs:

  • calculateSizeForCompressedApex - calculates the size required to decompress APEX files in an OTA package. This can be used to verify that a device has enough space before an OTA gets downloaded.
  • reserveSpaceForCompressedApex - reserves space on the disk for future use by apexd for decompressing compressed APEX files inside the OTA package.

In the case of an A/B OTA update, apexd attempts decompression in the background as part of the postinstall OTA routine. If decompression fails, apexd performs the decompression during the boot that applies the OTA update.

Alternatives considered when developing APEX

Here are some options that AOSP considered when designing the APEX file format, and why they were either included or excluded.

Regular package management systems

Linux distributions have package management systems like dpkg and rpm , which are powerful, mature, and robust. However, they weren't adopted for APEX because they can't protect the packages after installation. Verification is performed only when packages are being installed. Attackers can break the integrity of the installed packages, unnoticed. This is a regression for Android where all system components were stored in read-only file systems whose integrity is protected by dm-verity for every I/O. Any tampering to system components must either be prohibited, or be detectable so that the device can refuse to boot if compromised.

dm-crypt for integrity

The files in an APEX container are from built-in partitions (for example, the /system partition) that are protected by dm-verity, where any modification to the files are prohibited even after the partitions are mounted. To provide the same level of security to the files, all files in an APEX are stored in a file system image that is paired with a hash tree and a vbmeta descriptor. Without dm-verity, an APEX in the /data partition is vulnerable to unintended modifications that are made after it's been verified and installed.

In fact, the /data partition is also protected by encryption layers such as dm-crypt. Although this provides some level of protection against tampering, its primary purpose is privacy, not integrity. When an attacker gains access to the /data partition, there can be no further protection, and this again is a regression compared to every system component being in the /system partition. The hash tree inside an APEX file together with dm-verity provides the same level of content protection.

Redirect paths from /system to /apex

System component files packaged in an APEX are accessible via new paths like /apex/<name>/lib/libfoo.so . When the files were part of the /system partition, they were accessible via paths such as /system/lib/libfoo.so . A client of an APEX file (other APEX files or the platform) must use the new paths. You might need to update existing code as a result of the path change.

Although one way to avoid the path change is to overlay the file contents in an APEX file onto the /system partition, the Android team decided not to overlay files on the /system partition because this could impact performance as the number of files being overlayed (possibly even stacked one after another) increased.

Another option was to hijack file-access functions such as open , stat , and readlink , so that paths beginning with /system were redirected to their corresponding paths under /apex . The Android team discarded this option because it's infeasible to change all functions that accept paths. For example, some apps statically link Bionic, which implements the functions. In such cases, those apps don't get redirected.