Compatibilité avec le système de compilation VNDK

Sous Android 8.1 et versions ultérieures, le système de compilation est compatible avec le VNDK. Lorsque la prise en charge du VNDK est activée, le système de compilation vérifie les dépendances entre les modules, crée une variante spécifique au fournisseur pour les modules du fournisseur et installe automatiquement ces modules dans des répertoires désignés.

Exemple de compatibilité de compilation VNDK

Dans cet exemple, la définition du module Android.bp définit une bibliothèque nommée libexample. La propriété vendor_available indique que les modules de framework et les modules de fournisseurs peuvent dépendre de libexample:

libexample vendor_available:true et vndk.enabled:true

Figure 1 : Compatibilité du lecteur d'écran activée.

L'exécutable du framework /system/bin/foo et l'exécutable du fournisseur /vendor/bin/bar dépendent de libexample et contiennent libexample dans leurs propriétés shared_libs.

Si libexample est utilisé à la fois par les modules de framework et les modules du fournisseur, deux variantes de libexample sont créées. La variante principale (nommée d'après libexample) est utilisée par les modules de framework, tandis que la variante de fournisseur (nommée d'après libexample.vendor) est utilisée par les modules de fournisseurs. Les deux variantes sont installées dans des répertoires différents:

  • La variante de base est installée dans /system/lib[64]/libexample.so.
  • La variante du fournisseur est installée dans VNDK APEX, car vndk.enabled est true.

Pour en savoir plus, consultez la section Définition du module.

Configurer la compatibilité avec la compilation

Pour activer la compatibilité complète du système de compilation pour un appareil de produit, ajoutez BOARD_VNDK_VERSION à BoardConfig.mk:

BOARD_VNDK_VERSION := current

Ce paramètre a un effet global: lorsqu'il est défini dans BoardConfig.mk, tous les modules sont vérifiés. Comme il n'existe aucun mécanisme permettant d'ajouter un module à la liste noire ou à la liste blanche, vous devez nettoyer toutes les dépendances inutiles avant d'ajouter BOARD_VNDK_VERSION. Vous pouvez tester et compiler un module en définissant BOARD_VNDK_VERSION dans vos variables d'environnement:

$ BOARD_VNDK_VERSION=current m module_name.vendor

Lorsque BOARD_VNDK_VERSION est activé, plusieurs chemins de recherche d'en-tête globaux par défaut sont supprimés. En voici quelques exemples :

  • frameworks/av/include
  • frameworks/native/include
  • frameworks/native/opengl/include
  • hardware/libhardware/include
  • hardware/libhardware_legacy/include
  • hardware/ril/include
  • libnativehelper/include
  • libnativehelper/include_deprecated
  • system/core/include
  • system/media/audio/include

Si un module dépend des en-têtes de ces répertoires, vous devez spécifier (explicitement) les dépendances avec header_libs, static_libs et/ou shared_libs.

VNDK APEX

Sous Android 10 ou version antérieure, les modules avec vndk.enabled étaient installés dans /system/lib[64]/vndk[-sp]-${VER}. Dans Android 11 et versions ultérieures, les bibliothèques VNDK sont empaquetées au format APEX et leur nom est com.android.vndk.v${VER}. En fonction de la configuration de l'appareil, l'APEX VNDK est aplati ou non aplati, et est disponible à partir du chemin canonique /apex/com.android.vndk.v${VER}.

VNDK APEX

Figure 2. VNDK APEX.

Définition du module

Pour compiler Android avec BOARD_VNDK_VERSION, vous devez réviser la définition du module dans Android.mk ou Android.bp. Cette section décrit différents types de définitions de modules, plusieurs propriétés de module liées au VNDK et les vérifications de dépendances implémentées dans le système de compilation.

Modules du fournisseur

Les modules du fournisseur sont des exécutables ou des bibliothèques partagées spécifiques au fournisseur qui doivent être installés dans une partition du fournisseur. Dans les fichiers Android.bp, les modules du fournisseur doivent définir la propriété du fournisseur ou propriétaire sur true. Dans les fichiers Android.mk, les modules du fournisseur doivent définir LOCAL_VENDOR_MODULE ou LOCAL_PROPRIETARY_MODULE sur true.

