Аннотации в AIDL

AIDL поддерживает аннотации, которые предоставляют компилятору AIDL дополнительную информацию об аннотированном элементе, что также влияет на сгенерированный код-заглушку.

Синтаксис похож на синтаксис Java:

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

Здесь AnnotationName — имя аннотации, а AidlEntity — сущность AIDL, например interface Foo , void method() или int arg . Аннотация прикрепляется к следующей за ней сущности.

Некоторые аннотации могут содержать аргументы, заключенные в скобки, как показано выше. Аннотации без аргументов не нуждаются в скобках. Например:

@AnnotationName AidlEntity

Эти аннотации отличаются от аннотаций Java, хотя выглядят очень похоже. Пользователи не могут создавать собственные аннотации AIDL; все аннотации предопределены. Некоторые аннотации влияют только на определённый бэкенд и не выполняют никаких операций в других бэкендах. У них есть другие ограничения на места их присоединения.

Вот список предопределенных аннотаций AIDL:

Аннотации Добавлено в версию 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 объявляет, что значение аннотированной сущности не может быть предоставлено.

Эту аннотацию можно прикрепить только к возвращаемым типам метода, параметрам метода и пакетируемым полям.

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

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

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

Аннотации нельзя прикреплять к примитивным типам. Ниже приведен пример ошибки.

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

Эта аннотация не является операцией для бэкенда Java. Это связано с тем, что в Java все непримитивные типы передаются по ссылке, которая может быть равна null .

В бэкэнде CPP @nullable T сопоставляется с std::unique_ptr<T> в Android 11 и ниже и с std::optional<T> в Android 12 и выше.

В бэкэнде NDK @nullable T всегда сопоставляется с std::optional<T> .

В бэкэнде Rust @nullable T всегда сопоставляется с Option<T> .

Для типа списка L , такого как T[] или List<T> , @nullable L сопоставляется с std::optional<std::vector<std::optional<T>>> (или std::unique_ptr<std::vector<std::unique_ptr<T>>> в случае бэкэнда CPP для Android 11 или ниже).

Из этого сопоставления есть исключение. Когда T — это IBinder или интерфейс AIDL, @nullable является no-op для всех бэкендов, кроме Rust. Другими словами, как @nullable IBinder так и IBinder в равной степени отображаются в android::sp<IBinder> , который уже допускает значение NULL, поскольку является строгим указателем (чтения CPP по-прежнему обеспечивают возможность значения NULL, но тип по-прежнему android::sp<IBinder> ). В Rust эти типы допускают nullable только при наличии аннотации @nullable . При наличии аннотации они отображаются в Option<T> .

Начиная с Android 13, @nullable(heap=true) можно использовать для полей, подлежащих разделению, для моделирования рекурсивных типов. Аннотацию @nullable(heap=true) нельзя использовать с параметрами методов или возвращаемыми типами. При использовании этой аннотации поле сопоставляется с выделенной в куче ссылкой std::unique_ptr<T> в бэкендах CPP/NDK. В бэкенде Java аннотация @nullable(heap=true) не является оператором.

utf8InCpp

utf8InCpp объявляет, что String представлена ​​в формате UTF8 для бэкенда CPP. Как следует из названия, эта аннотация не является операцией для других бэкендов. В частности, String всегда представлена ​​в формате UTF16 в бэкенде Java и в формате UTF8 в бэкенде NDK.

Эту аннотацию можно прикрепить в любом месте, где может использоваться тип String , включая возвращаемые значения, параметры, объявления констант и пакетируемые поля.

Для бэкэнда CPP @utf8InCpp String в AIDL сопоставляется с std::string , тогда как String без аннотации сопоставляется с android::String16 , где используется UTF16.

Обратите внимание, что наличие аннотации utf8InCpp не влияет на способ передачи строк по сети. Строки всегда передаются по сети в кодировке UTF16. Строка с аннотацией utf8InCpp преобразуется в UTF16 перед передачей. При получении строка преобразуется из UTF16 в UTF8, если она была аннотирована как utf8InCpp .

VintfStability

VintfStability заявляет, что определяемый пользователем тип (интерфейс, парцелляционный тип и перечисление) может использоваться как в системных, так и в доменах поставщиков. Подробнее о взаимодействии систем и поставщиков см. в разделе AIDL для HAL .

Аннотация не изменяет сигнатуру типа, но при ее установке экземпляр типа помечается как стабильный, чтобы его можно было передавать через процессы поставщика и системы.

Аннотацию можно прикрепить только к объявлениям типов, определяемых пользователем, как показано здесь:

@VintfStability
interface IFoo {
    ....
}

@VintfStability
parcelable Data {
    ....
}

@VintfStability
enum Type {
    ....
}

Если тип аннотирован VintfStability , любой другой тип, на который он ссылается, также должен быть аннотирован. В следующем примере Data и IBar должны быть аннотированы 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 {...}

Кроме того, файлы AIDL, определяющие типы, аннотированные VintfStability могут быть созданы только с использованием типа модуля aidl_interface Soong, при этом свойство stability установлено на "vintf" .

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

Неподдерживаемое использование приложения

Аннотация UnsupportedAppUsage означает, что аннотированный тип AIDL является частью интерфейса, не входящего в SDK, который был доступен для устаревших приложений. Подробнее о скрытых API см. в разделе «Ограничения для интерфейсов, не входящих в SDK» .

