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 serve como a interface entre as duas partições e, portanto, o esquema precisa ser especificado explicitamente e evoluir de maneira compatível com versões anteriores.
Antes do Android 10, a plataforma não oferecia
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 documento de esquema XML (XSD, na sigla em inglês). 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 deles modela 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++. É possível
vincular as bibliotecas aos módulos em que os arquivos XML que estão em conformidade com o
XSD são lidos e usados. É possível usar a regra de build para seus próprios arquivos XML usados
nas partições system
e vendor
.
API Build Config File Schema
Esta seção descreve como criar a API Config File Schema.
Configurar a regra de build xsd_config no 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órios:
├── 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 ele. Essa verificação de API é adicionada ao DroidCore e executada em m -j
.
Criar arquivos de listas de API
As verificações de API exigem arquivos de listas da API no código-fonte.
Os arquivos listados pela API incluem:
current.txt
eremoved.txt
verificam se as APIs foram alteradas comparando com os arquivos de API gerados no momento do build.last_current.txt
elast_removed.txt
verificam se as APIs são compatíveis com versões anteriores ao comparar com os arquivos de API.
Para criar os arquivos de listas de API:
- Crie arquivos de listas vazias.
- Execute o comando
make update-api
.
Usar o código do analisador gerado
Para usar o código Java gerado, adicione :
como prefixo ao nome do módulo
xsd_config
na propriedade srcs
do Java. O pacote do código Java gerado é igual à
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
é necessá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 letras concatenadas (por
exemplo, ElementName
) e usados como a variável, o método e o nome
da 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, você não precisa executar xsdc
diretamente. Em vez disso, use a regra de build
xsd_config
, conforme descrito em
Como 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 informar à 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