Si BOARD_VNDK_VERSION est défini, le système de compilation interdit les dépendances entre les modules du fournisseur et les modules du framework, et émet des erreurs si:

  • un module sans vendor:true dépend d'un module avec vendor:true, ou
  • un module avec vendor:true dépend d'un module autre que llndk_library qui ne possède ni vendor:true ni vendor_available:true.

La vérification des dépendances s'applique à header_libs, static_libs et shared_libs dans Android.bp, et à LOCAL_HEADER_LIBRARIES, LOCAL_STATIC_LIBRARIES et LOCAL_SHARED_LIBRARIES dans Android.mk.

LL-NDK

Les bibliothèques partagées LL-NDK sont des bibliothèques partagées avec des ABI stables. Les modules de framework et de fournisseur partagent la même implémentation, mais la dernière. Pour chaque bibliothèque partagée LL-NDK, cc_library contient une propriété llndk avec un fichier de symboles:

cc_library {
    name: "libvndksupport",
    llndk: {
        symbol_file: "libvndksupport.map.txt",
    },
}

Le fichier de symboles décrit les symboles visibles par les modules du fournisseur. Exemple :

LIBVNDKSUPPORT {
  global:
    android_load_sphal_library; # llndk
    android_unload_sphal_library; # llndk
  local:
    *;
};

Sur la base du fichier de symboles, le système de compilation génère une bibliothèque partagée de bouchon pour les modules du fournisseur, qui s'associe à ces bibliothèques lorsque BOARD_VNDK_VERSION est activé. Un symbole n'est inclus dans la bibliothèque partagée de bouchon que si:

  • N'est pas défini à la fin de la section avec _PRIVATE ou _PLATFORM,
  • ne comporte pas de balise #platform-only ;
  • Aucune balise #introduce* n'est configurée ou la balise correspond à la cible.

VNDK

Dans les fichiers Android.bp, les définitions de module cc_library, cc_library_static, cc_library_shared et cc_library_headers acceptent trois propriétés liées au VNDK: vendor_available, vndk.enabled et vndk.support_system_process.

Si vendor_available ou vndk.enabled est true, deux variantes (core et vendor) peuvent être créées. La variante de base doit être traitée comme un module de framework, et la variante du fournisseur doit être traitée comme un module du fournisseur. Si certains modules de framework en dépendent, la variante de base est créée. Si certains modules du fournisseur dépendent de ce module, la variante du fournisseur est créée. Le système de compilation applique les vérifications de dépendance suivantes:

  • La variante de base est toujours réservée au framework et inaccessible aux modules du fournisseur.
  • La variante du fournisseur est toujours inaccessible aux modules du framework.
  • Toutes les dépendances de la variante du fournisseur, qui sont spécifiées dans header_libs, static_libs et/ou shared_libs, doivent être un llndk_library ou un module avec vendor_available ou vndk.enabled.
  • Si la valeur de vendor_available est true, la variante du fournisseur est accessible à tous les modules du fournisseur.
  • Si vendor_available est false, la variante du fournisseur n'est accessible qu'aux autres modules VNDK ou VNDK-SP (c'est-à-dire que les modules avec vendor:true ne peuvent pas associer les modules vendor_available:false).

Le chemin d'installation par défaut de cc_library ou cc_library_shared est déterminé par les règles suivantes:

  • La variante de base est installée dans /system/lib[64].
  • Le chemin d'installation de la variante du fournisseur peut varier :
    • Si vndk.enabled est false, la variante du fournisseur est installée dans /vendor/lib[64].
    • Si vndk.enabled est true, la variante du fournisseur est installée dans VNDK APEX(com.android.vndk.v${VER}).

Le tableau ci-dessous récapitule la façon dont le système de compilation gère les variantes du fournisseur:

vendor_available vndk
activé
vndk
support_same_process
Descriptions des variantes du fournisseur
true false false Les variantes du fournisseur sont VND-ONLY. Les bibliothèques partagées sont installées dans /vendor/lib[64].
true Non valide (erreur de compilation)
true false Les variantes du fournisseur sont VNDK. Les bibliothèques partagées sont installées dans VNDK APEX.
true Les variantes du fournisseur sont VNDK-SP. Les bibliothèques partagées sont installées dans VNDK APEX.

false

false

false

Aucune variante de fournisseur. Ce module est FWK-ONLY.

true Non valide (erreur de compilation)
true false Les variantes du fournisseur sont VNDK-Private. Les bibliothèques partagées sont installées dans VNDK APEX. Ils ne doivent pas être utilisés directement par les modules du fournisseur.
true Les variantes de fournisseur sont VNDK-SP-Private. Les bibliothèques partagées sont installées dans VNDK APEX. Ils ne doivent pas être utilisés directement par les modules du fournisseur.

Extensions VNDK

Les extensions VNDK sont des bibliothèques partagées VNDK avec des API supplémentaires. Les extensions sont installées dans /vendor/lib[64]/vndk[-sp] (sans suffixe de version) et remplacent les bibliothèques partagées VNDK d'origine au moment de l'exécution.

Définir des extensions VNDK

Sur Android 9 ou version ultérieure, Android.bp est compatible de manière native avec les extensions VNDK. Pour créer une extension VNDK, définissez un autre module avec une propriété vendor:true et extends:

cc_library {
    name: "libvndk",
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libvndk_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk",
    },
}

