Android 平台包含许多用于存储配置数据(如音频配置)的 XML 文件。很多 XML 文件都位于 vendor
分区中,但读取它们的操作在 system
分区中进行。在这种情况下,XML 文件的架构充当这两个分区之间的接口,因此您必须明确指定架构,并且必须以向后兼容的方式改进该架构。
在 Android 10 之前的版本中,Android 平台没有提供需要指定和使用 XML 架构的机制,也没有提供防止架构中出现不兼容更改的机制。Android 10 提供了这种机制,称为 Config File Schema API。该机制由一个名为 xsdc
的工具和一个名为 xsd_config
的 build 规则组成。
xsdc
工具是一种 XML 架构文档 (XSD) 编译器。它用于解析描述 XML 文件架构的 XSD 文件,并生成 Java 和 C++ 代码。生成的代码会将符合 XSD 架构的 XML 文件解析为对象树,其中的每个对象均为一个 XML 标记建模。XML 属性会建模为对象的字段。
xsd_config
build 规则会将 xsdc
工具集成到构建系统中。
对于给定的 XSD 输入文件,该 build 规则会生成 Java 和 C++ 库。您可以将这些库与读取和使用符合 XSD 的 XML 文件的模块相关联。跨 system
和 vendor
分区使用的 XML 文件也可使用该 build 规则。
构建 Config File Schema API
本部分介绍了如何构建 Config File Schema API。
在 Android.bp 中配置 xsd_config 构建规则
xsd_config
build 规则使用 xsdc
工具生成解析器代码。xsd_config
build 规则的 package_name
属性决定了生成的 Java 代码的软件包名称。
Android.bp
中的 xsd_config
build 规则示例:
xsd_config {
name: "hal_manifest",
srcs: ["hal_manifest.xsd"],
package_name: "hal.manifest",
}
目录结构示例:
├── Android.bp
├── api
│ ├── current.txt
│ ├── last_current.txt
│ ├── last_removed.txt
│ └── removed.txt
└── hal_manifest.xsd
构建系统用生成的 Java 代码生成 API 列表,并根据该列表检查 API。此项 API 检查已添加到 DroidCore 并在 m -j
处执行。
创建 API 列表文件
要执行 API 检查,源代码中必须包含 API 列表文件。
API 列表文件包含以下内容:
current.txt
和removed.txt
可在构建时与生成的 API 文件进行比较来检查 API 是否发生更改。last_current.txt
和last_removed.txt
可通过与 API 文件进行比较来检查 API 是否向后兼容。
如需创建 API 列表文件,请执行以下操作:
- 创建空列表文件。
- 运行命令
make update-api
。
使用生成的解析器代码
如需使用生成的 Java 代码,请在 Java srcs
属性中添加 :
作为 xsd_config
模块名称的前缀。生成的 Java 代码的软件包与 package_name
属性相同。
java_library {
name: "vintf_test_java",
srcs: [
"srcs/**/*.java"
":hal_manifest"
],
}
如需使用生成的 C++ 代码,请将 xsd_config
模块名称添加到 generated_sources
和 generated_headers
属性中。将 libxml2
添加到 static_libs
或 shared_libs
中,因为需要在生成的解析器代码中使用 libxml2
。生成的 C++ 代码的命名空间与 package_name
属性相同。例如,如果 xsd_config
模块的名称为 hal.manifest
,则命名空间为 hal::manifest
。
cc_library{
name: "vintf_test_cpp",
srcs: ["main.cpp"],
generated_sources: ["hal_manifest"],
generated_headers: ["hal_manifest"],
shared_libs: ["libxml2"],
}
使用解析器
如需使用 Java 解析器代码,请使用 XmlParser#read
或 read{class-name}
方法返回根元素的类。此时会进行解析。
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();
…
}
}
…
}
如需使用 C++ 解析器代码,请先添加头文件。将软件包名称中的句点 (.) 转换为下划线 (_),即是头文件的名称。然后,使用 read
或 read{class-name}
方法返回根元素的类。此时会进行解析。返回值是 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();
…
}
…
}
提供的所有可以使用解析器的 API 均位于 api/current.txt
中。为保持统一,所有元素和属性名称均会转换为驼峰式大小写形式(如 ElementName
),并用作相应的变量、方法和类名称。您可以使用 read{class-name}
函数获取已解析的根元素的类。如果只有一个根元素,则函数名称为 read
。您可以使用 get{variable-name}
函数获取已解析的子元素或属性的值。
生成解析器代码
在大多数情况下,您不需要直接运行 xsdc
,而是可以使用 xsd_config
build 规则,详见在 Android.bp 中配置 xsd_config build 规则中的描述。考虑到本文档的完整性,本部分介绍了 xsdc
命令行界面。对于调试可能会有帮助。
您必须为 xsdc
工具提供 XSD 文件的路径和一个软件包。该软件包是指 Java 代码中的软件包名称和 C++ 代码中的命名空间。可使用 -j
及 -c
分别确定生成的代码是否是 Java 代码及 C 代码。-o
选项表示输出目录的路径。
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
命令示例:
$ xsdc audio_policy_configuration.xsd -p audio.policy -j