Anotaciones en el AIDL

El AIDL admite anotaciones que le proporcionan al compilador del AIDL información adicional sobre el elemento anotado, lo que también afecta el código stub generado.

La sintaxis es similar a la de Java:

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

Aquí, AnnotationName es el nombre de la anotación y AidlEntity es una entidad del AIDL, como interface Foo, void method() o int arg. Se adjunta una anotación a la entidad que la sigue.

Algunas anotaciones pueden tener argumentos dentro de los paréntesis, como se muestra más arriba. Las anotaciones que no tienen un argumento no necesitan el paréntesis. Por ejemplo:

@AnnotationName AidlEntity

Estas anotaciones no son iguales a las anotaciones de Java, aunque se ven muy similares. Los usuarios no pueden definir anotaciones personalizadas del AIDL. Todas las anotaciones están predefinidas. Algunas anotaciones afectan solo a un backend determinado y no son operativas en otros backends. Tienen diferentes restricciones a las que se pueden adjuntar.

Esta es la lista de anotaciones de AIDL predefinidas:

Anotaciones Se agregó en la versión de 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

nullable

nullable declara que no se puede proporcionar el valor de la entidad anotada.

Esta anotación solo se puede adjuntar a tipos de datos que se devuelven, parámetros de métodos y campos parcelables.

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

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

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

No se pueden adjuntar anotaciones a los tipos primitivos. El siguiente es un error.

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

Esta anotación es no-op para el backend de Java. Esto se debe a que, en Java, todos los tipos no primitivos se pasan por referencia, que podría ser null.

En el backend de la CPP, @nullable T se asigna a std::unique_ptr<T> en Android 11 o versiones anteriores, y a std::optional<T> en Android 12 o versiones posteriores.

En el backend del NDK, @nullable T siempre se asigna a std::optional<T>.

Para un tipo similar a una lista L, como T[] o List<T>, @nullable L se asigna a std::optional<std::vector<std::optional<T>>> (o std::unique_ptr<std::vector<std::unique_ptr<T>>> en caso del backend de CPP de Android 11 o versiones anteriores).

Hay una excepción a esta asignación. Cuando T es IBinder o una interfaz de AIDL, @nullable es no-op. En otras palabras, tanto @nullable IBinder como IBinder se asignan por igual a android::sp<IBinder>, que ya es anulable porque es un puntero sólido (las lecturas de la CPP siguen aplicando la nulabilidad, pero el tipo sigue siendo android::sp<IBinder>).

A partir de Android 13, @nullable(heap=true) se puede usar para campos parcelables a fin de modelar tipos recursivos. @nullable(heap=true) no se puede usar con parámetros de método ni tipos de datos que se muestran. Cuando se lo anota, el campo se asigna a una referencia std::unique_ptr<T> asignada por el montón en los backends de CPP/NDK. @nullable(heap=true) es no-op en el backend de Java.

utf8InCpp

utf8InCpp declara que una String se representa en formato UTF8 para el backend de la CPP. Como su nombre lo indica, la anotación es una no-op para otros backends. Específicamente, String siempre es UTF16 en el backend de Java y UTF8 en el backend del NDK.

Esta anotación se puede adjuntar siempre que se pueda usar el tipo String, incluidos los valores que se muestran, los parámetros, las declaraciones de constantes y los campos parcelables.

Para el backend de la CPP, @utf8InCpp String en el AIDL se asigna a std::string, mientras que String sin la anotación se asigna a android::String16, donde se utiliza UTF16.

Ten en cuenta que la existencia de la anotación utf8InCpp no cambia la forma en que se transmiten las strings por cable. Las cadenas siempre se transmiten como UTF16 por el cable. Una string con anotaciones utf8InCpp se convierte en UTF16 antes de transmitirse. Cuando se recibe una cadena, se convierte de UTF16 a UTF8 si se anotó como utf8InCpp.

VintfStability

VintfStability declara que se puede usar un tipo definido por el usuario (interfaz, parcelable y enumeración) en los dominios del sistema y del proveedor. Si quieres obtener más información sobre la interoperabilidad entre sistemas y proveedores, consulta AIDL para HAL.

La anotación no cambia la firma del tipo, pero cuando se establece, la instancia del tipo se marca como estable para que pueda viajar a través de los procesos del proveedor y del sistema.

La anotación solo se puede adjuntar a declaraciones de tipo definidas por el usuario, como se muestra a continuación:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

Cuando un tipo se anota con VintfStability, cualquier otro tipo al que se haga referencia en el tipo también se debe anotar como tal. En el siguiente ejemplo, Data y IBar deberían tener anotaciones con 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 {...}

Además, los archivos AIDL que definen los tipos anotados con VintfStability solo se pueden compilar con el tipo de módulo aidl_interface de Soong, con la propiedad stability establecida en "vintf".

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

Uso de la aplicación no admitido

