Аннотации в AIDL

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

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

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

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

В некоторых аннотациях в скобках могут быть указаны аргументы, как в предыдущем примере. Если у аннотации нет аргумента, скобки не нужны. Пример:

@AnnotationName AidlEntity

Эти аннотации не совпадают с аннотациями Java, хотя и похожи на них. Все аннотации заранее определены и имеют ограничения на то, где их можно прикреплять. Некоторые аннотации влияют только на определенный серверный код и не действуют на других.

Ниже приведен список стандартных аннотаций AIDL.

АннотацииДобавлено в версии Android
nullable7
utf8InCpp7
VintfStability11
UnsupportedAppUsage10
Hide11
Backing11
NdkOnlyStableParcelable14
JavaOnlyStableParcelable11
JavaDerive12
JavaPassthrough12
FixedSize12
Descriptor12

nullable

nullable указывает, что значение аннотированного объекта может быть нулевым.

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

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 не выполняет никаких действий для серверных частей Java, CPP и NDK. Например, в бэкенде CPP оба типа @nullable IBinder и IBinder одинаково сопоставляются с типом android::sp<IBinder>, который уже может иметь допустимость значения NULL, поскольку является указателем (при чтении в CPP и NDK по-прежнему требуется допустимость значения NULL, но тип остается android::sp<IBinder> или ndk::SpAIBinder).

В бэкенде Rust типы, которые не реализуют Default (IBinder, интерфейсы AIDL, ParcelFileDescriptor и неструктурированные объекты Parcelable), сопоставляются с Option<T> в зависимости от контекста:

  • Типы возвращаемых значений методов, параметры in и параметры inout. Эти типы допускают значение NULL, только если они аннотированы с помощью @nullable, сопоставляемого с Option<T> (или Option<&T> для параметров in и &mut Option<T> для параметров inout). Без @nullable они напрямую сопоставляются с T (или &T и &mut T).
  • Поля Parcelable и параметры out. Поскольку сгенерированные объекты Parcelable в Rust наследуют параметры Default и out, которые инициализируются по умолчанию, эти типы всегда сопоставляются с Option<T> (а массивы фиксированного размера в этих контекстах сопоставляются с [Option<T>; N], тогда как динамические массивы в параметрах out сопоставляются с Vec<Option<T>>), даже без @nullable. Если аннотация @nullable отсутствует, проверка на возможность иметь значение null выполняется во время выполнения при передаче данных (если значение равно None, возвращается StatusCode::UNEXPECTED_NULL).

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

utf8InCpp

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

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

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

VintfStability

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, можно создавать только с помощью модуля Soong типа aidl_interface, для которого свойство stability имеет значение vintf:

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

UnsupportedAppUsage

Аннотация UnsupportedAppUsage указывает, что аннотированный тип AIDL является частью интерфейса, не относящегося к SDK, который был доступен для устаревших приложений. Подробнее об ограничениях на интерфейсы, не относящиеся к 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,
}

Если аннотация не указана, то для type используется значение byte, которое сопоставляется с int8_t для серверной части CPP.

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

  • byte (8-битная шина)
  • int (32-разрядная)
  • long (64-разрядная версия)

NdkOnlyStableParcelable

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

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

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

JavaOnlyStableParcelable

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

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

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 автоматически создает методы для типов Parcelable в серверной части 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)")

в сгенерированном коде Java будет выглядеть так:

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

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

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

RustDerive

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

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

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

Описание этих черт можно найти в документации по языку Rust.

FixedSize

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

Объекты FixedSize имеют стабильные размеры и выравнивание в серверной части ndk.

Тип Размер (байты) Выравнивание (байты)
boolean 1 1
byte 1 1
char 2 2
int 4 4
long 8 8
float 4 4
double 8 8
parcelable Общий размер всех полей Самое большое выравнивание всех полей
union Самый большой размер всех полей Выравнивание по самому большому полю
enum Размер типа поддержки Соответствие типа резервного копирования
T[N] (массив фиксированного размера) Размер: T * N Соответствие: T
String, IBinder, FileDescriptor, ParcelFileDescriptor N/A N/A

Дескриптор

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

package android.foo;

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

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

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

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

Компилятор 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.