В Android 10 добавлена поддержка стабильного языка определения интерфейса Android (AIDL) — нового способа отслеживания интерфейса прикладных программ (API) и двоичного интерфейса приложений (ABI), предоставляемых интерфейсами AIDL. Стабильный AIDL работает точно так же, как AIDL, но система сборки отслеживает совместимость интерфейсов, и существуют ограничения на ваши действия:
- Интерфейсы определяются в системе сборки с помощью
aidl_interfaces
. - Интерфейсы могут содержать только структурированные данные. Parcelables, представляющие предпочтительные типы, автоматически создаются на основе их определения AIDL и автоматически маршаллируются и демаршаллируются.
- Интерфейсы могут быть объявлены стабильными (обратно совместимыми). В этом случае их API отслеживается и версионируется в файле рядом с интерфейсом AIDL.
Структурированный и стабильный AIDL
Структурированный AIDL относится к типам, определённым исключительно в AIDL. Например, объявление parcelable (пользовательский parcelable) не является структурированным AIDL. Parcelable, поля которых определены в AIDL, называются структурированными parcelable .
Для стабильного AIDL требуется структурированный AIDL, чтобы система сборки и компилятор могли определить, являются ли изменения, внесённые в parcelable, обратно совместимыми. Однако не все структурированные интерфейсы стабильны. Чтобы быть стабильным, интерфейс должен использовать только структурированные типы, а также следующие функции управления версиями. И наоборот, интерфейс нестабилен, если для его сборки используется основная система сборки или установлено 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, составляющих интерфейс. Путь к типу AIDLFoo
, определённому в пакете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
. Если замороженных версий интерфейса нет, это поле указывать не нужно, и проверки совместимости не будут выполняться. Для Android 13 и более поздних версий это поле заменено наversions_with_info
. -
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, для бэкендовcpp
иjava
значение по умолчанию равноtrue
. -
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 аналогичны традиционным интерфейсам, за исключением того, что им не разрешено использовать неструктурированные объекты Parcelable (поскольку они нестабильны! см. раздел Структурированный и стабильный AIDL ). Основное отличие в стабильном AIDL заключается в том, как определяются объекты Parcelable. Раньше объекты Parcelable объявлялись заранее ; в стабильном (и, следовательно, структурированном) AIDL поля и переменные Parcelable определяются явно.
// 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: ...,
// use `shared_libs:` to load your library and its transitive dependencies
// dynamically
shared_libs: ["my-module-name-cpp"],
// use `static_libs:` to include the library in this binary and drop
// transitive dependencies
static_libs: ["my-module-name-cpp"],
...
}
# or
java_... {
name: ...,
// use `static_libs:` to add all jars and classes to this jar
static_libs: ["my-module-name-java"],
// use `libs:` to make these classes available during build time, but
// not add them to the jar, in case the classes are already present on the
// boot classpath (such as if it's in framework.jar) or another jar.
libs: ["my-module-name-java"],
// use `srcs:` with `-java-sources` if you want to add classes in this
// library jar directly, but you get transitive dependencies from
// somewhere else, such as the boot classpath or another jar.
srcs: ["my-module-name-java-source", ...],
...
}
# or
rust_... {
name: ...,
rustlibs: ["my-module-name-rust"],
...
}
Пример на C++:
#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
указано, система сборки запускает проверки совместимости между замороженными версиями, а также между Top of Tree (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».
Стабильность импорта
Обновление версий импорта для замороженных версий интерфейса обратно совместимо на уровне Stable 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
в makefile устройства:
ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
android.hardware.health.storage-service
endif
Если служба является частью более крупного процесса и добавить её на устройство по условию невозможно, можно проверить, объявлена ли служба с помощью IServiceManager::isDeclared()
. Если служба объявлена, но не зарегистрирована, процесс прерывается. Если служба не объявлена, ожидается, что её регистрация не произойдёт.
Новые стабильные интерфейсы расширения VINTF
Новые интерфейсы расширения не имеют предыдущей версии, к которой можно было бы вернуться, и поскольку они не зарегистрированы в ServiceManager
или не объявлены в манифестах VINTF, IServiceManager::isDeclared()
нельзя использовать для определения того, когда следует присоединить интерфейс расширения к другому интерфейсу.
Переменная RELEASE_AIDL_USE_UNFROZEN
позволяет определить, следует ли прикреплять новый незамороженный интерфейс расширения к существующему, чтобы избежать его использования на освобожденных устройствах. Для использования на освобожденных устройствах интерфейс необходимо заморозить.
Тесты VTS vts_treble_vintf_vendor_test
и vts_treble_vintf_framework_test
определяют, когда в выпущенном устройстве используется незамороженный интерфейс расширения, и выдают ошибку.
Если интерфейс расширения не новый и имеет ранее замороженную версию, то он возвращается к ранее замороженной версии, и никаких дополнительных действий не требуется.
Каракатица как инструмент развития
Каждый год после заморозки VINTF мы корректируем target-level
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()
следующим образом ( super
используется вместо IFoo
, чтобы избежать ошибок копирования и вставки. Для отключения предупреждений может потребоваться аннотация @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
и т. д.) являются общими для клиента и сервера (например, классы могут находиться в загрузочном classpath). При использовании общих классов сервер также линкуется с последней версией классов, даже если он мог быть собран с использованием более старой версии интерфейса. Если этот метаинтерфейс реализован в общем классе, он всегда возвращает самую новую версию. Однако при реализации метода, как указано выше, номер версии интерфейса встраивается в код сервера (поскольку 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. Если пакет не определен, его необходимо преобразовать.
Преобразуйте все парцелевые объекты (parcelables) в вашем интерфейсе в стабильные (сами файлы интерфейсов могут оставаться неизменными). Для этого необходимо выразить их структуру непосредственно в файлах AIDL. Классы управления необходимо переписать для использования этих новых типов. Это можно сделать до создания пакета
aidl_interface
(см. ниже).Создайте пакет
aidl_interface
(как описано выше), содержащий имя вашего модуля, его зависимости и любую другую необходимую информацию. Чтобы сделать его стабилизированным (а не просто структурированным), его также необходимо версионировать. Подробнее см. в разделе Версионирование интерфейсов .