Un module avec les propriétés vendor:true, vndk.enabled:true et extends définit l'extension VNDK:

  • La propriété extends doit spécifier un nom de bibliothèque partagée de base VNDK (ou un nom de bibliothèque partagée VNDK-SP).
  • Les extensions VNDK (ou extensions VNDK-SP) sont nommées d'après les noms des modules de base à partir desquels elles s'étendent. Par exemple, le binaire de sortie de libvndk_ext est libvndk.so au lieu de libvndk_ext.so.
  • Les extensions VNDK sont installées dans /vendor/lib[64]/vndk.
  • Les extensions VNDK-SP sont installées dans /vendor/lib[64]/vndk-sp.
  • Les bibliothèques partagées de base doivent contenir à la fois vndk.enabled:true et vendor_available:true.

Une extension VNDK-SP doit s'étendre à partir d'une bibliothèque partagée VNDK-SP (vndk.support_system_process doit être égal):

cc_library {
    name: "libvndk_sp",
    vendor_available: true,
    vndk: {
        enabled: true,
        support_system_process: true,
    },
}

cc_library {
    name: "libvndk_sp_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk_sp",
        support_system_process: true,
    },
}

Les extensions VNDK (ou extensions VNDK-SP) peuvent dépendre d'autres bibliothèques partagées de fournisseurs:

cc_library {
    name: "libvndk",
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libvndk_ext",
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libvndk",
    },
    shared_libs: [
        "libvendor",
    ],
}

cc_library {
    name: "libvendor",
    vendor: true,
}

Utiliser des extensions VNDK

Si un module fournisseur dépend d'API supplémentaires définies par des extensions VNDK, le module doit spécifier le nom de l'extension VNDK dans sa propriété shared_libs:

// A vendor shared library example
cc_library {
    name: "libvendor",
    vendor: true,
    shared_libs: [
        "libvndk_ext",
    ],
}

// A vendor executable example
cc_binary {
    name: "vendor-example",
    vendor: true,
    shared_libs: [
        "libvndk_ext",
    ],
}

Si un module fournisseur dépend d'extensions VNDK, ces extensions VNDK sont automatiquement installées dans /vendor/lib[64]/vndk[-sp]. Si un module ne dépend plus d'une extension VNDK, ajoutez une étape de nettoyage à CleanSpec.mk pour supprimer la bibliothèque partagée. Exemple :

$(call add-clean-step, rm -rf $(TARGET_OUT_VENDOR)/lib/libvndk.so)

Compilation conditionnelle