Аннотация UnsupportedAppUsage не влияет на поведение сгенерированного кода. Она лишь добавляет к сгенерированному классу Java аннотацию Java с тем же именем.

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

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

Это не является операцией для бэкэндов, не использующих Java.

Поддержка

Аннотация Backing указывает тип хранения перечисления AIDL.

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

В бэкэнде CPP это создает перечисление класса C++ типа int32_t .

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

Если аннотация опущена, предполагается, что typebyte , что соответствует int8_t для бэкэнда CPP.

Аргумент type может быть установлен только на следующие целочисленные типы:

  • byte (8 бит)
  • int (32-битный)
  • long (64-битный)

NdkOnlyStableParcelable

NdkOnlyStableParcelable помечает объявление (а не определение) с возможностью парцелляции как стабильное, чтобы на него можно было ссылаться из других стабильных типов AIDL. Это похоже на JavaOnlyStableParcelable , но NdkOnlyStableParcelable помечает объявление с возможностью парцелляции как стабильное для бэкенда NDK, а не для Java.

Чтобы использовать этот пакет:

  • Необходимо указать ndk_header .
  • Необходима библиотека NDK, определяющая пакет, и библиотека должна быть скомпилирована в библиотеку. Например, в системе сборки ядра в модуле cc_* используйте static_libs или shared_libs . Для aidl_interface добавьте библиотеку в additional_shared_libraries в Android.bp .

JavaOnlyStableParcelable

JavaOnlyStableParcelable помечает объявление (не определение) parcelable как стабильное, чтобы на него можно было ссылаться из других стабильных типов AIDL.

Стабильный AIDL требует, чтобы все пользовательские типы были стабильными. Для Parcelables стабильность требует, чтобы их поля были явно описаны в исходном файле 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
}

Если парцелляемый объект неструктурирован (или просто объявлен), то на него нельзя ссылаться.

parcelable Data; // Data is NOT a structured parcelable

parcelable AnotherData {
    Data d; // Error
}

JavaOnlyStableParcelable позволяет вам переопределить проверку, если parcelable, на который вы ссылаетесь, уже безопасно доступен как часть Android SDK.

@JavaOnlyStableParcelable
parcelable Data;

parcelable AnotherData {
    Data d; // OK
}

JavaDerive

JavaDerive автоматически генерирует методы для парцеллируемых типов в бэкэнде Java.

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

Для управления генерируемыми данными аннотации требуются дополнительные параметры. Поддерживаются следующие параметры:

  • equals=true генерирует методы equals и hashCode .
  • toString=true генерирует метод toString , который выводит имя типа и полей. Например: Data{number: 42, str: foo}

JavaDefault

JavaDefault , добавленный в Android 13, управляет генерацией поддержки управления версиями реализации по умолчанию (для setDefaultImpl ). Эта поддержка больше не генерируется по умолчанию для экономии места.

JavaPassthrough

JavaPassthrough позволяет аннотировать сгенерированный Java API с помощью произвольной аннотации Java.

Следующие аннотации в AIDL

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

становиться

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

в сгенерированном коде Java.

Значение параметра annotation выдаётся напрямую. Компилятор AIDL не анализирует значение параметра. Если есть какая-либо синтаксическая ошибка на уровне Java, она будет обнаружена не компилятором AIDL, а компилятором Java.

Эту аннотацию можно прикрепить к любой сущности AIDL. Для бэкендов, отличных от Java, эта аннотация не является операцией.

RustDerive

RustDerive автоматически реализует черты для сгенерированных типов Rust.

Для управления генерируемыми данными аннотации требуются дополнительные параметры. Поддерживаются следующие параметры:

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

Объяснения этих характеристик см. на сайте https://doc.rust-lang.org.

Фиксированный размер

FixedSize помечает структурированный объект Parcelable как имеющий фиксированный размер. После этого в него нельзя будет добавлять новые поля. Все поля объекта Parcelable также должны иметь фиксированный размер, включая примитивные типы, перечисления, массивы фиксированного размера и другие объекты Parcelable, помеченные как FixedSize .

Это не дает никаких гарантий для разных разрядностей и не должно применяться для связи со смешанной разрядностью.

Дескриптор

Descriptor принудительно задает дескриптор интерфейса.

package android.foo;

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

Дескриптор этого интерфейса — android.bar.IWorld . Если аннотация Descriptor отсутствует, дескриптор будет android.foo.IHello .

Это полезно для переименования уже опубликованного интерфейса. Совпадение дескриптора переименованного интерфейса с дескриптором интерфейса до переименования позволяет двум интерфейсам взаимодействовать друг с другом.

@скрыть в комментариях

Компилятор AIDL распознаёт @hide в комментариях и передаёт его в вывод Java для последующего использования металавой. Этот комментарий гарантирует, что система сборки Android понимает, что API AIDL не являются API SDK.

@deprecated в комментариях

Компилятор AIDL распознает @deprecated в комментариях как тег, идентифицирующий сущность AIDL, которую больше не следует использовать.

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

Каждый бэкенд помечает устаревшие сущности аннотацией или атрибутом, специфичным для бэкенда, чтобы клиентский код получал предупреждение при обращении к устаревшим сущностям. Например, аннотация @Deprecated и тег @deprecated добавляются к сгенерированному коду Java.