Внедрить API схемы файла конфигурации

Платформа 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 . Свойство 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, сравнивая их с файлами API, сгенерированными во время сборки.
  • last_current.txt и last_removed.txt проверяют обратную совместимость API путем сравнения с файлами API.

Чтобы создать файлы списков API:

  1. Создайте пустые файлы списков.
  2. Выполните команду 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_confighal.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