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

Платформа 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 .

Создание 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