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, na sigla em inglês). Ela 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 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++. É possível vincular as bibliotecas aos módulos em que os arquivos XML 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 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 no 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 de API exigem arquivos de listas de APIs no código-fonte.
Os arquivos de listas de APIs incluem:
current.txteremoved.txtverificam se as APIs foram alteradas comparando com arquivos de API gerados no tempo de build.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 APIs:
- Crie arquivos de listas vazios.
- Execute o comando
make update-api.
Usar o código do 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 é 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 XmlParser#read ou
read{class-name} método para retornar a classe do root
elemento. 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 principal. O nome do arquivo principal é 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 em camel case (por exemplo, ElementName) e usados como o nome da variável, do método e da classe correspondentes. A classe do elemento raiz analisado pode ser obtida usando a
read{class-name} função. 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 get{variable-name}
função.
Gerar código do analisador
Na maioria dos casos, não é necessário executar xsdc diretamente. Use a regra de build xsd_config em vez disso, conforme descrito em
Configurar a regra de build xsd_config no Android.bp. Esta seção explica a interface de linha de comando xsdc, apenas para fins de conclusão. 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