В Android 11 появилась возможность использовать AIDL для HAL в Android, что позволяет реализовать части Android без HIDL. Переходите HAL на использование исключительно AIDL, где это возможно (когда восходящие HAL используют HIDL, необходимо использовать HIDL).
HAL, использующие AIDL для связи между компонентами платформы, например, в system.img
, и аппаратными компонентами, например, vendor.img
, должны использовать стабильный AIDL. Однако для связи внутри раздела, например, от одного HAL к другому, нет никаких ограничений на использование механизма IPC.
Мотивация
AIDL существует дольше, чем HIDL, и используется во многих других местах, например, между компонентами платформы Android или в приложениях. Теперь, когда AIDL имеет поддержку стабильности, можно реализовать весь стек с помощью одной среды выполнения IPC. AIDL также имеет лучшую систему управления версиями, чем HIDL. Вот некоторые преимущества AIDL:
- Использование одного языка IPC означает, что нужно изучать, отлаживать, оптимизировать и защищать только одну вещь.
- AIDL поддерживает управление версиями на месте для владельцев интерфейса:
- Владельцы могут добавлять методы в конец интерфейсов или поля в объекты. Это означает, что с годами легче обновлять код, а также годовые затраты меньше (типы можно изменять на месте, и нет необходимости в дополнительных библиотеках для каждой версии интерфейса).
- Интерфейсы расширений можно подключать во время выполнения, а не в системе типов, поэтому нет необходимости перебазировать последующие расширения на более новые версии интерфейсов.
- Существующий интерфейс AIDL может использоваться напрямую, когда его владелец решит его стабилизировать. Раньше в HIDL приходилось создавать полную копию интерфейса.
Сборка на основе среды выполнения AIDL
AIDL имеет три разных бэкэнда: Java, NDK и CPP. Чтобы использовать стабильный AIDL, всегда используйте системную копию libbinder
по адресу system/lib*/libbinder.so
и общайтесь по адресу /dev/binder
. Для кода в образе vendor
это означает, что libbinder
(из VNDK) использовать нельзя: эта библиотека имеет нестабильный C++ API и нестабильные внутренние компоненты. Вместо этого собственный код поставщика должен использовать серверную часть NDK AIDL, ссылаться на libbinder_ndk
(который поддерживается системой libbinder.so
) и связываться с библиотеками NDK, созданными записями aidl_interface
. Точные имена модулей см. в разделе Правила именования модулей .
Напишите интерфейс AIDL HAL.
Чтобы интерфейс AIDL мог использоваться между системой и поставщиком, в интерфейс необходимо внести два изменения:
- Каждое определение типа должно быть аннотировано
@VintfStability
. - Объявление
aidl_interface
должно включатьstability: "vintf",
.
Эти изменения может вносить только владелец интерфейса.
Когда вы вносите эти изменения, для работы интерфейс должен присутствовать в манифесте VINTF . Проверьте это (и связанные с ним требования, например проверку того, что выпущенные интерфейсы заморожены) с помощью теста VTS vts_treble_vintf_vendor_test
. Вы можете использовать интерфейс @VintfStability
без этих требований, вызвав AIBinder_forceDowngradeToLocalStability
в серверной части NDK, android::Stability::forceDowngradeToLocalStability
в внутренней части C++ или android.os.Binder#forceDowngradeToSystemStability
в внутренней части Java для объекта привязки перед его отправкой в другой процесс.
Кроме того, для максимальной переносимости кода и во избежание потенциальных проблем, таких как ненужные дополнительные библиотеки, отключите серверную часть CPP.
Обратите внимание, что использование backends
в следующем примере кода является правильным, поскольку имеется три серверных части (Java, NDK и CPP). Код показывает, как отключить серверную часть CPP:
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Найдите интерфейсы AIDL HAL
Стабильные AOSP интерфейсы AIDL для HALS находятся в папках aidl
в тех же базовых каталогах, что и интерфейсы HIDL:
-
hardware/interfaces
предназначены для интерфейсов, обычно предоставляемых аппаратным обеспечением. -
frameworks/hardware/interfaces
предназначен для высокоуровневых интерфейсов, предоставляемых аппаратному обеспечению. -
system/hardware/interfaces
предназначен для низкоуровневых интерфейсов, предоставляемых аппаратному обеспечению.
Поместите интерфейсы расширения в другие подкаталоги hardware/interfaces
в vendor
или hardware
.
Интерфейсы расширения
В каждом выпуске Android имеется набор официальных интерфейсов AOSP. Когда партнеры Android хотят добавить возможности к этим интерфейсам, им не следует изменять их напрямую, поскольку это делает их среду выполнения Android несовместимой со средой выполнения AOSP Android. Не изменяйте эти интерфейсы, чтобы образ GSI мог продолжать работать.
Расширения могут регистрироваться двумя разными способами:
- Во время выполнения; см. интерфейсы подключенных расширений
- Как самостоятельное решение, зарегистрированное по всему миру и в VINTF.
Однако расширение зарегистрировано, когда компоненты, специфичные для конкретного поставщика (то есть не являющиеся частью вышестоящего AOSP), используют интерфейс, конфликты слияния невозможны. Однако при внесении последующих изменений в вышестоящие компоненты AOSP могут возникнуть конфликты слияния, и рекомендуются следующие стратегии:
- Дополнения к интерфейсу AOSP в следующем выпуске.
- Дополнения к восходящему интерфейсу, обеспечивающие дополнительную гибкость (без конфликтов слияния) в следующем выпуске.
Расширение посылки: ParcelableHolder
ParcelableHolder
— это экземпляр интерфейса Parcelable
, который может содержать другой экземпляр Parcelable
.
Основной вариант использования ParcelableHolder
— сделать Parcelable
расширяемым. Например, представьте, что разработчики устройств ожидают возможности расширить Parcelable
, определенный AOSP, AospDefinedParcelable
, включив в него дополнительные функции.
Используйте интерфейс ParcelableHolder
, чтобы расширить Parcelable
дополнительными функциями. Интерфейс ParcelableHolder
содержит экземпляр Parcelable
. Если вы попытаетесь добавить поля в Parcelable
напрямую, это вызовет ошибку:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
Как видно из предыдущего кода, эта практика нарушена, поскольку поля, добавленные разработчиком устройства, могут иметь конфликт при пересмотре Parcelable
в следующих выпусках Android.
Используя ParcelableHolder
, владелец Parcelable может определить точку расширения в экземпляре Parcelable
:
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Затем разработчики устройств могут определить свой собственный экземпляр Parcelable
для своего расширения:
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Новый экземпляр Parcelable
можно прикрепить к исходному Parcelable
с помощью поля ParcelableHolder
:
// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;
ap.extension.setParcelable(op);
...
OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);
// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();
ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);
...
std::shared_ptr<OemDefinedParcelable> op_ptr;
ap.extension.getParcelable(&op_ptr);
// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);
...
std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);
// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });
ap.extension.set_parcelable(Rc::clone(&op));
...
let op = ap.extension.get_parcelable::<OemDefinedParcelable>();
Имена экземпляров сервера AIDL HAL
По соглашению службы AIDL HAL имеют имя экземпляра в формате $package.$type/$instance
. Например, экземпляр вибратора HAL зарегистрирован как android.hardware.vibrator.IVibrator/default
.
Напишите сервер AIDL HAL.
@VintfStability
Серверы AIDL должны быть объявлены в манифесте VINTF, например:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
В противном случае им следует зарегистрировать службу AIDL обычным способом. При запуске тестов VTS ожидается, что все объявленные AIDL HAL доступны.
Напишите клиент AIDL
Клиенты AIDL должны заявить о себе в матрице совместимости, например:
<hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
<name>IVibrator</name>
<instance>default</instance>
</interface>
</hal>
Преобразование существующего HAL из HIDL в AIDL
Используйте инструмент hidl2aidl
для преобразования интерфейса HIDL в AIDL.
Возможности hidl2aidl
:
- Создайте файлы AIDL (
.aidl
) на основе файлов HAL (.hal
) для данного пакета. - Создайте правила сборки для вновь созданного пакета AIDL со всеми включенными серверными модулями.
- Создайте методы перевода в серверных модулях Java, CPP и NDK для перевода из типов HIDL в типы AIDL.
- Создайте правила сборки для библиотек перевода с необходимыми зависимостями.
- Создайте статические утверждения, чтобы гарантировать, что перечислители HIDL и AIDL имеют одинаковые значения в серверных модулях CPP и NDK.
Выполните следующие шаги, чтобы преобразовать пакет файлов HAL в файлы AIDL:
Создайте инструмент, расположенный в
system/tools/hidl/hidl2aidl
.Создание этого инструмента из новейшего источника обеспечивает наиболее полный опыт. Вы можете использовать последнюю версию для преобразования интерфейсов в старых ветках из предыдущих выпусков:
m hidl2aidl
Запустите инструмент, указав выходной каталог, за которым следует конвертируемый пакет.
При необходимости используйте аргумент
-l
, чтобы добавить содержимое нового файла лицензии вверху всех созданных файлов. Обязательно используйте правильную лицензию и дату:hidl2aidl -o <output directory> -l <file with license> <package>
Например:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Прочтите сгенерированные файлы и исправьте любые проблемы с преобразованием:
-
conversion.log
содержит все необработанные проблемы, которые необходимо устранить в первую очередь. - Созданные файлы AIDL могут содержать предупреждения и предложения, требующие действий. Эти комментарии начинаются с
//
. - Очистите и внесите улучшения в пакет.
- Проверьте аннотацию
@JavaDerive
на наличие функций, которые могут потребоваться, напримерtoString
илиequals
.
-
Создавайте только те цели, которые вам нужны:
- Отключите серверные части, которые не будут использоваться. Предпочитайте серверную часть NDK серверной части CPP; см. Сборка для среды выполнения AIDL .
- Удалите библиотеки перевода или любой их сгенерированный код, который не будет использоваться.
См. Основные различия между AIDL и HIDL :
- Использование встроенного
Status
и исключений AIDL обычно улучшает интерфейс и устраняет необходимость в другом типе статуса, специфичном для интерфейса. - Аргументы интерфейса AIDL в методах по умолчанию не
@nullable
как в HIDL.
- Использование встроенного
SEPolicy для AIDL HAL
Тип службы AIDL, видимый коду поставщика, должен иметь атрибут hal_service_type
. В остальном конфигурация sepolicy такая же, как и у любой другой службы AIDL (хотя для HAL существуют специальные атрибуты). Вот пример определения контекста службы HAL:
type hal_foo_service, service_manager_type, hal_service_type;
Для большинства сервисов, определенных платформой, контекст сервиса с правильным типом уже добавлен (например, android.hardware.foo.IFoo/default
уже помечен как hal_foo_service
). Однако если клиент платформы поддерживает несколько имен экземпляров, дополнительные имена экземпляров необходимо добавить в файлы service_contexts
для конкретного устройства:
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
При создании нового типа HAL необходимо добавить атрибуты HAL. Конкретный атрибут HAL может быть связан с несколькими типами служб (каждый из которых может иметь несколько экземпляров, как только что обсуждалось). Для HAL foo
существует hal_attribute(foo)
. Этот макрос определяет атрибуты hal_foo_client
и hal_foo_server
. Для данного домена макросы hal_client_domain
и hal_server_domain
связывают домен с данным атрибутом HAL. Например, системный сервер, являющийся клиентом этого HAL, соответствует политике hal_client_domain(system_server, hal_foo)
. Сервер HAL аналогичным образом включает hal_server_domain(my_hal_domain, hal_foo)
.
Обычно для данного атрибута HAL также создается домен, например hal_foo_default
, для справки или примеров HAL. Однако некоторые устройства используют эти домены для своих собственных серверов. Различие между доменами для нескольких серверов имеет значение только в том случае, если существует несколько серверов, которые обслуживают один и тот же интерфейс и требуют разных наборов разрешений в своих реализациях. Во всех этих макросах hal_foo
не является объектом сеполитики. Вместо этого этот токен используется этими макросами для ссылки на группу атрибутов, связанных с парой клиент-сервер.
Однако до сих пор hal_foo_service
и hal_foo
(пара атрибутов из hal_attribute(foo)
) не связаны. Атрибут HAL связан со службами AIDL HAL с помощью макроса hal_attribute_service
(HAL HIDL использует макрос hal_attribute_hwservice
), например, hal_attribute_service(hal_foo, hal_foo_service)
. Это означает, что процессы hal_foo_client
могут получить доступ к HAL, а процессы hal_foo_server
могут зарегистрировать HAL. Применение этих правил регистрации осуществляется менеджером контекста ( servicemanager
).
Имена служб могут не всегда соответствовать атрибутам HAL, например, hal_attribute_service(hal_foo, hal_foo2_service)
. В общем, поскольку это подразумевает, что службы всегда используются вместе, вы можете удалить hal_foo2_service
и использовать hal_foo_service
для всех контекстов служб. Когда HAL устанавливает несколько экземпляров hal_attribute_service
, это происходит потому, что исходное имя атрибута HAL недостаточно общее и не может быть изменено.
Объединив все это, пример HAL выглядит следующим образом:
public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
Присоединенные интерфейсы расширения
Расширение можно прикрепить к любому интерфейсу связывания, будь то интерфейс верхнего уровня, зарегистрированный непосредственно в диспетчере служб, или подинтерфейс. При получении расширения вы должны подтвердить, что тип расширения соответствует ожидаемому. Вы можете устанавливать расширения только из процесса, обслуживающего связующее.
Используйте прикрепленные расширения всякий раз, когда расширение изменяет функциональность существующего HAL. Когда необходимы совершенно новые возможности, этот механизм не нужен, и вы можете зарегистрировать интерфейс расширения напрямую у менеджера служб. Присоединенные интерфейсы расширения имеют наибольший смысл, когда они присоединены к подинтерфейсам, поскольку эти иерархии могут быть глубокими или состоять из нескольких экземпляров. Использование глобального расширения для отражения иерархии интерфейса связывания другой службы требует тщательного учета, чтобы обеспечить эквивалентные возможности напрямую подключенным расширениям.
Чтобы установить расширение для подшивки, используйте следующие API:
- Серверная часть NDK:
AIBinder_setExtension
- Серверная часть Java:
android.os.Binder.setExtension
- Серверная часть CPP:
android::Binder::setExtension
- Серверная часть Rust:
binder::Binder::set_extension
Чтобы получить расширение для подшивки, используйте следующие API:
- Серверная часть NDK:
AIBinder_getExtension
- Серверная часть Java:
android.os.IBinder.getExtension
- Серверная часть CPP:
android::IBinder::getExtension
- Серверная часть Rust:
binder::Binder::get_extension
Дополнительную информацию об этих API можно найти в документации функции getExtension
в соответствующем бэкэнде. Пример использования расширений находится в файле hardware/interfaces/tests/extension/vibrator
.
Основные различия между AIDL и HIDL
При использовании AIDL HAL или интерфейсов AIDL HAL помните о различиях по сравнению с написанием HIDL HAL.
- Синтаксис языка AIDL ближе к Java. Синтаксис HIDL аналогичен синтаксису C++.
- Все интерфейсы AIDL имеют встроенные статусы ошибок. Вместо создания пользовательских типов состояния создайте целые числа постоянного состояния в файлах интерфейса и используйте
EX_SERVICE_SPECIFIC
в серверных модулях CPP и NDK иServiceSpecificException
в серверной части Java. См. Обработка ошибок . - AIDL не запускает пулы потоков автоматически при отправке объектов связывания. Вы должны запустить их вручную (см. Управление потоками ).
- AIDL не прерывается при непроверенных ошибках транспорта (HIDL
Return
прерывается при непроверенных ошибках). - AIDL может объявлять только один тип в файле.
- Аргументы AIDL могут быть указаны как
in
,out
илиinout
в дополнение к выходному параметру (синхронные обратные вызовы отсутствуют). - AIDL использует
fd
в качестве примитивного типа вместоhandle
. - HIDL использует основные версии для несовместимых изменений и второстепенные версии для совместимых изменений. В AIDL обратно совместимые изменения выполняются на месте. AIDL не имеет четкого понятия основных версий; вместо этого это включается в имена пакетов. Например, AIDL может использовать имя пакета
bluetooth2
. - AIDL по умолчанию не наследует приоритет реального времени. Функцию
setInheritRt
необходимо использовать для каждого связывателя, чтобы включить наследование приоритета в реальном времени.
Тесты Vendor Test Suite (VTS) для HAL
Android использует Vendor Test Suite (VTS) для проверки ожидаемых реализаций HAL. VTS помогает обеспечить обратную совместимость Android со старыми реализациями поставщиков. Реализации, не работающие с VTS, имеют известные проблемы совместимости, которые могут помешать им работать с будущими версиями ОС.
Существует две основные части VTS для HAL.
1. Убедитесь, что HAL на устройстве известны и ожидаются Android.
Этот набор тестов можно найти в test/vts-testcase/hal/treble/vintf
. Эти тесты отвечают за проверку:
- Каждый интерфейс
@VintfStability
, объявленный в манифесте VINTF, фиксируется на известной выпущенной версии. Это гарантирует, что обе стороны интерфейса согласуют точное определение этой версии интерфейса. Это необходимо для базовой работы. - Все HAL, объявленные в манифесте VINTF, доступны на этом устройстве. Любой клиент, обладающий достаточными разрешениями на использование объявленной службы HAL, должен иметь возможность получать и использовать эти службы в любое время.
- Все HAL, объявленные в манифесте VINTF, обслуживают версию интерфейса, объявленную в манифесте.
- На устройстве не обслуживаются устаревшие HAL. Android прекращает поддержку более ранних версий интерфейсов HAL, как описано в жизненном цикле FCM .
- На устройстве присутствуют необходимые HAL. Для правильной работы Android необходимы некоторые HAL.
2. Проверьте ожидаемое поведение каждого HAL.
Каждый интерфейс HAL имеет свои собственные тесты VTS для проверки ожидаемого поведения клиентов. Тестовые случаи выполняются для каждого экземпляра объявленного интерфейса HAL и обеспечивают определенное поведение в зависимости от реализованной версии интерфейса.
Эти тесты пытаются охватить все аспекты реализации HAL, на которые опирается платформа Android или которые могут использоваться в будущем.
Эти тесты включают проверку поддержки функций, обработку ошибок и любое другое поведение, которое клиент может ожидать от службы.
Вехи VTS для развития HAL
Ожидается, что тесты VTS будут обновляться при создании или изменении интерфейсов HAL Android.
Тесты VTS должны быть завершены и готовы к проверке реализаций поставщиков, прежде чем они будут заморожены для выпусков Android Vendor API. Они должны быть готовы до того, как интерфейсы будут заморожены, чтобы разработчики могли создавать свои реализации, проверять их и предоставлять отзывы разработчикам интерфейса HAL.
СУДС на каракатицах
Когда оборудование недоступно, Android использует Cuttlefish в качестве средства разработки интерфейсов HAL. Это позволяет масштабировать VTS и интеграционное тестирование Android.
hal_implementation_test
проверяет, что у Cuttlefish есть реализации последних версий интерфейса HAL, чтобы убедиться, что Android готов обрабатывать новые интерфейсы, а тесты VTS готовы протестировать реализации новых поставщиков, как только станут доступны новые аппаратные средства и устройства.