Implémenter l'API Config File Schema

La plate-forme Android contient de nombreux fichiers XML pour stocker la configuration données (par exemple, configuration audio). De nombreux fichiers XML se trouvent dans le fichier vendor. mais elles sont lues dans la partition system. Dans ce cas, le schéma du fichier XML sert d'interface entre les deux partitions. le schéma doit être explicitement spécifié et doit évoluer vers un modèle rétrocompatible de la même manière.

Avant Android 10, la plate-forme ne fournissait pour exiger la spécification et l'utilisation du schéma XML, ou empêcher des modifications incompatibles dans le schéma. Android 10 offre ce mécanisme, appelé API Config File Schema. Ce mécanisme se compose d'un outil appelée xsdc et une règle de compilation appelée xsd_config.

L'outil xsdc est un compilateur XSD (XML Schema Document). Il analyse un fichier XSD décrivant le schéma d'un fichier XML et génère du code Java et C++. La le code généré analyse les fichiers XML conformes au schéma XSD dans une arborescence de modélisant chacun une balise XML. Les attributs XML sont modélisés sous forme de champs des objets.

La règle de compilation xsd_config intègre l'outil xsdc au système de compilation. Pour un fichier d'entrée XSD donné, la règle de compilation génère des bibliothèques Java et C++. Toi peuvent lier les bibliothèques aux modules dans lesquels les fichiers XML conformes au XSD sont lus et utilisés. Vous pouvez utiliser la règle de compilation pour vos propres fichiers XML utilisés entre les partitions system et vendor.

API Build Config File Schema

Cette section explique comment créer une API Config File Schema.

Configurer la règle de compilation xsd_config dans Android.bp

La règle de compilation xsd_config génère le code de l'analyseur avec l'outil xsdc. La La propriété package_name de la règle de compilation xsd_config détermine le nom du package le code Java généré.

Exemple de règle de compilation xsd_config dans Android.bp:

xsd_config {
    name: "hal_manifest",
    srcs: ["hal_manifest.xsd"],
    package_name: "hal.manifest",
}

Exemple de structure de répertoire:

├── Android.bp
├── api
│   ├── current.txt
│   ├── last_current.txt
│   ├── last_removed.txt
│   └── removed.txt
└── hal_manifest.xsd

Le système de compilation génère une liste d'API à l'aide du code Java généré et vérifie l'API. Cette vérification de l'API est ajoutée à DroidCore et exécutée à m -j.

Créer des fichiers de listes d'API

Les vérifications de l'API nécessitent des fichiers de listes d'API dans le code source.

Les fichiers de liste de l'API incluent:

  • current.txt et removed.txt vérifient si les API sont modifiées par avec les fichiers d'API générés au moment de la compilation.
  • last_current.txt et last_removed.txt vérifient si les API sont rétrocompatible par comparaison avec des fichiers d'API.

Pour créer les fichiers de listes d'API:

  1. Créez des fichiers de listes vides.
  2. Exécutez la commande make update-api.

Utiliser le code d'analyseur généré

Pour utiliser le code Java généré, ajoutez : en tant que préfixe au module xsd_config. dans la propriété Java srcs. Le package du code Java généré est identique à la propriété package_name.

java_library {
    name: "vintf_test_java",
    srcs: [
        "srcs/**/*.java"
        ":hal_manifest"
    ],
}

Pour utiliser le code C++ généré, ajoutez le nom du module xsd_config au generated_sources et generated_headers. Et ajoutez libxml2 à static_libs ou shared_libs, car libxml2 est requis dans l'analyseur généré du code source. L'espace de noms du le code C++ généré est identique à la propriété package_name. Par exemple, si le nom du module xsd_config est hal.manifest, l'espace de noms est hal::manifest

cc_library{
    name: "vintf_test_cpp",
    srcs: ["main.cpp"],
    generated_sources: ["hal_manifest"],
    generated_headers: ["hal_manifest"],
    shared_libs: ["libxml2"],
}

Utiliser l'analyseur

Pour utiliser le code de l'analyseur Java, utilisez la méthode XmlParser#read ou read{class-name} pour renvoyer la classe de la racine . L'analyse a lieu à ce moment-là.

import hal.manifest.*;

…

class HalInfo {
    public String name;
    public String format;
    public String optional;
    …
}

void readHalManifestFromXml(File file) {
    …
    try (InputStream str = new BufferedInputStream(new FileInputStream(file))) {
        Manifest manifest = XmlParser.read(str);
        for (Hal hal : manifest.getHal()) {
            HalInfo halinfo;
            HalInfo.name = hal.getName();
            HalInfo.format = hal.getFormat();
            HalInfo.optional = hal.getOptional();
            …
        }
    }
    …
}

Pour utiliser le code d'analyseur C++, commencez par inclure le fichier d'en-tête. Le nom du est le nom du package avec les points (.) convertis en traits de soulignement (_). Utilisez ensuite la méthode read ou read{class-name} pour renvoyer classe de l'élément racine. L'analyse a lieu à ce moment-là. La valeur renvoyée est un std::optional<>.

include "hal_manifest.h"

…
using namespace hal::manifest

struct HalInfo {
    public std::string name;
    public std::string format;
    public std::string optional;
    …
};

void readHalManifestFromXml(std::string file_name) {
    …
    Manifest manifest = *read(file_name.c_str());
    for (Hal hal : manifest.getHal()) {
        struct HalInfo halinfo;
        HalInfo.name = hal.getName();
        HalInfo.format = hal.getFormat();
        HalInfo.optional = hal.getOptional();
        …
    }
    …
}

Toutes les API fournies pour utiliser l'analyseur se trouvent dans api/current.txt. Pour l'uniformité, tous les noms d'éléments et d'attributs sont convertis en camel case (par exemple, exemple, ElementName) et sont utilisées comme variable, méthode et nom de la classe. La classe de l'élément racine analysé peut être obtenue à l'aide de la méthode fonction read{class-name}. S'il n'y a qu'une seule racine , le nom de la fonction est read. la valeur d'un sous-élément analysé ou peut être obtenu à l'aide de get{variable-name} .

Générer le code de l'analyseur

Dans la plupart des cas, vous n'avez pas besoin d'exécuter xsdc directement. Utiliser le build xsd_config à la place, comme décrit dans Configurer la règle de compilation xsd_config dans Android.bp Ce explique l'interface de ligne de commande xsdc, par souci d'exhaustivité. Ce peut être utile pour le débogage.

Vous devez indiquer à l'outil xsdc le chemin d'accès au fichier XSD et à un package. La package est un nom de package en code Java et un espace de noms en code C++. Les options pour déterminer si le code généré est Java, ou si les C sont -j ou -c, respectivement. L'option -o est le chemin d'accès au répertoire de sortie.

usage: xsdc path/to/xsd_file.xsd [-c] [-j] [-o <arg>] [-p]
 -c,--cpp           Generate C++ code.
 -j,--java          Generate Java code.
 -o,--outDir <arg>  Out Directory
 -p,--package       Package name of the generated java file. file name of
                    generated C++ file and header

Exemple de commande:

$ xsdc audio_policy_configuration.xsd -p audio.policy -j