Adnotacje w AIDL

AIDL obsługuje adnotacje, które dają kompilatorowi AIDL dodatkowe informacje o elemencie z adnotacjami, co ma również wpływ na wygenerowany kod pośredniczący.

Składnia jest podobna do tej w Javie:

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

Tutaj AnnotationName jest nazwą adnotacji, a AidlEntity jest jednostką AIDL, taką jak interface Foo , void method() lub int arg . Adnotacja jest dołączona do encji, która po niej następuje.

Niektóre adnotacje mogą mieć argumenty umieszczone w nawiasach, jak pokazano powyżej. Adnotacje, które nie zawierają argumentu, nie wymagają nawiasów. Na przykład:

@AnnotationName AidlEntity

Te adnotacje nie są takie same, jak adnotacje w Javie, 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 działają w innych backendach. Mają różne ograniczenia, do których można je podłączyć.

Poniżej znajduje się lista predefiniowanych adnotacji AIDL:

Adnotacje Dodano w wersji na 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ący wartość null

nullable deklaruje, że nie można podać wartości jednostki z adnotacją.

Adnotację tę można dołączyć wyłącznie do typów zwracanych metod, parametrów metod i pól, które można dzielić.

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 pierwotnych. Poniżej znajduje się błąd.

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

Ta adnotacja nie ma zastosowania w przypadku zaplecza Java. Dzieje się tak, ponieważ w Javie wszystkie typy inne niż pierwotne są przekazywane przez referencję, która może mieć null .

W zapleczu CPP @nullable T mapuje na std::unique_ptr<T> w systemie Android 11 lub starszym oraz na std::optional<T> w systemie Android 12 lub nowszym.

W zapleczu NDK @nullable T zawsze mapuje na std::optional<T> .

W przypadku typu listowego L , takiego jak T[] lub List<T> , @nullable L mapuje na std::optional<std::vector<std::optional<T>>> (lub std::unique_ptr<std::vector<std::unique_ptr<T>>> w przypadku backendu CPP dla Androida 11 lub starszego).

Istnieje wyjątek od tego mapowania. Gdy T jest interfejsem IBinder lub AIDL, @nullable nie działa. Innymi słowy, zarówno @nullable IBinder jak i IBinder w równym stopniu odwzorowują na android::sp<IBinder> , który już dopuszcza wartość null, ponieważ jest silnym wskaźnikiem (odczyty CPP nadal wymuszają dopuszczalność wartości null, ale typ nadal to android::sp<IBinder> ).

Począwszy od Androida 13, @nullable(heap=true) może być używane w polach, które można dzielić w celu modelowania typów rekurencyjnych. Nie można używać @nullable(heap=true) z parametrami metod ani typami zwracanymi. Po dodaniu adnotacji pole jest mapowane na odwołanie przydzielone do sterty std::unique_ptr<T> w backendach CPP/NDK. @nullable(heap=true) nie działa w zapleczu Java.

utf8InCpp

utf8InCpp deklaruje, że String jest reprezentowany w formacie UTF8 dla zaplecza CPP. Jak sama nazwa wskazuje, adnotacja nie ma zastosowania w przypadku innych backendów. W szczególności String ma zawsze wartość UTF16 w zapleczu Java i UTF8 w zapleczu NDK.

Tę adnotację można dołączyć wszędzie tam, gdzie można zastosować typ String , włączając w to wartości zwracane, parametry, deklaracje stałych i pola, które można dzielić.

W przypadku backendu CPP @utf8InCpp String w AIDL odwzorowuje na std::string , podczas gdy String bez adnotacji odwzorowuje na android::String16 , gdzie używany jest UTF16.

Należy zauważyć, że istnienie adnotacji utf8InCpp nie zmienia sposobu przesyłania łańcuchów przez kabel. Ciągi znaków są zawsze przesyłane przewodowo w formacie UTF16. Ciąg z adnotacjami utf8InCpp jest konwertowany na UTF16 przed przesłaniem. Po odebraniu ciągu znaków jest on konwertowany z UTF16 na UTF8, jeśli został oznaczony jako utf8InCpp .

VintfStabilność

VintfStability deklaruje, że typ zdefiniowany przez użytkownika (interfejs, pakietowanie i wyliczenie) może być używany w całym systemie i domenach dostawców. Zobacz AIDL dla HAL, aby uzyskać więcej informacji na temat współdziałania systemów z dostawcami.

Adnotacja nie zmienia sygnatury typu, ale gdy jest ustawiona, instancja typu jest oznaczana jako stabilna, dzięki czemu może przechodzić przez procesy dostawcy i systemu.

Adnotację można dołączyć wyłącznie do deklaracji typu zdefiniowanego przez użytkownika, jak pokazano poniżej:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

Gdy typ jest opatrzony adnotacją VintfStability , każdy inny typ, do którego odwołuje się typ, powinien również zostać oznaczony jako taki. W poniższym przykładzie Data i IBar powinny być opatrzone 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 {...}

Ponadto pliki AIDL definiujące typy z adnotacją VintfStability można budować wyłącznie przy użyciu typu modułu aidl_interface Soong, z właściwością stability ustawioną na "vintf" .

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

Nieobsługiwane użycie aplikacji

