O Android 10 adiciona suporte para Android Interface Definition Language (AIDL) estável, uma nova maneira de acompanhar a interface de programa de aplicativo (API)/interface binária de aplicativo (ABI) fornecida pelas interfaces AIDL. O AIDL estável tem as seguintes diferenças importantes do AIDL:
- As interfaces são definidas no sistema de construção com
aidl_interfaces
. - As interfaces podem conter apenas dados estruturados. Parceláveis que representam os tipos desejados são criados automaticamente com base em sua definição AIDL e são automaticamente empacotados e desempacotados.
- As interfaces podem ser declaradas como estáveis (compatíveis com versões anteriores). Quando isso acontece, sua API é rastreada e versionada em um arquivo próximo à interface AIDL.
AIDL estruturado versus estável
AIDL estruturado refere-se a tipos definidos puramente em AIDL. Por exemplo, uma declaração parcelavel (uma parcelable personalizada) não é AIDL estruturada. Os parcelables com seus campos definidos em AIDL são chamados de parcelables estruturados .
O AIDL estável requer AIDL estruturado para que o sistema de compilação e o compilador possam entender se as alterações feitas nos parcelables são compatíveis com versões anteriores. No entanto, nem todas as interfaces estruturadas são estáveis. Para ser estável, uma interface deve usar apenas tipos estruturados e também deve usar os seguintes recursos de controle de versão. Por outro lado, uma interface não é estável se o sistema de compilação principal for usado para construí-la ou se unstable:true
estiver definido.
Definindo uma interface AIDL
Uma definição de aidl_interface
é assim:
aidl_interface {
name: "my-aidl",
srcs: ["srcs/aidl/**/*.aidl"],
local_include_dir: "srcs/aidl",
imports: ["other-aidl"],
versions_with_info: [
{
version: "1",
imports: ["other-aidl-V1"],
},
{
version: "2",
imports: ["other-aidl-V3"],
}
],
stability: "vintf",
backend: {
java: {
enabled: true,
platform_apis: true,
},
cpp: {
enabled: true,
},
ndk: {
enabled: true,
},
rust: {
enabled: true,
},
},
}
-
name
: o nome do módulo de interface AIDL que identifica exclusivamente uma interface AIDL. -
srcs
: A lista de arquivos de origem AIDL que compõem a interface. O caminho para um tipo AIDLFoo
definido em um pacotecom.acme
deve estar em<base_path>/com/acme/Foo.aidl
, onde<base_path>
pode ser qualquer diretório relacionado ao diretório ondeAndroid.bp
está. No exemplo acima,<base_path>
ésrcs/aidl
. -
local_include_dir
: o caminho de onde o nome do pacote começa. Corresponde ao<base_path>
explicado acima. -
imports
: uma lista de módulosaidl_interface
que usa. Se uma de suas interfaces AIDL usa uma interface ou parcelable de outraaidl_interface
, coloque seu nome aqui. Pode ser o próprio nome, para se referir à versão mais recente, ou o nome com o sufixo de versão (como-V1
) para se referir a uma versão específica. A especificação de uma versão é compatível desde o Android 12 -
versions
: as versões anteriores da interface que estão congeladas emapi_dir
. A partir do Android 11, asversions
são congeladas emaidl_api/ name
. Se não houver versões congeladas de uma interface, isso não deverá ser especificado e não haverá verificações de compatibilidade. Este campo foi substituído porversions_with_info
para versões 13 e superiores. -
versions_with_info
: Lista de tuplas, cada uma contendo o nome de uma versão congelada e uma lista com importações de versões de outros módulos aidl_interface que esta versão do aidl_interface importou. A definição da versão V de uma interface AIDL IFACE está localizada emaidl_api/ IFACE / V
. Este campo foi introduzido no Android 13 e não deve ser modificado diretamente no Android.bp. O campo é adicionado ou atualizado invocando*-update-api
ou*-freeze-api
. Além disso, os camposversions
são migrados automaticamente paraversions_with_info
quando um usuário invoca*-update-api
ou*-freeze-api
. -
stability
: o sinalizador opcional para a promessa de estabilidade desta interface. Atualmente suporta apenas"vintf"
. Se não estiver definido, corresponde a uma interface com estabilidade dentro deste contexto de compilação (portanto, uma interface carregada aqui só pode ser usada com itens compilados juntos, por exemplo, em system.img). Se estiver definido como"vintf"
, isso corresponde a uma promessa de estabilidade: a interface deve ser mantida estável enquanto for usada. -
gen_trace
: O sinalizador opcional para ativar ou desativar o rastreamento. A partir do Android 14, o padrão étrue
para back-endscpp
ejava
. -
host_supported
: o sinalizador opcional que, quando definido comotrue
, disponibiliza as bibliotecas geradas para o ambiente host. -
unstable
: o sinalizador opcional usado para marcar que esta interface não precisa ser estável. Quando definido comotrue
, o sistema de compilação não cria o dump da API para a interface nem exige que ela seja atualizada. -
frozen
: O sinalizador opcional que quando definido comotrue
significa que a interface não sofreu alterações desde a versão anterior da interface. Isso permite mais verificações em tempo de construção. Quando definido comofalse
, isso significa que a interface está em desenvolvimento e tem novas alterações, portanto, a execução defoo-freeze-api
gerará uma nova versão e alterará automaticamente o valor paratrue
. Introduzido no Android 14. -
backend.<type>.enabled
: esses sinalizadores alternam cada um dos back-ends para os quais o compilador AIDL gera código. Atualmente, quatro backends são suportados: Java, C++, NDK e Rust. Os back-ends Java, C++ e NDK são habilitados por padrão. Se algum desses três back-ends não for necessário, ele precisará ser desabilitado explicitamente. Rust está desabilitado por padrão. -
backend.<type>.apex_available
: a lista de nomes APEX para os quais a biblioteca stub gerada está disponível. -
backend.[cpp|java].gen_log
: O sinalizador opcional que controla se deve gerar código adicional para coletar informações sobre a transação. -
backend.[cpp|java].vndk.enabled
: o sinalizador opcional para tornar esta interface parte do VNDK. O padrão éfalse
. -
backend.[cpp|ndk].additional_shared_libraries
: introduzido no Android 14, este sinalizador adiciona dependências às bibliotecas nativas. Este sinalizador é útil comndk_header
ecpp_header
. -
backend.java.sdk_version
: o sinalizador opcional para especificar a versão do SDK na qual a biblioteca stub Java é construída. O padrão é"system_current"
. Isso não deve ser definido quandobackend.java.platform_apis
for verdadeiro. -
backend.java.platform_apis
: o sinalizador opcional que deve ser definido comotrue
quando as bibliotecas geradas precisam ser construídas na API da plataforma em vez do SDK.
Para cada combinação de versões e backends habilitados, uma biblioteca stub é criada. Para saber como consultar a versão específica da biblioteca stub para um back-end específico, consulte Regras de nomenclatura de módulos .
Gravando arquivos AIDL
As interfaces em AIDL estável são semelhantes às interfaces tradicionais, com a exceção de que não podem usar parcelables não estruturados (porque não são estáveis! consulte AIDL estruturado versus estável ). A principal diferença no AIDL estável é como os parcelables são definidos. Anteriormente, os parcelables eram declarados a termo ; em AIDL estável (e, portanto, estruturado), campos e variáveis parceláveis são definidos explicitamente.
// in a file like 'some/package/Thing.aidl'
package some.package;
parcelable SubThing {
String a = "foo";
int b;
}
Atualmente há suporte para um padrão (mas não obrigatório) para boolean
, char
, float
, double
, byte
, int
, long
e String
. No Android 12, os padrões para enumerações definidas pelo usuário também são compatíveis. Quando um padrão não é especificado, um valor semelhante a 0 ou vazio é usado. Enumerações sem valor padrão são inicializadas com 0 mesmo se não houver enumerador zero.
Usando bibliotecas stub
Depois de adicionar bibliotecas stub como uma dependência ao seu módulo, você pode incluí-las em seus arquivos. Aqui estão alguns exemplos de bibliotecas stub no sistema de compilação ( Android.mk
também pode ser usado para definições de módulos legados):
cc_... {
name: ...,
shared_libs: ["my-module-name-cpp"],
...
}
# or
java_... {
name: ...,
// can also be shared_libs if desire is to load a library and share
// it among multiple users or if you only need access to constants
static_libs: ["my-module-name-java"],
...
}
# or
rust_... {
name: ...,
rustlibs: ["my-module-name-rust"],
...
}
Exemplo em C++:
#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
// use just like traditional AIDL
Exemplo em Java:
import some.package.IFoo;
import some.package.Thing;
...
// use just like traditional AIDL
Exemplo em ferrugem:
use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
// use just like traditional AIDL
Interfaces de versionamento
Declarar um módulo com o nome foo também cria um destino no sistema de compilação que você pode usar para gerenciar a API do módulo. Quando compilado, foo-freeze-api adiciona uma nova definição de API em api_dir
ou aidl_api/ name
, dependendo da versão do Android, e adiciona um arquivo .hash
, ambos representando a versão recém-congelada da interface. foo-freeze-api também atualiza a propriedade versions_with_info
para refletir a versão adicional e imports
da versão. Basicamente, imports
em versions_with_info
são copiadas do campo imports
. Mas a versão estável mais recente é especificada nas imports
em versions_with_info
para a importação que não possui uma versão explícita. Depois que a propriedade versions_with_info
for especificada, o sistema de compilação executa verificações de compatibilidade entre versões congeladas e também entre o Top of Tree (ToT) e a versão congelada mais recente.
Além disso, você precisa gerenciar a definição da API da versão do ToT. Sempre que uma API for atualizada, execute foo-update-api para atualizar aidl_api/ name /current
que contém a definição da API da versão ToT.
Para manter a estabilidade de uma interface, os proprietários podem adicionar novos:
- Métodos até o final de uma interface (ou métodos com novas séries explicitamente definidas)
- Elementos no final de um parcelable (requer que um padrão seja adicionado para cada elemento)
- Valores constantes
- No Android 11, recenseadores
- No Android 12, campos até o final de uma união
Nenhuma outra ação é permitida e ninguém mais pode modificar uma interface (caso contrário, há o risco de colisão com as alterações feitas pelo proprietário).
Para testar se todas as interfaces estão congeladas para lançamento, você pode criar com o seguinte conjunto de variáveis ambientais:
-
AIDL_FROZEN_REL=true m ...
- build requer que todas as interfaces AIDL estáveis sejam congeladas e que não tenhamowner:
campo especificado. -
AIDL_FROZEN_OWNERS="aosp test"
- a construção requer que todas as interfaces AIDL estáveis sejam congeladas com oowner:
campo especificado como "aosp" ou "test".
Estabilidade das importações
A atualização das versões de importações para versões congeladas de uma interface é compatível com versões anteriores na camada AIDL estável. No entanto, atualizá-los requer a atualização de todos os servidores e clientes que usam a versão antiga da interface, e alguns aplicativos podem ficar confusos ao misturar diferentes versões de tipos. Geralmente, para pacotes somente de tipos ou comuns, isso é seguro porque o código já precisa estar escrito para lidar com tipos desconhecidos de transações IPC.
No código da plataforma Android android.hardware.graphics.common
é o maior exemplo desse tipo de atualização de versão.
Usando interfaces versionadas
Métodos de interface
Em tempo de execução, ao tentar chamar novos métodos em um servidor antigo, os novos clientes recebem um erro ou uma exceção, dependendo do backend.
- back-end
cpp
obtém::android::UNKNOWN_TRANSACTION
. - O back-end
ndk
obtémSTATUS_UNKNOWN_TRANSACTION
. - O back-end
java
obtémandroid.os.RemoteException
com uma mensagem informando que a API não está implementada.
Para estratégias para lidar com isso, consulte consultando versões e usando padrões .
Parceláveis
Quando novos campos são adicionados aos parcelables, clientes e servidores antigos os descartam. Quando novos clientes e servidores recebem parcelas antigas, os valores padrão para novos campos são preenchidos automaticamente. Isso significa que os padrões precisam ser especificados para todos os novos campos em uma parcela.
Os clientes não devem esperar que os servidores usem os novos campos, a menos que saibam que o servidor está implementando a versão que possui o campo definido (consulte consultando versões ).
Enums e constantes
Da mesma forma, clientes e servidores devem rejeitar ou ignorar valores constantes e enumeradores não reconhecidos, conforme apropriado, uma vez que mais poderão ser adicionados no futuro. Por exemplo, um servidor não deve abortar quando recebe um enumerador que não conhece. Ele deve ignorá-lo ou retornar algo para que o cliente saiba que não há suporte nesta implementação.
Sindicatos
Tentar enviar uma união com um novo campo falhará se o destinatário for antigo e não souber sobre o campo. A implementação nunca verá a união com o novo campo. A falha será ignorada se for uma transação unilateral; caso contrário, o erro será BAD_VALUE
(para backend C++ ou NDK) ou IllegalArgumentException
(para backend Java). O erro é recebido se o cliente estiver enviando um conjunto de união para o novo campo para um servidor antigo ou quando for um cliente antigo recebendo a união de um novo servidor.
Desenvolvimento baseado em bandeiras
Interfaces em desenvolvimento (descongeladas) não podem ser usadas em dispositivos de lançamento, porque não há garantia de compatibilidade com versões anteriores.
AIDL oferece suporte ao tempo de execução para essas bibliotecas de interface descongeladas para que o código seja escrito na versão descongelada mais recente e ainda seja usado em dispositivos de lançamento. O comportamento compatível com versões anteriores dos clientes é semelhante ao comportamento existente e, com o fallback, as implementações também precisam seguir esses comportamentos. Consulte Usando interfaces versionadas .
Sinalizador de construção AIDL
O sinalizador que controla esse comportamento é RELEASE_AIDL_USE_UNFROZEN
definido em build/release/build_flags.bzl
. true
significa que a versão descongelada da interface é usada em tempo de execução e false
significa que todas as bibliotecas das versões descongeladas se comportam como sua última versão congelada. Você pode substituir o sinalizador como true
para desenvolvimento local, mas deve revertê-lo para false
antes do lançamento. Normalmente, o desenvolvimento é feito com uma configuração que possui o sinalizador definido como true
.
Matriz de compatibilidade e manifestos
Os objetos de interface do fornecedor (objetos VINTF) definem quais versões são esperadas e quais versões são fornecidas em cada lado da interface do fornecedor.
A maioria dos dispositivos não Cuttlefish tem como alvo a matriz de compatibilidade mais recente somente depois que as interfaces são congeladas, portanto não há diferença nas bibliotecas AIDL baseadas em RELEASE_AIDL_USE_UNFROZEN
.
Matrizes
As interfaces de propriedade do parceiro são adicionadas a matrizes de compatibilidade específicas do dispositivo ou do produto que o dispositivo visa durante o desenvolvimento. Portanto, quando uma versão nova e descongelada de uma interface é adicionada a uma matriz de compatibilidade, as versões congeladas anteriores precisam permanecer para RELEASE_AIDL_USE_UNFROZEN=false
. Você pode lidar com isso usando diferentes arquivos de matriz de compatibilidade para diferentes configurações RELEASE_AIDL_USE_UNFROZEN
ou permitindo ambas as versões em um único arquivo de matriz de compatibilidade que é usado em todas as configurações.
Por exemplo, ao adicionar uma versão 4 descongelada, use <version>3-4</version>
.
Quando a versão 4 está congelada, você pode remover a versão 3 da matriz de compatibilidade porque a versão 4 congelada é usada quando RELEASE_AIDL_USE_UNFROZEN
é false
.
Manifestos
No Android 15 (AOSP experimental), uma alteração no libvintf
é introduzida para modificar os arquivos de manifesto no momento da compilação com base no valor de RELEASE_AIDL_USE_UNFROZEN
.
Os manifestos e os fragmentos de manifesto declaram qual versão de uma interface um serviço implementa. Ao usar a versão descongelada mais recente de uma interface, o manifesto deve ser atualizado para refletir esta nova versão. Quando RELEASE_AIDL_USE_UNFROZEN=false
as entradas do manifesto são ajustadas pela libvintf
para refletir a mudança na biblioteca AIDL gerada. A versão é modificada da versão descongelada, N
, para a última versão congelada N - 1
. Portanto, os usuários não precisam gerenciar vários manifestos ou fragmentos de manifestos para cada um dos seus serviços.
Mudanças no cliente HAL
O código do cliente HAL deve ser compatível com versões anteriores congeladas com suporte. Quando RELEASE_AIDL_USE_UNFROZEN
é false
, os serviços sempre se parecem com a última versão congelada ou anterior (por exemplo, chamar novos métodos descongelados retorna UNKNOWN_TRANSACTION
ou novos campos parcelable
têm seus valores padrão). Os clientes da estrutura Android precisam ser compatíveis com versões anteriores adicionais, mas esse é um novo detalhe para clientes fornecedores e clientes de interfaces de propriedade de parceiros.
Mudanças na implementação de HAL
A maior diferença no desenvolvimento HAL com o desenvolvimento baseado em flag é o requisito para que as implementações HAL sejam compatíveis com versões anteriores da última versão congelada para funcionar quando RELEASE_AIDL_USE_UNFROZEN
for false
. Considerar a compatibilidade com versões anteriores em implementações e código de dispositivo é um exercício novo. Consulte Usando interfaces versionadas .
As considerações de compatibilidade com versões anteriores são geralmente as mesmas para clientes e servidores, e para código de estrutura e código de fornecedor, mas há diferenças sutis das quais você precisa estar ciente, já que agora você está implementando efetivamente duas versões que usam o mesmo código-fonte (a versão atual descongelada).
Exemplo: Uma interface possui três versões congeladas. A interface é atualizada com um novo método. O cliente e o serviço foram atualizados para usar a nova biblioteca da versão 4. Como a biblioteca V4 é baseada em uma versão descongelada da interface, ela se comporta como a última versão congelada, versão 3, quando RELEASE_AIDL_USE_UNFROZEN
é false
e impede o uso do novo método.
Quando a interface é congelada, todos os valores de RELEASE_AIDL_USE_UNFROZEN
usam essa versão congelada e o código que trata da compatibilidade com versões anteriores pode ser removido.
Ao chamar métodos em retornos de chamada, você deve lidar normalmente com o caso quando UNKNOWN_TRANSACTION
é retornado. Os clientes podem estar implementando duas versões diferentes de um retorno de chamada com base na configuração da versão, portanto, você não pode presumir que o cliente enviará a versão mais recente e novos métodos poderão retornar isso. Isso é semelhante ao modo como os clientes AIDL estáveis mantêm a compatibilidade com versões anteriores com servidores, descrito em Usando interfaces versionadas .
// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
mMyCallback = cb;
// Get the version of the callback for later when we call methods on it
auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
return status;
}
// Example of using the callback later
void NotifyCallbackLater() {
// From the latest frozen version (V2)
mMyCallback->foo();
// Call this method from the unfrozen V3 only if the callback is at least V3
if (mMyCallbackVersion >= 3) {
mMyCallback->bar();
}
}
Novos campos em tipos existentes ( parcelable
, enum
, union
) podem não existir ou conter seus valores padrão quando RELEASE_AIDL_USE_UNFROZEN
for false
e os valores de novos campos que um serviço tenta enviar são descartados na saída do processo.
Novos tipos adicionados nesta versão descongelada não podem ser enviados ou recebidos pela interface.
A implementação nunca recebe uma chamada para novos métodos de nenhum cliente quando RELEASE_AIDL_USE_UNFROZEN
é false
.
Tenha o cuidado de usar novos enumeradores apenas com a versão em que foram introduzidos, e não com a versão anterior.
Normalmente, você usa foo->getInterfaceVersion()
para ver qual versão a interface remota está usando. No entanto, com o suporte de controle de versão baseado em sinalizadores, você está implementando duas versões diferentes, portanto, talvez queira obter a versão da interface atual. Você pode fazer isso obtendo a versão da interface do objeto atual, por exemplo, this->getInterfaceVersion()
ou os outros métodos para my_ver
. Consulte Consultando a versão da interface do objeto remoto para obter mais informações.
Novas interfaces VINTF estáveis
Quando um novo pacote de interface AIDL é adicionado, não há uma última versão congelada, portanto, não há comportamento para o qual recorrer quando RELEASE_AIDL_USE_UNFROZEN
for false
. Não use essas interfaces. Quando RELEASE_AIDL_USE_UNFROZEN
for false
, o Service Manager não permitirá que o serviço registre a interface e os clientes não a encontrarão.
Você pode adicionar os serviços condicionalmente com base no valor do sinalizador RELEASE_AIDL_USE_UNFROZEN
no makefile do dispositivo:
ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
android.hardware.health.storage-service
endif
Se o serviço fizer parte de um processo maior e você não puder adicioná-lo ao dispositivo condicionalmente, poderá verificar se o serviço foi declarado com IServiceManager::isDeclared()
. Se for declarado e não for registrado, interrompa o processo. Se não for declarado, espera-se que não seja registrado.
O choco como ferramenta de desenvolvimento
Todos os anos, após o VINTF ser congelado, ajustamos o target-level
da matriz de compatibilidade de estrutura (FCM) e o PRODUCT_SHIPPING_API_LEVEL
do Cuttlefish para que reflitam o lançamento dos dispositivos com o lançamento do próximo ano. Ajustamos target-level
e PRODUCT_SHIPPING_API_LEVEL
para garantir que haja algum dispositivo de lançamento testado e que atenda aos novos requisitos para o lançamento do próximo ano.
Quando RELEASE_AIDL_USE_UNFROZEN
for true
, Cuttlefish será usado para desenvolvimento de versões futuras do Android. Ele tem como alvo o nível FCM e PRODUCT_SHIPPING_API_LEVEL
da versão Android do próximo ano, exigindo que ele satisfaça os requisitos de software do fornecedor (VSR) da próxima versão.
Quando RELEASE_AIDL_USE_UNFROZEN
é false
, Cuttlefish tem o target-level
anterior e PRODUCT_SHIPPING_API_LEVEL
para refletir um dispositivo de liberação. No Android 14 e versões anteriores, essa diferenciação seria realizada com diferentes ramificações do Git que não captam a alteração no target-level
do FCM, no nível da API de remessa ou em qualquer outro código direcionado à próxima versão.
Regras de nomenclatura de módulos
No Android 11, para cada combinação de versões e back-ends habilitados, um módulo de biblioteca stub é criado automaticamente. Para se referir a um módulo de biblioteca stub específico para vinculação, não use o nome do módulo aidl_interface
, mas o nome do módulo de biblioteca stub, que é ifacename - version - backend , onde
-
ifacename
: nome do móduloaidl_interface
-
version
é qualquer um dos-
V version-number
para as versões congeladas -
V latest-frozen-version-number + 1
para a versão da ponta da árvore (ainda a ser congelada)
-
-
backend
é um dos-
java
para o back-end Java, -
cpp
para o back-end C++, -
ndk
oundk_platform
para o back-end do NDK. O primeiro é para aplicativos e o último é para uso da plataforma, -
rust
para back-end do Rust.
-
Suponha que exista um módulo com o nome foo e sua versão mais recente seja 2 e suporte NDK e C++. Neste caso, AIDL gera estes módulos:
- Baseado na versão 1
-
foo-V1-(java|cpp|ndk|ndk_platform|rust)
-
- Baseado na versão 2 (a versão estável mais recente)
-
foo-V2-(java|cpp|ndk|ndk_platform|rust)
-
- Baseado na versão ToT
-
foo-V3-(java|cpp|ndk|ndk_platform|rust)
-
Em comparação com o Android 11,
-
foo- backend
, que se referia à última versão estável torna-sefoo- V2 - backend
-
foo-unstable- backend
, que se referia à versão ToT torna-sefoo- V3 - backend
Os nomes dos arquivos de saída são sempre iguais aos nomes dos módulos.
- Baseado na versão 1:
foo-V1-(cpp|ndk|ndk_platform|rust).so
- Baseado na versão 2:
foo-V2-(cpp|ndk|ndk_platform|rust).so
- Baseado na versão ToT:
foo-V3-(cpp|ndk|ndk_platform|rust).so
Observe que o compilador AIDL não cria um módulo de versão unstable
ou um módulo sem versão para uma interface AIDL estável. A partir do Android 12, o nome do módulo gerado a partir de uma interface AIDL estável sempre inclui sua versão.
Novos métodos de metainterface
O Android 10 adiciona vários métodos de metainterface para o AIDL estável.
Consultando a versão da interface do objeto remoto
Os clientes podem consultar a versão e o hash da interface que o objeto remoto está implementando e comparar os valores retornados com os valores da interface que o cliente está usando.
Exemplo com o backend cpp
:
sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
// the remote side is using an older interface
}
std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();
Exemplo com o backend ndk
(e ndk_platform
):
IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
// the remote side is using an older interface
}
std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);
Exemplo com back-end java
:
IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
// the remote side is using an older interface
}
String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();
Para a linguagem Java, o lado remoto DEVE implementar getInterfaceVersion()
e getInterfaceHash()
como segue ( super
é usado em vez de IFoo
para evitar erros de copiar/colar. A anotação @SuppressWarnings("static")
pode ser necessária para desabilitar avisos, dependendo de a configuração javac
):
class MyFoo extends IFoo.Stub {
@Override
public final int getInterfaceVersion() { return super.VERSION; }
@Override
public final String getInterfaceHash() { return super.HASH; }
}
Isso ocorre porque as classes geradas ( IFoo
, IFoo.Stub
, etc.) são compartilhadas entre o cliente e o servidor (por exemplo, as classes podem estar no classpath de inicialização). Quando as classes são compartilhadas, o servidor também é vinculado à versão mais recente das classes, mesmo que possa ter sido construído com uma versão mais antiga da interface. Se esta metainterface for implementada na classe compartilhada, ela sempre retornará a versão mais recente. No entanto, ao implementar o método acima, o número da versão da interface é incorporado no código do servidor (porque IFoo.VERSION
é um static final int
que é embutido quando referenciado) e, portanto, o método pode retornar a versão exata em que o servidor foi construído com.
Lidando com interfaces mais antigas
É possível que um cliente seja atualizado com a versão mais recente de uma interface AIDL, mas o servidor esteja usando a interface AIDL antiga. Nesses casos, chamar um método em uma interface antiga retorna UNKNOWN_TRANSACTION
.
Com AIDL estável, os clientes têm mais controle. No lado do cliente, você pode definir uma implementação padrão para uma interface AIDL. Um método na implementação padrão é invocado somente quando o método não é implementado no lado remoto (porque foi construído com uma versão mais antiga da interface). Como os padrões são definidos globalmente, eles não devem ser usados em contextos potencialmente compartilhados.
Exemplo em C++ no Android 13 e posterior:
class MyDefault : public IFooDefault {
Status anAddedMethod(...) {
// do something default
}
};
// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());
foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
// remote side is not implementing it
Exemplo em Java:
IFoo.Stub.setDefaultImpl(new IFoo.Default() {
@Override
public xxx anAddedMethod(...) throws RemoteException {
// do something default
}
}); // once per an interface in a process
foo.anAddedMethod(...);
Você não precisa fornecer a implementação padrão de todos os métodos em uma interface AIDL. Os métodos que têm garantia de implementação no lado remoto (porque você tem certeza de que o controle remoto foi construído quando os métodos estavam na descrição da interface AIDL) não precisam ser substituídos na classe impl
padrão.
Convertendo AIDL existente em AIDL estruturado/estável
Se você tiver uma interface AIDL existente e um código que a utilize, use as etapas a seguir para converter a interface em uma interface AIDL estável.
Identifique todas as dependências da sua interface. Para cada pacote do qual a interface depende, determine se o pacote está definido em AIDL estável. Se não for definido, o pacote deverá ser convertido.
Converta todos os parcelables da sua interface em parcelables estáveis (os próprios arquivos da interface podem permanecer inalterados). Faça isso expressando sua estrutura diretamente em arquivos AIDL. As classes de gerenciamento devem ser reescritas para usar esses novos tipos. Isto pode ser feito antes de você criar um pacote
aidl_interface
(abaixo).Crie um pacote
aidl_interface
(conforme descrito acima) que contenha o nome do seu módulo, suas dependências e qualquer outra informação necessária. Para torná-lo estabilizado (não apenas estruturado), ele também precisa ser versionado. Para obter mais informações, consulte Interfaces de versionamento .