The Android platform contains many XML files for storing config
data (for example, audio config). Many of the XML files are in the vendor
partition, but they're read in the system
partition. In this case, the schema
of the XML file serves as the interface across the two partitions, and therefore
the schema must be explicitly specified and must evolve in a backward-compatible
manner.
Before Android 10, the platform didn't provide
mechanisms to require specifying and using the XML schema, or to prevent
incompatible changes in the schema. Android 10 provides
this mechanism, called Config File Schema API. This mechanism consists of a tool
called xsdc
and a build rule called xsd_config
.
The xsdc
tool is an XML Schema Document (XSD) compiler. It parses an XSD file
describing the schema of an XML file and generates Java and C++ code. The
generated code parses XML files that conform to the XSD schema into a tree of
objects, each of which models an XML tag. XML attributes are modeled as fields
of the objects.
The xsd_config
build rule integrates the xsdc
tool into the build system.
For a given XSD input file, the build rule generates Java and C++ libraries. You
can link the libraries to the modules where the XML files that conform to the
XSD are read and used. You can use the build rule for your own XML files used
across the system
and vendor
partitions.
Build Config File Schema API
This section describes how to build Config File Schema API.
Configure the xsd_config build rule in Android.bp
The xsd_config
build rule generates the parser code with the xsdc
tool. The
xsd_config
build rule’s package_name
property determines the package name of
the generated Java code.
Example xsd_config
build rule in Android.bp
:
xsd_config {
name: "hal_manifest",
srcs: ["hal_manifest.xsd"],
package_name: "hal.manifest",
}
Example directory structure:
├── Android.bp
├── api
│ ├── current.txt
│ ├── last_current.txt
│ ├── last_removed.txt
│ └── removed.txt
└── hal_manifest.xsd
The build system generates an API list using the generated Java code and checks
the API against it. This API check is added to DroidCore and executed at m -j
.
Create API lists files
The API checks require API lists files in the source code.
The API lists files include:
current.txt
andremoved.txt
check whether the APIs are changed by comparing with generated API files at build time.last_current.txt
andlast_removed.txt
check whether the APIs are backward compatible by comparing with API files.
To create the API lists files:
- Create empty lists files.
- Run the command
make update-api
.
Use generated parser code
To use the generated Java code, add :
as a prefix to the xsd_config
module
name in the Java srcs
property. The package of the generated Java code is the
same as the package_name
property.
java_library {
name: "vintf_test_java",
srcs: [
"srcs/**/*.java"
":hal_manifest"
],
}
To use the generated C++ code, add the xsd_config
module name to the
generated_sources
and generated_headers
properties. And add libxml2
to
static_libs
or shared_libs
, since libxml2
is required in generated parser
code. The namespace of the
generated C++ code is the same as the package_name
property. For example, if
the xsd_config
module name is hal.manifest
, the namespace is
hal::manifest
.
cc_library{
name: "vintf_test_cpp",
srcs: ["main.cpp"],
generated_sources: ["hal_manifest"],
generated_headers: ["hal_manifest"],
shared_libs: ["libxml2"],
}
Use the parser
To use the Java parser code, use the XmlParser#read
or
read{class-name}
method to return the class of the root
element. Parsing happens at this time.
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();
…
}
}
…
}
To use the C++ parser code, first include the header file. The name of the
header file is the package name with periods (.) converted to underscores (_).
Then use the read
or read{class-name}
method to return
the class of the root element. Parsing happens at this time. The return value is
a 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();
…
}
…
}
All the APIs provided to use the parser are in api/current.txt
. For
uniformity, all element and attribute names are converted to camel case (for
example, ElementName
) and used as the corresponding variable, method, and
class name. The class of the parsed root element can be obtained using the
read{class-name}
function. If there is only one root
element, then the function name is read
. The value of a parsed subelement or
attribute can be obtained using the get{variable-name}
function.
Generate parser code
In most cases, you don't need to run xsdc
directly. Use the xsd_config
build
rule instead, as described in
Configuring the xsd_config build rule in Android.bp. This
section explains the xsdc
command line interface, just for completeness. This
might be useful for debugging.
You must give the xsdc
tool the path to the XSD file, and a package. The
package is a package name in Java code and a namespace in C++ code. The options
to determine whether the generated code is Java or C are -j
or -c
,
respectively. The -o
option is the path of the output directory.
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
Example command:
$ xsdc audio_policy_configuration.xsd -p audio.policy -j