Стабильный AIDL

В Android 10 добавлена ​​поддержка стабильного языка определения интерфейса Android (AIDL), нового способа отслеживания интерфейса прикладной программы (API)/двоичного интерфейса приложения (ABI), предоставляемого интерфейсами AIDL. Стабильный AIDL имеет следующие ключевые отличия от AIDL:

  • Интерфейсы определяются в системе сборки с помощью aidl_interfaces .
  • Интерфейсы могут содержать только структурированные данные. Parcelables, представляющие нужные типы, автоматически создаются на основе их определения AIDL и автоматически маршалируются и демаршализуются.
  • Интерфейсы могут быть объявлены как стабильные (обратно совместимые). Когда это происходит, их API отслеживается и версионируется в файле рядом с интерфейсом AIDL.

Структурированный и стабильный AIDL

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

Стабильный AIDL требует структурированного AIDL, чтобы система сборки и компилятор могли понять, являются ли изменения, внесенные в посылки, обратно совместимыми. Однако не все структурированные интерфейсы стабильны. Чтобы быть стабильным, интерфейс должен использовать только структурированные типы, а также следующие функции управления версиями. И наоборот, интерфейс нестабилен, если для его сборки используется базовая система сборки или если установлено unstable:true .

Определение интерфейса AIDL

Определение aidl_interface выглядит следующим образом:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["other-aidl-V1"],
        },
        {
            version: "2",
            imports: ["other-aidl-V3"],
        }
    ],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
        rust: {
            enabled: true,
        },
    },

}
  • name : Имя модуля интерфейса AIDL, которое однозначно идентифицирует интерфейс AIDL.
  • srcs : список исходных файлов AIDL, составляющих интерфейс. Путь для типа Foo AIDL, определенного в пакете com.acme должен находиться по адресу <base_path>/com/acme/Foo.aidl , где <base_path> может быть любым каталогом, связанным с каталогом, в котором находится Android.bp . В приведенном выше примере <base_path> — это srcs/aidl .
  • local_include_dir : путь, с которого начинается имя пакета. Он соответствует <base_path> описанному выше.
  • imports : список модулей aidl_interface , которые он использует. Если один из ваших интерфейсов AIDL использует интерфейс или элемент из другого aidl_interface , укажите здесь его имя. Это может быть само имя, обозначающее последнюю версию, или имя с суффиксом версии (например, -V1 ), обозначающее конкретную версию. Указание версии поддерживается с Android 12.
  • versions : предыдущие версии интерфейса, которые заморожены под api_dir . Начиная с Android 11, versions заморожены под aidl_api/ name . Если нет замороженных версий интерфейса, это не следует указывать и проверки совместимости не будет. Это поле было заменено versions_with_info для версии 13 и выше.
  • versions_with_info : список кортежей, каждый из которых содержит имя замороженной версии и список импортированных версий других модулейaidl_interface, которые импортировала эта версияaidl_interface. Определение версии V интерфейса AIDL IFACE находится по адресу aidl_api/ IFACE / V . Это поле появилось в Android 13, и его нельзя изменять напрямую в Android.bp. Поле добавляется или обновляется путем вызова *-update-api или *-freeze-api . Кроме того, поля versions автоматически переносятся versions_with_info , когда пользователь вызывает *-update-api или *-freeze-api .
  • stability : необязательный флаг для обещания стабильности этого интерфейса. В настоящее время поддерживается только "vintf" . Если этот параметр не установлен, это соответствует интерфейсу со стабильностью в этом контексте компиляции (поэтому загруженный здесь интерфейс можно использовать только с скомпилированными вместе вещами, например, в system.img). Если для этого параметра установлено значение "vintf" , это соответствует обещанию стабильности: интерфейс должен оставаться стабильным, пока он используется.
  • gen_trace : необязательный флаг для включения или выключения трассировки. Начиная с Android 14, значение по умолчанию true для серверных частей cpp и java .
  • host_supported : необязательный флаг, который, если установлено значение true , делает сгенерированные библиотеки доступными для среды хоста.
  • unstable : необязательный флаг, используемый для обозначения того, что этот интерфейс не обязательно должен быть стабильным. Если для этого параметра установлено значение true , система сборки не создает дамп API для интерфейса и не требует его обновления.
  • frozen : необязательный флаг, значение которого равно true означает, что в интерфейсе нет изменений со времени предыдущей версии интерфейса. Это позволяет проводить больше проверок во время сборки. Если установлено значение false это означает, что интерфейс находится в разработке и в него внесены новые изменения, поэтому запуск foo-freeze-api создаст новую версию и автоматически изменит значение на true . Представлено в Android 14.
  • backend.<type>.enabled : эти флаги переключают каждый из серверов, для которых компилятор AIDL генерирует код. В настоящее время поддерживаются четыре бэкэнда: Java, C++, NDK и Rust. Серверные части Java, C++ и NDK включены по умолчанию. Если какой-либо из этих трех бэкэндов не нужен, его необходимо явно отключить. По умолчанию Rust отключен.
  • backend.<type>.apex_available : список имен APEX, для которых доступна созданная библиотека-заглушка.
  • backend.[cpp|java].gen_log : необязательный флаг, который определяет, создавать ли дополнительный код для сбора информации о транзакции.
  • backend.[cpp|java].vndk.enabled : необязательный флаг, позволяющий сделать этот интерфейс частью VNDK. По умолчанию — false .
  • backend.[cpp|ndk].additional_shared_libraries : этот флаг, представленный в Android 14, добавляет зависимости к собственным библиотекам. Этот флаг полезен с ndk_header и cpp_header .
  • backend.java.sdk_version : необязательный флаг для указания версии SDK, на основе которого построена библиотека-заглушка Java. По умолчанию используется "system_current" . Это не должно быть установлено, если backend.java.platform_apis имеет значение true.
  • backend.java.platform_apis : необязательный флаг, для которого должно быть установлено значение true , когда сгенерированные библиотеки необходимо собирать с использованием API платформы, а не SDK.

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

