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 檔案的模組。您可以使用建構規則,為 system
和 vendor
分區中使用的 XML 檔案建構規則。
Build Config File Schema API
本節說明如何建構設定檔結構定義 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.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
建構規則,詳情請參閱「在 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