Платформа Android содержит множество XML-файлов для хранения данных конфигурации (например, конфигурации аудио). Многие XML-файлы находятся в разделе vendor, но считываются из раздела system. В этом случае схема XML-файла служит интерфейсом между двумя разделами, поэтому ее необходимо явно указать и развивать с обратной совместимостью.
До Android 10 платформа не предоставляла механизмов, требующих указания и использования XML-схемы или предотвращающих несовместимые изменения в схеме. В Android 10 реализован механизм, который называется Config File Schema API. Этот механизм состоит из инструмента xsdc и правила сборки xsd_config.
Инструмент xsdc – это компилятор XML-схем (XSD). Он анализирует XSD-файл, описывающий схему XML-файла, и генерирует код на Java и C++. Сгенерированный код анализирует XML-файлы, соответствующие схеме XSD, и преобразует их в дерево объектов, каждый из которых моделирует XML-тег. Атрибуты XML представлены в виде полей объектов.
Правило сборки xsd_config интегрирует инструмент xsdc в систему сборки.
Для заданного входного файла XSD правило сборки создает библиотеки Java и C++. Вы можете связать библиотеки с модулями, в которых считываются и используются XML-файлы, соответствующие XSD. Вы можете использовать правило сборки для собственных XML-файлов, которые используются в разделах system и vendor.
Build Config File Schema API
В этом разделе рассказывается, как создать API схемы файла конфигурации.
Настройте правило сборки xsd_config в Android.bp
Правило сборки xsd_config создает код синтаксического анализатора с помощью инструмента xsdc. Свойство package_name правила сборки xsd_config определяет название пакета сгенерированного кода 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, сравнивая их с файлами, созданными во время сборки.last_current.txtиlast_removed.txtпроверяют обратную совместимость API, сравнивая их с файлами API.
Чтобы создать файлы списков API, выполните следующие действия:
- Создавать пустые файлы списков.
- Выполните команду
make update-api.
Как использовать сгенерированный код парсера
Чтобы использовать сгенерированный код Java, добавьте : в качестве префикса к названию модуля xsd_config в свойстве srcs Java. Пакет сгенерированного кода 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. Для единообразия все названия элементов и атрибутов преобразуются в формат camel case (например, ElementName) и используются в качестве соответствующих названий переменных, методов и классов. Класс обработанного корневого элемента можно получить с помощью функции read{class-name}. Если корневой элемент только один, то имя функции – read. Значение проанализированного вложенного элемента или атрибута можно получить с помощью функции get{variable-name}.
Сгенерировать код синтаксического анализатора
В большинстве случаев запускать xsdc напрямую не нужно. Вместо этого используйте правило сборки xsd_config, как описано в разделе Настройка правила сборки xsd_config в Android.bp. В этом разделе описан интерфейс командной строки xsdc. Это может быть полезно для отладки.
В инструменте xsdc необходимо указать путь к XSD-файлу и пакету. package – это название пакета в коде 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