Написание файлов AIDL

Интерфейсы в стабильном AIDL аналогичны традиционным интерфейсам, за исключением того, что им не разрешено использовать неструктурированные объекты (поскольку они не стабильны! см. Структурированный и стабильный AIDL ). Основное отличие стабильного AIDL заключается в том, как определяются передаваемые посылки. Раньше посылки декларировались заранее ; в стабильном (и, следовательно, структурированном) AIDL поля и переменные, которые можно разделить, определяются явно.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

В настоящее время поддерживается (но не обязательно) значение по умолчанию для boolean , char , float , double , byte , int , long и String . В Android 12 также поддерживаются значения по умолчанию для пользовательских перечислений. Если значение по умолчанию не указано, используется значение, подобное 0, или пустое. Перечисления без значения по умолчанию инициализируются значением 0, даже если нулевой перечислитель отсутствует.

Использование библиотек-заглушек

После добавления библиотек-заглушек в качестве зависимости к вашему модулю вы можете включить их в свои файлы. Вот примеры библиотек-заглушек в системе сборки ( Android.mk также можно использовать для определений устаревших модулей):

cc_... {
    name: ...,
    shared_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // can also be shared_libs if desire is to load a library and share
    // it among multiple users or if you only need access to constants
    static_libs: ["my-module-name-java"],
    ...
}
# or
rust_... {
    name: ...,
    rustlibs: ["my-module-name-rust"],
    ...
}

Пример на С++:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

Пример на Java:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

Пример в Rust:

use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
    // use just like traditional AIDL

Интерфейсы управления версиями

Объявление модуля с именем foo также создает цель в системе сборки, которую вы можете использовать для управления API модуля. При сборке foo-freeze-api добавляет новое определение API в api_dir или aidl_api/ name , в зависимости от версии Android, а также добавляет файл .hash , оба из которых представляют недавно замороженную версию интерфейса. foo-freeze-api также обновляет versions_with_info , чтобы отразить дополнительную версию и imports для этой версии. По сути, imports versions_with_info копируется из поля imports . Но последняя стабильная версия указана при imports versions_with_info для импорта, у которого нет явной версии. После указания versions_with_info система сборки выполняет проверки совместимости между замороженными версиями, а также между вершиной дерева (ToT) и последней замороженной версией.

