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 system
i vendor
.
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_config
w 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 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.txt
iremoved.txt
sprawdzają, czy interfejsy API zostały zmienione, porównując je z wygenerowanymi plikami interfejsu API w czasie kompilacji.last_current.txt
ilast_removed.txt
sprawdzają, czy interfejsy API są wstecznie zgodne, porównując je z plikami interfejsu API.
Aby utworzyć pliki list interfejsu API:
- Utwórz puste pliki list.
- 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_sources
i generated_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
i -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