В Android 10 добавлена поддержка стабильного языка определения интерфейса Android (AIDL) — нового способа отслеживания интерфейса прикладного программирования (API) и двоичного интерфейса приложения (ABI), предоставляемых интерфейсами AIDL. Стабильный AIDL работает точно так же, как и обычный AIDL, но система сборки отслеживает совместимость интерфейсов, и существуют ограничения на то, что вы можете делать:
- Интерфейсы определяются в системе сборки с помощью
aidl_interfaces. - Интерфейсы могут содержать только структурированные данные. Объекты Parcelable, представляющие предпочтительные типы, автоматически создаются на основе их определения 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, составляющих интерфейс. Путь к типу 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, по умолчанию он имеет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 также поддерживаются значения по умолчанию для пользовательских перечислений. Если значение по умолчанию не указано, используется значение, подобное нулю, или пустое значение. Перечисления без значения по умолчанию инициализируются нулем, даже если нет нулевого перечислителя.
Используйте библиотеки-заглушки
После добавления библиотек-заглушек в качестве зависимости к вашему модулю, вы можете включить их в свои файлы. Вот примеры библиотек-заглушек в системе сборки (для определений устаревших модулей также можно использовать 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.
Для поддержания стабильности интерфейса владельцы могут добавлять новые:
- Методы, завершающие интерфейс (или методы с явно определенными новыми последовательными вызовами).
- Элементы в конце объекта Parcelable (требуется добавить значение по умолчанию для каждого элемента)
- Постоянные значения
- В Android 11 перечислители
- В Android 12 поля доходят до конца объединения.
Никакие другие действия не допускаются, и никто другой не может изменять интерфейс (иначе существует риск конфликта с изменениями, внесенными владельцем).
Чтобы убедиться, что все интерфейсы зафиксированы для выпуска, вы можете выполнить сборку со следующими установленными переменными окружения:
-
AIDL_FROZEN_REL=true m ...- сборка требует заморозить все стабильные интерфейсы AIDL, у которых не указано полеowner::. -
AIDL_FROZEN_OWNERS="aosp test"- сборка требует, чтобы все стабильные интерфейсы AIDL были заморожены с указаниемowner:в поле "aosp" или "test".
Стабильность импорта
Обновление версий импорта для замороженных версий интерфейса обеспечивает обратную совместимость на уровне стабильного AIDL. Однако для этого требуется обновление всех серверов и клиентов, использующих предыдущую версию интерфейса, и некоторые приложения могут испытывать путаницу при смешивании разных версий типов. Как правило, для пакетов, содержащих только типы или общие пакеты, это безопасно, поскольку код уже должен быть написан для обработки неизвестных типов из транзакций межпроцессного взаимодействия.
В коде платформы 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();
}
}
Если параметр RELEASE_AIDL_USE_UNFROZEN имеет false новые поля в существующих типах ( parcelable , enum , union ) могут отсутствовать или содержать свои значения по умолчанию, и значения новых полей, которые служба пытается отправить, отбрасываются при завершении процесса.
Новые типы данных, добавленные в этой размороженной версии, нельзя отправлять или получать через интерфейс.
Реализация никогда не получает вызовов новых методов от каких-либо клиентов, если 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 и PRODUCT_SHIPPING_API_LEVEL следующей версии Android, что требует от него соответствия требованиям Vendor Software Requirements (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 , чтобы избежать ошибок копирования и вставки. В зависимости от конфигурации javac может потребоваться аннотация @SuppressWarnings("static") для отключения предупреждений):
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. Если он не определен, пакет необходимо преобразовать.
Преобразуйте все типы данных, доступные для разделения на части (parcelable), в вашем интерфейсе в стабильные типы данных, доступные для разделения на части (parcelable) (сами файлы интерфейса могут оставаться неизменными). Сделайте это, выразив их структуру непосредственно в файлах AIDL. Классы управления необходимо переписать для использования этих новых типов. Это можно сделать до создания пакета
aidl_interface(см. ниже).Создайте пакет
aidl_interface(как описано выше), содержащий имя вашего модуля, его зависимости и любую другую необходимую информацию. Для обеспечения стабильности (а не просто структурированности) его также необходимо версионировать. Для получения дополнительной информации см. раздел «Версионирование интерфейсов» .