В Android 10 добавлена поддержка стабильного языка определения интерфейса Android (AIDL), нового способа отслеживания интерфейса прикладной программы (API) и двоичного интерфейса приложения (ABI), предоставляемого интерфейсами AIDL. Стабильный AIDL работает точно так же, как AIDL, но система сборки отслеживает совместимость интерфейсов, и существуют ограничения на то, что вы можете сделать:
- Интерфейсы определяются в системе сборки с помощью
aidl_interfaces
. - Интерфейсы могут содержать только структурированные данные. Parcelables, представляющие предпочтительные типы, автоматически создаются на основе их определения AIDL и автоматически маршаллируются и демаршаллируются.
- Интерфейсы могут быть объявлены как стабильные (обратно совместимые). Когда это происходит, их API отслеживается и версионируется в файле рядом с интерфейсом AIDL.
Структурированный и стабильный AIDL
Структурированный AIDL относится к типам, определенным исключительно в AIDL. Например, декларация parcelable (пользовательский parcelable) не является структурированным AIDL. Parcelables с полями, определенными в AIDL, называются структурированными parcelables .
Stable AIDL требует структурированного AIDL, чтобы система сборки и компилятор могли понять, являются ли изменения, внесенные в parcelables, обратно совместимыми. Однако не все структурированные интерфейсы стабильны. Чтобы быть стабильным, интерфейс должен использовать только структурированные типы, а также он должен использовать следующие функции управления версиями. И наоборот, интерфейс не является стабильным, если для его сборки используется основная система сборки или если установлено 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 использует интерфейс или parcelable из другого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 похожи на традиционные интерфейсы, за исключением того, что им не разрешено использовать неструктурированные parcelables (потому что они нестабильны! см. Структурированный и стабильный AIDL ). Основное отличие в стабильном AIDL заключается в том, как определяются parcelables. Ранее parcelables объявлялись заранее ; в стабильном (и, следовательно, структурированном) AIDL поля и переменные parcelables определяются явно.
// 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 не реализован.
Стратегии решения этой проблемы см. в разделе «Запрос версий и использование значений по умолчанию» .
Посылки
Когда новые поля добавляются в parcelables, старые клиенты и серверы удаляют их. Когда новые клиенты и серверы получают старые parcelables, значения по умолчанию для новых полей автоматически заполняются. Это означает, что значения по умолчанию должны быть указаны для всех новых полей в parcelable.
Клиентам не следует ожидать, что серверы будут использовать новые поля, если они не знают, что сервер реализует версию, в которой определено поле (см. запрос версий ).
Перечисления и константы
Аналогично, клиенты и серверы должны либо отклонять, либо игнорировать нераспознанные константные значения и перечислители по мере необходимости, поскольку в будущем их может быть добавлено больше. Например, сервер не должен прерывать работу, если он получает перечислитель, о котором он не знает. Сервер должен либо игнорировать перечислитель, либо возвращать что-то, чтобы клиент знал, что он не поддерживается в этой реализации.
Профсоюзы
Попытка отправить объединение с новым полем не удалась, если получатель старый и не знает о поле. Реализация никогда не увидит объединение с новым полем. Ошибка игнорируется, если это односторонняя транзакция; в противном случае ошибка будет BAD_VALUE
(для бэкэнда C++ или NDK) или IllegalArgumentException
(для бэкэнда Java). Ошибка возникает, если клиент отправляет набор объединений в новое поле на старый сервер или когда старый клиент получает объединение с нового сервера.
Управление несколькими версиями
Пространство имен компоновщика в Android может иметь только 1 версию определенного интерфейса 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
матрицы совместимости фреймворка (FCM) и PRODUCT_SHIPPING_API_LEVEL
Cuttlefish, чтобы они отражали устройства, запускаемые с выпуском следующего года. Мы корректируем target-level
и PRODUCT_SHIPPING_API_LEVEL
, чтобы убедиться, что есть какое-то запускаемое устройство, которое протестировано и соответствует новым требованиям для выпуска следующего года.
Когда RELEASE_AIDL_USE_UNFROZEN
имеет true
, Cuttlefish используется для разработки будущих релизов Android. Он нацелен на уровень FCM релиза Android следующего года и PRODUCT_SHIPPING_API_LEVEL
, требуя от него удовлетворения Vendor Software Requirements (VSR) следующего релиза.
Когда RELEASE_AIDL_USE_UNFROZEN
имеет false
, Cuttlefish имеет предыдущий target-level
и PRODUCT_SHIPPING_API_LEVEL
для отражения устройства выпуска. В Android 14 и ниже эта дифференциация будет достигнута с помощью различных веток Git, которые не подхватывают изменение в FCM target-level
, API-уровень доставки или любой другой код, нацеленный на следующий выпуск.
Правила именования модулей
В Android 11 для каждой комбинации версий и включенных бэкендов автоматически создается модуль библиотеки-заглушки. Чтобы сослаться на определенный модуль библиотеки-заглушки для линковки, не используйте имя модуля aidl_interface
, а имя модуля библиотеки-заглушки, то есть ifacename - version - backend , где
-
ifacename
: имя модуляaidl_interface
-
version
является либо из-
V version-number
для замороженных версий -
V latest-frozen-version-number + 1
для версии tip-of-tree (еще не замороженной)
-
-
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
и т. д.) являются общими для клиента и сервера (например, классы могут находиться в пути к классам загрузки). Когда классы являются общими, сервер также связывается с новейшей версией классов, даже если он мог быть построен с использованием более старой версии интерфейса. Если этот метаинтерфейс реализован в общем классе, он всегда возвращает новейшую версию. Однако, реализуя метод, как указано выше, номер версии интерфейса встраивается в код сервера (потому что 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 в вашем интерфейсе в стабильные parcelables (сами файлы интерфейса могут оставаться неизменными). Сделайте это, выразив их структуру непосредственно в файлах AIDL. Классы управления должны быть переписаны для использования этих новых типов. Это можно сделать до создания пакета
aidl_interface
(ниже).Создайте пакет
aidl_interface
(как описано выше), содержащий имя вашего модуля, его зависимости и любую другую необходимую вам информацию. Чтобы сделать его стабилизированным (а не просто структурированным), его также нужно версионировать. Для получения дополнительной информации см. Версионирование интерфейсов .