O Android 10 adiciona suporte à interface estável do Android Linguagem de definição (AIDL), uma nova maneira de acompanhar o programa de aplicação interface (API) e interface binária de aplicativo (ABI) fornecida pela AIDL do Google Cloud. A AIDL estável funciona exatamente como a AIDL, mas o sistema de build rastreia a compatibilidade da interface, e há restrições sobre o que você pode fazer:
- As interfaces são definidas no sistema de build com
aidl_interfaces
. - As interfaces só podem conter dados estruturados. Os parceláveis que representam os tipos preferidos são criados automaticamente com base na definição do AIDL e são marshalled e unmarshalled automaticamente.
- As interfaces podem ser declaradas como estáveis (compatíveis com versões anteriores). Quando isso acontece, a API é rastreada e controlada em um arquivo ao lado da AIDL interface gráfica do usuário.
AIDL estruturada e estável
AIDL estruturada refere-se aos tipos definidos puramente na AIDL. Por exemplo, A declaração parcelable (uma parcelable personalizada) não está estruturada como AIDL. Parcelables com os campos definidos em AIDL são chamados de parcelas estruturadas.
A AIDL estável exige uma AIDL estruturada para que o sistema de build e o compilador
possam entender se as mudanças feitas em parceláveis 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 precisa usar apenas tipos estruturados e também os seguintes
recursos de controle de versão. Por outro lado, uma interface não é estável se o sistema de build
principal for usado para criá-la ou se unstable:true
estiver definido.
Definir uma interface AIDL
Uma definição de aidl_interface
tem esta aparência:
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 um interface AIDL.srcs
: a lista de arquivos de origem AIDL que compõem a interface. O caminho para um tipo de AIDLFoo
definido em um pacotecom.acme
deve estar<base_path>/com/acme/Foo.aidl
, em que<base_path>
pode ser qualquer diretório relacionadas ao diretório em queAndroid.bp
está. No exemplo anterior,<base_path>
ésrcs/aidl
.local_include_dir
: o caminho em que o nome do pacote começa. Ele corresponde a<base_path>
explicado acima.imports
: uma lista de módulosaidl_interface
usados. Se um dos seus Interfaces AIDL usam uma interface ou parcelable de outroaidl_interface
, coloque o nome dele aqui. Ele pode ser o nome sozinho, 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 congelado comapi_dir
. A partir do Android 11,versions
são congelados emaidl_api/name
. Se não houver versões congeladas de uma interface, isso não será especificado e não haverá verificações de compatibilidade. Esse campo foi substituído porversions_with_info
para o Android 13 e versões mais recentes.versions_with_info
: lista de tuplas, cada uma contendo o nome de uma versão congelada e uma lista com importações de versão de outros módulos aidl_interface que essa versão do aidl_interface importou. A definição da versão V de uma interface AIDL IFACE está localizada emaidl_api/IFACE/V
. Esse campo foi introduzido no Android 13 e não deve ser modificado diretamente emAndroid.bp
. O campo é adicionados ou atualizados 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
: a flag opcional para a promessa de estabilidade dessa interface. Isso é compatível apenas com"vintf"
. Sestability
não for definida, o build o sistema verifica se a interface é compatível com versões anteriores, a menos queunstable
é especificado. Ser desativado corresponde a uma interface com estabilidade dentro deste contexto de compilação (ou seja, todas as coisas do sistema, por exemplo, as coisas emsystem.img
e partições relacionadas ou todas as coisas do fornecedor, por exemplo, as coisas emvendor.img
e partições relacionadas). Sestability
for definido como"vintf"
, isso corresponde a uma promessa de estabilidade: a interface precisa ser mantida estável enquanto for usada.gen_trace
: a flag opcional para ativar ou desativar o rastreamento. No Android 14 e versões mais recentes, o padrão étrue
para os back-endscpp
ejava
.host_supported
: a flag opcional que, quando definida comotrue
, disponibiliza as bibliotecas geradas para o ambiente de hospedagem.unstable
: a flag opcional usada para marcar que essa interface não precisa ser estável. Quando ele é definido comotrue
, o sistema de build cria o despejo de API para a interface e não exige que ele seja atualizado.frozen
: a flag opcional que, quando definida comotrue
, significa que a interface não tem mudanças desde a versão anterior da interface. Isso permite mais verificações durante o tempo de build. Quando definido comofalse
, isso significa que a interface está no em desenvolvimento e tem novas mudanças. Portanto, executarfoo-freeze-api
gera uma a nova versão e alterará automaticamente o valor paratrue
. Primeira aparição em Android 14backend.<type>.enabled
: essas flags alternam cada um dos back-ends para os quais o compilador AIDL gera código. Quatro back-ends são com suporte: Java, C++, NDK e Rust. Os back-ends de Java, C++ e NDK estão ativados por padrão. Se algum desses três back-ends não for necessário, ele precisará ser desativada explicitamente. O Rust fica desativado por padrão até o Android 15.backend.<type>.apex_available
: a lista de nomes APEX gerados está disponível.backend.[cpp|java].gen_log
: a sinalização opcional que controla se é necessário gerar código adicional para coletar informações sobre a transação.backend.[cpp|java].vndk.enabled
: a sinalização opcional para tornar essa interface como parte do VNDK. O padrão éfalse
.backend.[cpp|ndk].additional_shared_libraries
: lançado em No Android 14, essa flag adiciona dependências ao bibliotecas nativas do Cloud. Essa flag é útil comndk_header
ecpp_header
.backend.java.sdk_version
: a sinalização opcional para especificar a versão do SDK em que a biblioteca de stubs Java é criada. O padrão é"system_current"
: Ele não deve ser definido quandobackend.java.platform_apis
fortrue
.backend.java.platform_apis
: a sinalização opcional que precisa ser definida comotrue
quando as bibliotecas geradas precisam ser criadas com base na API da plataforma em vez do SDK.
Para cada combinação de versões e back-ends ativados, uma biblioteca stub é criada. Para consultar a versão específica da biblioteca de stubs Para um back-end específico, consulte Regras de nomenclatura do módulo.
Gravar arquivos AIDL
As interfaces na AIDL estável são semelhantes às interfaces tradicionais, com a no entanto, eles não podem usar parcelables não estruturados (porque não estão estáveis. consulte Modelos estruturados vs. estáveis AIDL). A principal diferença na AIDL estável é a forma como os elementos parceláveis são definidos. Anteriormente, os parcelables eram declarados com antecedência. Na AIDL estável (e, portanto, estruturada), os 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;
}
Um padrão é aceito (mas não obrigatório) para boolean
, char
,
float
, double
, byte
, int
, long
e String
. No Android
12, também há suporte para padrões de enumerações definidas pelo usuário. Quando um padrão não é especificado, é usado um valor vazio ou semelhante a 0.
Enumerações sem um valor padrão são inicializadas como 0 mesmo se houver
sem enumerador zero.
Usar bibliotecas de stubs
Depois de adicionar bibliotecas de stubs como uma dependência ao módulo, você
pode incluí-las nos arquivos. Confira alguns exemplos de bibliotecas de stubs
sistema de build (Android.mk
também pode ser usado para definições de módulo legado):
cc_... {
name: ...,
shared_libs: ["my-module-name-cpp"],
...
}
# or
java_... {
name: ...,
// can also be shared_libs if your preference 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 Rust:
use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
// use just like traditional AIDL
Interfaces de controle de versão
A declaração de um módulo com o nome foo também cria um destino no sistema de build.
que você pode usar para gerenciar a API do módulo. Quando criado, foo-freeze-api
adiciona uma nova definição de API em api_dir
; ou
aidl_api/name
, dependendo da versão do Android.
adiciona um arquivo .hash
, ambos representando a versão recém-congelada
interface gráfica do usuário. foo-freeze-api também atualiza a propriedade versions_with_info
.
para refletir a versão adicional e imports
para a versão. Basicamente,
imports
em versions_with_info
foi copiado do campo imports
. No entanto, a
versão estável mais recente é especificada em imports
em versions_with_info
para a
importação, que não tem uma versão explícita.
Depois que a propriedade versions_with_info
for especificada, o sistema de build será executado.
verificações de compatibilidade entre versões congeladas e também entre o topo da árvore (ToT)
e a versão congelada mais recente.
Além disso, você precisa gerenciar a definição da API da versão da 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 da ToT.
Para manter a estabilidade de uma interface, os proprietários podem adicionar novas:
- Métodos para o fim de uma interface (ou métodos com novos sequências explicitamente definidos)
- Elementos no final de um parcelable (exige a adição de um padrão para cada )
- Valores constantes
- No Android 11, os enumeradores
- No Android 12, campos até o final de uma união
Nenhuma outra ação é permitida e ninguém pode modificar uma interface Caso contrário, eles correm o risco de colisão com alterações feitas por um proprietário.
Para testar se todas as interfaces estão congeladas para lançamento, você pode criar com o as seguintes variáveis de ambiente definidas:
AIDL_FROZEN_REL=true m ...
: o build exige que todas as interfaces estáveis da AIDL sejam congeladas e não tenham um campoowner:
especificado.AIDL_FROZEN_OWNERS="aosp test"
: o build requer todas as interfaces AIDL estáveis ser congelado com o campoowner:
especificado como "aosp" ou "teste".
Estabilidade das importações
Atualizar as versões de importações para versões congeladas de uma interface é são compatíveis com versões anteriores na camada da AIDL estável. No entanto, atualizar esses recursos requer a atualização de todos os servidores e clientes que usam uma versão anterior da interface, e alguns apps podem ficar confusos ao misturar diferentes versões de tipos. Geralmente, para pacotes comuns ou somente tipos, isso é seguro porque o código precisa já foram gravados para lidar com tipos desconhecidos de transações de IPC.
No Android, o código android.hardware.graphics.common
é o maior
exemplo desse tipo de upgrade de versão.
Usar interfaces com controle de versão
Métodos de interface
Durante a execução, ao tentar chamar novos métodos em um servidor antigo, os novos clientes recebem um erro ou uma exceção, dependendo do back-end.
- O back-end
cpp
recebe::android::UNKNOWN_TRANSACTION
. - O back-end
ndk
recebeSTATUS_UNKNOWN_TRANSACTION
. - O back-end
java
recebeandroid.os.RemoteException
com uma mensagem dizendo A API não está implementada.
Para conferir estratégias para lidar com isso, consulte consultar versões e usando padrões.
Parcelables
Quando novos campos são adicionados a parcelables, eles são descartados pelos clientes e servidores antigos. Quando novos clientes e servidores recebem parceláveis antigos, os valores padrão dos novos campos são preenchidos automaticamente. Isso significa que os padrões precisam ser especificado para todos os novos campos em um parcelable.
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 tem o campo definido (consulte consultar versões).
Tipos enumerados e constantes
Da mesma forma, clientes e servidores precisam rejeitar ou ignorar valores constantes e enumeradores não reconhecidos, conforme apropriado, já que mais podem ser adicionados no futuro. Por exemplo, um servidor não deve ser abortado quando recebe um enumerador que ele não conhece. O servidor deve ignorar o enumerador ou retornar algo para que o cliente saiba que não é compatível essa implementação.
Sindicatos
A tentativa de enviar uma união com um novo campo falhará se o receptor for antigo e
que não conhece a área. A implementação nunca vai encontrar a união com
o novo campo. A falha será ignorada se for uma
transação unidirecional. Caso contrário, o erro será BAD_VALUE
(para o back-end C++ ou
NDK) ou IllegalArgumentException
(para o back-end Java). O erro é
recebido se o cliente estiver enviando um conjunto de união para o novo campo em um servidor
antigo ou quando for um cliente antigo recebendo a união de um novo servidor.
Gerenciar várias versões
Um namespace de linker no Android pode ter apenas uma versão de uma interface aidl
específica para evitar situações em que os tipos aidl
gerados têm várias
definições. O C++ tem a regra de uma definição que requer apenas uma definição
de cada símbolo.
O build do Android gera um erro quando um módulo depende de versões
diferentes da mesma biblioteca aidl_interface
. O módulo pode depender
dessas bibliotecas diretamente ou indiretamente por dependências das
dependências. Esses erros mostram o gráfico de dependências do módulo com falha até
as versões conflitantes da biblioteca aidl_interface
. Todas as
as dependências precisam ser atualizadas para incluir a mesma versão (geralmente a mais recente)
dessas bibliotecas.
Se a biblioteca de interface for usada por muitos módulos diferentes, pode ser útil
criar cc_defaults
, java_defaults
e rust_defaults
para qualquer grupo de
bibliotecas e processos que precisem usar a mesma versão. Ao introduzir uma
nova versão da interface, esses padrões podem ser atualizados e todos os módulos
que os usam são atualizados juntos, garantindo que eles não usem versões diferentes
da interface.
cc_defaults {
name: "my.aidl.my-process-group-ndk-shared",
shared_libs: ["my.aidl-V3-ndk"],
...
}
cc_library {
name: "foo",
defaults: ["my.aidl.my-process-group-ndk-shared"],
...
}
cc_binary {
name: "bar",
defaults: ["my.aidl.my-process-group-ndk-shared"],
...
}
Quando os módulos aidl_interface
importam outros módulos aidl_interface
, isso cria
outras dependências que exigem o uso de versões específicas em conjunto. Essa
situação pode se tornar difícil de gerenciar quando há módulos aidl_interface
comuns importados em vários módulos aidl_interface
usados
juntos nos mesmos processos.
A aidl_interfaces_defaults
pode ser usada para manter uma definição das
versões mais recentes de dependências de um aidl_interface
que pode ser atualizado em
um único lugar e usado por todos os módulos aidl_interface
que querem importar
essa interface comum.
aidl_interface_defaults {
name: "android.popular.common-latest-defaults",
imports: ["android.popular.common-V3"],
...
}
aidl_interface {
name: "android.foo",
defaults: ["my.aidl.latest-ndk-shared"],
...
}
aidl_interface {
name: "android.bar",
defaults: ["my.aidl.latest-ndk-shared"],
...
}
Desenvolvimento baseado em flags
As 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.
A AIDL oferece suporte ao substituto do ambiente de execução para essas bibliotecas de interface descongeladas para para que o código seja escrito com base na versão descongelada mais recente e ainda seja usado em dispositivos de lançamento. O comportamento de clientes compatível com versões anteriores é semelhante a comportamento existente e, com o substituto, as implementações também precisam seguir esses comportamentos. Consulte Usar interfaces com controle de versão.
Sinalização de build da AIDL
A flag que controla esse comportamento é RELEASE_AIDL_USE_UNFROZEN
definida em build/release/build_flags.bzl
. true
significa a versão descongelada
a interface é usada em tempo de execução e false
significa as bibliotecas do
todas as versões descongeladas se comportam como a última versão congelada.
É possível substituir a sinalização por true
para
no desenvolvimento local, mas precisa revertê-la para false
antes do lançamento. Normalmente
O desenvolvimento é feito com uma configuração que tem a flag definida como true
.
Matriz de compatibilidade e manifestos
Objetos da interface do fornecedor (objetos VINTF) definem quais versões são esperadas e quais versões são fornecidas em cada lado do interface do fornecedor.
A maioria dos dispositivos que não são do Cuttlefish tem como alvo a matriz de compatibilidade mais recente
apenas 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 determinados dispositivos ou produtos
matrizes de compatibilidade que o dispositivo visa durante o desenvolvimento. Então, quando um
versão nova e descongelada de uma interface é adicionada a uma matriz de compatibilidade,
as versões congeladas anteriores precisam permanecer por
RELEASE_AIDL_USE_UNFROZEN=false
: Para isso, use arquivos de matriz de compatibilidade diferentes para diferentes configurações de RELEASE_AIDL_USE_UNFROZEN
ou permita as duas versões em um único arquivo de matriz de compatibilidade
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 estiver congelada, você poderá remover a versão 3 da matriz de compatibilidade
porque a versão 4 congelada será usada quando RELEASE_AIDL_USE_UNFROZEN
for
false
.
Manifestos
No Android 15, uma mudança em libvintf
foi introduzida para
modificar os arquivos de manifesto no momento da build 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 precisa ser atualizado para refletir essa nova versão. Quando
RELEASE_AIDL_USE_UNFROZEN=false
, as entradas do manifesto são ajustadas
libvintf
para refletir a mudança na biblioteca AIDL gerada. A versão
foi modificado da versão descongelada, N
, para
a última versão congelada N - 1
. Assim, os usuários não precisam gerenciar várias
ou fragmentos de manifesto para cada um dos serviços.
Mudanças no cliente HAL
O código do cliente HAL precisa ser compatível com versões anteriores de cada versão congelada
para a versão anterior. Quando RELEASE_AIDL_USE_UNFROZEN
for false
, os serviços sempre vão procurar
como a última versão congelada ou anterior (por exemplo, chamar novos
métodos retornam UNKNOWN_TRANSACTION
, ou novos campos parcelable
têm as
valores padrão). Os clientes do framework do Android precisam ser anteriores
compatível com outras versões anteriores, mas esse é um novo detalhe para
clientes de fornecedores e clientes de interfaces de propriedade do parceiro.
Mudanças na implementação da HAL
A maior diferença no desenvolvimento de HAL com o desenvolvimento baseado em sinalizações é a
exigência de que as implementações de 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 nas implementações e no código do dispositivo é um novo
exercício. Consulte Usar controle de versão
do Terraform.
As considerações de compatibilidade com versões anteriores geralmente são as mesmas para clientes e servidores, bem como para código de framework e código de fornecedor, diferenças sutis que você precisa conhecer, pois agora conhece implementar duas versões que usam o mesmo código-fonte (a versão atual, descongelada versão).
Exemplo: uma interface tem três versões congeladas. A interface é atualizada com um
novo método. O cliente e o serviço são atualizados para usar a nova versão 4
biblioteca. Como a biblioteca V4 é baseada em uma versão descongelada do
do servidor, ela se comporta como a última versão congelada, a 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 esse
versão congelada e o código que lida com a compatibilidade com versões anteriores pode ser removido.
Ao chamar métodos em callbacks, você precisa lidar com o caso de forma adequada.
UNKNOWN_TRANSACTION
é retornado. Os clientes podem implementar duas
de um callback com base na configuração de lançamento, ou seja, não é possível
pressupõem que o cliente envia a versão mais recente, e novos métodos podem retornar
isso. Isso é semelhante à forma como os clientes estáveis do AIDL mantêm a compatibilidade
reversa com os servidores, conforme descrito em Usar interfaces
com versão.
// 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();
}
}
Os novos campos nos tipos existentes (parcelable
, enum
, union
) podem
não existem ou contêm seus valores padrão quando RELEASE_AIDL_USE_UNFROZEN
for
false
e os valores dos novos campos que um serviço tenta enviar são descartados
a saída do processo.
Não é possível enviar novos tipos adicionados a esta versão descongelada 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 cuidado para usar novos enumeradores somente com a versão em que foram introduzidos. e não a versão anterior.
Normalmente, você usa foo->getInterfaceVersion()
para saber 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, é recomendável receber a versão da
interface atual. Você pode fazer isso obtendo a versão da interface do
objeto atual, por exemplo, this->getInterfaceVersion()
ou o outro
métodos para my_ver
. Consulte Como consultar a versão da interface do objeto
remoto
para mais informações.
Novas interfaces VINTF estáveis
Quando um novo pacote de interface da AIDL é adicionado, não há uma última versão congelada. Portanto,
não há um comportamento alternativo quando RELEASE_AIDL_USE_UNFROZEN
é
false
. Não use essas interfaces. Quando RELEASE_AIDL_USE_UNFROZEN
é
false
, o Service Manager não permite que o serviço registre a interface
e os clientes não a encontram.
É possível adicionar os serviços de forma condicional com base no valor da
flag 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 faz parte de um processo maior que não pode ser adicionado ao dispositivo
condicionalmente, é possível verificar se o serviço foi declarado com
IServiceManager::isDeclared()
: Se ele for declarado e não conseguir se registrar,
interrompa o processo. Se ela não for declarada, ocorre uma falha no registro.
Cuttlefish como ferramenta de desenvolvimento
Todos os anos, depois que o VINTF é congelado, ajustamos a matriz de compatibilidade
de framework (FCM) target-level
e o PRODUCT_SHIPPING_API_LEVEL
do Cuttlefish
para que reflitam os dispositivos lançados com a versão do ano que vem. 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 a versão
do ano que vem.
Quando RELEASE_AIDL_USE_UNFROZEN
é true
, o Cuttlefish é
usado para o desenvolvimento de futuras versões do Android. Ele é destinado ao nível do FCM e ao PRODUCT_SHIPPING_API_LEVEL
da versão do Android do próximo ano, exigindo que ele satisfaça
os requisitos de software do fornecedor (VSR, na sigla em inglês) da próxima versão.
Quando RELEASE_AIDL_USE_UNFROZEN
for false
, o Cuttlefish vai ter a
target-level
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 Git que não captam a mudança no FCM
target-level
, nível da API de frete ou qualquer outro código destinado ao próximo
lançamento.
Regras de nomenclatura de módulos
No Android 11, para cada combinação das versões e
com os back-ends ativados, um módulo de biblioteca de stubs será 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, em que
ifacename
: nome do móduloaidl_interface
version
é um dosVversion-number
para as versões congeladasVlatest-frozen-version-number + 1
para a versão da ponta da árvore (ainda não congelada)
backend
é um dosjava
para o back-end Java,cpp
para o back-end de C++;ndk
oundk_platform
para o back-end do NDK. O primeiro é para aplicativos, e o até o Android 13. No Android 13 e versões mais recentes, use apenasndk
.rust
para o back-end do Rust.
Suponha que haja um módulo com o nome foo e que a versão mais recente dele seja 2, com suporte para NDK e C++. Nesse caso, a AIDL gera estes módulos:
- Com base na versão 1
foo-V1-(java|cpp|ndk|ndk_platform|rust)
- Com base na versão 2 (a versão estável mais recente)
foo-V2-(java|cpp|ndk|ndk_platform|rust)
- Com base na versão do ToT
foo-V3-(java|cpp|ndk|ndk_platform|rust)
Em comparação com o Android 11:
foo-backend
, que se referia à versão estável mais recente, passa a serfoo-V2-backend
foo-unstable-backend
, que se referia à versão do ToT, passa a serfoo-V3-backend
Os nomes dos arquivos de saída são sempre iguais aos nomes dos módulos.
- Com base na versão 1:
foo-V1-(cpp|ndk|ndk_platform|rust).so
- Com base na versão 2:
foo-V2-(cpp|ndk|ndk_platform|rust).so
- Com base na versão do ToT:
foo-V3-(cpp|ndk|ndk_platform|rust).so
O compilador AIDL não cria um módulo de versão unstable
.
ou um módulo sem controle de versão para uma interface AIDL estável.
No Android 12 e versões mais recentes, o nome do módulo gerado com base em um
interface AIDL estável sempre inclui a versão dela.
Novos métodos de metainterface
O Android 10 adiciona vários métodos de metainterface para o uma AIDL estável.
Consultar 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 back-end 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 back-end 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 o 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 PRECISA implementar getInterfaceVersion()
e
getInterfaceHash()
da seguinte maneira (super
é usado em vez de IFoo
para evitar
copiar e colar erros. A anotação @SuppressWarnings("static")
pode
ser necessária para desativar avisos, dependendo da 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 sistema
classpath). Quando as classes são compartilhadas, o servidor também é vinculado à
versão mais recente das classes, mesmo que tenha sido criado com uma versão
anterior da interface. Se essa metainterface for implementada na classe
compartilhada, ela sempre retornará a versão mais recente. No entanto, ao implementar o método
como acima, o número da versão da interface é incorporado ao código do servidor
porque IFoo.VERSION
é um static final int
inline quando referenciado.
Assim, o método pode retornar a versão exata com que o servidor foi criado.
Lidar 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 a AIDL estável, os clientes têm mais controle. No lado do cliente, é possível definir uma implementação padrão para uma interface AIDL. Um método na classe implementação é invocada apenas quando o método não está implementado no (porque foi criado com uma versão mais antiga da interface). Como padrão forem definidos globalmente, eles não devem ser usados em locais com compartilhamento contextos de negócios diferentes.
Exemplo em C++ no Android 13 e versões mais recentes:
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(...);
Não é necessário 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 é criado quando os métodos estão na
descrição da interface AIDL) não precisam ser substituídos na classe impl
padrão.
Converter a AIDL atual em AIDL estruturada ou estável
Se você já tiver uma interface AIDL e um código que a usa, use o seguinte para converter a interface em uma interface AIDL estável.
Identifique todas as dependências da interface. Para cada pacote, o interface depende, determine se o pacote está definido em AIDL estável. Se não for definido, o pacote precisará ser convertido.
Converta todos os parceláveis na interface em parceláveis estáveis. Os arquivos da interface podem permanecer inalterados. Faça isso até expressando a estrutura deles diretamente em arquivos AIDL. As classes de gerenciamento devem ser reescritos para usar esses novos tipos. Isso pode ser feito antes da criação
aidl_interface
(abaixo).Crie um pacote
aidl_interface
(conforme descrito acima) que contenha as o nome do módulo, as dependências dele e qualquer outra informação necessária. Para estabilizar (e não apenas estruturar), ele também precisa de controle de versões. Para mais informações, consulte Controle de versões de interfaces.