La plate-forme Android contient de nombreux fichiers XML pour stocker les données de configuration (par exemple, la configuration audio). La plupart des fichiers XML se trouvent dans la partition vendor
, mais ils sont lus dans la partition system
. Dans ce cas, le schéma du fichier XML sert d'interface entre les deux partitions. Par conséquent, le schéma doit être spécifié explicitement et doit évoluer de manière rétrocompatible.
Avant Android 10, la plate-forme ne fournissait pas de mécanismes permettant d'exiger la spécification et l'utilisation du schéma XML, ni d'empêcher les modifications incompatibles dans le schéma. Android 10 fournit ce mécanisme, appelé API de schéma de fichier de configuration. Ce mécanisme se compose d'un outil appelé xsdc
et d'une règle de compilation appelée xsd_config
.
L'outil xsdc
est un compilateur de document de schéma XML (XSD). Il analyse un fichier XSD décrivant le schéma d'un fichier XML et génère du code Java et C++. Le code généré analyse les fichiers XML conformes au schéma XSD dans un arbre d'objets, chacun modélisant une balise XML. Les attributs XML sont modélisés en tant que 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++. Vous pouvez associer 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 dans les partitions system
et vendor
.
API Build Config File Schema
Cette section explique comment créer l'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 propriété package_name
de la règle de compilation xsd_config
détermine le nom du package du 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 par rapport à cette liste. 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 listes de fichiers de l'API incluent les éléments suivants :
current.txt
etremoved.txt
vérifient si les API ont été modifiées en les comparant aux fichiers d'API générés au moment de la compilation.last_current.txt
etlast_removed.txt
vérifient si les API sont rétrocompatibles en les comparant aux fichiers d'API.
Pour créer les fichiers de listes d'API :
- Créez des fichiers de listes vides.
- Exécutez la commande
make update-api
.
Utiliser le code du parseur généré
Pour utiliser le code Java généré, ajoutez :
comme préfixe au nom du module xsd_config
dans la propriété Java srcs
. Le package du code Java généré est le même que 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
aux propriétés generated_sources
et generated_headers
. Ajoutez libxml2
à static_libs
ou shared_libs
, car libxml2
est requis dans le code du parseur généré. L'espace de noms du 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 du parseur Java, utilisez la méthode XmlParser#read
ou read{class-name}
pour renvoyer la classe de l'élément 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 du parseur C++, commencez par inclure le fichier d'en-tête. Le nom du fichier d'en-tête correspond au nom du package, avec les points (.) remplacés par des traits de soulignement (_).
Utilisez ensuite la méthode read
ou read{class-name}
pour renvoyer la 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 plus d'uniformité, tous les noms d'éléments et d'attributs sont convertis en camel case (par exemple, ElementName
) et utilisés comme nom de variable, de méthode et de classe correspondant. La classe de l'élément racine analysé peut être obtenue à l'aide de la fonction read{class-name}
. S'il n'y a qu'un seul élément racine, le nom de la fonction est read
. La valeur d'un sous-élément ou d'un attribut analysé peut être obtenue à l'aide de la fonction get{variable-name}
.
Générer du code d'analyseur
Dans la plupart des cas, vous n'avez pas besoin d'exécuter xsdc
directement. Utilisez plutôt la règle de compilation xsd_config
, comme décrit dans Configurer la règle de compilation xsd_config dans Android.bp. Cette section explique l'interface de ligne de commande xsdc
, juste pour être complet. Cela peut être utile pour le débogage.
Vous devez fournir à l'outil xsdc
le chemin d'accès au fichier XSD et à un package. Le package est un nom de package dans le code Java et un espace de noms dans le code C++. Les options permettant de déterminer si le code généré est en Java ou en C sont respectivement -j
ou -c
. L'option -o
correspond au 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