Annotazioni in AIDL

AIDL supporta le annotazioni che forniscono al compilatore AIDL informazioni aggiuntive sull'elemento annotato, che influiscono anche sul codice stub generato.

La sintassi è simile a quella di Java:

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

Qui, AnnotationName è il nome dell'annotazione e AidlEntity è un'entità AIDL come interface Foo, void method() o int arg. Un'annotazione è associata all'entità che la segue.

Per alcune annotazioni è possibile impostare gli argomenti all'interno delle parentesi, come mostrato sopra. Le annotazioni che non hanno un argomento non richiedono le parentesi. Ad esempio:

@AnnotationName AidlEntity

Queste annotazioni non sono uguali alle annotazioni Java, anche se sembrano molto simili. Gli utenti non possono definire annotazioni AIDL personalizzate, poiché sono tutte predefinite. Alcune annotazioni interessano solo un determinato backend e non hanno alcun effetto in altri backend. Hanno diverse limitazioni per quanto riguarda i dispositivi a cui possono essere collegati.

Di seguito è riportato l'elenco delle annotazioni AIDL predefinite:

Annotazioni Aggiunta nella versione per 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 dichiara che il valore dell'entità annotata potrebbe non essere fornito.

Questa annotazione può essere associata solo ai tipi di ritorno, ai parametri e ai campi parcelable dei metodi.

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

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

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

Le annotazioni non possono essere associate a tipi primitivi. Di seguito è riportato un errore.

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

Questa annotazione è un'operazione no-op per il backend Java. Questo perché, in Java, tutti i tipi non primitivi vengono passati per riferimento, che potrebbe essere null.

Nel backend CPP, @nullable T corrisponde a std::unique_ptr<T> in Android 11 o versioni precedenti e a std::optional<T> in Android 12 o versioni successive.

Nel backend NDK, @nullable T viene sempre mappato a std::optional<T>.

Nel backend Rust, @nullable T viene sempre mappato a Option<T>.

Per un tipo L simile a un elenco, come T[] o List<T>, @nullable L viene mappato a std::optional<std::vector<std::optional<T>>> (o std::unique_ptr<std::vector<std::unique_ptr<T>>> nel caso del backend CPP per Android 11 o versioni precedenti).

Esiste un'eccezione a questa mappatura. Quando T è IBinder o un'interfaccia AIDL, @nullable è un'operazione no-op per tutti i backend tranne per Rust. In altre parole, sia @nullable IBinder che IBinder vengono mappati allo stesso modo a android::sp<IBinder>, che è già nullable perché è un puntatore forte (le letture CPP applicano comunque la nullabilità, ma il tipo è ancora android::sp<IBinder>). In Rust, questi tipi sono nullable solo se annotati con @nullable. Vengono mappati a Option<T> se annotati.

A partire da Android 13, @nullable(heap=true) può essere utilizzato per i campi parcellabili per modellare i tipi ricorsivi. @nullable(heap=true) non può essere utilizzato con i parametri del metodo o i tipi di ritorno. Se viene annotato, il campo viene mappato a un riferimento std::unique_ptr<T> allocato nell'heap nei backend CPP/NDK. @nullable(heap=true) è un'operazione no-op nel backend Java.

utf8InCpp

utf8InCpp dichiara che un String è rappresentato in formato UTF8 per il backend in CPP. Come suggerisce il nome, l'annotazione non viene eseguita per altri backend. Nello specifico, String è sempre UTF16 nel backend Java e UTF8 nel backend NDK.

Questa annotazione può essere allegata ovunque sia possibile utilizzare il tipo String, inclusi valori restituiti, parametri, dichiarazioni di costanti e campi parcelable.

Per il backend C++, @utf8InCpp String in AIDL mappa a std::string, mentre String senza l'annotazione mappa a android::String16 se viene utilizzato UTF16.

Tieni presente che l'esistenza dell'annotazione utf8InCpp non cambia il modo in cui le stringhe vengono trasmesse tramite cavo. Le stringhe vengono sempre trasmesse come UTF16 tramite rete. Una stringa annotata utf8InCpp viene convertita in UTF-16 prima di essere trasmessa. Quando viene ricevuta una stringa, viene convertita da UTF16 a UTF8 se è stata annotata come utf8InCpp.

VintfStability

VintfStability dichiara che un tipo definito dall'utente (interface, parcelable e enum) può essere utilizzato nei domini di sistema e del fornitore. Per saperne di più sull'interoperabilità tra il sistema e il fornitore, consulta AIDL per gli HAL.

L'annotazione non modifica la firma del tipo, ma quando è impostata, l'istanza del tipo viene contrassegnata come stabile in modo da poter essere trasferita tra i processi del fornitore e del sistema.

L'annotazione può essere associata solo alle dichiarazioni di tipo definite dall'utente, come mostrato qui:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

Quando un tipo è annotato con VintfStability, qualsiasi altro tipo a cui viene fatto riferimento nel tipo deve essere annotato come tale. Nell'esempio seguente, Data e IBar devono essere entrambi annotati 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 {...}

Inoltre, i file AIDL che definiscono i tipi annotati con VintfStability possono essere compilati solo utilizzando il tipo di modulo Soong aidl_interface, con la proprietà stability impostata su "vintf".

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

UnsupportedAppUsage

L'annotazione UnsupportedAppUsage indica che il tipo AIDL annotato fa parte dell'interfaccia non SDK accessibile per le app precedenti. Per ulteriori informazioni sulle API nascoste, consulta la sezione Limitazioni relative alle interfacce non SDK.

