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 поддерживает управление версиями на месте для владельцев интерфейса:
- Владельцы могут добавлять методы в конец интерфейсов или поля в parcelables. Это означает, что проще версионировать код на протяжении многих лет, а также годовые затраты меньше (типы можно изменять на месте, и нет необходимости в дополнительных библиотеках для каждой версии интерфейса).
- Интерфейсы расширения можно подключать во время выполнения, а не в системе типов, поэтому нет необходимости переносить нижестоящие расширения на более новые версии интерфейсов.
- Существующий интерфейс AIDL может быть использован напрямую, когда его владелец решит стабилизировать его. Раньше для этого приходилось создавать полную копию интерфейса в HIDL.
Сборка с использованием среды выполнения AIDL
AIDL имеет три различных бэкэнда: Java, NDK и CPP. Чтобы использовать стабильный AIDL, всегда используйте системную копию libbinder
в system/lib*/libbinder.so
и talk на /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
Стабильные интерфейсы AIDL AOSP для HALS находятся в папках aidl
в тех же базовых каталогах, что и интерфейсы HIDL:
-
hardware/interfaces
— для интерфейсов, обычно предоставляемых аппаратным обеспечением. -
frameworks/hardware/interfaces
— для высокоуровневых интерфейсов, предоставляемых оборудованию. -
system/hardware/interfaces
— для низкоуровневых интерфейсов, предоставляемых оборудованию.
Поместите интерфейсы расширения в другие подкаталоги hardware/interfaces
в vendor
или hardware
.
Интерфейсы расширения
Android имеет набор официальных интерфейсов AOSP с каждым релизом. Когда партнеры Android хотят добавить возможности к этим интерфейсам, они не должны изменять их напрямую, поскольку это делает их среду выполнения Android несовместимой с средой выполнения Android AOSP. Избегайте изменения этих интерфейсов, чтобы образ GSI мог продолжать работать.
Расширения можно зарегистрировать двумя способами:
- Во время выполнения; см. Прикрепленные интерфейсы расширения
- Как самостоятельный, зарегистрированный во всем мире и в VINTF
Однако расширение зарегистрировано, когда компоненты, специфичные для поставщика (т. е. не являющиеся частью вышестоящего AOSP), используют интерфейс, конфликты слияния невозможны. Однако при внесении нижестоящих изменений в вышестоящие компоненты AOSP могут возникнуть конфликты слияния, и рекомендуются следующие стратегии:
- В следующем выпуске будут реализованы дополнения к интерфейсу AOSP.
- Дополнения к интерфейсу восходящей линии, обеспечивающие большую гибкость (без конфликтов слияния) в следующем выпуске.
Расширение parcelables: 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 ожидается, что все заявленные HAL AIDL доступны.
Написать клиент 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 :
- Использование встроенных в AIDL
Status
и исключений обычно улучшает интерфейс и устраняет необходимость в другом типе статуса, специфичном для интерфейса. - Аргументы интерфейса AIDL в методах по умолчанию не являются
@nullable
, как в HIDL.
- Использование встроенных в AIDL
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
не является объектом sepolicy. Вместо этого этот токен используется этими макросами для ссылки на группу атрибутов, связанных с парой клиент-сервер.
Однако до сих пор hal_foo_service
и hal_foo
(пара атрибутов из hal_attribute(foo)
) не связаны. Атрибут HAL связан со службами AIDL HAL с помощью макроса hal_attribute_service
(HIDL HAL используют макрос 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. Некоторые HAL необходимы для корректной работы Android.
2. Проверьте ожидаемое поведение каждого HAL
Каждый интерфейс HAL имеет свои собственные тесты VTS для проверки ожидаемого поведения от своих клиентов. Тестовые случаи запускаются против каждого экземпляра объявленного интерфейса HAL и обеспечивают определенное поведение на основе версии реализованного интерфейса.
Эти тесты пытаются охватить все аспекты реализации HAL, на которые опирается фреймворк Android или на которые он может опираться в будущем.
Эти тесты включают проверку поддержки функций, обработки ошибок и любого другого поведения, которое клиент может ожидать от сервиса.
Основные этапы разработки VTS для HAL
Ожидается, что тесты VTS будут обновляться при создании или изменении интерфейсов HAL Android.
Тесты VTS должны быть завершены и готовы к проверке реализаций поставщиков до того, как они будут заморожены для выпусков API поставщиков Android. Они должны быть готовы до того, как интерфейсы будут заморожены, чтобы разработчики могли создавать свои реализации, проверять их и предоставлять обратную связь разработчикам интерфейсов HAL.
СУДС на каракатице
Когда аппаратное обеспечение недоступно, Android использует Cuttlefish в качестве средства разработки интерфейсов HAL. Это позволяет масштабировать VTS и проводить интеграционное тестирование Android.
hal_implementation_test
проверяет, что Cuttlefish имеет реализации последних версий интерфейса HAL, чтобы убедиться, что Android готов обрабатывать новые интерфейсы, а тесты VTS готовы тестировать реализации новых поставщиков, как только появятся новые аппаратные средства и устройства.