Anotações na AIDL

A AIDL oferece suporte a anotações que oferecem ao compilador da AIDL informações extras sobre o elemento anotado, o que também afeta o código stub gerado.

A sintaxe é semelhante à do Java:

@AnnotationName(argument1=value, argument2=value) AidlEntity

Aqui, AnnotationName é o nome da anotação e AidlEntity é uma entidade AIDL como interface Foo, void method() ou int arg. Uma anotação é anexada à entidade que a segue.

Algumas anotações podem ter argumentos definidos entre parênteses, como mostrado acima. As anotações que não têm um argumento não precisam dos parênteses. Por exemplo:

@AnnotationName AidlEntity

Essas anotações não são iguais às anotações Java, embora sejam muito semelhantes. Os usuários não podem definir anotações AIDL personalizadas. Todas as anotações são predefinidas. Algumas anotações afetam apenas um determinado back-end e não operam em outros. Eles têm diferentes restrições às quais podem ser anexados.

Confira a lista de anotações AIDL predefinidas:

Anotações Adicionado à versão do Android
nullable 7
utf8InCpp 7
VintfStability 11
UnsupportedAppUsage 10
Hide 11
Backing 11
NdkOnlyStableParcelable 14
JavaOnlyStableParcelable 11
JavaDerive 12
JavaPassthrough 12
FixedSize 12
Descriptor 12

anulável

nullable declara que o valor da entidade anotada pode não ser fornecido.

Essa anotação só pode ser anexada a tipos de retorno de método, parâmetros de método e campos parcelable.

interface IFoo {
    // method return types
    @nullable Data method();

    // method parameters
    void method2(in @nullable Data d);
}

parcelable Data {
    // parcelable fields
    @nullable Data d;
}

Anotações não podem ser anexadas a tipos primitivos. Veja a seguir um erro.

void method(in @nullable int a); // int is a primitive type

Essa anotação não opera para o back-end Java. Isso ocorre porque, em Java, todos os tipos não primitivos são transmitidos por referência, que pode ser null.

No back-end do CPP, @nullable T é mapeado para std::unique_ptr<T> no Android 11 ou versões anteriores e para std::optional<T> no Android 12 ou versões mais recentes.

No back-end do NDK, @nullable T sempre é mapeado para std::optional<T>.

Para um tipo de lista L, como T[] ou List<T>, @nullable L é mapeado para std::optional<std::vector<std::optional<T>>> (ou std::unique_ptr<std::vector<std::unique_ptr<T>>> no caso do back-end de CPP do Android 11 ou anterior).

Há uma exceção nesse mapeamento. Quando T é IBinder ou uma interface AIDL, @nullable é um ambiente autônomo. Em outras palavras, @nullable IBinder e IBinder são mapeados igualmente para android::sp<IBinder>, que já é anulável porque é um ponteiro forte. As leituras de CPP ainda aplicam nulidade, mas o tipo ainda é android::sp<IBinder>.

No Android 13 e versões mais recentes, a @nullable(heap=true) pode ser usada em campos comparáveis para modelar tipos recursivos. @nullable(heap=true) não pode ser usada com parâmetros de método ou tipos de retorno. Quando anotado com ele, o campo é mapeado para uma referência std::unique_ptr<T> alocada por heap nos back-ends do CPP/NDK. @nullable(heap=true) é um ambiente autônomo no back-end Java.

utf8InCpp

utf8InCpp declara que um String é representado no formato UTF8 para o back-end do CPP. Como o nome indica, a anotação é um ambiente autônomo para outros back-ends. Especificamente, String é sempre UTF16 no back-end do Java e UTF8 no back-end do NDK.

Essa anotação pode ser anexada em qualquer lugar em que o tipo String possa ser usado, incluindo valores de retorno, parâmetros, declarações constantes e campos parceláveis.

Para o back-end do CPP, @utf8InCpp String na AIDL é mapeado para std::string, enquanto String sem a anotação é mapeado para android::String16 em que UTF16 é usado.

A existência da anotação utf8InCpp não muda a maneira como as strings são transmitidas pela rede. As strings são sempre transmitidas como UTF16 pela rede. Uma string com a anotação utf8InCpp é convertida em UTF16 antes de ser transmitida. Quando uma string é recebida, ela é convertida de UTF16 para UTF8, caso tenha sido anotada como utf8InCpp.

VintfStability

VintfStability declara que um tipo definido pelo usuário (interface, parcelable e enum) pode ser usado nos domínios do sistema e do fornecedor. Consulte AIDL para HALs para saber mais sobre a interoperabilidade do sistema com o fornecedor.

A anotação não muda a assinatura do tipo, mas, quando é definida, a instância do tipo é marcada como estável para que possa viajar pelo fornecedor e pelos processos do sistema.

A anotação só pode ser anexada a declarações de tipo definidas pelo usuário, conforme mostrado aqui:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

Quando um tipo é anotado com VintfStability, qualquer outro tipo referenciado nele também precisa ser anotado dessa forma. No exemplo a seguir, Data e IBar precisam ser anotados com VintfStability.

@VintfStability
interface IFoo {
    void doSomething(in IBar b); // references IBar
    void doAnother(in Data d); // references Data
}

@VintfStability // required
interface IBar {...}

@VintfStability // required
parcelable Data {...}

Além disso, os arquivos AIDL que definem os tipos anotados com VintfStability só podem ser criados usando o tipo de módulo Soong aidl_interface, com a propriedade stability definida como "vintf".

aidl_interface {
    name: "my_interface",
    srcs: [...],
    stability: "vintf",
}

Uso não aceito no app

A anotação UnsupportedAppUsage indica que o tipo AIDL com anotação faz parte da interface externa ao SDK que pode ser acessada por apps legados. Consulte Restrições para interfaces não SDK para mais informações sobre as APIs ocultas.

