AIDL estável

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 compatibilidade de interface e há restrições sobre o que pode ser feito:

  • As interfaces são definidas no sistema de build com aidl_interfaces.
  • As interfaces só podem conter dados estruturados. Parcelables que representam o os tipos preferidos são criados automaticamente com base na definição da AIDL são organizados e desmarcados 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 requer AIDL estruturada para que o sistema de build e o compilador entender se as alterações feitas em 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 o seguinte recursos de controle de versão. Por outro lado, uma interface não será estável se o build principal sistema será usado para criá-lo ou se unstable:true estiver definido.

Definir uma interface AIDL

Uma definição de aidl_interface é semelhante a esta:

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 AIDL Foo definido em um pacote com.acme deve estar <base_path>/com/acme/Foo.aidl, em que <base_path> pode ser qualquer diretório relacionadas ao diretório em que Android.bp está. No exemplo anterior, <base_path> é srcs/aidl.
  • local_include_dir: o caminho em que o nome do pacote começa. Ela corresponde ao <base_path> explicado acima.
  • imports: uma lista de módulos aidl_interface usados. Se um dos seus Interfaces AIDL usam uma interface ou parcelable de outro aidl_interface, coloque o nome dele aqui. Pode ser apenas o nome, para consultar as últimas versão, ou o nome com o sufixo da versão (como -V1) para se referir à uma versão específica. Especificar uma versão tem suporte desde o Android 12
  • versions: as versões anteriores da interface que estão congelado com api_dir. A partir do Android 11, versions são congelados em aidl_api/name. Se não houver versões congeladas de uma interface, isso não deve ser especificado, e não haverá verificações de compatibilidade. Este campo foi substituído por versions_with_info para Android 13 e superior.
  • 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 atributos módulos importados por essa versão da interface aidl_interface. A definição da versão V de uma interface AIDL IFACE está localizada em aidl_api/IFACE/V: Esse campo foi lançado no Android 13, e não precisa ser modificado diretamente no Android.bp. O campo é adicionados ou atualizados invocando *-update-api ou *-freeze-api. Além disso, os campos versions são migrados automaticamente para versions_with_info quando um usuário invoca *-update-api ou *-freeze-api.
  • stability: a sinalização opcional para a promessa de estabilidade dessa interface. Isso é compatível apenas com "vintf". Se stability não for definida, o build o sistema verifica se a interface é compatível com versões anteriores, a menos unstable é especificado. A falta de definição corresponde a uma interface com a estabilidade nesse contexto de compilação (ou seja, todas as coisas do sistema, por por exemplo, itens em system.img e partições relacionadas ou todas as itens como vendor.img e partições relacionadas). Se stability é definido como "vintf", o que corresponde a uma promessa de estabilidade: a interface precisa ser mantida estável enquanto for usada.
  • gen_trace: a sinalização opcional para ativar ou desativar o rastreamento. A partir de No Android 14, o padrão é true para a cpp e java back-ends.
  • host_supported: a sinalização opcional que, quando definida como true, faz o bibliotecas geradas e disponíveis para o ambiente do host.
  • unstable: a sinalização opcional usada para marcar que essa interface não precisam ser estáveis. Quando ele é definido como true, o sistema de build cria o despejo de API para a interface e não exige que ele seja atualizado.
  • frozen: a sinalização opcional que, quando definida como true, significa que a interface não tem alterações desde a versão anterior da interface. Isso permite mais verificações durante o tempo de build. Quando definido como false, isso significa que a interface está no em desenvolvimento e tem novas mudanças. Portanto, executar foo-freeze-api gera uma a nova versão e alterará automaticamente o valor para true. Primeira aparição em Android 14
  • backend.<type>.enabled: essas sinalizações alternam cada um dos back-ends que para o qual 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 sinalização é útil com ndk_header e cpp_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": Não defina quando backend.java.platform_apis é true.
  • backend.java.platform_apis: a sinalização opcional que precisa ser definida como true quando as bibliotecas geradas precisam ser criadas com base na API da plataforma em vez do SDK.

