实现 Config File Schema API

Android 平台包含许多用于存储配置数据(例如,音频配置)的 XML 文件。很多 XML 文件都位于 vendor 分区中,但读取它们的操作在 system 分区中进行。在这种情况下,XML 文件的架构充当这两个分区之间的接口,因此您必须明确指定架构,并且必须以向后兼容的方式改进该架构。

在 Android 10 之前的版本中,Android 平台没有提供需要指定和使用 XML 架构的机制,也没有提供防止架构中出现不兼容更改的机制。Android 10 提供了这种机制,称为 Config File Schema API。该机制由一个名为 xsdc 的工具和一个名为 xsd_config 的构建规则组成。

xsdc 工具是一种 XML 架构文档 (XSD) 编译器。它用于解析描述 XML 文件架构的 XSD 文件,并生成 Java 和 C++ 代码。生成的代码会将符合 XSD 架构的 XML 文件解析到对象树,其中的每个对象均会为一个 XML 标记建模。XML 属性会建模为对象的字段。

xsd_config 构建规则会将 xsdc 工具集成到构建系统中。对于给定的 XSD 输入文件,该构建规则会生成 Java 和 C++ 库。您可以将这些库与在其中读取和使用符合 XSD 的 XML 文件的模块相关联。您可以将该构建规则用于跨 systemvendor 分区使用的 XML 文件。

构建 Config File Schema API

本部分介绍了如何构建 Config File Schema API。

在 Android.bp 中配置 xsd_config 构建规则

xsd_config 构建规则使用 xsdc 工具生成解析器代码。xsd_config 构建规则的 package_name 属性决定了生成的 Java 代码的数据包名称。

Android.bp 中的 xsd_config 构建规则示例:

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.txtremoved.txt:通过在构建时与生成的 API 文件进行比较来检查 API 是否发生更改。
  • last_current.txtlast_removed.txt:通过与 API 文件进行比较来检查 API 是否向后兼容。

要创建 API 列表文件,请执行以下操作:

  1. 创建空列表文件。
  2. 运行命令 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_sourcesgenerated_headers 属性中。生成的 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"],
    }
    

使用解析器

要使用 Java 解析器代码,请使用 readread{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 = read(str);
            for (Hal hal : manifest.getHal()) {
                HalInfor halinfo;
                HalInfo.name = hal.getName();
                HalInfo.format = hal.getFormat();
                HalInfor.optional = hal.getOptional();
                …
            }
        }
        …
    }
    

要使用 C++ 解析器代码,请先添加头文件。将数据包名称中的句点 (.) 转换为下划线 (_),该名称即是头文件的名称。然后,使用 readread{class-name} 方法返回根元素的类。此时会进行解析。返回值是 std::optional<>

include "hal_manifest.h"

    …
    using namespace hal::manifest

    struct HalInfor {
        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 HalInfor halinfo;
            HalInfo.name = hal.getName();
            HalInfo.format = hal.getFormat();
            HalInfor.optional = hal.getOptional();
            …
        }
        …
    }
    

提供的所有可以使用解析器的 API 均位于 api/current.txt 中。为保持统一,所有元素和属性名称均会转换为驼峰式大小写形式(例如,ElementName),并用作相应的变量、方法和类名称。您可以使用 read{class-name} 函数获取已解析的根元素的类。如果只有一个根元素,则函数名称为 read。您可以使用 get{variable-name} 函数获取已解析的子元素或属性的值。

生成解析器代码

在大多数情况下,您不需要直接运行 xsdc。您可以改为使用 xsd_config 构建规则,如在 Android.bp 中配置 xsd_config 构建规则中所述。本部分介绍 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