O Android 11 apresenta a capacidade de usar a AIDL para HALs no Android. Assim, possível implementar partes do Android sem o HIDL. HALs de transição para usar a AIDL exclusivamente quando possível. Quando as HALs upstream usarem o HIDL, ele precisará ser usado.
HALs que usam AIDL para comunicação entre componentes do framework, como os da
system.img
e componentes de hardware, como os de vendor.img
, precisam usar
AIDL estável. No entanto, para se comunicar dentro de uma partição, por exemplo, de um
HAL para outro. Não há restrições quanto ao uso do mecanismo de IPC.
Motivação
A AIDL existe há mais tempo que a HIDL e é usada em muitos outros lugares, entre componentes do framework do Android ou dentro de apps. Agora que a AIDL tem estabilidade é possível implementar uma pilha inteira com um único ambiente de execução de IPC. A AIDL também tem um sistema de controle de versões melhor do que o HIDL.
- Usar uma única linguagem IPC significa ter apenas uma coisa para aprender, depurar, otimizar e proteger.
- A AIDL oferece suporte ao controle de versões no local para os proprietários de uma interface:
- Os proprietários podem adicionar métodos ao final das interfaces ou campos para parcelables. Isso significa que é mais fácil criar o código de versão ao longo dos anos e também depois do ano é menor (os tipos podem ser alterados no local e não há necessidade de bibliotecas extras para cada versão da interface).
- As interfaces de extensão podem ser anexadas no ambiente de execução em vez de no tipo portanto, não é necessário realocar as extensões downstream em mais recentes de interfaces.
- Uma interface AIDL já existente pode ser usada diretamente quando o proprietário optar por para que ele possa se estabilizar. Antes, uma cópia inteira da interface tinha de ser criados no HIDL.
Criar no ambiente de execução da AIDL
A AIDL tem três back-ends diferentes: Java, NDK e CPP. Para usar a AIDL estável, você precisa
sempre use a cópia do sistema do libbinder em system/lib*/libbinder.so
e fale
em /dev/binder
. Para o código na imagem do fornecedor, isso significa que libbinder
(do VNDK) não pode ser usado: essa biblioteca tem uma API C++ instável e
por partes internas instáveis. Em vez disso, o código do fornecedor nativo precisa usar o back-end do NDK
AIDL, link para libbinder_ndk
(que é apoiado pelo sistema libbinder.so
),
e vincular às bibliotecas do NDK criadas por entradas aidl_interface
. Para
os nomes exatos dos módulos, consulte
regras de nomenclatura de módulo.
Criar uma interface HAL de AIDL
Para que uma interface AIDL seja usada entre o sistema e o fornecedor, a interface precisa duas mudanças:
- Cada definição de tipo precisa ter a anotação
@VintfStability
. - A declaração
aidl_interface
precisa incluirstability: "vintf",
.
Somente o proprietário de uma interface pode fazer essas alterações.
Quando você faz essas alterações, a interface deve estar no
manifesto VINTF para funcionar. Teste isto (e relacionado
requisitos, como verificar se as interfaces lançadas estão congeladas) usando o
Teste VTS vts_treble_vintf_vendor_test
. Você pode usar um @VintfStability
interface de entrada sem esses requisitos, chamando
AIBinder_forceDowngradeToLocalStability
no back-end do NDK
android::Stability::forceDowngradeToLocalStability
no back-end em C++.
ou android.os.Binder#forceDowngradeToSystemStability
no back-end Java
em um objeto de vinculação antes de ser enviada para outro processo. Fazer downgrade de um serviço
para a estabilidade do fornecedor não tem suporte em Java porque todos os aplicativos são executados em um sistema
contexto.
Além disso, para garantir a máxima portabilidade de código e evitar possíveis problemas, como como bibliotecas extras desnecessárias, desative o back-end do CPP.
Observe que o uso de backends
no exemplo de código abaixo está correto, porque há
são três back-ends (Java, NDK e CPP). O código abaixo informa como selecionar
back-end de CPP especificamente para desativá-lo.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Encontrar interfaces HAL da AIDL
As interfaces AIDL estáveis do AOSP para HALs estão nos mesmos diretórios base que
Interfaces HIDL, em pastas aidl
.
- hardware/interfaces
- estruturas/hardware/interfaces
- sistema/hardware/interfaces
Coloque interfaces de extensão em outras hardware/interfaces
.
subdiretórios em vendor
ou hardware
.
Interfaces de extensão
O Android tem um conjunto de interfaces oficiais do AOSP em cada versão. Quando o Android parceiros quiserem adicionar funcionalidade a essas interfaces, elas não poderão mudar diretamente, porque isso significaria que o tempo de execução do Android foi incompatível com o ambiente de execução do Android no AOSP. Para dispositivos GMS, evitar a mudança essas interfaces também é o que garante que a imagem GSI continue funcionando.
As extensões podem ser registradas de duas maneiras diferentes:
- durante a execução, consulte as extensões anexadas.
- independente, registrada globalmente e no VINTF.
No entanto, uma extensão é registrada, quando específica do fornecedor (ou seja, não faz parte AOSP upstream) usam a interface, não há possibilidade de mesclagem conflitos. No entanto, quando as modificações downstream em componentes upstream do AOSP forem podem ocorrer conflitos de mesclagem. Recomendamos as seguintes estratégias:
- as adições de interface podem ser enviadas para o AOSP na próxima versão
- a interface adicional que permitem mais flexibilidade, sem conflitos de mesclagem, poderão ser incluídos na próxima versão
Parcelables da extensão: ParcelableHolder
ParcelableHolder
é uma Parcelable
que pode conter outra Parcelable
.
O principal caso de uso de ParcelableHolder
é tornar um Parcelable
extensível.
Por exemplo, uma imagem que os implementadores de dispositivos esperam poder estender uma
O AOSP definiu Parcelable
, AospDefinedParcelable
, para incluir o valor agregado.
atributos de machine learning.
Antes, sem a ParcelableHolder
, os implementadores de dispositivos não podiam modificar
uma interface AIDL estável definida pelo AOSP, porque seria um erro adicionar mais
:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
Como vimos no código anterior, essa prática não funciona porque os campos adicionado pelo implementador do dispositivo pode ter um conflito quando o Parcelable é revistos nas próximas versões do Android.
Usando ParcelableHolder
, o proprietário de um parcelable pode definir uma extensão.
ponto em uma Parcelable
.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Em seguida, os implementadores de dispositivos podem definir o próprio Parcelable
para os
.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Por fim, o novo Parcelable
pode ser anexado ao Parcelable
original com
no campo ParcelableHolder
.
// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;
ap.extension.setParcelable(op);
...
OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);
// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();
ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);
...
std::shared_ptr<OemDefinedParcelable> op_ptr;
ap.extension.getParcelable(&op_ptr);
// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);
...
std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);
// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });
ap.extension.set_parcelable(Rc::clone(&op));
...
let op = ap.extension.get_parcelable::<OemDefinedParcelable>();
Nomes de instâncias do servidor HAL da AIDL
Por convenção, os serviços de HAL da AIDL têm um nome de instância com o formato
$package.$type/$instance
: Por exemplo, uma instância da HAL de vibração
registrado como android.hardware.vibrator.IVibrator/default
.
Criar um servidor de HAL da AIDL
@VintfStability
servidores AIDL precisam ser declarados no manifesto VINTF, por
exemplo, da seguinte forma:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
Caso contrário, eles devem registrar um serviço AIDL normalmente. Ao executar o VTS é esperado que todas as HALs da AIDL declaradas estejam disponíveis.
Criar um cliente AIDL
Os clientes da AIDL precisam se declarar na matriz de compatibilidade, por exemplo, assim:
<hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
<name>IVibrator</name>
<instance>default</instance>
</interface>
</hal>
Converter uma HAL de HIDL para AIDL
Use a ferramenta hidl2aidl
para converter uma interface HIDL em AIDL.
Recursos do hidl2aidl
:
- Criar arquivos
.aidl
com base nos arquivos.hal
do pacote especificado - Crie regras de build para o pacote AIDL recém-criado com todos os back-ends ativada
- Criar métodos de tradução nos back-ends de Java, CPP e NDK para traduzir dos tipos HIDL para os de AIDL
- Criar regras de build para bibliotecas de tradução com dependências necessárias
- Crie declarações estáticas para garantir que os enumeradores HIDL e AIDL tenham o mesmos valores nos back-ends de CPP e NDK
Siga estas etapas para converter um pacote de arquivos .hal em arquivos .aidl:
Crie a ferramenta localizada em
system/tools/hidl/hidl2aidl
.Criar essa ferramenta a partir da fonte mais recente oferece a ferramenta do usuário. Você pode usar a versão mais recente para converter interfaces em versões ramificações de versões anteriores.
m hidl2aidl
Execute a ferramenta com um diretório de saída seguido pelo pacote a ser convertido.
Se quiser, use o argumento
-l
para adicionar o conteúdo de um novo arquivo de licença. na parte superior de todos os arquivos gerados. Use a licença e a data corretas.hidl2aidl -o <output directory> -l <file with license> <package>
Exemplo:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Leia os arquivos gerados e corrija os problemas com a conversão.
conversion.log
contém problemas não processados que precisam ser corrigidos primeiro.- Os arquivos
.aidl
gerados podem ter avisos e sugestões que podem que precisam de ação. Esses comentários começam com//
. - Aproveite para fazer uma limpeza e fazer melhorias no pacote.
- Confira o
@JavaDerive
para recursos que podem ser necessários, comotoString
ouequals
.
Crie apenas os destinos necessários.
- Desative os back-ends que não serão usados. Preferir o back-end do NDK em vez do CPP back-end, consulte Como escolher o ambiente de execução.
- Remova bibliotecas de tradução ou qualquer um dos códigos gerados que não serão usados.
Consulte Principais diferenças de AIDL/HIDL.
- Usar o
Status
integrado da AIDL e as exceções normalmente melhoram a interface e elimina a necessidade de outro tipo de status específico da interface. - Os argumentos da interface AIDL em métodos não são
@nullable
por padrão, como em HIDL.
- Usar o
SEPolicy para HALs da AIDL
Um tipo de serviço AIDL que é visível para o código do fornecedor deve ter a
hal_service_type
. Caso contrário, a configuração de sepolicy será a mesma
que qualquer outro serviço AIDL, embora existam atributos especiais para HALs. Aqui
é um exemplo de definição de um contexto de serviço HAL:
type hal_foo_service, service_manager_type, hal_service_type;
Para a maioria dos serviços definidos pela plataforma, um contexto de serviço com a
o tipo já tiver sido adicionado (por exemplo, android.hardware.foo.IFoo/default
já estar marcado como hal_foo_service
). No entanto, se um cliente do framework oferecer suporte
vários nomes de instância, outros nomes de instância devem ser adicionados
arquivos service_contexts
específicos do dispositivo.
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
Os atributos da HAL precisam ser adicionados ao criar um novo tipo de HAL. Uma HAL específica
atributo pode ser associado a vários tipos de serviço (cada um deles pode
várias instâncias, como acabamos de discutir). Para uma HAL, foo
, temos
hal_attribute(foo)
. Essa macro define os atributos hal_foo_client
e
hal_foo_server
. Para um determinado domínio, os hal_client_domain
e
hal_server_domain
macros associam um domínio a um determinado atributo HAL. Para
exemplo, o servidor do sistema sendo cliente dessa HAL corresponde à política
hal_client_domain(system_server, hal_foo)
: Um servidor HAL inclui
hal_server_domain(my_hal_domain, hal_foo)
: Normalmente, para uma determinada HAL
também criamos um domínio como hal_foo_default
para referência ou
exemplos de HALs. No entanto, alguns dispositivos usam esses domínios nos próprios servidores.
A distinção entre domínios para múltiplos servidores só importa se tivermos
vários servidores que exibem a mesma interface e precisam de uma permissão diferente
definidos nas implementações. Em todas essas macros, hal_foo
não é realmente
um objeto sepolicy. Em vez disso, esse token é usado por essas macros para se referir a
grupo de atributos associados a um par de servidor cliente.
No entanto, até agora, não associamos hal_foo_service
e hal_foo
(o par de atributos de hal_attribute(foo)
). Um atributo HAL está associado
com serviços de HAL da AIDL usando a macro hal_attribute_service
(as HALs HIDL usam
a macro hal_attribute_hwservice
). Por exemplo:
hal_attribute_service(hal_foo, hal_foo_service)
: Isso significa que
Os processos hal_foo_client
podem acessar a HAL, e hal_foo_server
e processos podem registrar a HAL. A aplicação dessas regras de registro é
feita pelo gerenciador de contexto (servicemanager
). Observe que os nomes de serviço podem
nem sempre correspondem aos atributos da HAL. Por exemplo, podemos ver
hal_attribute_service(hal_foo, hal_foo2_service)
: Geralmente, uma vez que
significa que os serviços são sempre usados juntos, podemos remover
o hal_foo2_service
e o uso de hal_foo_service
para todos os nossos serviços
contextos de negócios diferentes. A maioria das HALs que definem vários hal_attribute_service
ocorrem
o nome do atributo original da HAL não é geral o suficiente e não pode ser alterado.
Juntando tudo isso, um exemplo de HAL fica assim:
public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
Interfaces de extensão anexadas
Uma extensão pode ser anexada a qualquer interface de vinculação, seja ela de nível superior registrada diretamente no gerenciador de serviços ou é uma subinterface. Ao receber uma extensão, você precisa confirmar se o tipo dela é como o esperado. As extensões só podem ser definidas a partir do processo de exibição de um binder.
As extensões anexadas devem ser usadas sempre que uma extensão modificar a funcionalidade de uma HAL existente. Quando é necessária uma funcionalidade totalmente nova, esse mecanismo não precisa ser usado, e uma interface de extensão pode ser registrados diretamente com o gerente de serviço. Interfaces de extensão anexadas fazem mais sentido quando são anexadas a subinterfaces, porque as hierarquias podem ser profundas ou com várias instâncias. Como usar uma extensão global para espelhar a hierarquia da interface de vinculação de outro serviço exigiria um escopo maior a contabilidade para fornecer funcionalidade equivalente às extensões anexadas diretamente.
Para definir uma extensão no binder, use as seguintes APIs:
- No back-end do NDK:
AIBinder_setExtension
- No back-end do Java:
android.os.Binder.setExtension
- No back-end do CPP:
android::Binder::setExtension
- No back-end do Rust:
binder::Binder::set_extension
Para conseguir uma extensão em um binder, use as seguintes APIs:
- No back-end do NDK:
AIBinder_getExtension
- No back-end do Java:
android.os.IBinder.getExtension
- No back-end do CPP:
android::IBinder::getExtension
- No back-end do Rust:
binder::Binder::get_extension
Você pode encontrar mais informações sobre essas APIs na documentação do
getExtension
no back-end correspondente. Um exemplo de como usar
extensões podem ser encontradas
hardware/interfaces/tests/extension/vibrator.
Principais diferenças de AIDL e HIDL
Ao usar HALs AIDL ou interfaces HAL da AIDL, esteja ciente das diferenças em comparação com a criação de HALs HIDL.
- A sintaxe da linguagem AIDL é mais próxima do Java. A sintaxe do HIDL é semelhante ao C++.
- Todas as interfaces AIDL têm status de erro integrados. Em vez de criar configurações
de status diversos, criar ints de status constantes em arquivos de interface e usar
EX_SERVICE_SPECIFIC
nos back-ends de CPP/NDK eServiceSpecificException
no back-end do Java. Ver Erro Manuseio. - A AIDL não inicia pools de linhas de execução automaticamente quando os objetos de vinculação são enviados. Eles precisam ser iniciados manualmente. Consulte conversa de projetos).
- A AIDL não é cancelada em erros de transporte não verificados (HIDL
Return
é cancelado em erros não marcados). - A AIDL só pode declarar um tipo por arquivo.
- Os argumentos da AIDL podem ser especificados como entrada/saída/saída, além da saída (não há "callbacks síncronos").
- A AIDL usa um fd como tipo primitivo em vez de handle.
- O HIDL usa versões principais para mudanças incompatíveis e versões secundárias para
alterações compatíveis. Na AIDL, são feitas alterações compatíveis com versões anteriores.
a AIDL não tem um conceito explícito de versões principais. em vez disso, é
incorporadas aos nomes dos pacotes. Por exemplo, a AIDL pode usar o nome do pacote
bluetooth2
: - A AIDL não herda a prioridade em tempo real por padrão. O
setInheritRt
precisa ser usada por vinculação para ativar a herança de prioridade em tempo real.