Para cada combinação das versões e dos back-ends ativados, um 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 é como parcelables são definidos. Antes, os parcelables eram declarados para encaminhamento. no uma AIDL estável (e, portanto, estruturada), os campos e variáveis parcelables são explicitamente definidos.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

Um padrão tem suporte (mas não é obrigatório) para boolean, char e float, double, byte, int, long e String. No Android 12, os padrões para enumerações definidas pelo usuário também serão suporte. 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 dependência ao seu módulo, faça o seguinte: pode incluí-las em seus 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 no 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. Mas o a versão estável mais recente é especificada em imports em versions_with_info para import, 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 do ToT. Sempre que uma API é atualizado, execute foo-update-api para atualizar aidl_api/name/current que contém a definição de API da versão do ToT.

Para manter a estabilidade de uma interface, os proprietários podem adicionar novas:

  • Métodos no final de uma interface (ou métodos com novos métodos explicitamente definidos séries)
  • 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 requer que todas as interfaces AIDL estáveis ser congelados sem um campo owner: especificado.
  • AIDL_FROZEN_OWNERS="aosp test": o build requer todas as interfaces AIDL estáveis ser congelado com o campo owner: 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, para atualizá-los, atualizando 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ões

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 recebe STATUS_UNKNOWN_TRANSACTION.
  • O back-end java recebe android.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 parcelables antigos, os valores padrão para os 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 o de rede está implementando a versão que tem o campo definido (consulte consultar versões).

Enumerações e constantes

Da mesma forma, os clientes e servidores devem rejeitar ou ignorar as solicitações valores constantes e enumeradores conforme apropriado, já que mais podem ser adicionados ao no futuro. Por exemplo, um servidor não deve ser cancelado ao receber uma 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.

União

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 verá a união com o novo campo. A falha será ignorada transação unidirecional Caso contrário, o erro será BAD_VALUE. Para a versão C++ ou NDK back-end) ou IllegalArgumentException(para o back-end Java). O erro é recebido se o cliente estiver enviando uma união definida com o novo campo como um antigo ou quando é um cliente antigo que recebe a união de um novo servidor.

Gerenciar várias versões

Um namespace do vinculador no Android pode ter apenas uma versão de um aidl específico interface para evitar situações em que os tipos de aidl gerados têm várias e 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 apresenta um erro quando um módulo depende de diferentes versões da mesma biblioteca aidl_interface. O módulo pode depender dessas bibliotecas de forma direta ou indireta, usando dependências dependências. Esses erros mostram o gráfico de dependências do módulo com falha para 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 é usada por muitos módulos diferentes, pode ser útil para criar cc_defaults, java_defaults e rust_defaults para qualquer grupo de bibliotecas e processos que precisam usar a mesma versão. Ao apresentar nova versão da interface, esses padrões podem ser atualizados e todos os módulos que os usam sejam atualizados juntos, garantindo que 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. Isso a situação pode ser difícil de gerenciar quando há aidl_interface módulos importados em vários módulos aidl_interface que são usados nos mesmos processos.

aidl_interfaces_defaults pode ser usada para manter uma definição do versões mais recentes das dependências para uma aidl_interface, que podem ser atualizadas em em um único local e usada 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 definido em build/release/build_flags.bzl. true significa a versão descongelada a interface é usada no ambiente de execução e false significa as bibliotecas da 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 Cuttlefish segmentam a matriz de compatibilidade mais recente somente depois que as interfaces estiverem congeladas, então não há diferença na 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: Isso pode ser feito usando diferentes arquivos de matriz de compatibilidade para diferentes RELEASE_AIDL_USE_UNFROZEN ou permitir 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 congelada 4 é usada quando RELEASE_AIDL_USE_UNFROZEN é false.

Manifestos

No Android 15, uma mudança no libvintf é introduzida no modificar os arquivos de manifesto em tempo de compilação com base no valor RELEASE_AIDL_USE_UNFROZEN.

Os manifestos e os fragmentos do manifesto declaram qual versão de uma interface um serviço implementa. Ao usar a última versão descongelada de uma interface, o manifesto deve 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 de HAL

