A plataforma Android contém muitos arquivos XML para armazenar dados de
configuração (por exemplo, configuração de áudio). Muitos dos arquivos XML estão na partição vendor, mas são lidos na partição system. Nesse caso, o esquema do arquivo XML atua como interface entre as duas partições e, portanto, o esquema precisa ser especificado explicitamente e evoluir de uma maneira compatível com versões anteriores.
Antes do Android 10, a plataforma não fornecia mecanismos para exigir a especificação e o uso do esquema XML nem para impedir mudanças incompatíveis no esquema. O Android 10 fornece
esse mecanismo, chamado API Config File Schema. Esse mecanismo consiste em uma ferramenta
chamada xsdc e uma regra de build chamada xsd_config.
A ferramenta xsdc é um compilador de documentos de esquema XML (XSD). Ele analisa um arquivo XSD
que descreve o esquema de um arquivo XML e gera código Java e C++. O
código gerado analisa arquivos XML que estão em conformidade com o esquema XSD em uma árvore de
objetos, cada um modelando uma tag XML. Os atributos XML são modelados como campos dos objetos.
A regra de build xsd_config integra a ferramenta xsdc ao sistema de build.
Para um determinado arquivo de entrada XSD, a regra de build gera bibliotecas Java e C++. Você pode vincular as bibliotecas aos módulos em que os arquivos XML que estão em conformidade com o XSD são lidos e usados. Você pode usar a regra de build para seus próprios arquivos XML usados
nas partições system e vendor.
Criar a API Config File Schema
Esta seção descreve como criar a API Config File Schema.
Configurar a regra de build xsd_config em Android.bp
A regra de build xsd_config gera o código do analisador com a ferramenta xsdc. A propriedade package_name da regra de build xsd_config determina o nome do pacote do código Java gerado.
Exemplo de regra de build xsd_config em Android.bp:
xsd_config {
name: "hal_manifest",
srcs: ["hal_manifest.xsd"],
package_name: "hal.manifest",
}
Exemplo de estrutura de diretório:
├── Android.bp
├── api
│ ├── current.txt
│ ├── last_current.txt
│ ├── last_removed.txt
│ └── removed.txt
└── hal_manifest.xsd
O sistema de build gera uma lista de APIs usando o código Java gerado e verifica
a API em relação a ela. Essa verificação de API é adicionada ao DroidCore e executada em m -j.
Criar arquivos de listas de APIs
As verificações da API exigem arquivos de listas de APIs no código-fonte.
As listas de arquivos da API incluem:
current.txteremoved.txtverificam se as APIs foram alteradas comparando com os arquivos de API gerados durante a compilação.last_current.txtelast_removed.txtverificam se as APIs são compatíveis com versões anteriores comparando com arquivos de API.
Para criar os arquivos de listas de API:
- Crie arquivos de listas vazias.
- Execute o comando
make update-api.
Usar código de analisador gerado
Para usar o código Java gerado, adicione : como um prefixo ao nome do módulo xsd_config na propriedade srcs do Java. O pacote do código Java gerado é o mesmo da propriedade package_name.
java_library {
name: "vintf_test_java",
srcs: [
"srcs/**/*.java"
":hal_manifest"
],
}
Para usar o código C++ gerado, adicione o nome do módulo xsd_config às propriedades
generated_sources e generated_headers. E adicione libxml2 a
static_libs ou shared_libs, já que libxml2 é obrigatório no código
do analisador gerado. O namespace do código C++ gerado é o mesmo da propriedade package_name. Por exemplo, se
o nome do módulo xsd_config for hal.manifest, o namespace será
hal::manifest.
cc_library{
name: "vintf_test_cpp",
srcs: ["main.cpp"],
generated_sources: ["hal_manifest"],
generated_headers: ["hal_manifest"],
shared_libs: ["libxml2"],
}
Usar o analisador
Para usar o código do analisador Java, use o método XmlParser#read ou
read{class-name} para retornar a classe do elemento
raiz. A análise acontece nesse momento.
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();
…
}
}
…
}
Para usar o código do analisador C++, primeiro inclua o arquivo de cabeçalho. O nome do arquivo de cabeçalho é o nome do pacote com pontos (.) convertidos em sublinhados (_).
Em seguida, use o método read ou read{class-name} para retornar
a classe do elemento raiz. A análise acontece nesse momento. O valor de retorno é um 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();
…
}
…
}
Todas as APIs fornecidas para usar o analisador estão em api/current.txt. Para uniformidade, todos os nomes de elementos e atributos são convertidos para camel case (por exemplo, ElementName) e usados como a variável, o método e o nome de classe correspondentes. A classe do elemento raiz analisado pode ser obtida usando a função
read{class-name}. Se houver apenas um elemento raiz, o nome da função será read. O valor de um subelemento ou atributo analisado pode ser obtido usando a função get{variable-name}.
Gerar código do analisador
Na maioria dos casos, não é necessário executar xsdc diretamente. Use a regra de build xsd_config, conforme descrito em Configurar a regra de build xsd_config em Android.bp. Esta seção explica a interface de linha de comando xsdc apenas para fins de integridade. Isso
pode ser útil para depuração.
Você precisa fornecer à ferramenta xsdc o caminho para o arquivo XSD e um pacote. O
pacote é um nome de pacote no código Java e um namespace no código C++. As opções para determinar se o código gerado é Java ou C são -j ou -c, respectivamente. A opção -o é o caminho do diretório de saída.
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
Exemplo de comando:
$ xsdc audio_policy_configuration.xsd -p audio.policy -j