Implementacja interfejsu Config File Schema API

Platforma Android zawiera wiele plików XML do przechowywania danych konfiguracyjnych (np. konfiguracji dźwięku). Wiele plików XML znajduje się w partycji vendor, ale są one odczytywane w partycji system. W tym przypadku schemat pliku XML służy jako interfejs między tymi 2 partycjami, dlatego schemat musi być wyraźnie określony i musi ewoluować w sposób zapewniający zgodność wsteczną.

Przed Androidem 10 platforma nie udostępniała mechanizmów wymagających określenia i używania schematu XML ani zapobiegających niezgodnym zmianom w schemacie. Android 10 udostępnia taki mechanizm, który nazywa się Config File Schema API. Ten mechanizm składa się z narzędzia o nazwie xsdc i reguły kompilacji o nazwie xsd_config.

Narzędzie xsdc to kompilator dokumentów schematu XML (XSD). Analizuje plik XSD opisujący schemat pliku XML i generuje kod w językach Java i C++. Wygenerowany kod analizuje pliki XML zgodne ze schematem XSD i tworzy drzewo obiektów, z których każdy modeluje tag XML. Atrybuty XML są modelowane jako pola obiektów.

Reguła kompilacji xsd_config integruje narzędzie xsdc z systemem kompilacji. W przypadku danego wejściowego pliku XSD reguła kompilacji generuje biblioteki w językach Java i C++. Możesz połączyć biblioteki z modułami, w których odczytywane i używane są pliki XML zgodne ze schematem XSD. Możesz użyć reguły kompilacji w przypadku własnych plików XML używanych w partycjach system i vendor.

Kompilowanie Config File Schema API

W tej sekcji opisujemy, jak skompilować Config File Schema API.

Konfigurowanie reguły kompilacji xsd_config w pliku Android.bp

Reguła kompilacji xsd_config generuje kod analizatora za pomocą narzędzia xsdc. Właściwość package_name reguły kompilacji xsd_config określa nazwę pakietu wygenerowanego kodu w języku Java.

Przykład reguły kompilacji xsd_config w pliku Android.bp:

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

Przykładowa struktura katalogu:

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

System kompilacji generuje listę interfejsów API na podstawie wygenerowanego kodu w języku Java i sprawdza, czy interfejs API jest z nią zgodny. To sprawdzenie interfejsu API jest dodawane do DroidCore i wykonywane w m -j.

Tworzenie plików list interfejsów API

Sprawdzanie interfejsów API wymaga plików list interfejsów API w kodzie źródłowym.

Pliki list interfejsów API obejmują:

  • current.txt i removed.txt sprawdzają, czy interfejsy API zostały zmienione, porównując je z wygenerowanymi plikami interfejsów API w czasie kompilacji.
  • last_current.txt i last_removed.txt sprawdzają, czy interfejsy API są zgodne wstecznie, porównując je z plikami interfejsów API.

Aby utworzyć pliki list interfejsów API:

  1. Utwórz puste pliki list.
  2. Uruchom polecenie make update-api.

Używanie wygenerowanego kodu analizatora

Aby użyć wygenerowanego kodu w języku Java, dodaj : jako prefiks do nazwy modułu xsd_config we właściwości srcs w języku Java. Pakiet wygenerowanego kodu w języku Java jest taki sam jak właściwość package_name.

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

Aby użyć wygenerowanego kodu w języku C++, dodaj nazwę modułu xsd_config do właściwości generated_sources i generated_headers. Dodaj też libxml2 do static_libs lub shared_libs, ponieważ libxml2 jest wymagana w wygenerowanym kodzie analizatora. Przestrzeń nazw wygenerowanego kodu w języku C++ jest taka sama jak właściwość package_name. Jeśli na przykład nazwa modułu xsd_config to hal.manifest, przestrzeń nazw to hal::manifest.

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

Używanie analizatora

Aby użyć kodu analizatora w języku Java, użyj metody XmlParser#read lub read{class-name}, aby zwrócić klasę elementu głównego. W tym momencie następuje analiza.

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

Aby użyć kodu analizatora w języku C++, najpierw dołącz plik nagłówkowy. Nazwa pliku nagłówkowego to nazwa pakietu z kropkami (.) zamienionymi na podkreślenia (_). Następnie użyj metody read lub read{class-name}, aby zwrócić klasę elementu głównego. W tym momencie następuje analiza. Wartość zwracana to a 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();
        
    }
    
}

Wszystkie interfejsy API udostępniane do używania analizatora znajdują się w api/current.txt. Dla zachowania spójności wszystkie nazwy elementów i atrybutów są przekształcane na notację camel case (np. ElementName) i używane jako odpowiednia nazwa zmiennej, metody i klasy. Klasę przeanalizowanego elementu głównego można uzyskać za pomocą read{class-name} funkcji. Jeśli jest tylko 1 element główny, nazwa funkcji to read. Wartość przeanalizowanego podelementu lub atrybutu można uzyskać za pomocą funkcji get{variable-name}.

Generowanie kodu analizatora

W większości przypadków nie musisz bezpośrednio uruchamiać narzędzia xsdc. Zamiast tego użyj reguły kompilacji xsd_config, jak opisano w sekcji Konfigurowanie reguły kompilacji xsd_config w pliku Android.bp. W tej sekcji opisujemy interfejs wiersza poleceń xsdc tylko dla pełności informacji. Może to być przydatne podczas debugowania.

Musisz podać narzędziu xsdc ścieżkę do pliku XSD i pakiet. Pakiet to nazwa pakietu w kodzie w języku Java i przestrzeń nazw w kodzie w języku C++. Opcje określające, czy wygenerowany kod ma być w języku Java czy C, to odpowiednio -j lub -c. Opcja -o to ścieżka katalogu wyjściowego.

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

Przykładowe polecenie:

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