L'annotazione UnsupportedAppUsage non influisce sul comportamento del codice generato. L'annotazione annota solo la classe Java generata con l'annotazione Java dello stesso nome.

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

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

Questa operazione non viene eseguita per i backend non Java.

Supporto

L'annotazione Backing specifica il tipo di archiviazione di un tipo di enumerazione AIDL.

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

Nel backend C++, viene emessa una classe enum C++ di tipo int32_t.

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

Se l'annotazione viene omessa, si presume che type sia byte, che corrisponde a int8_t per il backend CPP.

L'argomento type può essere impostato solo sui seguenti tipi di numeri interi:

  • byte (larghezza 8 bit)
  • int (larghezza 32 bit)
  • long (larghezza 64 bit)

NdkOnlyStableParcelable

NdkOnlyStableParcelable contrassegna una dichiarazione parcellabile (non la definizione) come stabile in modo che possa essere richiamata da altri tipi AIDL stabili. È simile a JavaOnlyStableParcelable, ma NdkOnlyStableParcelable contrassegna una dichiarazione parcellabile come stabile per il backend NDK anziché per Java.

Per utilizzare questo oggetto Parcelable:

  • Devi specificare ndk_header.
  • Devi disporre di una libreria NDK che specifichi il parcelable e la libreria deve essere compilata nella libreria. Ad esempio, nel sistema di compilazione di base di un modulo cc_*, utilizza static_libs o shared_libs. Per aidl_interface, aggiungi la raccolta in additional_shared_libraries in Android.bp.

JavaOnlyStableParcelable

JavaOnlyStableParcelable contrassegna una dichiarazione parcellabile (non la definizione) come stabile in modo che possa essere richiamata da altri tipi AIDL stabili.

AIDL stabile richiede che tutti i tipi definiti dall'utente siano stabili. Per essere stabili, i campi dei componenti parcellabili devono essere descritti esplicitamente nel file di origine 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
}

Se il parcelable non è strutturato (o è stato semplicemente dichiarato), non è possibile farvi riferimento.

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable ti consente di ignorare il controllo quando il parcelable a cui fai riferimento è già disponibile in sicurezza nell'SDK Android.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive genera automaticamente metodi per i tipi parcelable nel backend Java.

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

L'annotazione richiede parametri aggiuntivi per controllare cosa generare. I parametri supportati sono:

  • equals=true genera i metodi equals e hashCode.
  • toString=true genera il metodo toString che stampa il nome del tipo e dei campi. Ad esempio: Data{number: 42, str: foo}

JavaDefault

JavaDefault, aggiunto in Android 13, controlla se viene generato il supporto per il controllo delle versioni dell'implementazione predefinita (per setDefaultImpl). Per risparmiare spazio, questo supporto non viene più generato per impostazione predefinita.

JavaPassthrough

JavaPassthrough consente di annotare l'API Java generata con un'annotazione Java arbitraria.

Le seguenti annotazioni in AIDL

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

diventare

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

nel codice Java generato.

Il valore del parametro annotation viene emesso direttamente. Il compilatore AIDL non esamina il valore del parametro. Se è presente un errore di sintassi a livello di Java, non verrà rilevato dal compilatore AIDL, ma dal compilatore Java.

Questa annotazione può essere collegata a qualsiasi entità AIDL. Questa annotazione è un'operazione non valida per i backend non Java.

RustDerive

RustDerive implementa automaticamente i tratti per i tipi Rust generati.

L'annotazione richiede parametri aggiuntivi per controllare cosa generare. I parametri supportati sono:

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

Per spiegazioni di queste caratteristiche, visita la pagina https://doc.rust-lang.org.

FixedSize

FixedSize contrassegna un parcelable strutturato come di dimensioni fisse. Una volta contrassegnata, al lotti non sarà consentito aggiungere nuovi campi. Tutti i campi del parcelable devono essere anche di tipo con dimensioni fisse, inclusi tipi primitivi, enum, array di dimensioni fisse e altri parcelable contrassegnati da FixedSize.

Ciò non fornisce alcuna garanzia per diverse larghezze di bit e non deve essere usato per le comunicazioni con larghezze di bit diverse.

Descrittore

Descriptor specifica forzatamente il descrittore dell'interfaccia di un'interfaccia.

package android.foo;

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

Il descrittore di questa interfaccia è android.bar.IWorld. Se manca l'annotazione Descriptor, il descrittore sarà android.foo.IHello.

Questa operazione è utile per rinominare un'interfaccia già pubblicata. Se imposti il descrittore dell'interfaccia rinominata come quello dell'interfaccia precedente alla ridenominazione, le due interfacce possono comunicare tra loro.

@hide nei commenti

Il compilatore AIDL riconosce @hide nei commenti e lo passa all'output Java per il recupero da parte di Metalava. Questo commento garantisce che il sistema di compilazione Android sappia che le API AIDL non sono API SDK.

@deprecated nei commenti

Il compilatore AIDL riconosce @deprecated nei commenti come un tag per identificare un'entità AIDL che non deve più essere utilizzata.

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

Ogni backend contrassegna le entità ritirate con un attributo o un'annotazione specifica del backend in modo che il codice client riceva un avviso se fa riferimento alle entità ritirate. Ad esempio, l'annotazione @Deprecated e il tag @deprecated vengono associati al codice Java generato.