Langage AIDL

Le langage AIDL est basé sur le langage Java. Les fichiers spécifient un contrat d'interface, ainsi que divers types de données et constantes utilisés dans ce contrat.

Package

Chaque fichier AIDL commence par un package facultatif qui correspond aux noms de package dans différents backends. Une déclaration de package se présente comme suit :

    package my.package;

Comme pour Java, les fichiers AIDL doivent se trouver dans une structure de dossiers correspondant à leur package. Les fichiers avec le package my.package doivent se trouver dans le dossier my/package/.

Types

Dans les fichiers AIDL, les types peuvent être spécifiés à de nombreux endroits. Pour obtenir la liste exacte des types acceptés dans le langage AIDL, consultez Types de backend AIDL.

Annotations

Plusieurs parties du langage AIDL sont compatibles avec les annotations. Pour obtenir la liste des annotations et des endroits où elles peuvent être appliquées, consultez Annotations AIDL.

Importations

Pour utiliser des types définis dans d'autres interfaces, vous devez d'abord ajouter des dépendances dans le système de compilation. Dans les modules Soong cc_* et java_*, où les fichiers .aidl sont utilisés directement sous srcs dans les builds de la plate-forme Android, vous pouvez ajouter des répertoires à l'aide du champ aidl: { include_dirs: ... }. Pour les importations utilisant aidl_interface, consultez cette page.

Voici à quoi ressemble une importation :

    import some.package.Foo;  // explicit import

Lorsque vous importez un type dans le même package, vous pouvez omettre le package. Toutefois, l'omission du package peut entraîner des erreurs d'importation ambiguës lorsque les types sont spécifiés sans package et placés dans l'espace de noms global (en général, tous les types doivent être associés à un espace de noms) :

    import Foo;  // same as my.package.Foo

Définir des types

Les fichiers AIDL définissent généralement des types utilisés comme interface.

Interfaces

Voici un exemple d'interface AIDL :

    interface ITeleport {
        // Location defined elsewhere
        void teleport(Location baz, float speed);
        String getName();

        // ITeleportCallback defined elsewhere
        void methodWithCallback(ITeleportCallback callback);

        // ITeleportSession defined elsewhere
        ITeleportSession getASubInterface();
    }

Une interface définit un objet avec une série de méthodes. Les méthodes peuvent être oneway (oneway void doFoo()) ou synchrones. Si une interface est définie comme oneway (oneway interface ITeleport {...}), toutes les méthodes qu'elle contient sont implicitement oneway. Les méthodes unidirectionnelles sont distribuées de manière asynchrone et ne peuvent pas renvoyer de résultat. Les méthodes unidirectionnelles du même thread vers le même binder s'exécutent également de manière séquentielle (bien que potentiellement sur des threads différents). Pour en savoir plus sur la configuration des threads, consultez Gestion des threads des backends AIDL.

Binder permet de partager de nombreuses interfaces et de nombreux objets Binder via des interfaces Binder. Les interfaces AIDL utilisent fréquemment des rappels dans les appels de méthode, comme avec ITeleportCallback dans l'exemple précédent. Vous pouvez réutiliser des objets de rappel entre les appels à la même méthode ou à différentes méthodes. Une autre utilisation courante des types d'interface consiste à renvoyer des sous-interfaces ou des objets de session à partir de méthodes telles que ITeleportSession dans l'exemple précédent. Cette imbrication permet d'encapsuler différentes API au niveau de l'API ou en fonction de l'état d'exécution. Par exemple, une session peut représenter la propriété d'une ressource particulière. Lorsque des interfaces sont transmises plusieurs fois ou renvoyées au client ou au serveur dont elles proviennent, elles conservent toujours l'égalité des pointeurs de l'objet Binder sous-jacent.

Les méthodes peuvent avoir zéro ou plusieurs arguments. Les arguments des méthodes peuvent être in, out ou inout. Pour en savoir plus sur l'impact de cette fonctionnalité sur les types d'arguments, consultez Directionnalité des backends AIDL.

Objets Parcelable

Pour savoir comment créer des Parcelables spécifiques au backend, consultez Parcelables personnalisés des backends AIDL.

Android 10 et versions ultérieures sont compatibles avec les définitions de parcelables directement dans AIDL. Ce type de parcelable est appelé parcelable structuré. Pour en savoir plus sur la relation entre AIDL structuré et stable dans le compilateur AIDL et notre système de compilation, consultez AIDL structuré et AIDL stable.

Exemple :

    package my.package;

    import my.package.Boo;

    parcelable Baz {
        @utf8InCpp String name = "baz";
        Boo boo;
    }

Unions

Android 12 et les versions ultérieures sont compatibles avec les déclarations d'union taguée. Exemple :

    package my.package;

    import my.package.FooSettings;
    import my.package.BarSettings;

    union Settings {
        FooSettings fooSettings;
        BarSettings barSettings;
        @utf8InCpp String str;
        int number;
    }

Pour en savoir plus sur les backends, consultez Unions de backends AIDL.

Énumérations

Android 11 et versions ultérieures sont compatibles avec les déclarations d'énumération. Exemple :

    package my.package;

    enum Boo {
        A = 1 * 4,
        B = 3,
    }

Déclarations de type imbriquées

Android 13 et versions ultérieures sont compatibles avec les déclarations de types imbriqués. Exemple :

    package my.package;

    import my.package.Baz;

    interface IFoo {
        void doFoo(Baz.Nested nested);  // defined in my/package/Baz.aidl
        void doBar(Bar bar);            // defined below

        parcelable Bar { ... }          // nested type definition
    }

Constantes

Les interfaces, les parcelables et les unions AIDL personnalisés peuvent également contenir des constantes entières et de chaîne, telles que :

    const @utf8InCpp String HAPPY = ":)";
    const String SAD = ":(";
    const byte BYTE_ME = 1;
    const int ANSWER = 6 * 7;

Expressions constantes

Les constantes AIDL, les tailles de tableaux et les énumérateurs peuvent être spécifiés à l'aide d'expressions constantes. Les expressions peuvent utiliser des parenthèses pour imbriquer des opérations. Les valeurs d'expression constante peuvent être utilisées avec des valeurs entières ou à virgule flottante.

Les littéraux true et false représentent des valeurs booléennes. Les valeurs avec un ., mais sans suffixe, telles que 3.8, sont considérées comme des valeurs doubles. Les valeurs float sont associées au suffixe f, comme 2.4f. Une valeur entière avec le suffixe l ou L indique une valeur longue de 64 bits. Sinon, les valeurs intégrales obtiennent le plus petit type signé préservant la valeur entre 8 bits (octet), 32 bits (int) et 64 bits (long). 256 est donc considéré comme un int, mais 255 + 1 déborde pour devenir le byte 0. Les valeurs hexadécimales, telles que 0x3, sont d'abord interprétées comme le plus petit type non signé préservant la valeur entre 32 et 64 bits, puis réinterprétées comme des valeurs non signées. Par conséquent, 0xffffffff a la valeur int : -1. À partir d'Android 13, le suffixe u8 peut être ajouté aux constantes, telles que 3u8, pour représenter une valeur byte. Ce suffixe est important pour qu'un calcul tel que 0xffu8 * 3 soit interprété comme -3 de type byte, tandis que 0xff * 3 est 765 de type int.

Les opérateurs compatibles ont une sémantique C++ et Java. Les opérateurs binaires sont || && | ^ & == != < > <= >= << >> + - * / %, du plus faible au plus élevé. Les opérateurs unaires sont + - ! ~.