Adnotacje w AIDL

AIDL obsługuje adnotacje, które dostarczają kompilatorowi AIDL dodatkowych informacji o elementach z adnotacjami. Ma to też wpływ na wygenerowany kod stub.

Składnia jest podobna do składni języka Java:

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

W tym przypadku AnnotationName to nazwa adnotacji, a AidlEntity to element AIDL, np. interface Foo, void method() lub int arg. Adnotacja jest dołączona do podmiotu, który po niej następuje.

Niektóre adnotacje mogą mieć argumenty ustawione w nawiasach, jak pokazano w poprzednim przykładzie. Adnotacje, które nie mają argumentu, nie wymagają nawiasów. Na przykład:

@AnnotationName AidlEntity

Te adnotacje nie są takie same jak adnotacje Java, chociaż wyglądają podobnie. Wszystkie adnotacje są predefiniowane i mają ograniczenia dotyczące miejsc, w których można je dołączyć. Niektóre adnotacje mają wpływ tylko na określony backend, a w innych backendach nie mają żadnego działania.

Oto lista wstępnie zdefiniowanych adnotacji AIDL:

AdnotacjeDodano w wersji Androida
nullable7
utf8InCpp7
VintfStability11
UnsupportedAppUsage10
Hide11
Backing11
NdkOnlyStableParcelable14
JavaOnlyStableParcelable11
JavaDerive12
JavaPassthrough12
FixedSize12
Descriptor12

dopuszczająca wartość null

nullable deklaruje, że wartość adnotowanego elementu może być pusta.

Tę adnotację można dołączyć tylko do typów zwracanych przez metody, parametrów metod i pól z możliwością przekazywania:

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

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

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

Adnotacji nie można dołączać do typów prostych. Wystąpił ten błąd:

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

W przypadku systemu zaplecza Java ta adnotacja nie ma żadnego efektu. W języku Java wszystkie typy niepierwotne są przekazywane przez odwołanie, co może być null.

W backendzie CPP @nullable T jest mapowane na std::unique_ptr<T> na Androidzie 11 lub starszym oraz na std::optional<T> na Androidzie 12 lub nowszym.

W backendzie NDK @nullable T jest mapowany na std::optional<T>.

W backendzie Rust @nullable T jest mapowane na Option<T>.

W przypadku typu listy L, np. T[] lub List<T>, @nullable L jest mapowany na std::optional<std::vector<std::optional<T>>> (lub std::unique_ptr<std::vector<std::unique_ptr<T>>> w przypadku backendu CPP na Androidzie 11 lub starszym).

Od tego mapowania jest wyjątek. Gdy T jest IBinder lub interfejsem AIDL, @nullable nie ma wpływu na wszystkie backendy z wyjątkiem Rusta. Innymi słowy, zarówno @nullable IBinder, jak i IBinder są mapowane na android::sp<IBinder>, które jest już dopuszczalne, ponieważ jest silnym wskaźnikiem (odczyty w C++ nadal wymuszają dopuszczalność, ale typ to nadal android::sp<IBinder>). W Rust te typy to nullable tylko wtedy, gdy są oznaczone adnotacją @nullable. Jeśli są oznaczone, są przypisane do Option<T>.

Od Androida 13 można używać adnotacji @nullable(heap=true) w polach z możliwością przekazywania w pakietach do modelowania typów rekurencyjnych. @nullable(heap=true) nie można używać z parametrami metod ani typami zwracanymi. Jeśli pole jest oznaczone tym adnotacją, jest mapowane na referencję przydzieloną na stercie std::unique_ptr<T> w backendach CPP i NDK. @nullable(heap=true) nie ma wpływu na działanie backendu Java.

utf8InCpp

utf8InCpp deklaruje, że String jest reprezentowany w formacie UTF8 na potrzeby backendu CPP. Jak sama nazwa wskazuje, ta adnotacja nie ma wpływu na inne systemy backendowe. W szczególności String jest zawsze kodowany w formacie UTF16 w przypadku backendu Java i UTF8 w przypadku backendu NDK.