A anotação UnsupportedAppUsage não afeta o comportamento do código gerado. A anotação apenas anota a classe Java gerada com a anotação Java do mesmo nome.

// in AIDL
@UnsupportedAppUsage
interface IFoo {...}

// in Java
@android.compat.annotation.UnsupportedAppUsage
public interface IFoo {...}

Este é um ambiente autônomo para back-ends não Java.

Apoio

A anotação Backing especifica o tipo de armazenamento de um tipo de enumeração AIDL.

@Backing(type="int")
enum Color { RED, BLUE, }

No back-end do CPP, isso emite uma classe de enumeração C++ do tipo int32_t.

enum class Color : int32_t {
    RED = 0,
    BLUE = 1,
}

Se a anotação for omitida, o type será considerado byte, que é mapeado para int8_t para o back-end do CPP.

O argumento type pode ser definido apenas para os seguintes tipos de integrais:

  • byte (8 bits de largura)
  • int (32 bits de largura)
  • long (64 bits de largura)

NdkOnlyStableParcelable

NdkOnlyStableParcelable marca uma declaração fracionável (não definição) como estável para que possa ser referenciada em outros tipos de AIDL estáveis. Isso é semelhante a JavaOnlyStableParcelable, mas NdkOnlyStableParcelable marca uma declaração parcelable como estável para o back-end do NDK em vez de para Java.

Para usar o parcelable:

  • É necessário especificar ndk_header.
  • Você precisa ter uma biblioteca do NDK que especifique o parcelable, e a biblioteca precisa ser compilada na biblioteca. Por exemplo, no sistema de build principal em um módulo cc_*, use static_libs ou shared_libs. Para aidl_interface, adicione a biblioteca em additional_shared_libraries em Android.bp.

JavaOnlyStableParcelable

JavaOnlyStableParcelable marca uma declaração fracionável (não definição) como estável para que possa ser referenciada em outros tipos de AIDL estáveis.

A AIDL estável exige que todos os tipos definidos pelo usuário sejam estáveis. Para parcelas, ser estável exige que os campos sejam descritos explicitamente no arquivo de origem da AIDL.

parcelable Data { // Data is a structured parcelable.
    int x;
    int y;
}

parcelable AnotherData { // AnotherData is also a structured parcelable
    Data d; // OK, because Data is a structured parcelable
}

Se o parcelable não foi estruturado (ou acabou de ser declarado), ele não poderá ser referenciado.

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable permite que você substitua a verificação quando o parcelable que você está referenciando já está disponível com segurança como parte do SDK do Android.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

O JavaDerive gera automaticamente métodos para tipos fracionáveis no back-end Java.

@JavaDerive(equals = true, toString = true)
parcelable Data {
  int number;
  String str;
}

A anotação requer outros parâmetros para controlar o que será gerado. Os parâmetros aceitos são:

  • equals=true gera os métodos equals e hashCode.
  • toString=true gera um método toString que mostra o nome do tipo e os campos. Por exemplo: Data{number: 42, str: foo}

Padrão Java

O JavaDefault, adicionado no Android 13, controla se o suporte ao controle de versões de implementação padrão é gerado (para setDefaultImpl). Esse suporte não é mais gerado por padrão para economizar espaço.

JavaPassthrough

JavaPassthrough permite que a API Java gerada seja anotada com uma anotação Java arbitrária.

As seguintes anotações na AIDL

@JavaPassthrough(annotation="@android.annotation.Alice")
@JavaPassthrough(annotation="@com.android.Alice(arg=com.android.Alice.Value.A)")

se tornar

@android.annotation.Alice
@com.android.Alice(arg=com.android.Alice.Value.A)

no código Java gerado.

O valor do parâmetro annotation é emitido diretamente. O compilador AIDL não analisa o valor do parâmetro. Se houver algum erro de sintaxe no nível do Java, ele não será capturado pelo compilador AIDL, mas pelo compilador Java.

Essa anotação pode ser anexada a qualquer entidade AIDL. Essa anotação é um ambiente autônomo para back-ends não Java.

Tamanho fixo

FixedSize marca um parcelable estruturado como tamanho fixo. Depois de marcado, o parcelável não poderá ter novos campos adicionados a ele. Todos os campos do parcelable também precisam ter tipos de tamanho fixos, incluindo tipos primitivos, enumeração, matrizes de tamanho fixo e outros parcelables marcados com FixedSize.

Isso não fornece nenhuma garantia para diferentes bits e não deve ser confiado para comunicação de bits mistas.

Descritor

Descriptor especifica à força o descritor de uma interface.

package android.foo;

@Descriptor(value="android.bar.IWorld")
interface IHello {...}

O descritor dessa interface é android.bar.IWorld. Se a anotação Descriptor estiver ausente, o descritor será android.foo.IHello.

Isso é útil para renomear uma interface já publicada. Tornar o descritor da interface renomeada igual ao descritor da interface antes da renomeação permite que as duas interfaces se comuniquem.

@ocultar nos comentários

O compilador AIDL reconhece @hide nos comentários e o transmite para a saída Java para que metalava seja retirado. Esse comentário garante que o sistema de build do Android saiba que as APIs AIDL não são APIs do SDK.

@descontinuado nos comentários

O compilador AIDL reconhece @deprecated nos comentários como uma tag para identificar uma entidade AIDL que não pode mais ser usada.

interface IFoo {
  /** @deprecated use bar() instead */
  void foo();
  void bar();
}

Cada back-end marca entidades obsoletas com uma anotação ou atributo específico do back-end para que o código do cliente seja avisado caso se refira a essas entidades. Por exemplo, a anotação @Deprecated e a tag @deprecated são anexadas ao código gerado por Java.