Язык AIDL

Язык AIDL частично основан на языке Java. В файлах указываются условия интерфейса, а также различные типы данных и константы, используемые в этом интерфейсе.

Пакет

Каждый файл AIDL начинается с необязательного пакета, который соответствует названиям пакетов в различных серверных частях. Объявление пакета выглядит следующим образом:

    package my.package;

Как и в случае с Java, файлы AIDL должны находиться в папках, структура которых соответствует их пакету. Файлы с пакетом my.package должны находиться в папке my/package/.

Типы

В файлах AIDL есть много мест, где можно указать типы. Точный список типов, поддерживаемых в языке AIDL, приведен в разделе Типы AIDL для разных платформ.

Аннотации

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

Импорт

Чтобы использовать типы, определенные в других интерфейсах, сначала необходимо добавить зависимости в систему сборки. В модулях Soong cc_* и java_*, где файлы .aidl используются непосредственно в srcs при сборке платформы Android, можно добавлять каталоги с помощью поля aidl: { include_dirs: ... }. Инструкции по импорту с помощью aidl_interface приведены в этой статье.

Вот как выглядит импорт:

    import some.package.Foo;  // explicit import

При импорте типа в тот же пакет, пакет можно опустить. Однако отсутствие пакета может привести к ошибкам импорта, если типы указаны без пакета и помещены в глобальное пространство имен (как правило, все типы должны иметь пространство имен):

    import Foo;  // same as my.package.Foo

Как задать типы

В файлах AIDL обычно определяются типы, которые используются в качестве интерфейса.

Интерфейсы

Вот пример интерфейса AIDL:

    interface ITeleport {
        // Location defined elsewhere
        void teleport(Location baz, float speed);
        String getName();

        // ITeleportCallback defined elsewhere
        void methodWithCallback(ITeleportCallback callback);

        // ITeleportSession defined elsewhere
        ITeleportSession getASubInterface();
    }

Интерфейс определяет объект с набором методов. Методы могут быть асинхронными (oneway, oneway void doFoo()) или синхронными. Если интерфейс определен как oneway (oneway interface ITeleport {...}), то все его методы неявно являются oneway. Односторонние методы отправляются асинхронно и не могут возвращать результат. Односторонние методы из одного потока в один и тот же связыватель также выполняются последовательно (хотя и в разных потоках). Информацию о том, как настроить потоки, можно найти в разделе Управление потоками в серверных частях AIDL.

Binder позволяет передавать через интерфейсы Binder множество интерфейсов и объектов Binder. В интерфейсах AIDL часто используются обратные вызовы как часть вызовов методов, например ITeleportCallback в предыдущем примере. Объекты обратного вызова можно использовать повторно при вызовах одного и того же или разных методов. Другой распространенный вариант использования типов интерфейсов – возврат субинтерфейсов или объектов сеанса из методов, как в случае с ITeleportSession в предыдущем примере. Такое вложение позволяет инкапсулировать разные API на уровне API или на основе состояния среды выполнения. Например, сеанс может представлять собой владение определенным ресурсом. При передаче интерфейсов несколько раз или их возврате клиенту или серверу, от которого они были получены, всегда сохраняется равенство указателей базового объекта связывания.

У методов может быть несколько аргументов или не быть ни одного. Аргументы методов могут быть типа in, out или inout. Информацию о том, как это влияет на типы аргументов, можно найти в разделе Направленность серверных частей AIDL.

Parcelables

Инструкции по созданию объектов Parcelable для конкретных серверных частей приведены в статье Специальные объекты Parcelable для серверных частей AIDL.

Android 10 и более поздние версии поддерживают определения parcelable непосредственно в AIDL. Такой объект Parcelable называется структурированным. Дополнительную информацию о том, как структурированный и стабильный AIDL связаны в компиляторе AIDL и нашей системе сборки, см. в разделе Структурированный и стабильный AIDL .

Пример:

    package my.package;

    import my.package.Boo;

    parcelable Baz {
        @utf8InCpp String name = "baz";
        Boo boo;
    }

Профсоюзы

Android 12 и более поздние версии поддерживают объявления размеченных объединений. Пример:

    package my.package;

    import my.package.FooSettings;
    import my.package.BarSettings;

    union Settings {
        FooSettings fooSettings;
        BarSettings barSettings;
        @utf8InCpp String str;
        int number;
    }

Подробнее о бэкендах можно узнать в разделе Объединения AIDL.

Перечисления

Android 11 и более поздние версии поддерживают объявления enum. Пример:

    package my.package;

    enum Boo {
        A = 1 * 4,
        B = 3,
    }

Вложенные объявления типа

В Android 13 и более поздних версиях поддерживаются вложенные объявления типов. Пример:

    package my.package;

    import my.package.Baz;

    interface IFoo {
        void doFoo(Baz.Nested nested);  // defined in my/package/Baz.aidl
        void doBar(Bar bar);            // defined below

        parcelable Bar { ... }          // nested type definition
    }

Константы

Пользовательские интерфейсы AIDL, объекты Parcelable и объединения также могут содержать целочисленные и строковые константы, например:

    const @utf8InCpp String HAPPY = ":)";
    const String SAD = ":(";
    const byte BYTE_ME = 1;
    const int ANSWER = 6 * 7;

Постоянные выражения

Константы AIDL, размеры массивов и перечислители можно задавать с помощью постоянных выражений. В выражениях можно использовать скобки для вложенных операций. Значения выражений Constant можно использовать с целочисленными или вещественными значениями.

Литералы true и false представляют логические значения. Значения с символом ., но без суффикса, например 3.8, считаются двойными. Значения типа float имеют суффикс f, например 2.4f. Целое число с суффиксом l или L указывает на 64-битное длинное значение. В противном случае целочисленные значения получают наименьший тип со знаком, сохраняющий значение, из 8-битного (byte), 32-битного (int) и 64-битного (long). Таким образом, 256 считается int, а 255 + 1 переносится на следующую строку и становится byte 0. Шестнадцатеричные значения, например 0x3, сначала интерпретируются как наименьшее беззнаковое значение, сохраняющееся в 32- или 64-разрядном типе, а затем повторно интерпретируются как беззнаковые значения. Таким образом, в столбце 0xffffffff указано значение int-1. В Android 13 и более поздних версий к константам, например 3u8, можно добавить суффикс u8, чтобы представить значение byte. Этот суффикс важен, чтобы вычисление, например 0xffu8 * 3, интерпретировалось как -3 с типом byte, а 0xff * 3 – как 765 с типом int.

Поддерживаемые операторы имеют семантику C++ и Java. Бинарные операторы в порядке возрастания приоритета: || && | ^ & == != < > <= >= << >> + - * / %. Унарные операторы: + - ! ~.