Anotações na AIDL

A AIDL oferece suporte a anotações que fornecem 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 no exemplo anterior. Anotações sem um argumento não precisam de parênteses. Exemplo:

@AnnotationName AidlEntity

Essas anotações não são as mesmas do Java, embora pareçam semelhantes. Todas as anotações são predefinidas e têm restrições quanto ao local em que podem ser anexadas. Algumas anotações afetam apenas um determinado back-end e não têm efeito em outros.

Confira a lista de anotações AIDL predefinidas:

AnotaçõesAdição na versão do Android
nullable7
utf8InCpp7
VintfStability11
UnsupportedAppUsage10
Hide11
Backing11
NdkOnlyStableParcelable14
JavaOnlyStableParcelable11
JavaDerive12
JavaPassthrough12
FixedSize12
Descriptor12

nullable

nullable declara que o valor da entidade anotada pode ser nulo.

Você pode anexar essa anotação apenas a tipos de retorno de métodos, parâmetros de métodos e campos parceláveis:

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

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

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

Não é possível anexar anotações a tipos primitivos. O seguinte é um erro:

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

Essa anotação não faz nada no back-end Java. Em Java, todos os tipos não primitivos são transmitidos por referência, o 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 é mapeado para std::optional<T>.

No back-end Rust, @nullable T é mapeado para Option<T>.

Para um tipo semelhante a uma 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 do CPP para Android 11 ou versões anteriores).

Há uma exceção a esse mapeamento. Quando T é IBinder ou uma interface AIDL, @nullable não faz nada para todos os back-ends, exceto Rust. Em outras palavras, @nullable IBinder e IBinder mapeiam igualmente para android::sp<IBinder>, que já é anulável porque é um ponteiro forte. As leituras de CPP ainda impõem a capacidade de ser nulo, mas o tipo ainda é android::sp<IBinder>. Em Rust, esses tipos são nullable somente se forem anotados com @nullable. Eles são mapeados para Option<T> se forem anotados.

A partir do Android 13, @nullable(heap=true) pode ser usado para campos parceláveis e modelar tipos recursivos. @nullable(heap=true) não pode ser usado 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 e do NDK. @nullable(heap=true) não faz nada no back-end Java.

utf8InCpp

utf8InCpp declara que String é representado no formato UTF8 para o back-end CPP. Como o nome indica, a anotação é uma operação nula para outros back-ends. Especificamente, String é sempre UTF16 no back-end 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 de constantes e campos parceláveis.

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

VintfStability

VintfStability declara que um tipo definido pelo usuário (interface, parcelable e enum) pode ser usado em todos os domínios do sistema e do fornecedor. Consulte AIDL para HALs para saber mais sobre interoperabilidade entre fornecedores e sistemas.

A anotação não muda a assinatura do tipo, mas, quando ela é definida, a instância do tipo é marcada como estável para que possa transitar entre os processos do fornecedor e 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 como tal. 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 tipos anotados com VintfStability só podem ser criados usando o tipo de módulo aidl_interface do Soong, com a propriedade stability definida como vintf:

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

UnsupportedAppUsage

A anotação UnsupportedAppUsage indica que o tipo AIDL anotado faz parte da interface não SDK que estava acessível para apps legados. Consulte Restrições para interfaces que não sã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 anota apenas a classe Java gerada com a anotação Java de mesmo nome:

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

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

Essa é uma operação nula para back-ends que não são Java.

Anotação de 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 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, type será considerado byte, que é mapeado para int8_t no back-end do CPP.

O argumento type só pode ser definido como os seguintes tipos integrais:

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

NdkOnlyStableParcelable

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

Para usar esse parcelable:

  • É necessário especificar ndk_header.
  • Você precisa ter uma biblioteca do NDK especificando o parcelable, e ela 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 no Android.bp.

JavaOnlyStableParcelable

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

A AIDL estável exige que todos os tipos definidos pelo usuário sejam estáveis. Para parcelables, ser estável exige que os campos sejam descritos explicitamente no arquivo de origem 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 for estruturado (ou apenas declarado), não será possível fazer referência a ele:

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable permite substituir a verificação quando o parcelable a que você está fazendo referência 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 parceláveis no back-end Java:

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

A anotação exige mais parâmetros para controlar o que gerar. Os parâmetros aceitos são:

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

JavaDefault (descontinuado)

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

JavaPassthrough

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

Estas anotações em AIDL:

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

serão transformados no seguinte código Java gerado:

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

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

Essa anotação pode ser anexada a qualquer entidade AIDL. Essa anotação é uma operação nula para back-ends que não são Java.

RustDerive

O RustDerive implementa automaticamente características para tipos Rust gerados.

A anotação exige mais parâmetros para controlar o que gerar. Os parâmetros aceitos são:

  • Copy=true
  • Clone=true
  • Ord=true
  • PartialOrd=true
  • Eq=true
  • PartialEq=true
  • Hash=true

Para explicações sobre essas características, consulte a documentação do Rust (em inglês).

FixedSize

FixedSize marca um parcelable estruturado como de tamanho fixo. Depois que ele é marcado, não é possível adicionar novos campos ao parcelable. Todos os campos do parcelable precisam ser tipos de tamanho fixo, incluindo tipos primitivos, enums, matrizes de tamanho fixo e outros parcelables marcados com FixedSize.

Descritor

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

package android.foo;

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

O descritor desta 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. Ao fazer com que o descritor da interface renomeada seja igual ao descritor da interface antes da renomeação, as duas interfaces podem se comunicar.

@hide nos comentários

O compilador AIDL reconhece @hide em comentários e o transmite para a saída Java para que o metalava o colete. Esse comentário ajuda a garantir que o sistema de build do Android reconheça que as APIs AIDL não são APIs do SDK.

@deprecated em comentários

O compilador AIDL reconhece @deprecated em comentários como uma tag para identificar uma entidade AIDL que não deve mais ser usada:

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

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