Die Android-Plattform enthält viele XML-Dateien zum Speichern von Konfigurationsdaten (z. B. Audiokonfiguration). Viele der XML-Dateien befinden sich in der Partition vendor
, werden aber in der Partition system
gelesen. In diesem Fall dient das Schema der XML-Datei als Schnittstelle zwischen den beiden Partitionen. Daher muss das Schema explizit angegeben werden und sich rückwärtskompatibel entwickeln.
Vor Android 10 bot die Plattform keine Mechanismen, um die Angabe und Verwendung des XML-Schemas zu erzwingen oder inkompatible Änderungen am Schema zu verhindern. Android 10 bietet diesen Mechanismus, der als Config File Schema API bezeichnet wird. Dieser Mechanismus besteht aus einem Tool namens xsdc
und einer Build-Regel namens xsd_config
.
Das xsdc
-Tool ist ein XSD-Compiler (XML Schema Document). Es parst eine XSD-Datei, die das Schema einer XML-Datei beschreibt, und generiert Java- und C++-Code. Der generierte Code parst XML-Dateien, die dem XSD-Schema entsprechen, in einen Baum von Objekten, von denen jedes ein XML-Tag modelliert. XML-Attribute werden als Felder der Objekte modelliert.
Die xsd_config
-Build-Regel integriert das xsdc
-Tool in das Build-System.
Für eine bestimmte XSD-Eingabedatei generiert die Build-Regel Java- und C++-Bibliotheken. Sie können die Bibliotheken mit den Modulen verknüpfen, in denen die XML-Dateien, die dem XSD entsprechen, gelesen und verwendet werden. Sie können die Build-Regel für Ihre eigenen XML-Dateien verwenden, die in den Partitionen system
und vendor
verwendet werden.
Build Config File Schema API
In diesem Abschnitt wird beschrieben, wie Sie die Config File Schema API erstellen.
xsd_config-Build-Regel in Android.bp konfigurieren
Mit der xsd_config
-Build-Regel wird der Parsercode mit dem Tool xsdc
generiert. Die package_name
-Eigenschaft der Build-Regel xsd_config
bestimmt den Paketnamen des generierten Java-Codes.
Beispiel für eine xsd_config
-Build-Regel in Android.bp
:
xsd_config {
name: "hal_manifest",
srcs: ["hal_manifest.xsd"],
package_name: "hal.manifest",
}
Beispiel für Verzeichnisstruktur:
├── Android.bp
├── api
│ ├── current.txt
│ ├── last_current.txt
│ ├── last_removed.txt
│ └── removed.txt
└── hal_manifest.xsd
Das Build-System generiert eine API-Liste mit dem generierten Java-Code und vergleicht die API damit. Diese API-Prüfung wird DroidCore hinzugefügt und unter m -j
ausgeführt.
API-Listendateien erstellen
Für die API-Prüfungen sind API-Listen-Dateien im Quellcode erforderlich.
Die API-Dateilisten enthalten:
current.txt
undremoved.txt
prüfen, ob die APIs geändert wurden, indem sie mit den generierten API-Dateien zur Build-Zeit verglichen werden.last_current.txt
undlast_removed.txt
prüfen, ob die APIs abwärtskompatibel sind, indem sie sie mit API-Dateien vergleichen.
So erstellen Sie die API-Listendateien:
- Leere Listendateien erstellen.
- Führen Sie den Befehl
make update-api
aus.
Generierten Parsercode verwenden
Wenn Sie den generierten Java-Code verwenden möchten, fügen Sie :
als Präfix zum Namen des xsd_config
-Moduls in der Java-srcs
-Eigenschaft hinzu. Das Paket des generierten Java-Codes ist dasselbe wie das Attribut package_name
.
java_library {
name: "vintf_test_java",
srcs: [
"srcs/**/*.java"
":hal_manifest"
],
}
Wenn Sie den generierten C++-Code verwenden möchten, fügen Sie den Modulnamen xsd_config
den Eigenschaften generated_sources
und generated_headers
hinzu. Fügen Sie libxml2
zu static_libs
oder shared_libs
hinzu, da libxml2
im generierten Parsercode erforderlich ist. Der Namespace des generierten C++-Codes entspricht der package_name
-Eigenschaft. Wenn der Modulname xsd_config
beispielsweise hal.manifest
lautet, ist der Namespace hal::manifest
.
cc_library{
name: "vintf_test_cpp",
srcs: ["main.cpp"],
generated_sources: ["hal_manifest"],
generated_headers: ["hal_manifest"],
shared_libs: ["libxml2"],
}
Parser verwenden
Wenn Sie den Java-Parsercode verwenden möchten, verwenden Sie die Methode XmlParser#read
oder read{class-name}
, um die Klasse des Stammelements zurückzugeben. Das Parsing erfolgt zu diesem Zeitpunkt.
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();
…
}
}
…
}
Wenn Sie den C++-Parsercode verwenden möchten, müssen Sie zuerst die Headerdatei einfügen. Der Name der Headerdatei ist der Paketname, wobei Punkte (.) in Unterstriche (_) umgewandelt werden.
Verwenden Sie dann die Methode read
oder read{class-name}
, um die Klasse des Stammelements zurückzugeben. Das Parsing erfolgt zu diesem Zeitpunkt. Der Rückgabewert ist ein 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();
…
}
…
}
Alle APIs, die für die Verwendung des Parsers bereitgestellt werden, befinden sich in api/current.txt
. Zur Einheitlichkeit werden alle Element- und Attributnamen in CamelCase konvertiert (z. B. ElementName
) und als entsprechende Variablen-, Methoden- und Klassennamen verwendet. Die Klasse des geparsten Stammelements kann mit der Funktion read{class-name}
abgerufen werden. Wenn es nur ein Stammelement gibt, lautet der Funktionsname read
. Der Wert eines geparsten Unterelements oder Attributs kann mit der Funktion get{variable-name}
abgerufen werden.
Parsercode generieren
In den meisten Fällen müssen Sie xsdc
nicht direkt ausführen. Verwenden Sie stattdessen die Build-Regel xsd_config
, wie unter xsd_config-Build-Regel in Android.bp konfigurieren beschrieben. In diesem Abschnitt wird die xsdc
-Befehlszeilenschnittstelle nur der Vollständigkeit halber erläutert. Das kann beim Debugging hilfreich sein.
Sie müssen dem Tool xsdc
den Pfad zur XSD-Datei und ein Paket angeben. Das Paket ist ein Paketname im Java-Code und ein Namespace im C++-Code. Die Optionen, mit denen festgelegt wird, ob der generierte Code Java oder C ist, sind -j
bzw. -c
. Die Option -o
ist der Pfad des Ausgabeverzeichnisses.
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
Beispielbefehl:
$ xsdc audio_policy_configuration.xsd -p audio.policy -j