Platforma Android zawiera wiele plików XML do przechowywania danych konfiguracyjnych (np. audio config). 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 rozwijany w sposób zapewniający zgodność wsteczną.
Przed Androidem 10 platforma nie udostępniała mechanizmów wymagających określenia schematu XML i korzystania z niego oraz zapobiegania niezgodnym zmianom w 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 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.
Dla danego pliku wejściowego XSD reguła kompilacji generuje biblioteki Java i C++. Możesz łączyć 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 system
i vendor
.
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 przy użyciu wygenerowanego kodu w Javie i porównuje z nim interfejs 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.txt
iremoved.txt
sprawdź, czy interfejsy API zostały zmienione, porównując je z wygenerowanymi plikami API w czasie kompilacji.last_current.txt
ilast_removed.txt
sprawdź, czy interfejsy API są zgodne wstecz, porównując je z plikami API.
Aby utworzyć pliki list API:
- tworzyć puste pliki list.
- Uruchom polecenie
make update-api
.
Korzystanie z wygenerowanego kodu parsowania
Aby użyć wygenerowanego kodu Java, dodaj :
jako prefiks do nazwy modułu xsd_config
we właściwości srcs
Java. 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 właściwości 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 w C++ jest taka sama jak właściwość package_name
. Jeśli na przykład nazwa modułu xsd_config
ma nazwę hal.manifest
, przestrzenią nazw jest 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 użyć kodu parsera Java, użyj metody XmlParser#read
lub read{class-name}
, aby zwrócić klasę elementu głównego. W tym momencie odbywa się 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 parsera C++, najpierw dołącz plik nagłówka. Nazwa pliku 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 głównego. W tym momencie odbywa się 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
. W trosce o jednolitość wszystkie nazwy elementów i atrybutów są zamieniane na nazwy w stylu „wielkie litery w początku wyrazu” (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}
.
Wygeneruj kod parsera
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 objaśniamy interfejs wiersza poleceń xsdc
w celu pełnego wglądu. Może to być przydatne podczas debugowania.
Musisz podać narzędziu xsdc
ścieżkę do pliku XSD i ścieżkę do pakietu. W kodzie Javy 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