Annotations dans AIDL

AIDL accepte les annotations qui fournissent au compilateur AIDL des informations supplémentaires sur l'élément annoté, ce qui affecte également le code de stub généré.

La syntaxe est semblable à celle de Java :

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

Ici, AnnotationName est le nom de l'annotation et AidlEntity est une entité AIDL telle que interface Foo, void method() ou int arg. Une annotation est associée à l'entité qui la suit.

Comme indiqué ci-dessus, certains arguments peuvent être définis entre parenthèses dans les annotations. Les annotations sans argument n'ont pas besoin de parenthèses. Exemple :

@AnnotationName AidlEntity

Ces annotations ne sont pas les mêmes que les annotations Java, bien qu'elles se ressemblent beaucoup. Les utilisateurs ne peuvent pas définir d'annotations AIDL personnalisées. Elles sont toutes prédéfinies. Certaines annotations n'affectent qu'un certain backend et sont des no-op dans d'autres backends. Elles sont soumises à des restrictions différentes concernant les éléments auxquels elles peuvent être associées.

Voici la liste des annotations AIDL prédéfinies :

Annotations Ajouté dans la version 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 indique que la valeur de l'entité annotée ne peut pas être fournie.

Cette annotation ne peut être associée qu'aux types de retour de méthode, aux paramètres de méthode et aux champs pouvant être mis en paquet.

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

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

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

Les annotations ne peuvent pas être associées à des types primitifs. L'erreur suivante s'est produite.

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

Cette annotation est une opération nulle pour le backend Java. En effet, en Java, tous les types non primitifs sont transmis par référence, ce qui peut être null.

Dans le backend CPP, @nullable T correspond à std::unique_ptr<T> dans Android 11 ou version antérieure, et à std::optional<T> dans Android 12 ou version ultérieure.

Dans le backend NDK, @nullable T correspond toujours à std::optional<T>.

Dans le backend Rust, @nullable T correspond toujours à Option<T>.

Pour un type de liste L tel que T[] ou List<T>, @nullable L correspond à std::optional<std::vector<std::optional<T>>> (ou std::unique_ptr<std::vector<std::unique_ptr<T>>> dans le cas du backend CPP pour Android 11 ou version antérieure).

Il existe une exception à ce mappage. Lorsque T est IBinder ou une interface AIDL, @nullable est une opération sans effet pour tous les backends, à l'exception de Rust. En d'autres termes, @nullable IBinder et IBinder correspondent tous deux à android::sp<IBinder>, qui est déjà nullable, car il s'agit d'un pointeur fort (les lectures CPP appliquent toujours la possibilité de valeur nulle, mais le type reste android::sp<IBinder>). En Rust, ces types sont nullable uniquement s'ils sont annotés avec @nullable. Ils sont mappés sur Option<T> s'ils sont annotés.

À partir d'Android 13, @nullable(heap=true) peut être utilisé pour les champs Parcelable afin de modéliser les types récursifs. @nullable(heap=true) ne peut pas être utilisé avec les paramètres de méthode ni les types de retour. Lorsqu'il est annoté avec, le champ est mappé à une référence std::unique_ptr<T> allouée au tas dans les backends CPP/NDK. @nullable(heap=true) est une opération no-op dans le backend Java.

utf8InCpp

utf8InCpp déclare qu'un String est représenté au format UTF8 pour le backend CPP. Comme son nom l'indique, l'annotation est une opération nulle pour les autres backends. Plus précisément, String est toujours au format UTF16 dans le backend Java et au format UTF8 dans le backend NDK.

Cette annotation peut être associée à n'importe quel endroit où le type String peut être utilisé, y compris les valeurs de retour, les paramètres, les déclarations de constantes et les champs Parcelable.

Pour le backend CPP, @utf8InCpp String dans AIDL correspond à std::string, tandis que String sans l'annotation correspond à android::String16 où UTF16 est utilisé.

Notez que l'existence de l'annotation utf8InCpp ne modifie pas la façon dont les chaînes sont transmises sur le réseau. Les chaînes sont toujours transmises en tant que UTF16 sur le réseau. Une chaîne annotée utf8InCpp est convertie en UTF16 avant d'être transmise. Lorsqu'une chaîne est reçue, elle est convertie de UTF16 en UTF8 si elle a été annotée comme utf8InCpp.

VintfStability

VintfStability déclare qu'un type défini par l'utilisateur (interface, parcelable et enum) peut être utilisé dans les domaines système et fournisseur. Pour en savoir plus sur l'interopérabilité entre le système et le fournisseur, consultez AIDL pour les HAL.

L'annotation ne modifie pas la signature du type, mais lorsqu'elle est définie, l'instance du type est marquée comme stable afin qu'elle puisse transiter entre les processus du fournisseur et du système.

L'annotation ne peut être associée qu'à des déclarations de type définies par l'utilisateur, comme indiqué ici :

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

Lorsqu'un type est annoté avec VintfStability, tout autre type référencé dans le type doit également être annoté comme tel. Dans l'exemple suivant, Data et IBar doivent tous deux être annotés avec 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 {...}

De plus, les fichiers AIDL définissant des types annotés avec VintfStability ne peuvent être créés qu'à l'aide du type de module Soong aidl_interface, avec la propriété stability définie sur "vintf".

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

UnsupportedAppUsage