Tę adnotację można dołączyć w dowolnym miejscu, w którym można użyć typu String, w tym w przypadku wartości zwracanych, parametrów, deklaracji stałych i pól z możliwością przekazywania.

W przypadku backendu CPP @utf8InCpp String w AIDL odpowiada std::string, gdzie String bez adnotacji odpowiada android::String16, w którym używane jest kodowanie UTF16.

VintfStability

VintfStability deklaruje, że zdefiniowany przez użytkownika typ (interfejs, obiekt do serializacji, i wyliczenie) może być używany w domenach systemowych i dostawcy. Więcej informacji o współdziałaniu systemu z dostawcą znajdziesz w artykule AIDL dla HAL-i.

Adnotacja nie zmienia sygnatury typu, ale po jej ustawieniu instancja typu jest oznaczana jako stabilna, dzięki czemu może być przekazywana między procesami dostawcy i systemu.

Adnotację można dołączyć tylko do deklaracji typów zdefiniowanych przez użytkownika, jak pokazano tutaj:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

Jeśli typ jest oznaczony adnotacją VintfStability, każdy inny typ, do którego odwołuje się ten typ, powinien być również oznaczony w ten sposób. W poniższym przykładzie oba wyrażenia DataIBar powinny być oznaczone adnotacją 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 {...}

Dodatkowo pliki AIDL definiujące typy z adnotacjami VintfStability można tworzyć tylko za pomocą modułu Soong typu aidl_interface z właściwością stability ustawioną na vintf:

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

UnsupportedAppUsage

Adnotacja UnsupportedAppUsage oznacza, że typ AIDL z adnotacją jest częścią interfejsu innego niż SDK, który był dostępny dla starszych aplikacji. Więcej informacji o ukrytych interfejsach API znajdziesz w sekcji Ograniczenia dotyczące interfejsów innych niż SDK.

Adnotacja UnsupportedAppUsage nie wpływa na działanie wygenerowanego kodu. Adnotacja dodaje adnotację tylko do wygenerowanej klasy Java z adnotacją Java o tej samej nazwie:

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

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

W przypadku backendów innych niż Java ta operacja nie ma żadnego efektu.

Adnotacja dotycząca podkładu

Adnotacja Backing określa typ pamięci masowej typu wyliczeniowego AIDL:

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

W backendzie CPP generuje to klasę wyliczeniową C++ typu int32_t:

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

Jeśli adnotacja zostanie pominięta, przyjmuje się, że type ma wartość byte, która jest mapowana na int8_t w przypadku backendu CPP.

Argument type można ustawić tylko na te typy całkowite:

  • byte (8-bitowa)
  • int (32-bitowa)
  • long (64-bitowa)

NdkOnlyStableParcelable

NdkOnlyStableParcelable oznacza deklarację (nie definicję) typu parcelable jako stabilną, dzięki czemu można się do niej odwoływać z innych stabilnych typów AIDL. Działa to podobnie jak JavaOnlyStableParcelable, ale NdkOnlyStableParcelable oznacza deklarację z możliwością przekazywania jako stabilną w przypadku backendu NDK, a nie Javy.

Aby użyć tego obiektu Parcelable:

  • Musisz podać wartość ndk_header.
  • Musisz mieć bibliotekę NDK określającą obiekt Parcelable, a biblioteka musi być skompilowana w bibliotece. Na przykład w podstawowym systemie kompilacji w module cc_* użyj static_libs lub shared_libs. W przypadku elementu aidl_interface dodaj bibliotekę w sekcji additional_shared_libraries w elemencie Android.bp.

JavaOnlyStableParcelable

JavaOnlyStableParcelable oznacza deklarację (nie definicję) typu parcelable jako stabilną, dzięki czemu można się do niej odwoływać z innych stabilnych typów AIDL.

