實現配置文件模式 API

Android 平台包含許多用於存儲配置數據的 XML 文件(例如,音頻配置)。許多 XML 文件位於vendor分區中,但它們是在system分區中讀取的。在這種情況下,XML 文件的模式用作跨兩個分區的接口,因此必須明確指定模式並且必須以向後兼容的方式發展。

在 Android 10 之前,該平台沒有提供機制來要求指定和使用 XML 架構,或者防止架構中的不兼容更改。 Android 10 提供了這種機制,稱為 Config File Schema API。該機制由一個名為 xsdc 的工具和一個名為xsdc的構建規則xsd_config

xsdc工具是一個 XML Schema Document (XSD) 編譯器。它解析描述 XML 文件模式的 XSD 文件並生成 Java 和 C++ 代碼。生成的代碼將符合 XSD 模式的 XML 文件解析為對象樹,每個對像都模擬一個 XML 標記。 XML 屬性被建模為對象的字段。

xsd_config構建規則將xsdc工具集成到構建系統中。對於給定的 XSD 輸入文件,構建規則會生成 Java 和 C++ 庫。您可以將庫鏈接到讀取和使用符合 XSD 的 XML 文件的模塊。您可以將構建規則用於您自己的跨systemvendor分區使用的 XML 文件。

構建配置文件模式 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屬性。並將libxml2添加到static_libsshared_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#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 = XmlParser.read(str);
        for (Hal hal : manifest.getHal()) {
            HalInfo halinfo;
            HalInfo.name = hal.getName();
            HalInfo.format = hal.getFormat();
            HalInfo.optional = hal.getOptional();
            …
        }
    }
    …
}

要使用 C++ 解析器代碼,首先要包含頭文件。頭文件的名稱是包名,句點(.)轉換為下劃線(_)。然後使用readread{ 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構建規則,如在 Android.bp 中配置 xsd_config 構建規則中所述。本節解釋xsdc命令行界面,只是為了完整。這可能對調試有用。

您必須為xsdc工具提供 XSD 文件的路徑和一個包。包是 Java 代碼中的包名和 C++ 代碼中的命名空間。確定生成的代碼是 Java 還是 C 的選項分別是-j-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