La anotación UnsupportedAppUsage indica que el tipo de AIDL con anotaciones forma parte de la interfaz que no pertenece al SDK y a la que pueden acceder las apps heredadas. Consulta Restricciones en interfaces que no pertenecen al SDK para obtener más información sobre las APIs ocultas.

La anotación UnsupportedAppUsage no afecta el comportamiento del código generado. La anotación solo anota la clase de Java generada con la anotación Java del mismo nombre.

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

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

Esta es una no-op para backends que no son de Java.

Copia de seguridad

La anotación Backing especifica el tipo de almacenamiento de un tipo de enumeración del AIDL.

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

En el backend de la CPP, se emite una clase enum C++ de tipo int32_t.

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

Si se omite la anotación, se supone que type es byte, que se asigna a int8_t para el backend de la CPP.

El argumento type solo se puede establecer en los siguientes tipos de integrales:

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

NdkOnlyStableParcelable

NdkOnlyStableParcelable marca una declaración parcelable (no definición) como estable para que se pueda hacer referencia a ella desde otros tipos de AIDL estables. Es similar a JavaOnlyStableParcelable, pero NdkOnlyStableParcelable marca una declaración parcelable como estable para el backend del NDK en lugar de para Java.

Para usar este objeto parcelable:

  • Debes especificar ndk_header.
  • Debes tener una biblioteca de NDK que especifique el elemento parcelable, y la biblioteca debe compilarse en la biblioteca. Por ejemplo, en el sistema de compilación principal de un módulo cc_*, usa static_libs o shared_libs. Para aidl_interface, agrega la biblioteca en additional_shared_libraries, en Android.bp.

JavaOnlyStableParcelable

JavaOnlyStableParcelable marca una declaración parcelable (no definición) como estable para que se pueda hacer referencia a ella desde otros tipos de AIDL estables.

El AIDL estable requiere que todos los tipos definidos por el usuario sean estables. En el caso de los objetos parcelables, ser estables requiere que sus campos se describan de forma explícita en el archivo fuente del 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
}

Si el elemento parcelable no era estructurado (o recién se declaró), no se puede hacer referencia a él.

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable te permite anular la verificación cuando el objeto parcelable al que haces referencia ya está disponible de forma segura como parte del SDK de Android.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive genera automáticamente métodos para tipos parcelables en el backend de Java.

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

La anotación requiere parámetros adicionales para controlar qué generar. Los parámetros admitidos son los siguientes:

  • equals=true genera los métodos equals y hashCode.
  • toString=true genera el método toString que imprime el nombre del tipo y los campos. Por ejemplo: Data{number: 42, str: foo}

JavaDefault

JavaDefault, que se agregó en Android 13, controla si se genera la compatibilidad con el control de versiones de implementación predeterminada (para setDefaultImpl). Esta compatibilidad ya no se genera de forma predeterminada para ahorrar espacio.

JavaPassthrough

JavaPassthrough permite que se anoten la API de Java generada con una anotación de Java arbitraria.

Las siguientes anotaciones en el AIDL

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

convertirse en

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

en el código Java generado.

El valor del parámetro annotation se emite directamente. El compilador de AIDL no analiza el valor del parámetro. Si hay algún error de sintaxis a nivel de Java, no será capturado por el compilador del AIDL, sino por el compilador de Java.

Esta anotación se puede adjuntar a cualquier entidad de AIDL. Esta anotación es no-op para backends que no son de Java.

Tamaño fijo

FixedSize marca un objeto parcelable estructurado como tamaño fijo. Una vez marcado, no se podrán agregar campos nuevos al parcelable. Todos los campos del objeto parcelable también deben ser tipos de tamaño fijo, incluidos los tipos primitivos, enumeraciones, arreglos de tamaño fijo y otros objetos parcelables marcados con FixedSize.

Esto no proporciona ninguna garantía sobre los diferentes bits de bits y no se debe confiar en para una comunicación de bits mixtos.

Descriptor

Descriptor especifica de manera forzosa el descriptor de interfaz de una interfaz.

package android.foo;

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

El descriptor de esta interfaz es android.bar.IWorld. Si falta la anotación Descriptor, el descriptor sería android.foo.IHello.

Esto es útil para cambiar el nombre de una interfaz ya publicada. Hacer que el descriptor de la interfaz renombrada sea el mismo que el de la interfaz antes del cambio de nombre permite que las dos interfaces se comuniquen entre sí.

@ocultar en los comentarios

El compilador de AIDL reconoce @hide en los comentarios y lo pasa a la salida de Java para que metalava lo retire. Este comentario garantiza que el sistema de compilación de Android sepa que las APIs de AIDL no son APIs del SDK.

@obsoleto en los comentarios

El compilador del AIDL reconoce @deprecated en los comentarios como una etiqueta para identificar una entidad del AIDL que ya no debe usarse.

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

Cada backend marca las entidades obsoletas con una anotación o un atributo específico del backend para que el código del cliente reciba una advertencia si hace referencia a las entidades obsoletas. Por ejemplo, la anotación @Deprecated y la etiqueta @deprecated se adjuntan al código generado por Java.