A maior diferença no desenvolvimento da HAL com desenvolvimento baseado em sinalizações é a para que as implementações de HAL sejam compatíveis com versões anteriores do último 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 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 uma 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 a como os clientes da AIDL estáveis mantêm versões anteriores a compatibilidade com servidores é descrita em Usar controle de versões do Terraform.

// 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 conferir qual versão do controle remoto está usando. No entanto, com o suporte a controle de versão com base em sinalizações, você a implementação de duas versões diferentes, então pode ser útil ter a versão a 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 para mais informações.

Novas interfaces VINTF estáveis

Quando um novo pacote de interface AIDL é adicionado, não há última versão congelada, portanto, não há comportamento para voltar quando RELEASE_AIDL_USE_UNFROZEN for false. Não use essas interfaces. Quando RELEASE_AIDL_USE_UNFROZEN for false, o Service Manager não vai permitir que o serviço registre a interface e os clientes não o encontrarão.

É possível adicionar os serviços condicionalmente com base no valor do Sinalização 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 foi declarado e não foi registrado, abortar o processo. Se ela não for declarada, ocorre uma falha no registro.

Cuttlefish como ferramenta de desenvolvimento

Todos os anos após o VINTF ser congelado, ajustamos a compatibilidade do framework matriz (FCM) target-level e o PRODUCT_SHIPPING_API_LEVEL do Cuttlefish para refletir os dispositivos que serão lançados no próximo ano. Nós ajustamos target-level e PRODUCT_SHIPPING_API_LEVEL para garantir que há algumas será lançado um dispositivo testado e que atende aos novos requisitos para a lançamento.

Quando RELEASE_AIDL_USE_UNFROZEN for true, o Cuttlefish será para o desenvolvimento de versões futuras do Android. Ele tem como alvo o Android do próximo ano do FCM e PRODUCT_SHIPPING_API_LEVEL, exigindo que ela atenda 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 dos 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 indicar a um módulo específico de biblioteca de stubs para vinculação, não use o nome do aidl_interface, mas o nome do módulo da biblioteca de stubs, que é ifacename-version-backend, em que

  • ifacename: nome do módulo aidl_interface.
  • version é
    • Vversion-number para as versões congeladas
    • Vlatest-frozen-version-number + 1 para o versão dica-de-árvore (ainda não congelada)
  • backend é
    • java para o back-end Java;
    • cpp para o back-end de C++;
    • ndk ou ndk_platform para o back-end do NDK. O primeiro é para aplicativos, e o até o Android 13. Em Android 13 e versões mais recentes, use apenas ndk.
    • rust para back-end do Rust.

Suponha que há um módulo com o nome foo e que a versão mais recente dele é 2, e oferece suporte ao NDK e ao 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 refere à versão estável mais recente a versão se torna foo-V2-backend
  • foo-unstable-backend, que se referiam aos TOS a versão se torna foo-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 meta interface

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 em que o objeto remoto está implementando e comparando 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 necessário para desativar os 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 à a versão mais recente das classes, mesmo que tenha sido criada com uma versão mais recente da interface. Se essa meta interface for implementada na interface ela sempre retorna 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 que está em linha quando referenciado) e, portanto, 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 AIDL mas o servidor está usando a antiga interface AIDL. 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 AIDL interface gráfica do usuário. Métodos com garantia de implementação no lado remoto (porque tem certeza de que o controle remoto é criado quando os métodos estavam no descrição da interface AIDL) não precisam ser substituídos no impl padrão .

Converter a AIDL em uma 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.

  1. 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 estiver definido, o pacote precisará ser convertido.

  2. Converta todos os parcelables na sua interface em parcelables estáveis (a arquivos de interface em si 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).

  3. Crie um pacote aidl_interface (conforme descrito acima) que contenha as o nome do módulo, as dependências dele e outras informações necessárias. Para estabilizar (e não apenas estruturar), ele também precisa de controle de versões. Para mais informações, consulte Interfaces de controle de versão.