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 文件的模塊。您可以將構建規則用於您自己的跨system
和vendor
分區使用的 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.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