Cette section explique comment gérer les différences subtiles (par exemple, ajouter ou supprimer une fonctionnalité de l'une des variantes) entre les trois bibliothèques partagées VNDK suivantes:

  • Variante de base (par exemple, /system/lib[64]/libexample.so)
  • Variante du fournisseur (par exemple, /apex/com.android.vndk.v${VER}/lib[64]/libexample.so)
  • Extension VNDK (par exemple, /vendor/lib[64]/vndk[-sp]/libexample.so)

Indicateurs de compilation conditionnels

Le système de compilation Android définit __ANDROID_VNDK__ pour les variantes du fournisseur et les extensions VNDK par défaut. Vous pouvez protéger le code avec les protections du préprocesseur C:

void all() { }

#if !defined(__ANDROID_VNDK__)
void framework_only() { }
#endif

#if defined(__ANDROID_VNDK__)
void vndk_only() { }
#endif

En plus de __ANDROID_VNDK__, différents cflags ou cppflags peuvent être spécifiés dans Android.bp. Les cflags ou cppflags spécifiés dans target.vendor sont spécifiques à la variante du fournisseur.

Par exemple, le Android.bp suivant définit libexample et libexample_ext:

cc_library {
    name: "libexample",
    srcs: ["src/example.c"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
    target: {
        vendor: {
            cflags: ["-DLIBEXAMPLE_ENABLE_VNDK=1"],
        },
    },
}

cc_library {
    name: "libexample_ext",
    srcs: ["src/example.c"],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample",
    },
    cflags: [
        "-DLIBEXAMPLE_ENABLE_VNDK=1",
        "-DLIBEXAMPLE_ENABLE_VNDK_EXT=1",
    ],
}

Voici la liste de code de src/example.c:

void all() { }

#if !defined(LIBEXAMPLE_ENABLE_VNDK)
void framework_only() { }
#endif

#if defined(LIBEXAMPLE_ENABLE_VNDK)
void vndk() { }
#endif

#if defined(LIBEXAMPLE_ENABLE_VNDK_EXT)
void vndk_ext() { }
#endif

Selon ces deux fichiers, le système de compilation génère des bibliothèques partagées avec les symboles exportés suivants:

Chemin d'installation Symboles exportés
/system/lib[64]/libexample.so all, framework_only
/apex/com.android.vndk.v${VER}/lib[64]/libexample.so all, vndk
/vendor/lib[64]/vndk/libexample.so all, vndk, vndk_ext

Exigences concernant les symboles exportés

Le vérificateur d'ABI VNDK compare l'ABI des variantes de fournisseurs VNDK et des extensions VNDK aux vidages d'ABI de référence sous prebuilts/abi-dumps/vndk.

  • Les symboles exportés par les variantes de fournisseurs VNDK (par exemple, /apex/com.android.vndk.v${VER}/lib[64]/libexample.so) doivent être identiques (et non des sur-ensembles) aux symboles définis dans les vidages ABI.
  • Les symboles exportés par les extensions VNDK (par exemple, /vendor/lib[64]/vndk/libexample.so) doivent être des sur-ensembles des symboles définis dans les vidages ABI.

Si les variantes de fournisseurs VNDK ou les extensions VNDK ne respectent pas les exigences ci-dessus, le vérificateur d'ABI VNDK émet des erreurs de compilation et arrête la compilation.

Exclure les fichiers sources ou les bibliothèques partagées des variantes de fournisseurs

Pour exclure des fichiers sources de la variante du fournisseur, ajoutez-les à la propriété exclude_srcs. De même, pour vous assurer que les bibliothèques partagées ne sont pas associées à la variante du fournisseur, ajoutez-les à la propriété exclude_shared_libs. Exemple :

cc_library {
    name: "libexample_cond_exclude",
    srcs: ["fwk.c", "both.c"],
    shared_libs: ["libfwk_only", "libboth"],
    vendor_available: true,
    target: {
        vendor: {
            exclude_srcs: ["fwk.c"],
            exclude_shared_libs: ["libfwk_only"],
        },
    },
}

Dans cet exemple, la variante principale de libexample_cond_exclude inclut le code de fwk.c et both.c, et dépend des bibliothèques partagées libfwk_only et libboth. La variante du fournisseur de libexample_cond_exclude n'inclut que le code de both.c, car fwk.c est exclu par la propriété exclude_srcs. De même, elle ne dépend que de la bibliothèque partagée libboth, car libfwk_only est exclu par la propriété exclude_shared_libs.

Exporter des en-têtes à partir d'extensions VNDK

Une extension VNDK peut ajouter de nouvelles classes ou de nouvelles fonctions à une bibliothèque partagée VNDK. Nous vous recommandons de conserver ces déclarations dans des en-têtes indépendants et d'éviter de modifier les en-têtes existants.

Par exemple, un nouveau fichier d'en-tête include-ext/example/ext/feature_name.h est créé pour l'extension VNDK libexample_ext:

  • Android.bp
  • include-ext/example/ext/feature_name.h
  • include/example/example.h
  • src/example.c
  • src/ext/feature_name.c

Dans le fichier Android.bp suivant, libexample exporte uniquement include, tandis que libexample_ext exporte à la fois include et include-ext. Cela garantit que feature_name.h ne sera pas inclus de manière incorrecte par les utilisateurs de libexample:

cc_library {
    name: "libexample",
    srcs: ["src/example.c"],
    export_include_dirs: ["include"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libexample_ext",
    srcs: [
        "src/example.c",
        "src/ext/feature_name.c",
    ],
    export_include_dirs: [
        "include",
        "include-ext",
    ],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample",
    },
}

Si la séparation des extensions en fichiers d'en-tête indépendants n'est pas possible, vous pouvez ajouter des protections #ifdef. Assurez-vous toutefois que tous les utilisateurs de l'extension VNDK ajoutent les indicateurs de définition. Vous pouvez définir cc_defaults pour ajouter des indicateurs de définition à cflags et associer des bibliothèques partagées à shared_libs.

Par exemple, pour ajouter une fonction membre Example2::get_b() à l'extension VNDK libexample2_ext, vous devez modifier le fichier d'en-tête existant et ajouter un Guard #ifdef:

#ifndef LIBEXAMPLE2_EXAMPLE_H_
#define LIBEXAMPLE2_EXAMPLE_H_

class Example2 {
 public:
  Example2();

  void get_a();

#ifdef LIBEXAMPLE2_ENABLE_VNDK_EXT
  void get_b();
#endif

 private:
  void *impl_;
};

#endif  // LIBEXAMPLE2_EXAMPLE_H_

Un cc_defaults nommé libexample2_ext_defaults est défini pour les utilisateurs de libexample2_ext:

cc_library {
    name: "libexample2",
    srcs: ["src/example2.cpp"],
    export_include_dirs: ["include"],
    vendor_available: true,
    vndk: {
        enabled: true,
    },
}

cc_library {
    name: "libexample2_ext",
    srcs: ["src/example2.cpp"],
    export_include_dirs: ["include"],
    vendor: true,
    vndk: {
        enabled: true,
        extends: "libexample2",
    },
    cflags: [
        "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1",
    ],
}

cc_defaults {
    name: "libexample2_ext_defaults",
    shared_libs: [
        "libexample2_ext",
    ],
    cflags: [
        "-DLIBEXAMPLE2_ENABLE_VNDK_EXT=1",
    ],
}

Les utilisateurs de libexample2_ext peuvent simplement inclure libexample2_ext_defaults dans leur propriété defaults:

cc_binary {
    name: "example2_user_executable",
    defaults: ["libexample2_ext_defaults"],
    vendor: true,
}

Packages de produits

Dans le système de compilation Android, la variable PRODUCT_PACKAGES spécifie les exécutables, les bibliothèques partagées ou les packages à installer sur l'appareil. Les dépendances transitives des modules spécifiés sont également installées implicitement sur l'appareil.

Si BOARD_VNDK_VERSION est activé, les modules avec vendor_available ou vndk.enabled reçoivent un traitement spécial. Si un module de framework dépend d'un module avec vendor_available ou vndk.enabled, la variante de base est incluse dans l'ensemble d'installation transitive. Si un module de fournisseur dépend d'un module avec vendor_available, la variante du fournisseur est incluse dans l'ensemble d'installation transitive. Toutefois, les variantes du fournisseur de modules avec vndk.enabled sont installées, qu'elles soient utilisées ou non par les modules du fournisseur.

Lorsque les dépendances sont invisibles pour le système de compilation (par exemple, les bibliothèques partagées pouvant être ouvertes avec dlopen() au moment de l'exécution), vous devez spécifier les noms des modules dans PRODUCT_PACKAGES pour les installer explicitement.

Si un module contient vendor_available ou vndk.enabled, le nom du module représente sa variante de base. Pour spécifier explicitement la variante du fournisseur dans PRODUCT_PACKAGES, ajoutez un suffixe .vendor au nom du module. Exemple :

cc_library {
    name: "libexample",
    srcs: ["example.c"],
    vendor_available: true,
}

Dans cet exemple, libexample représente /system/lib[64]/libexample.so et libexample.vendor représente /vendor/lib[64]/libexample.so. Pour installer /vendor/lib[64]/libexample.so, ajoutez libexample.vendor à PRODUCT_PACKAGES:

PRODUCT_PACKAGES += libexample.vendor