L'annotation UnsupportedAppUsage indique que le type AIDL annoté fait partie de l'interface non SDK accessible aux anciennes applications. Pour en savoir plus sur les API masquées, consultez Restrictions concernant les interfaces non SDK.

L'annotation UnsupportedAppUsage n'affecte pas le comportement du code généré. L'annotation n'annote que la classe Java générée avec l'annotation Java du même nom.

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

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

Il s'agit d'une opération sans effet pour les backends non Java.

Support

L'annotation Backing spécifie le type de stockage d'un type d'énumération AIDL.

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

Dans le backend CPP, cela émet une classe d'énumération C++ de type int32_t.

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

Si l'annotation est omise, la valeur type est considérée comme étant byte, ce qui correspond à int8_t pour le backend CPP.

L'argument type ne peut être défini que sur les types intégraux suivants :

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

NdkOnlyStableParcelable

NdkOnlyStableParcelable marque une déclaration (et non une définition) Parcelable comme stable afin qu'elle puisse être référencée à partir d'autres types AIDL stables. C'est comme JavaOnlyStableParcelable, mais NdkOnlyStableParcelable marque une déclaration Parcelable comme stable pour le backend NDK au lieu de Java.

Pour utiliser ce parcelable :

  • Vous devez spécifier ndk_header.
  • Vous devez disposer d'une bibliothèque NDK spécifiant le parcelable, et la bibliothèque doit être compilée dans la bibliothèque. Par exemple, dans le système de compilation principal d'un module cc_*, utilisez static_libs ou shared_libs. Pour aidl_interface, ajoutez la bibliothèque sous additional_shared_libraries dans Android.bp.

JavaOnlyStableParcelable

JavaOnlyStableParcelable marque une déclaration (et non une définition) Parcelable comme stable afin qu'elle puisse être référencée à partir d'autres types AIDL stables.

AIDL stable exige que tous les types définis par l'utilisateur soient stables. Pour les parcelables, la stabilité exige que leurs champs soient explicitement décrits dans le fichier source 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 le parcelable n'était pas structuré (ou simplement déclaré), il ne peut pas être référencé.

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable vous permet de remplacer la vérification lorsque le Parcelable auquel vous faites référence est déjà disponible en toute sécurité dans le SDK Android.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive génère automatiquement des méthodes pour les types Parcelable dans le backend Java.

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

L'annotation nécessite des paramètres supplémentaires pour contrôler ce qui doit être généré. Voici les paramètres acceptés :

  • equals=true génère les méthodes equals et hashCode.
  • toString=true génère la méthode toString qui affiche le nom du type et des champs. Exemple : Data{number: 42, str: foo}

JavaDefault

JavaDefault, ajouté dans Android 13, contrôle si la prise en charge de la gestion des versions de l'implémentation par défaut est générée (pour setDefaultImpl). Cette prise en charge n'est plus générée par défaut afin de gagner de l'espace.

JavaPassthrough

JavaPassthrough permet d'annoter l'API Java générée avec une annotation Java arbitraire.

Annotations suivantes dans AIDL

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

devenir

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

dans le code Java généré.

La valeur du paramètre annotation est émise directement. Le compilateur AIDL n'examine pas la valeur du paramètre. Si une erreur de syntaxe au niveau Java se produit, elle ne sera pas détectée par le compilateur AIDL, mais par le compilateur Java.

Cette annotation peut être associée à n'importe quelle entité AIDL. Cette annotation est une opération sans effet pour les backends non Java.

RustDerive

RustDerive implémente automatiquement les traits pour les types Rust générés.

L'annotation nécessite des paramètres supplémentaires pour contrôler ce qui doit être généré. Voici les paramètres acceptés :

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

Pour obtenir des explications sur ces traits, consultez https://doc.rust-lang.org.

FixedSize

FixedSize marque un élément structuré pouvant être mis en forme comme étant de taille fixe. Une fois marqué, le parcelable ne pourra plus être enrichi de nouveaux champs. Tous les champs du Parcelable doivent également être des types de taille fixe, y compris les types primitifs, les énumérations, les tableaux de taille fixe et les autres Parcelables marqués avec FixedSize.

Cela ne fournit aucune garantie pour les différentes tailles de bits et ne doit pas être utilisé pour la communication avec différentes tailles de bits.

Titre

Descriptor spécifie de manière forcée le descripteur d'interface d'une interface.

package android.foo;

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

Le descripteur de cette interface est android.bar.IWorld. Si l'annotation Descriptor est manquante, le descripteur sera android.foo.IHello.

Cela est utile pour renommer une interface déjà publiée. En faisant en sorte que le descripteur de l'interface renommée soit identique à celui de l'interface avant le changement de nom, les deux interfaces peuvent communiquer entre elles.

@hide dans les commentaires

Le compilateur AIDL reconnaît @hide dans les commentaires et le transmet à la sortie Java pour que Metalava le récupère. Ce commentaire permet au système de compilation Android de savoir que les API AIDL ne sont pas des API SDK.

@deprecated dans les commentaires

Le compilateur AIDL reconnaît @deprecated dans les commentaires comme un tag permettant d'identifier une entité AIDL qui ne doit plus être utilisée.

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

Chaque backend marque les entités obsolètes avec une annotation ou un attribut spécifique au backend afin que le code client soit averti s'il fait référence aux entités obsolètes. Par exemple, l'annotation @Deprecated et la balise @deprecated sont associées au code Java généré.