Platforma Android zawiera wiele plików XML do przechowywania danych konfiguracyjnych (na przykład 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 dwiema partycjami i dlatego schemat musi być jawnie określony i musi ewoluować w sposób zapewniający kompatybilność wsteczną.
Przed Androidem 10 platforma nie zapewniała mechanizmów wymagających określania i używania schematu XML ani zapobiegających niezgodnym zmianom w schemacie. Android 10 udostępnia ten mechanizm o nazwie Config File Schema API. Mechanizm ten 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). Analizuje plik XSD opisujący schemat pliku XML i generuje kod Java i C++. Wygenerowany kod analizuje pliki XML zgodne ze schematem XSD w drzewo obiektów, z których każdy modeluje znacznik XML. Atrybuty XML modelowane są 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żna połączyć biblioteki z modułami, w których odczytywane i używane są pliki XML zgodne z XSD. Możesz użyć reguły kompilacji dla własnych plików XML używanych w partycjach system
i vendor
.
Tworzenie interfejsu API schematu pliku konfiguracyjnego
W tej sekcji opisano sposób tworzenia interfejsu API schematu pliku konfiguracyjnego.
Konfigurowanie reguły kompilacji xsd_config w Android.bp
Reguła kompilacji xsd_config
generuje kod analizatora składni 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 katalogów:
├── Android.bp
├── api
│ ├── current.txt
│ ├── last_current.txt
│ ├── last_removed.txt
│ └── removed.txt
└── hal_manifest.xsd
System kompilacji generuje listę API przy użyciu wygenerowanego kodu Java i sprawdza pod kątem API. Ta kontrola API jest dodawana do DroidCore i wykonywana w m -j
.
Tworzenie plików list API
Kontrole API wymagają plików list API w kodzie źródłowym.
Pliki list API obejmują:
-
current.txt
iremoved.txt
sprawdzają, czy interfejsy API zostały zmienione, porównując z wygenerowanymi plikami API w czasie kompilacji. -
last_current.txt
ilast_removed.txt
sprawdzają, czy interfejsy API są kompatybilne wstecz, porównując z plikami API.
Aby utworzyć pliki list API:
- Utwórz puste pliki list.
- Uruchom polecenie
make update-api
.
Korzystanie z wygenerowanego kodu parsera
Aby użyć wygenerowanego kodu Java, dodaj :
jako przedrostek do nazwy modułu xsd_config
we właściwości Java srcs
. 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
. I dodaj libxml2
do static_libs
shared_libs
, ponieważ libxml2
jest wymagane w wygenerowanym kodzie parsera. Przestrzeń nazw wygenerowanego kodu C++ jest taka sama jak właściwość 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"],
}
Korzystanie z parsera
Aby użyć kodu analizatora składni Java, użyj metody XmlParser#read
lub read{ class-name }
w celu zwrócenia klasy elementu głównego. W tym momencie odbywa się parsowanie.
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 składni C++, najpierw dołącz plik nagłówkowy. Nazwa pliku nagłówkowego to nazwa pakietu z kropkami (.) zamienionymi 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ę parsowanie. Zwracaną wartością jest 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 umożliwiające korzystanie z parsera znajdują się w api/current.txt
. Aby zapewnić jednolitość, wszystkie nazwy elementów i atrybutów są konwertowane na wielkość liter wielbłąda (na przykład ElementName
) i używane jako odpowiednia zmienna, metoda i nazwa klasy. Klasę analizowanego elementu głównego można uzyskać za pomocą funkcji read{ class-name }
. Jeśli jest tylko jeden element główny, to read
jest nazwa funkcji. Wartość przeanalizowanego podelementu lub atrybutu można uzyskać za pomocą funkcji get{ variable-name }
.
Generowanie kodu parsera
W większości przypadków nie trzeba bezpośrednio uruchamiać xsdc
. Zamiast tego użyj reguły kompilacji xsd_config
, zgodnie z opisem w sekcji Konfigurowanie reguły kompilacji xsd_config w pliku Android.bp . Ta sekcja wyjaśnia interfejs wiersza poleceń xsdc
, tylko dla kompletności. Może to być przydatne do debugowania.
Musisz podać narzędziu xsdc
ścieżkę do pliku XSD i pakietu. Pakiet to nazwa pakietu w kodzie Java i przestrzeń nazw w kodzie C++. Opcje określające, czy wygenerowany kod to Java czy C, to odpowiednio -j
lub -c
. Opcja -o
określa ścieżkę 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