Adnotacja UnsupportedAppUsage oznacza, że ​​typ AIDL z adnotacją jest częścią interfejsu innego niż SDK, który był dostępny dla starszych aplikacji. Aby uzyskać więcej informacji na temat ukrytych interfejsów API, zobacz Ograniczenia dotyczące interfejsów innych niż SDK .

Adnotacja UnsupportedAppUsage nie ma wpływu na zachowanie wygenerowanego kodu. Adnotacja zawiera wyłącznie adnotację wygenerowanej klasy Java z adnotacją Java o tej samej nazwie.

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

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

Nie jest to możliwe w przypadku backendów innych niż Java.

Poparcie

Adnotacja Backing określa typ przechowywania typu wyliczeniowego AIDL.

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

W zapleczu CPP powyższe emituje 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 jest byte , który jest mapowany na int8_t dla zaplecza CPP.

Argument type można ustawić tylko na następujące typy całkowite:

  • byte (szerokość 8 bitów)
  • int (szerokość 32-bitowa)
  • long (szerokość 64-bitowa)

NdkOnlyStableParcelable

NdkOnlyStableParcelable oznacza deklarację umożliwiającą pakowanie (nie definicję) jako stabilną, dzięki czemu można się do niej odwoływać z innych stabilnych typów AIDL. Działa to podobnie do JavaOnlyStableParcelable , ale NdkOnlyStableParcelable oznacza deklarację umożliwiającą pakowanie jako stabilną dla zaplecza NDK, a nie dla Java.

Aby użyć tej opcji parcelowania: * Musisz określić ndk_header . * Musisz mieć bibliotekę NDK określającą możliwość podzielenia na paczki, a biblioteka musi zostać wkompilowana do biblioteki. Na przykład w podstawowym systemie kompilacji modułu cc_* użyj static_libs lub shared_libs . W przypadku aidl_interface dodaj bibliotekę w obszarze additional_shared_libraries w Android.bp .

JavaOnlyStableParcelable

JavaOnlyStableParcelable oznacza deklarację umożliwiającą pakowanie (nie definicję) 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 pakietów, aby były stabilne, ich pola są 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 element nadający się do parcelowania był nieustrukturyzowany (lub właśnie 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 pominięcie sprawdzenia, gdy pakiet, do którego się odwołujesz, jest już bezpiecznie dostępny w ramach zestawu SDK systemu Android.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

Wyprowadzenie Java

JavaDerive automatycznie generuje metody dla typów parcelowanych w backendie Java.

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

Adnotacja wymaga dodatkowych parametrów, aby kontrolować, co ma zostać wygenerowane. Obsługiwane parametry to:

  • equals=true generuje metody equals i hashCode .
  • toString=true generuje metodę toString , która wypisuje nazwę typu i pól. Na przykład: Data{number: 42, str: foo}

Domyślna Java

JavaDefault dodany w systemie Android 13 kontroluje, czy generowana jest domyślna obsługa wersjonowania implementacji (dla setDefaultImpl ). Ta podpora nie jest już generowana domyślnie w celu zaoszczędzenia miejsca.

Przejście Java

JavaPassthrough umożliwia opatrzenie wygenerowanego interfejsu API Java dowolną adnotacją Java.

Poniższe 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 jakikolwiek błąd składni na poziomie Java, nie zostanie on wykryty przez kompilator AIDL, ale przez kompilator Java.

Adnotację tę można dołączyć do dowolnej jednostki AIDL. Ta adnotacja nie jest dostępna w przypadku backendów innych niż Java.

Stały rozmiar

FixedSize oznacza ustrukturyzowaną paczkę o stałym rozmiarze. Po zaznaczeniu do przesyłki nie będzie można dodawać nowych pól. Wszystkie pola elementu parcelable muszą być również typami o stałym rozmiarze, w tym typami pierwotnymi, wyliczeniami, tablicami o stałym rozmiarze i innymi elementami parcelable oznaczonymi za pomocą FixedSize .

Nie zapewnia to żadnej gwarancji w przypadku różnych bitowości i nie należy na tym polegać w przypadku komunikacji o mieszanej bitowości.

Deskryptor

Descriptor wymusza określenie deskryptora interfejsu.

package android.foo;

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

Deskryptor powyższego interfejsu to android.bar.IWorld . Jeśli brakuje adnotacji Descriptor , deskryptor będzie miał android.foo.IHello .

Jest to przydatne do zmiany nazwy już opublikowanego interfejsu. Ujednolicenie deskryptora interfejsu o zmienionej nazwie z deskryptorem interfejsu przed zmianą nazwy umożliwia komunikację między dwoma interfejsami.

@ukryj w komentarzach

Kompilator AIDL rozpoznaje @hide w komentarzach i przekazuje je do wyjścia Java, aby metalava mogła je przechwycić. Ten komentarz zapewnia, że ​​system kompilacji systemu Android wie, że interfejsy API AIDL nie są interfejsami API zestawu SDK.

@przestarzałe w komentarzach

Kompilator AIDL rozpoznaje @deprecated w komentarzach jako znacznik 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 przestarzałe jednostki adnotacją/atrybutem specyficznym dla backendu, dzięki czemu kod klienta jest ostrzegany, jeśli odwołuje się do przestarzałych jednostek. Na przykład adnotacja @Deprecated i znacznik @deprecated są dołączone do kodu wygenerowanego w języku Java.