Wdrażanie interfejsu Config File Schema API

Platforma Androida zawiera wiele plików XML do przechowywania konfiguracji danych (np. konfiguracji audio). Wiele plików XML znajduje się w folderze vendor ale są odczytywane w partycji system. W tym przypadku schemat pliku XML służy jako interfejs między dwiema partycjami i dlatego schemat musi być wyraźnie określony i rozwijać się w sposób zgodny wstecznie; w dobrym stylu.

Przed Androidem 10 platforma nie udostępniała mechanizmów wymagających określenia schematu XML i użycia go do tego celu, niezgodne zmiany w schemacie. Android 10 zapewnia ten mechanizm nazywany interfejsem Config File Schema API. Mechanizm ten składa się z narzędzia, o nazwie xsdc i regułie kompilacji o nazwie xsd_config.

Narzędzie xsdc to kompilator XML Schema Document (XSD). Przetwarza on 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++. Ty może łączyć biblioteki z modułami, w których pliki XML zgodne z XSD są odczytywane i używane. Zasady kompilacji możesz używać w przypadku własnych plików XML używanych w particjach systemvendor.

Interfejs API schematu pliku konfiguracyjnego kompilacji

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

Skonfiguruj regułę kompilacji xsd_config w 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ładowa reguła kompilacji xsd_config w Android.bp:

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

Przykładowa struktura katalogów:

├── 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. Ten test interfejsu API jest dodawany do DroidCore i wykonywany w m -j.

Tworzenie plików list interfejsów API

Kontrole interfejsu API wymagają, aby interfejsy API zawierały listę plików w kodzie źródłowym.

Lista plików w interfejsie API obejmuje:

  • 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. Utwórz pliki pustych list.
  2. Uruchom polecenie make update-api.

Użyj wygenerowanego kodu parsera

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żyć wygenerowanego kodu C++, dodaj nazwę modułu xsd_config do sekcji Usługi generated_sources i generated_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. Na przykład, jeśli 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 parsowania

Aby skorzystać z kodu parsera Java, użyj instrukcji XmlParser#read lub Metoda read{class-name} zwracająca klasę pierwiastka . 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 plik nagłówka to nazwa pakietu z kropkami (.) przekonwertowanymi na podkreślenia (_). 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ępniane do korzystania z parsera mają język api/current.txt. Dla: jednorodność, nazwy wszystkich elementów i atrybutów są konwertowane na wielkość liter wielbłąda (na np. ElementName) i jest używany jako odpowiednia zmienna, metoda i nazwę zajęć. klasę przeanalizowanego elementu 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 podelementu lub atrybutu można uzyskać za pomocą funkcji get{variable-name} .

Generowanie kodu parsowania

W większości przypadków nie musisz uruchamiać xsdc bezpośrednio. Użyj kompilacji xsd_config zgodnie z opisem w sekcji Konfigurowanie reguły kompilacji xsd_config w pliku Android.bp Ten wyjaśnia, jak działa interfejs wiersza poleceń xsdc. Może to być przydatne podczas debugowania.

Musisz podać narzędziu xsdc ścieżkę do pliku XSD oraz pakiet. W kodzie Java jest to nazwa pakietu, a w kodzie C++ – nazwa przestrzeni nazw. 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