Кроме того, вам необходимо управлять определением API версии ToT. Каждый раз при обновлении API запускайте foo-update-api , чтобы обновить aidl_api/ name /current , который содержит определение API версии ToT.

Для поддержания стабильности интерфейса владельцы могут добавить новые:

  • Методы конца интерфейса (или методы с явно определенными новыми серийными номерами)
  • Элементы в конце участка (требуется добавление значения по умолчанию для каждого элемента)
  • Постоянные значения
  • В Android 11 счетчики
  • В Android 12 поля до конца объединения

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

Чтобы проверить, что все интерфейсы заморожены для выпуска, вы можете выполнить сборку со следующими установленными переменными среды:

  • AIDL_FROZEN_REL=true m ... — при сборке необходимо заморозить все стабильные интерфейсы AIDL, для которых не указано поле owner: .
  • AIDL_FROZEN_OWNERS="aosp test" - сборка требует, чтобы все стабильные интерфейсы AIDL были заморожены с указанием поля owner: "aosp" или "test".

Стабильность импорта

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

Код платформы Android android.hardware.graphics.common является крупнейшим примером такого типа обновления версии.

Использование версионных интерфейсов

Методы интерфейса

Во время выполнения при попытке вызвать новые методы на старом сервере новые клиенты получают либо ошибку, либо исключение, в зависимости от бэкенда.

  • Серверная часть cpp получает ::android::UNKNOWN_TRANSACTION .
  • Серверная часть ndk получает STATUS_UNKNOWN_TRANSACTION .
  • Серверная часть java получает исключение android.os.RemoteException с сообщением о том, что API не реализован.

Стратегии решения этой проблемы см. в разделе «Запрос версий и использование значений по умолчанию» .

Посылки

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

Клиентам не следует ожидать, что серверы будут использовать новые поля, если они не знают, что сервер реализует версию, в которой определено это поле (см. Запрос версий ).

Перечисления и константы

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

Союзы

Попытка отправить объединение с новым полем не удалась, если получатель старый и не знает об этом поле. Реализация никогда не увидит объединения с новым полем. Сбой игнорируется, если это односторонняя транзакция; в противном случае возникает ошибка BAD_VALUE (для серверной части C++ или NDK) или IllegalArgumentException (для серверной части Java). Ошибка возникает, если клиент отправляет набор объединения для нового поля на старый сервер или когда старый клиент получает объединение с нового сервера.

Разработка на основе флагов

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

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

Флаг сборки AIDL

Флаг, управляющий этим поведением, — RELEASE_AIDL_USE_UNFROZEN , определенный в build/release/build_flags.bzl . true означает, что незамороженная версия интерфейса используется во время выполнения, а false означает, что все библиотеки незамороженных версий ведут себя как их последняя замороженная версия. Вы можете переопределить этот флаг на true для локальной разработки, но перед выпуском необходимо вернуть ему значение false . Обычно разработка выполняется с конфигурацией, для которой установлен флаг true .

Матрица совместимости и манифесты

Объекты интерфейса поставщика (объекты VINTF) определяют, какие версии ожидаются и какие версии предоставляются с обеих сторон интерфейса поставщика.

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

Матрицы

Интерфейсы, принадлежащие партнеру, добавляются в матрицы совместимости для конкретного устройства или продукта, на которые устройство нацелено во время разработки. Поэтому, когда в матрицу совместимости добавляется новая, незамороженная версия интерфейса, предыдущие замороженные версии должны оставаться для RELEASE_AIDL_USE_UNFROZEN=false . Это можно решить, используя разные файлы матрицы совместимости для разных конфигураций RELEASE_AIDL_USE_UNFROZEN или разрешив обе версии в одном файле матрицы совместимости, который используется во всех конфигурациях.

Например, при добавлении незамороженной версии 4 используйте <version>3-4</version> .

