Implementazione dell'API Config File Schema

La piattaforma Android contiene molti file XML per l'archiviazione (ad esempio la configurazione audio). Molti file XML si trovano in vendor ma verranno letti nella partizione system. In questo caso, lo schema del file XML funge da interfaccia tra le due partizioni. lo schema deve essere specificato esplicitamente e deve evolversi in una in modo adeguato.

Prima di Android 10, la piattaforma non forniva meccanismi per richiedere la specifica e l'utilizzo dello schema XML o per impedire modifiche incompatibili nello schema. Android 10 offre questo meccanismo, chiamato API Config File Schema. Questo meccanismo è costituito da uno strumento chiamato xsdc e da una regola di compilazione chiamata xsd_config.

Lo strumento xsdc è un compilatore di documenti XML Schema (XSD). Analizza un file XSD che descrive lo schema di un file XML e genera codice Java e C++. Il codice generato analizza i file XML conformi allo schema XSD in un albero di oggetti, ognuno dei quali modella un tag XML. Gli attributi XML sono modellati come campi degli oggetti.

La regola di build xsd_config integra lo strumento xsdc nel sistema di compilazione. Per un determinato file di input XSD, la regola di compilazione genera librerie Java e C++. Puoi collegare le librerie ai moduli in cui vengono letti e utilizzati i file XML conformi allo schema XSD. Puoi usare la regola di creazione per i tuoi file XML utilizzati tra le partizioni system e vendor.

API Build Config File Schema

Questa sezione descrive come creare l'API Config File Schema.

Configura la regola di compilazione xsd_config in Android.bp

La regola di compilazione xsd_config genera il codice del parser con lo strumento xsdc. La La proprietà package_name della regola di build xsd_config determina il nome del pacchetto di il codice Java generato.

Esempio di regola di compilazione xsd_config in Android.bp:

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

Esempio di struttura di directory:

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

Il sistema di compilazione genera un elenco di API utilizzando il codice Java generato e verifica l'API. Questo controllo dell'API viene aggiunto a DroidCore ed eseguito alle ore m -j.

Crea file degli elenchi delle API

I controlli dell'API richiedono che i file degli elenchi di API siano presenti nel codice sorgente.

I file elencati dall'API includono:

  • current.txt e removed.txt controllano se le API vengono modificate da con i file API generati in fase di creazione.
  • last_current.txt e last_removed.txt controllano se le API sono compatibili con le versioni precedenti, confrontandoli con i file API.

Per creare i file degli elenchi API:

  1. Crea file di elenchi vuoti.
  2. Esegui il comando make update-api.

Utilizzare il codice del parser generato

Per utilizzare il codice Java generato, aggiungi : come prefisso al modulo xsd_config nella proprietà Java srcs. Il pacchetto del codice Java generato è uguale alla proprietà package_name.

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

Per utilizzare il codice C++ generato, aggiungi il nome del modulo xsd_config alla generated_sources e generated_headers proprietà. Aggiungi libxml2 a static_libs o shared_libs, poiché libxml2 è obbligatorio nel codice del parser generato. Lo spazio dei nomi del codice C++ generato è uguale a quello della proprietà package_name. Ad esempio, se il nome del modulo xsd_config è hal.manifest, lo spazio dei nomi è hal::manifest.

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

Utilizzare il parser

Per utilizzare il codice dell'analizzatore sintattico Java, utilizza XmlParser#read o Metodo read{class-name} per restituire la classe del root . L'analisi viene eseguita in questo momento.

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();
            …
        }
    }
    …
}

Per utilizzare il codice del parser C++, includi innanzitutto il file di intestazione. Il nome del file di intestazione è il nome del pacchetto con i punti (.) convertiti in trattini bassi (_). Quindi utilizza il metodo read o read{class-name} per restituire la classe dell'elemento radice. L'analisi avviene in questo momento. Il valore restituito è 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();
        …
    }
    …
}

Tutte le API fornite per utilizzare il parser sono in api/current.txt. Per uniformità, tutti i nomi di elementi e attributi vengono convertiti in Camel Case (ad esempio ElementName) e utilizzati come nomi corrispondenti di variabili, metodi e classi. La classe dell'elemento radice analizzato può essere ottenuta utilizzando la funzione read{class-name}. Se è presente una sola radice , il nome della funzione sarà read. Il valore di un elemento secondario o di un attributo analizzati può essere ottenuto utilizzando la funzione get{variable-name}.

Genera codice del parser

Nella maggior parte dei casi, non è necessario eseguire xsdc direttamente. Utilizza invece la regola di compilazione xsd_config, come descritto in Configurare la regola di compilazione xsd_config in Android.bp. Questo spiega l'interfaccia a riga di comando xsdc, solo per completezza. Questo per il debug.

Devi assegnare allo strumento xsdc il percorso del file XSD e un pacchetto. Il pacchetto è un nome del pacchetto nel codice Java e uno spazio dei nomi nel codice C++. Le opzioni per determinare se il codice generato è Java o C sono -j o -c, rispettivamente. L'opzione -o è il percorso della directory di output.

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

Comando di esempio:

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