В 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
для Android 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"
. Еслиstability
не установлена, система сборки проверяет, что интерфейс обратно совместим, если не указанunstable
. Неустановленное состояние соответствует интерфейсу со стабильностью в этом контексте компиляции (то есть либо все системные вещи, например, вещи вsystem.img
и связанных разделах, либо все вещи поставщика, например, вещи вvendor.img
и связанных разделах). Если дляstability
установлено значение"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 отключен по умолчанию до Android 15. -
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 your preference 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). Ошибка возникает, если клиент отправляет набор объединения для нового поля на старый сервер или когда старый клиент получает объединение с нового сервера.
Управление несколькими версиями
Пространство имен компоновщика в Android может иметь только одну версию определенного интерфейса aidl
чтобы избежать ситуаций, когда сгенерированные типы aidl
имеют несколько определений. В C++ есть правило одного определения, которое требует только одного определения каждого символа.
Сборка Android выдает ошибку, если модуль зависит от разных версий одной и той же aidl_interface
. Модуль может зависеть от этих библиотек прямо или косвенно через зависимости от их зависимостей. Эти ошибки показывают график зависимостей неисправного модуля от конфликтующих версий aidl_interface
. Все зависимости необходимо обновить, чтобы они включали одну и ту же (обычно последнюю) версию этих библиотек.
Если библиотека интерфейса используется множеством разных модулей, может оказаться полезным создать cc_defaults
, java_defaults
rust_defaults
для любой группы библиотек и процессов, которым необходимо использовать одну и ту же версию. При внедрении новой версии интерфейса эти значения по умолчанию могут быть обновлены, и все модули, использующие их, обновляются вместе, гарантируя, что они не используют разные версии интерфейса.
cc_defaults {
name: "my.aidl.my-process-group-ndk-shared",
shared_libs: ["my.aidl-V3-ndk"],
...
}
cc_library {
name: "foo",
defaults: ["my.aidl.my-process-group-ndk-shared"],
...
}
cc_binary {
name: "bar",
defaults: ["my.aidl.my-process-group-ndk-shared"],
...
}
Когда aidl_interface
импортируют другие aidl_interface
, это создает дополнительные зависимости, которые требуют совместного использования определенных версий. Эту ситуацию может оказаться трудно решить, если существуют общие aidl_interface
, которые импортируются в несколько aidl_interface
, которые используются вместе в одних и тех же процессах.
aidl_interfaces_defaults
можно использовать для хранения одного определения последних версий зависимостей для aidl_interface
, которое может обновляться в одном месте и использоваться всеми aidl_interface
, которые хотят импортировать этот общий интерфейс.
aidl_interface_defaults {
name: "android.popular.common-latest-defaults",
imports: ["android.popular.common-V3"],
...
}
aidl_interface {
name: "android.foo",
defaults: ["my.aidl.latest-ndk-shared"],
...
}
aidl_interface {
name: "android.bar",
defaults: ["my.aidl.latest-ndk-shared"],
...
}
Разработка на основе флагов
Интерфейсы, находящиеся в разработке (незамороженные), нельзя использовать на выпущенных устройствах, поскольку их обратная совместимость не гарантируется.
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 внесено изменение в 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. Первый предназначен для приложений, а второй — для использования платформы до Android 13. В Android 13 и более поздних версиях используйте только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();
Пример с бэкэндом ndk
(и ndk_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.
Определите все зависимости вашего интерфейса. Для каждого пакета, от которого зависит интерфейс, определите, определен ли пакет в стабильном AIDL. Если не определен, пакет необходимо преобразовать.
Конвертируйте все посылки в вашем интерфейсе в стабильные посылки (сами файлы интерфейса могут оставаться неизменными). Сделайте это, выразив их структуру непосредственно в файлах AIDL. Классы управления необходимо переписать, чтобы использовать эти новые типы. Это можно сделать до того, как вы создадите пакет
aidl_interface
(ниже).Создайте пакет
aidl_interface
(как описано выше), содержащий имя вашего модуля, его зависимости и любую другую необходимую информацию. Чтобы сделать его стабилизированным (а не просто структурированным), ему также необходимо версионировать. Дополнительные сведения см. в разделе Интерфейсы управления версиями .