Когда версия 4 заморожена, вы можете удалить версию 3 из матрицы совместимости, поскольку замороженная версия 4 используется, когда RELEASE_AIDL_USE_UNFROZEN имеет false .

Манифесты

В Android 15 (экспериментальная AOSP) в libvintf внесено изменение для изменения файлов манифеста во время сборки на основе значения RELEASE_AIDL_USE_UNFROZEN .

Манифесты и фрагменты манифеста определяют, какую версию интерфейса реализует служба. При использовании последней незамороженной версии интерфейса манифест необходимо обновить, чтобы отразить эту новую версию. Когда RELEASE_AIDL_USE_UNFROZEN=false записи манифеста корректируются libvintf , чтобы отразить изменения в созданной библиотеке AIDL. Версия изменяется с незамороженной версии N на последнюю замороженную версию N - 1 . Таким образом, пользователям не нужно управлять несколькими манифестами или фрагментами манифестов для каждой из своих служб.

Изменения клиента HAL

Клиентский код HAL должен быть обратно совместим с каждой предыдущей поддерживаемой замороженной версией. Если RELEASE_AIDL_USE_UNFROZEN имеет false , службы всегда выглядят как последняя замороженная версия или более ранняя (например, вызов новых размороженных методов возвращает UNKNOWN_TRANSACTION или новые parcelable поля имеют значения по умолчанию). Клиенты платформы Android должны быть обратно совместимы с дополнительными предыдущими версиями, но это новая деталь для клиентов поставщиков и клиентов интерфейсов, принадлежащих партнерам.

Изменения реализации HAL

Самая большая разница между разработкой HAL и разработкой на основе флагов — это требование, чтобы реализации HAL были обратно совместимы с последней замороженной версией, чтобы работать, когда RELEASE_AIDL_USE_UNFROZEN имеет false . Рассмотрение обратной совместимости в реализациях и коде устройства — это новое упражнение. См. Использование интерфейсов с поддержкой версий .

Соображения обратной совместимости, как правило, одинаковы для клиентов и серверов, а также для кода платформы и кода поставщика, но существуют тонкие различия, о которых вам необходимо знать, поскольку теперь вы эффективно реализуете две версии, использующие один и тот же исходный код. (текущая, незамороженная версия).

Пример: интерфейс имеет три замороженные версии. Интерфейс обновлен новым методом. Клиент и служба обновлены для использования новой библиотеки версии 4. Поскольку библиотека V4 основана на незамороженной версии интерфейса, она ведет себя как последняя замороженная версия, версия 3, когда RELEASE_AIDL_USE_UNFROZEN имеет false и предотвращает использование нового метода.

Когда интерфейс заморожен, все значения RELEASE_AIDL_USE_UNFROZEN используют эту замороженную версию, а код, обеспечивающий обратную совместимость, можно удалить.

При вызове методов для обратных вызовов необходимо корректно обрабатывать случай, когда возвращается UNKNOWN_TRANSACTION . Клиенты могут реализовать две разные версии обратного вызова в зависимости от конфигурации выпуска, поэтому нельзя предполагать, что клиент отправит самую новую версию, и новые методы могут ее вернуть. Это похоже на то, как стабильные клиенты AIDL поддерживают обратную совместимость с серверами, описано в разделе Использование интерфейсов с поддержкой версий .

// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
    mMyCallback = cb;
    // Get the version of the callback for later when we call methods on it
    auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
    return status;
}

// Example of using the callback later
void NotifyCallbackLater() {
  // From the latest frozen version (V2)
  mMyCallback->foo();
  // Call this method from the unfrozen V3 only if the callback is at least V3
  if (mMyCallbackVersion >= 3) {
    mMyCallback->bar();
  }
}

Новые поля в существующих типах ( parcelable , enum , union ) могут не существовать или содержать значения по умолчанию, если RELEASE_AIDL_USE_UNFROZEN имеет значение false и значения новых полей, которые пытается отправить служба, удаляются на выходе из процесса.

Новые типы, добавленные в эту незамороженную версию, не могут быть отправлены или получены через интерфейс.