Stabilny AIDL wymaga, aby wszystkie typy zdefiniowane przez użytkownika były stabilne. W przypadku obiektów Parcelable stabilność wymaga, aby ich pola były wyraźnie opisane w pliku źródłowym 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
}

Jeśli obiekt Parcelable jest nieustrukturyzowany (lub tylko zadeklarowany), nie można się do niego odwołać:

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable umożliwia zastąpienie sprawdzania, gdy odwoływany obiekt Parcelable jest bezpiecznie dostępny w ramach pakietu SDK Androida:

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive automatycznie generuje metody dla typów możliwych do przekazania w pakiecie w backendzie Javy:

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

Adnotacja wymaga dodatkowych parametrów, aby określić, co ma być generowane. Obsługiwane parametry:

  • equals=true generuje metody equals i hashCode.
  • toString=true generuje metodę toString, która wyświetla nazwę typu i pola, np. Data{number: 42, str: foo}.

JavaDefault (wycofane)

JavaDefault, dodany w Androidzie 13, określa, czy ma być generowana obsługa wersji domyślnej implementacji (w przypadku setDefaultImpl). Aby zaoszczędzić miejsce, ta obsługa nie jest już generowana domyślnie.

JavaPassthrough

JavaPassthrough umożliwia dodanie do wygenerowanego interfejsu Java API dowolnej adnotacji Java.

Te adnotacje w AIDL:

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

w wygenerowanym kodzie Javy będzie wyglądać tak:

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

Wartość parametru annotation jest emitowana bezpośrednio. Kompilator AIDL nie sprawdza wartości parametru. Jeśli wystąpi błąd składni na poziomie języka Java, nie zostanie on wykryty przez kompilator AIDL, ale przez kompilator języka Java.

Tę adnotację można dołączyć do dowolnej jednostki AIDL. W przypadku backendów innych niż Java ta adnotacja nie ma żadnego efektu.

RustDerive

RustDerive automatycznie implementuje cechy dla wygenerowanych typów Rusta.

Adnotacja wymaga dodatkowych parametrów, aby określić, co ma być generowane. Obsługiwane parametry:

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

Wyjaśnienia tych cech znajdziesz w dokumentacji języka Rust.

FixedSize

FixedSize oznacza strukturalny obiekt Parcelable jako o stałym rozmiarze. Po oznaczeniu nie można dodawać nowych pól do obiektu Parcelable. Wszystkie pola obiektu z interfejsem Parcelable muszą być typami o stałym rozmiarze, w tym typami prostymi, wyliczeniowymi, tablicami o stałym rozmiarze i innymi obiektami z interfejsem Parcelable oznaczonymi symbolem FixedSize.

Deskryptor

Descriptor wymusza określenie deskryptora interfejsu:

package android.foo;

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

Deskryptor tego interfejsu to android.bar.IWorld. Jeśli brakuje adnotacji Descriptor, opis będzie wyglądać tak: android.foo.IHello.

Jest to przydatne w przypadku zmiany nazwy opublikowanego już interfejsu. Ustawienie deskryptora zmienionego interfejsu na taki sam jak deskryptor interfejsu przed zmianą nazwy umożliwia komunikację między tymi dwoma interfejsami.

@hide w komentarzach

Kompilator AIDL rozpoznaje symbol @hide w komentarzach i przekazuje go do danych wyjściowych w języku Java, aby mógł go odczytać program Metalava. Ten komentarz pomaga upewnić się, że system kompilacji Androida rozpoznaje, że interfejsy API AIDL nie są interfejsami API pakietu SDK.

@deprecated w komentarzach

Kompilator AIDL rozpoznaje @deprecated w komentarzach jako tag identyfikujący jednostkę AIDL, która nie powinna być już używana:

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

Każdy backend oznacza wycofane jednostki za pomocą adnotacji lub atrybutu specyficznego dla backendu, aby ostrzegać kod klienta, jeśli odwołuje się on do wycofanych jednostek. Na przykład adnotacja @Deprecated i tag @deprecated są dołączane do wygenerowanego kodu Java.