實作設定檔架構 API

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.在這種情況下,XML 檔案的模式充當跨兩個分區的接口,因此必須明確指定該模式,並且必須以向後相容的方式發展。

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 屬性被建模為物件的欄位。

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.

建構設定檔架構 API

This section describes how to build Config File Schema API.

在 Android.bp 中設定 xsd_config 建置規則

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.

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

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 .

建立 API 列表文件

The API checks require API lists files in the source code.

API 清單檔案包括:

  • current.txt and removed.txt check whether the APIs are changed by comparing with generated API files at build time.
  • last_current.txt and last_removed.txt check whether the APIs are backward compatible by comparing with API files.

若要建立 API 清單檔案:

  1. 建立空列表檔案。
  2. 運行命令make update-api

使用產生的解析器程式碼

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"],
}

使用解析器

To use the Java parser code, use the XmlParser#read or read{ class-name } method to return the class of the root element.解析此時發生。

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.解析此時發生。傳回值是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.可以使用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