Реализация никогда не получает вызов новых методов от клиентов, если RELEASE_AIDL_USE_UNFROZEN имеет false .

Будьте осторожны и используйте новые перечислители только в той версии, в которой они представлены, а не в предыдущей версии.

Обычно вы используете foo->getInterfaceVersion() , чтобы узнать, какую версию использует удаленный интерфейс. Однако при поддержке управления версиями на основе флагов вы реализуете две разные версии, поэтому вам может потребоваться получить версию текущего интерфейса. Вы можете сделать это, получив версию интерфейса текущего объекта, например, this->getInterfaceVersion() или другие методы для my_ver . Дополнительную информацию см. в разделе Запрос версии интерфейса удаленного объекта .

Новые стабильные интерфейсы VINTF

При добавлении нового пакета интерфейса AIDL не существует последней замороженной версии, поэтому нет поведения, к которому можно было бы вернуться, если RELEASE_AIDL_USE_UNFROZEN имеет false . Не используйте эти интерфейсы. Если RELEASE_AIDL_USE_UNFROZEN имеет значение false , Service Manager не позволит службе зарегистрировать интерфейс, и клиенты не смогут его найти.

Вы можете добавить службы условно на основе значения флага RELEASE_AIDL_USE_UNFROZEN в make-файле устройства:

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

Если служба является частью более крупного процесса, поэтому вы не можете добавить ее на устройство по условию, вы можете проверить, объявлена ​​ли служба с помощью IServiceManager::isDeclared() . Если он объявлен, но его не удалось зарегистрировать, прервите процесс. Если он не объявлен, то ожидается, что он не сможет зарегистрироваться.

Каракатица как инструмент разработки

Каждый год после замораживания VINTF мы корректируем target-level матрицы совместимости платформы (FCM) и PRODUCT_SHIPPING_API_LEVEL для Cuttlefish, чтобы они отражали устройства, которые будут выпущены в следующем году. Мы корректируем target-level и PRODUCT_SHIPPING_API_LEVEL , чтобы убедиться, что существует какое-то пусковое устройство, которое протестировано и соответствует новым требованиям для выпуска следующего года.

Если RELEASE_AIDL_USE_UNFROZEN имеет true , Cuttlefish используется для разработки будущих выпусков Android. Он нацелен на уровень FCM и PRODUCT_SHIPPING_API_LEVEL выпуска Android следующего года, что требует от него соответствия требованиям поставщика программного обеспечения (VSR) следующего выпуска.

Если RELEASE_AIDL_USE_UNFROZEN имеет значение false , Cuttlefish имеет предыдущий target-level и PRODUCT_SHIPPING_API_LEVEL для отражения устройства выпуска. В Android 14 и более ранних версиях это дифференциация будет осуществляться с помощью различных веток Git, которые не учитывают изменения target-level FCM, уровня API доставки или любого другого кода, предназначенного для следующего выпуска.

Правила именования модулей

В Android 11 для каждой комбинации версий и включенных серверов автоматически создается модуль библиотеки-заглушки. Чтобы обратиться к конкретному модулю библиотеки-заглушки для связывания, используйте не имя модуля aidl_interface , а имя модуля библиотеки-заглушки, то есть ifacename - version - backend , где

  • ifacename : имя модуля aidl_interface .
  • version одна из
    • V version-number для замороженных версий
    • V latest-frozen-version-number + 1 для версии на верхушке дерева (еще не замороженной)
  • backend является одним из
    • java для серверной части Java,
    • cpp для серверной части C++,
    • ndk или ndk_platform для серверной части NDK. Первый предназначен для приложений, а второй — для использования платформы.
    • rust для бэкэнда Rust.

Предположим, что существует модуль с именем foo , его последняя версия — 2 и он поддерживает как NDK, так и C++. В этом случае AIDL генерирует следующие модули:

  • На основе версии 1
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • На основе версии 2 (последней стабильной версии)
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • На основе версии ToT
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

По сравнению с Android 11,

  • foo- backend , который относится к последней стабильной версии, становится foo- V2 - backend
  • foo-unstable- backend , который относится к версии ToT, становится foo- V3 - backend

