Implementacja interfejsu Config File Schema API

Platforma Android zawiera wiele plików XML do przechowywania danych konfiguracji (np. konfiguracji audio). Wiele plików XML znajduje się na partycji vendor, ale są one odczytywane na partycji system. W tym przypadku schemat pliku XML służy jako interfejs między obiema 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 wymagających określenia i używania schematu XML ani zapobiegających wprowadzaniu niezgodnych zmian w tym schemacie. Android 10 udostępnia ten mechanizm, czyli interfejs 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 dokumentu schematu XML (XSD). Przetwarza plik XSD opisujący schemat pliku XML i generuje kod Java i C++. Wygenerowany kod analizuje pliki XML zgodne ze schematem XSD i przekształca je w 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 pliku wejściowego XSD reguła kompilacji generuje biblioteki Java i C++. Możesz powiązać biblioteki z modułami, w których pliki XML zgodne z plikiem XSD są odczytywane i używane. Możesz użyć reguły kompilacji dla własnych plików XML używanych w particjach systemvendor.

Interfejs API schematu pliku konfiguracji do tworzenia

W tej sekcji opisaliśmy, jak tworzyć interfejs API schematu pliku konfiguracji.

Konfigurowanie reguły kompilacji xsd_config w pliku Android.bp

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

Przykład reguły xsd_config w komponencie 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 za pomocą wygenerowanego kodu Java i porównuje ją z interfejsami API. Ta weryfikacja interfejsu API jest dodawana do DroidCore i wykonana w miejscu m -j.

Tworzenie plików list interfejsu API

Sprawdzanie interfejsu API wymaga, aby pliki list interfejsu API znajdowały się w źródle kodu.

Pliki list interfejsu API obejmują:

  • current.txtremoved.txt sprawdź, czy interfejsy API zostały zmienione, porównując je z wygenerowanymi plikami API w momencie kompilacji.
  • last_current.txtlast_removed.txt sprawdź, czy interfejsy API są zgodne wstecz, porównując je z plikami API.

Aby utworzyć pliki list interfejsu API:

  1. tworzyć puste pliki list.
  2. Uruchom polecenie make update-api.

Używanie wygenerowanego kodu parsowania

Aby użyć wygenerowanego kodu Java, dodaj : jako prefiks do nazwy modułu xsd_config w usłudze w Java srcs. Pakiet wygenerowanego kodu Java jest taki sam jak usługa package_name.

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

Aby używać wygenerowanego kodu C++, dodaj nazwę modułu xsd_config do właściwości generated_sourcesgenerated_headers. Dodaj też libxml2 do static_libs lub shared_libs, ponieważ libxml2 jest wymagany w wygenerowanym kodzie parsowania. Przestrzeń nazw wygenerowanego kodu C++ jest taka sama jak w przypadku właściwości package_name. Jeśli na przykład nazwa modułu xsd_config to hal.manifest, to 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"],
}

Korzystanie z analizatora

Aby użyć kodu parsowania w Javie, 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 parsowania C++, najpierw dołącz plik nagłówka. Nazwa pliku nagłówka to nazwa pakietu z kropkami (.) zastąpionymi podkreśleniami (_). Następnie użyj metody read lub read{class-name}, aby zwrócić klasę elementu. W tym momencie 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ępnione do obsługi parsowania znajdują się w api/current.txt. W trosce o jednolitość wszystkie nazwy elementów i atrybutów są zamieniane na format CamelCase (np. ElementName) i używane jako nazwy odpowiednich zmiennych, metod i klas. Klasę zanalizowanego elementu katalogu głównego można uzyskać za pomocą funkcji read{class-name}. Jeśli jest tylko 1 element główny, nazwa funkcji to read. Wartość przeanalizowanego atrybutu lub podelementu można uzyskać za pomocą funkcji get{variable-name}.

Generowanie kodu parsowania

W większości przypadków nie musisz uruchamiać xsdc bezpośrednio. Zamiast tego użyj reguły kompilacji xsd_config, jak opisano w artykule Konfigurowanie reguły kompilacji xsd_config w pliku Android.bp. W tej sekcji znajdziesz informacje o interfejsie wiersza poleceń xsdc. Może to być przydatne podczas debugowania.

Musisz podać narzędziu xsdc ścieżkę do pliku XSD oraz pakiet. pakiet to nazwa pakietu w kodzie Java i przestrzeń nazw w kodzie C++. Opcje określające, czy wygenerowany kod jest w języku Java czy C, to odpowiednio -j lub -c. Opcja -o to ścieżka do 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