Linguaggio AIDL

Il linguaggio AIDL si basa vagamente sul linguaggio Java. I file specificano un contratto di interfaccia e vari tipi di dati e costanti utilizzati in questo contratto.

Pacchetto

Ogni file AIDL inizia con un pacchetto facoltativo che corrisponde ai nomi dei pacchetti in vari backend. Ecco un esempio di dichiarazione del pacchetto:

    package my.package;

Analogamente a Java, i file AIDL devono trovarsi in una struttura di cartelle corrispondente al loro pacchetto. I file con il pacchetto my.package devono trovarsi nella cartella my/package/.

Tipi

Nei file AIDL, ci sono molti punti in cui è possibile specificare i tipi. Per un elenco esatto dei tipi supportati nel linguaggio AIDL, consulta Tipi di backend AIDL.

Annotazioni

Diverse parti del linguaggio AIDL supportano le annotazioni. Per un elenco delle annotazioni e delle posizioni in cui possono essere applicate, vedi Annotazioni AIDL.

Importazioni

Per utilizzare i tipi definiti in altre interfacce, devi prima aggiungere le dipendenze nel sistema di compilazione. Nei moduli Soong cc_* e java_*, in cui i file .aidl vengono utilizzati direttamente in srcs nelle build della piattaforma Android, puoi aggiungere delle directory usando il campo aidl: { include_dirs: ... }. Per le importazioni che utilizzano aidl_interface, vedi qui.

Ecco un esempio di importazione:

    import some.package.Foo;  // explicit import

Quando importi un tipo nello stesso pacchetto, il pacchetto può essere omesso. Tuttavia, l'omissione del pacchetto può causare errori di importazione ambigui quando i tipi vengono specificati senza un pacchetto e inseriti nello spazio dei nomi globale (in genere tutti i tipi devono essere nello spazio dei nomi):

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

Definizione dei tipi

I file AIDL in genere definiscono i tipi utilizzati come interfaccia.

Interfacce

Ecco un esempio di interfaccia 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();
    }

Un'interfaccia definisce un oggetto con una serie di metodi. I metodi possono essere oneway (oneway void doFoo()) o sincroni. Se un'interfaccia è definita come oneway (oneway interface ITeleport {...}), tutti i metodi al suo interno sono implicitamente oneway. I metodi unidirezionali vengono inviati in modo asincrono e non possono restituire un risultato. I metodi unidirezionali provenienti dallo stesso thread verso lo stesso binder vengono comunque eseguiti in serie (anche se potenzialmente su thread diversi). Per un approfondimento su come configurare i thread, consulta Gestione dei thread dei backend AIDL.

Binder consente di condividere molte interfacce e oggetti binder tramite le interfacce binder. Le interfacce AIDL utilizzano spesso i callback come parte delle chiamate di metodo, ad esempio con ITeleportCallback nell'esempio precedente. Puoi riutilizzare gli oggetti di callback tra chiamate allo stesso metodo o a metodi diversi. Un altro uso comune dei tipi di interfaccia consiste nel restituire interfacce secondarie o oggetti di sessione dai metodi, come con ITeleportSession nell'esempio precedente. Questa nidificazione consente di incapsulare API diverse nell'API o in base allo stato di runtime. Ad esempio, una sessione può rappresentare la proprietà di una risorsa specifica. Quando le interfacce vengono passate più volte o restituite al client o al server da cui provengono, mantengono sempre l'uguaglianza dei puntatori dell'oggetto binder sottostante.

I metodi possono avere zero o più argomenti. Gli argomenti dei metodi possono essere in, out o inout. Per un approfondimento su come questo influisce sui tipi di argomenti, consulta Direzionalità dei backend AIDL.

Parcelable

Per una descrizione di come creare parcelable specifici per il backend, consulta l'articolo sui parcelable personalizzati dei backend AIDL.

Android 10 e versioni successive supportano le definizioni parcelable direttamente in AIDL. Questo tipo di parcelable è chiamato parcelable strutturato. Per ulteriori informazioni sul rapporto tra AIDL strutturato e stabile nel compilatore AIDL e nel nostro sistema di compilazione, vedi AIDL strutturato e stabile.

Ad esempio:

    package my.package;

    import my.package.Boo;

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

Union

Android 12 e versioni successive supportano le dichiarazioni di union con tag Ad esempio:

    package my.package;

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

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

Per informazioni specifiche sul backend, consulta Union dei backend AIDL.

Enum

Android 11 e versioni successive supportano le dichiarazioni enum. Ad esempio:

    package my.package;

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

Dichiarazioni di tipi nidificati

Android 13 e versioni successive supportano le dichiarazioni di tipo nidificate. Ad esempio:

    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
    }

Costanti

Le interfacce AIDL personalizzate, i parcelable e le union possono anche contenere costanti intere e stringhe, ad esempio:

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

Espressioni costanti

Le costanti AIDL, le dimensioni degli array e gli enumeratori possono essere specificati utilizzando espressioni costanti. Le espressioni possono utilizzare le parentesi per nidificare le operazioni. I valori delle espressioni costanti possono essere utilizzati con valori interi o in virgola mobile.

I valori letterali true e false rappresentano valori booleani. I valori con un . ma senza suffisso, ad esempio 3.8, sono considerati valori doppi. I valori in virgola mobile hanno il suffisso f, ad esempio 2.4f. Un valore intero con il suffisso l o L indica un valore long a 64 bit. In caso contrario, i valori interi assumono il tipo con segno più piccolo che possa preservarne il valore, scegliendo tra 8 bit (byte), 32 bit (int) e 64 bit (long). Pertanto, 256 è considerato un int, ma 255 + 1 genera un overflow e diventa byte 0. I valori esadecimali, ad esempio 0x3, vengono prima interpretati come il tipo senza segno più piccolo che possa preservarne il valore, tra 32 e 64 bit, e poi reinterpretati come valori senza segno. Quindi, 0xffffffff ha il valore int -1. A partire da Android 13, il suffisso u8 può essere aggiunto a costanti, ad esempio 3u8, per rappresentare un valore byte. Questo suffisso è importante affinché un calcolo, ad esempio 0xffu8 * 3, venga interpretato come -3 con tipo byte, mentre 0xff * 3 è 765 con tipo int.

Gli operatori supportati hanno semantica C++ e Java. In ordine di precedenza dal più basso al più alto, gli operatori binari sono || && | ^ & == != < > <= >= << >> + - * / %. Gli operatori unari sono + - ! ~.