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 Javy:

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

W tym przypadku AnnotationName to nazwa adnotacji, a AidlEntity to jednostka 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 powyżej. 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ą bardzo podobnie. Użytkownicy nie mogą definiować niestandardowych adnotacji AIDL. Wszystkie adnotacje są wstępnie zdefiniowane. Niektóre adnotacje wpływają tylko na określony backend i nie mają żadnego działania w innych backendach. Mają one różne ograniczenia dotyczące tego, do czego można je dołączyć.

Oto lista wstępnie zdefiniowanych adnotacji AIDL:

Adnotacje Dodano w wersji Androida
nullable 7
utf8InCpp 7
VintfStability 11
UnsupportedAppUsage 10
Hide 11
Backing 11
NdkOnlyStableParcelable 14
JavaOnlyStableParcelable 11
JavaDerive 12
JavaPassthrough 12
FixedSize 12
Descriptor 12

dopuszczająca wartość null

nullable oznacza, że wartość adnotowanego elementu może nie być podana.

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ł błąd.

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

W przypadku systemu zaplecza Java ta adnotacja nie ma żadnego efektu. Dzieje się tak, ponieważ w języku Java wszystkie typy inne niż proste 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 NDK @nullable T zawsze odpowiada std::optional<T>.

W backendzie Rusta znak @nullable T zawsze jest mapowany na znak 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 ono mapowane na referencję przydzieloną na stercie std::unique_ptr<T> w backendach CPP/NDK. W backendzie Java funkcja @nullable(heap=true) nie wykonuje żadnych działań.

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 jest mapowane na std::string, a String bez adnotacji jest mapowane na android::String16, gdzie używane jest kodowanie UTF16.

Pamiętaj, że obecność adnotacji utf8InCpp nie zmienia sposobu przesyłania ciągów znaków. Ciągi znaków są zawsze przesyłane jako UTF16. Przed przesłaniem utf8InCpp adnotowany ciąg znaków jest konwertowany na format UTF16. Gdy ciąg znaków zostanie odebrany, jest konwertowany z UTF16 na UTF8, jeśli został oznaczony jako utf8InCpp.

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 gdy jest ustawiona, 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 elementy 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 oznaczone adnotacją VintfStability można tworzyć tylko za pomocą typu modułu Soong 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 do wygenerowanej klasy Java tylko 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.

Podkład

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 był 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 pozwala zastąpić sprawdzanie, gdy odwoływany obiekt Parcelable jest już 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 Java.

@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. Na przykład: Data{number: 42, str: foo}

JavaDefault

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.

Adnotacje w AIDL

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

stać się

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

w wygenerowanym kodzie Java.

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śnienie tych cech znajdziesz na stronie https://doc.rust-lang.org.

FixedSize

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

Nie daje to żadnej gwarancji w przypadku różnych szerokości bitowych i nie należy na nim polegać w przypadku komunikacji z użyciem różnych szerokości bitowych.

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 zapewnia, że system kompilacji Androida wie, że interfejsy API AIDL nie są interfejsami API pakietu SDK.

@deprecated w komentarzach

Kompilator AIDL rozpoznaje znak @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.