Имена выходных файлов всегда совпадают с именами модулей.

  • На основе версии 1: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • На основе версии 2: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • На основе версии ToT: foo-V3-(cpp|ndk|ndk_platform|rust).so

Обратите внимание, что компилятор AIDL не создает ни модуль unstable версии, ни модуль без версии для стабильного интерфейса AIDL. Начиная с Android 12, имя модуля, созданное из стабильного интерфейса AIDL, всегда включает его версию.

Новые методы метаинтерфейса

В Android 10 добавлено несколько методов метаинтерфейса для стабильной версии AIDL.

Запрос версии интерфейса удаленного объекта

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

Пример с серверной частью cpp :

sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();

Пример с бэкэндом ndkndk_platform ):

IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);

Пример с бэкэндом java :

IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
  // the remote side is using an older interface
}

String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();

Для языка Java удаленная сторона ДОЛЖНА реализовать getInterfaceVersion() и getInterfaceHash() следующим образом ( вместо IFoo используется super , чтобы избежать ошибок копирования/вставки. Для отключения предупреждений может потребоваться аннотация @SuppressWarnings("static") , в зависимости от конфигурация javac ):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

Это связано с тем, что сгенерированные классы ( IFoo , IFoo.Stub и т. д.) совместно используются клиентом и сервером (например, классы могут находиться в пути к классам загрузки). Когда классы являются общими, сервер также связан с новейшей версией классов, даже если он мог быть создан с использованием более старой версии интерфейса. Если этот метаинтерфейс реализован в общем классе, он всегда возвращает самую новую версию. Однако при реализации описанного выше метода номер версии интерфейса встраивается в код сервера (поскольку IFoo.VERSION — это static final int , который встраивается при ссылке), и, таким образом, метод может возвращать точную версию, на которой был создан сервер. с.

Работа со старыми интерфейсами

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

Благодаря стабильному AIDL клиенты получают больше контроля. На стороне клиента вы можете установить реализацию по умолчанию для интерфейса AIDL. Метод в реализации по умолчанию вызывается только тогда, когда метод не реализован на удаленной стороне (поскольку он был создан с использованием более старой версии интерфейса). Поскольку значения по умолчанию устанавливаются глобально, их не следует использовать из потенциально общих контекстов.

Пример на C++ в Android 13 и более поздних версиях:

class MyDefault : public IFooDefault {
  Status anAddedMethod(...) {
   // do something default
  }
};

// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());

foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
                         // remote side is not implementing it

Пример на Java:

IFoo.Stub.setDefaultImpl(new IFoo.Default() {
    @Override
    public xxx anAddedMethod(...)  throws RemoteException {
        // do something default
    }
}); // once per an interface in a process


foo.anAddedMethod(...);

Вам не нужно предоставлять реализацию по умолчанию для всех методов в интерфейсе AIDL. Методы, которые гарантированно будут реализованы на удаленной стороне (поскольку вы уверены, что удаленная часть создана, когда методы были в описании интерфейса AIDL), не нужно переопределять в классе impl по умолчанию.

Преобразование существующего AIDL в структурированный/стабильный AIDL

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

  1. Определите все зависимости вашего интерфейса. Для каждого пакета, от которого зависит интерфейс, определите, определен ли пакет в стабильном AIDL. Если не определен, пакет необходимо преобразовать.

  2. Конвертируйте все посылки в вашем интерфейсе в стабильные посылки (сами файлы интерфейса могут оставаться неизменными). Сделайте это, выразив их структуру непосредственно в файлах AIDL. Классы управления необходимо переписать, чтобы использовать эти новые типы. Это можно сделать до создания пакета aidl_interface (ниже).

  3. Создайте пакет aidl_interface (как описано выше), содержащий имя вашего модуля, его зависимости и любую другую необходимую информацию. Чтобы сделать его стабилизированным (а не просто структурированным), ему также необходимо версионировать. Дополнительные сведения см. в разделе Интерфейсы управления версиями .