Платформа Android содержит множество XML-файлов для хранения данных конфигурации (например, аудиоконфигурации). Многие XML-файлы находятся в разделе vendor
, но читаются они в system
разделе. В этом случае схема XML-файла служит интерфейсом между двумя разделами, и поэтому схема должна быть явно указана и должна развиваться с учетом обратной совместимости.
До Android 10 платформа не предоставляла механизмов, требующих указания и использования схемы XML или предотвращения несовместимых изменений в схеме. Android 10 предоставляет этот механизм, который называется Config File Schema API. Этот механизм состоит из инструмента xsdc
и правила сборки xsd_config
.
Инструмент xsdc
представляет собой компилятор XML Schema Document (XSD). Он анализирует файл XSD, описывающий схему файла XML, и генерирует код Java и C++. Сгенерированный код анализирует файлы XML, соответствующие схеме XSD, в дерево объектов, каждый из которых моделирует тег XML. Атрибуты XML моделируются как поля объектов.
Правило сборки xsd_config
интегрирует инструмент xsdc
в систему сборки. Для заданного входного файла XSD правило сборки создает библиотеки Java и C++. Вы можете связать библиотеки с модулями, в которых файлы XML, соответствующие XSD, считываются и используются. Вы можете использовать правило сборки для своих собственных XML-файлов, используемых в system
разделе и разделе vendor
.
Создание API схемы файлов конфигурации
В этом разделе описывается, как создать API схемы файла конфигурации.
Настройка правила сборки xsd_config в Android.bp
Правило сборки xsd_config
генерирует код синтаксического анализатора с помощью инструмента xsdc
. Свойство xsd_config
правила сборки package_name
определяет имя пакета сгенерированного кода Java.
Пример правила сборки xsd_config
в Android.bp
:
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
Система сборки создает список API, используя сгенерированный код Java, и сверяет 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, добавьте :
в качестве префикса к имени модуля xsd_config
в свойстве Java srcs
. Пакет сгенерированного кода 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
, как описано в разделе Настройка правила сборки xsd_config в Android.bp . В этом разделе объясняется интерфейс командной строки 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