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.

Pacco

Ogni file AIDL inizia con un pacchetto facoltativo che corrisponde ai nomi dei pacchetti in vari backend. Una dichiarazione del pacchetto ha il seguente aspetto:

    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 build. Nei moduli Soong cc_* e java_*, in cui i file .aidl vengono utilizzati direttamente in srcs nelle build della piattaforma Android, puoi aggiungere directory utilizzando il campo aidl: { include_dirs: ... }. Per le importazioni che utilizzano aidl_interface, vedi qui.

Un'importazione ha il seguente aspetto:

    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

Definisci i 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 dello stesso thread allo stesso binder vengono eseguiti anche in serie (anche se potenzialmente su thread diversi). Per una discussione 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 utilizzo comune dei tipi di interfaccia è per le interfacce secondarie o gli oggetti sessione da restituire da metodi come ITeleportSession nell'esempio precedente. Questo nesting 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 una discussione su come questo influisce sui tipi di argomenti, vedi Direzionalità dei backend AIDL.

Parcelable

Per una descrizione di come creare parcelable specifici per il backend, AIDL backends custom parcelables.

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 build, vedi AIDL strutturato e stabile.

Ad esempio:

    package my.package;

    import my.package.Boo;

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

Sindacati

Android 12 e versioni successive supportano le dichiarazioni unione 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 Unioni dei backend AIDL.

Enumerazionis

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 unioni possono contenere anche 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, le dimensioni degli array e gli enumeratori AIDL 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 float hanno il suffisso f, ad esempio 2.4f. Un valore intero con il suffisso l o L indica un valore lungo a 64 bit. In caso contrario, i valori integrali ottengono il tipo con segno che preserva il valore più piccolo 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 preserva 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 crescente, gli operatori binari sono || && | ^ & == != < > <= >= << >> + - * / %. Gli operatori unari sono + - ! ~.