Аннотации в 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) можно использовать для полей parcelable для моделирования рекурсивных типов. @nullable(heap=true) нельзя использовать с параметрами метода или возвращаемыми типами. При аннотации с ним поле сопоставляется с выделенной в куче ссылкой std::unique_ptr<T> в бэкэндах CPP/NDK. @nullable(heap=true) является no-op в бэкэнде Java.

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 .

VintfСтабильность

VintfStability заявляет, что определяемый пользователем тип (интерфейс, parcelable и enum) может использоваться в доменах системы и поставщика. Подробнее о взаимодействии системы и поставщика см. в 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 является частью интерфейса non-SDK, который был доступен для устаревших приложений. Дополнительные сведения о скрытых API см. в разделе Ограничения на интерфейсы non-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 помечает parcelable объявление (не определение) как стабильное, чтобы на него можно было ссылаться из других стабильных типов AIDL. Это похоже на JavaOnlyStableParcelable , но NdkOnlyStableParcelable помечает parcelable объявление как стабильное для бэкэнда NDK, а не для Java.

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

  • Необходимо указать ndk_header .
  • У вас должна быть библиотека NDK, указывающая parcelable, и библиотека должна быть скомпилирована в библиотеку. Например, в основной системе сборки на модуле 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 также должны быть типами фиксированного размера, включая примитивные типы, перечисления, массивы фиксированного размера и другие parcelable, помеченные FixedSize .

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

Дескриптор

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

package android.foo;

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

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

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

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

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

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

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

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

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