Implementacja interfejsu Config File Schema API

Platforma Android zawiera wiele plików XML do przechowywania danych konfiguracyjnych (np. konfiguracji audio). Wiele plików XML znajduje się na partycji vendor, ale są one odczytywane z partycji system. W tym przypadku schemat pliku XML służy jako interfejs między 2 partycjami, dlatego 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, które wymagałyby określania i używania schematu XML ani zapobiegałyby wprowadzaniu w nim niezgodnych zmian. Android 10 udostępnia ten mechanizm, który nazywa się Config File Schema API. Ten mechanizm składa się z narzędzia xsdc i reguły kompilacji 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 z nich 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 Java i C++. Możesz połączyć biblioteki z modułami, w których są odczytywane i używane pliki XML zgodne ze schematem XSD. Możesz użyć reguły kompilacji dla własnych plików XML używanych w partycjach systemvendor.

Build Config File Schema API

W tej sekcji opisaliśmy, jak utworzyć interfejs Config File Schema API.

Skonfiguruj regułę kompilacji xsd_config w pliku Android.bp.

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

Przykładowa reguła kompilacji xsd_configAndroid.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 Java i sprawdza, czy interfejs API jest zgodny z tą listą. Ta weryfikacja interfejsu API jest dodawana do DroidCore i wykonywana w momencie m -j.

Tworzenie plików list interfejsów API

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

Listy plików interfejsu API obejmują:

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

Aby utworzyć pliki list interfejsu API:

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

Używanie wygenerowanego kodu parsera

Aby użyć wygenerowanego kodu w języku Java, dodaj znak : jako prefiks do nazwy modułu xsd_config we właściwości srcs w języku Java. Pakiet wygenerowanego kodu 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 C++, dodaj nazwę modułu xsd_config do właściwości generated_sourcesgenerated_headers. Dodaj libxml2 do static_libs lub shared_libs, ponieważ libxml2 jest wymagany w wygenerowanym kodzie analizatora. Przestrzeń nazw wygenerowanego kodu C++ jest taka sama jak właściwość package_name. Jeśli np. 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 parsera

Aby użyć kodu analizatora Java, użyj metody XmlParser#read lub read{class-name}, aby zwrócić klasę elementu głównego. W tym czasie 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 C++, najpierw dołącz plik nagłówkowy. Nazwa pliku nagłówkowego to nazwa pakietu, w której kropki (.) zostały zamienione na podkreślenia (_). Następnie użyj metody read lub read{class-name}, aby zwrócić klasę elementu głównego. W tym czasie następuje analiza. Zwracana wartość to 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 korzystania z analizatora znajdują się w api/current.txt. Dla zachowania spójności wszystkie nazwy elementów i atrybutów są przekształcane na format camel case (np. ElementName) i używane jako odpowiednie nazwy zmiennych, metod i klas. Klasę przeanalizowanego elementu głównego można uzyskać za pomocą funkcji read{class-name}. Jeśli jest tylko jeden element główny, nazwa funkcji to read. Wartość przeanalizowanego podelementu lub atrybutu można uzyskać za pomocą funkcji get{variable-name}.

Generowanie kodu parsera

W większości przypadków nie musisz uruchamiać xsdc bezpośrednio. Zamiast tego użyj reguły xsd_config build rule, zgodnie z opisem w artykule Konfigurowanie reguły kompilacji xsd_config w pliku Android.bp. Ta sekcja zawiera opis interfejsu wiersza poleceń xsdc. 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 Java i przestrzeń nazw w kodzie C++. Opcje określające, czy wygenerowany kod ma być w języku Java czy C, to odpowiednio -j-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