導入設定檔結構定義 API

Android 平台包含許多 XML 檔案,用於儲存設定資料 (例如音訊設定)。許多 XML 檔案位於 vendor 分區,但會在 system 分區中讀取。在這種情況下,XML 檔案的結構定義會做為兩個區隔的介面,因此必須明確指定結構定義,並以向後相容的方式進行調整。

在 Android 10 之前,這個平台並未提供要求指定及使用 XML 結構定義的機制,也未提供防止結構定義發生不相容的變更。Android 10 提供此機制,稱為設定檔架構 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 檔案,使用建構規則。

建構設定檔案架構 API

本節說明如何建構設定檔 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