В Android 11 появилась возможность использовать AIDL для HAL в Android, что позволяет реализовывать части Android без HIDL. HAL следует переводить на использование исключительно AIDL там, где это возможно (если HAL в исходном коде используют HIDL, то HIDL должен использоваться).
HAL, использующие AIDL для связи между компонентами фреймворка, такими как компоненты в system.img , и аппаратными компонентами, такими как компоненты в vendor.img , должны использовать стабильный AIDL. Однако для связи внутри раздела, например, от одного HAL к другому, нет никаких ограничений на используемый механизм межпроцессного взаимодействия.
Мотивация
AIDL существует дольше, чем HIDL, и используется во многих других местах, например, между компонентами фреймворка Android или в приложениях. Теперь, когда AIDL поддерживает стабильность, стало возможным реализовать весь стек с помощью одной среды выполнения IPC. AIDL также имеет лучшую систему версионирования, чем HIDL. Вот некоторые преимущества AIDL:
- Использование единого языка межпроцессного взаимодействия означает, что вам нужно изучать, отлаживать, оптимизировать и обеспечивать безопасность только одного инструмента.
- AIDL поддерживает версионирование на месте для владельцев интерфейса:
- Владельцы могут добавлять методы в конец интерфейсов или поля в параллелизуемые объекты. Это упрощает версионирование кода на протяжении многих лет, а также снижает ежегодные затраты (типы можно изменять на месте, и нет необходимости в дополнительных библиотеках для каждой версии интерфейса).
- Расширения можно подключать во время выполнения, а не в системе типов, поэтому нет необходимости перебазировать нижестоящие расширения на более новые версии интерфейсов.
- Существующий интерфейс AIDL можно использовать напрямую, если его владелец решит его стабилизировать. Раньше для этого приходилось создавать полную копию интерфейса на языке HIDL.
Сборка осуществляется с использованием среды выполнения AIDL.
AIDL имеет три разных бэкенда: Java, NDK и C++. Для использования стабильной версии AIDL всегда используйте системную копию libbinder , расположенную в system/lib*/libbinder.so , и взаимодействуйте с /dev/binder . Для кода на образе vendor это означает, что libbinder (из VNDK) использовать нельзя: эта библиотека имеет нестабильный API на C++ и нестабильную внутреннюю структуру. Вместо этого, нативный код поставщика должен использовать бэкенд AIDL на основе NDK, быть связан с libbinder_ndk (который поддерживается системным libbinder.so ) и связан с библиотеками NDK, созданными записями aidl_interface . Точные имена модулей см. в разделе «Правила именования модулей» .
Напишите интерфейс AIDL HAL.
Для использования интерфейса AIDL между системой и поставщиком необходимо внести два изменения в интерфейс:
- Каждое определение типа должно быть аннотировано аннотацией
@VintfStability. - В объявлении
aidl_interfaceнеобходимо указатьstability: "vintf",.
Вносить эти изменения может только владелец интерфейса.
При внесении этих изменений интерфейс должен быть включен в манифест VINTF для корректной работы. Проверьте это (и связанные с этим требования, такие как проверка того, что освобожденные интерфейсы заморожены) с помощью теста vts_treble_vintf_vendor_test из набора тестов поставщика (VTS) . Вы можете использовать интерфейс @VintfStability без этих требований, вызвав либо AIBinder_forceDowngradeToLocalStability в бэкенде NDK, android::Stability::forceDowngradeToLocalStability в бэкенде C++, либо android.os.Binder#forceDowngradeToSystemStability в бэкенде Java для объекта Binder перед его отправкой в другой процесс.
Кроме того, для обеспечения максимальной переносимости кода и во избежание потенциальных проблем, таких как ненужные дополнительные библиотеки, отключите бэкенд C++.
В приведенном коде показано, как отключить бэкенд на языке C++:
aidl_interface: {
...
backend: {
cpp: {
enabled: false,
},
},
}
Найдите интерфейсы AIDL HAL.
Стабильные интерфейсы AIDL для HALS в AOSP находятся в папках aidl в тех же базовых каталогах, что и интерфейсы HIDL:
- В разделе
hardware/interfacesобозначаются интерфейсы, обычно предоставляемые аппаратным обеспечением. -
frameworks/hardware/interfacesпредназначен для высокоуровневых интерфейсов, предоставляемых аппаратному обеспечению. -
system/hardware/interfacesобозначает низкоуровневые интерфейсы, предоставляемые аппаратному обеспечению.
Разместите интерфейсы расширения в подкаталогах other hardware/interfaces в папке vendor или hardware .
Интерфейсы расширений
В каждом релизе Android присутствует набор официальных интерфейсов AOSP. Когда партнеры Android хотят добавить возможности в эти интерфейсы, им не следует вносить в них прямые изменения, поскольку это делает их среду выполнения Android несовместимой со средой выполнения AOSP Android. Избегайте изменения этих интерфейсов, чтобы образ GSI продолжал работать.
Расширения могут регистрироваться двумя разными способами:
- Во время выполнения; см. Подключенные интерфейсы расширений.
- Является самостоятельным продуктом, зарегистрированным на глобальном уровне и в VINTF.
Однако, если расширение зарегистрировано, и интерфейс используется компонентами, специфичными для конкретного поставщика (то есть не входящими в состав вышестоящего AOSP), конфликты слияния невозможны. Однако, если в компоненты вышестоящего AOSP вносятся изменения, могут возникнуть конфликты слияния, и в этом случае рекомендуются следующие стратегии:
- В следующем релизе мы добавим новые интерфейсы в AOSP.
- В следующем релизе будут добавлены новые интерфейсы от разработчиков, обеспечивающие дополнительную гибкость (без конфликтов слияния).
Расширение Parcelables: ParcelableHolder
ParcelableHolder — это экземпляр интерфейса Parcelable , который может содержать другой экземпляр Parcelable .
Основное назначение ParcelableHolder — сделать Parcelable расширяемым. Например, представьте, что разработчики устройств ожидают возможности расширить определенный в AOSP объект Parcelable , 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.
Серверы AIDL с @VintfStability должны быть указаны в манифесте 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 со всеми включенными бэкэндами.
- Создайте методы translate в бэкендах Java, C++ и 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 перед бэкендом C++; см. Сборка с использованием среды выполнения AIDL .
- Удалите библиотеки перевода или любой сгенерированный ими код, который не будет использоваться.
См. основные различия между AIDL и HIDL :
- Использование встроенных в AIDL
Statusи исключений обычно улучшает интерфейс и устраняет необходимость в другом типе статуса, специфичном для интерфейса. - Аргументы в методах, использующих интерфейс AIDL, по умолчанию не допускают
@nullableкак это было в HIDL.
- Использование встроенных в AIDL
Политика SEPolicy для AIDL HALs
Тип службы 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. Например, если system server является клиентом этого 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)
Подключенные интерфейсы расширения
Расширение можно прикрепить к любому интерфейсу Binder, будь то интерфейс верхнего уровня, зарегистрированный непосредственно в диспетчере служб, или подинтерфейс. При получении расширения необходимо убедиться, что его тип соответствует ожидаемому. Устанавливать расширения можно только из процесса, обслуживающего Binder.
Используйте прикрепленные расширения всякий раз, когда расширение изменяет функциональность существующего 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для каждого биндера.
Тесты для HAL
В этом разделе описаны лучшие практики тестирования HAL. Эти практики действительны даже в том случае, если интеграционный тест для вашего HAL не включен в VTS .
Android использует VTS для проверки ожидаемых реализаций HAL. VTS помогает обеспечить обратную совместимость Android со старыми реализациями от разных производителей. Реализации, не прошедшие проверку VTS, имеют известные проблемы совместимости, которые могут помешать их работе с будущими версиями ОС.
Система VTS для HAL состоит из двух основных частей.
1. Убедитесь, что HAL-компоненты на устройстве известны и ожидаются Android.
Android полагается на наличие статического, точного списка всех установленных HAL. Этот список представлен в манифесте VINTF . Специальные общеплатформенные тесты проверяют целостность слоев HAL во всей системе. Перед написанием каких-либо тестов, специфичных для HAL, следует также запустить эти тесты, поскольку они могут выявить несогласованные конфигурации VINTF в каком-либо HAL.
Этот набор тестов можно найти в test/vts-testcase/hal/treble/vintf . Если вы работаете с реализацией HAL от стороннего поставщика, используйте vts_treble_vintf_vendor_test для ее проверки. Вы можете запустить этот тест с помощью команды atest vts_treble_vintf_vendor_test .
Эти тесты отвечают за проверку:
- Каждый интерфейс
@VintfStability, объявленный в манифесте VINTF, фиксируется на известной выпущенной версии. Это гарантирует, что обе стороны интерфейса согласны с точным определением этой версии интерфейса. Это необходимо для базовой работы. - Все HAL, указанные в манифесте VINTF, доступны на этом устройстве. Любой клиент, обладающий достаточными правами для использования указанной службы HAL, должен иметь возможность получать и использовать эти службы в любое время.
- Все HAL, указанные в манифесте VINTF, обслуживают ту версию интерфейса, которую они указывают в манифесте.
- На устройстве не используются устаревшие HAL. Android прекращает поддержку более старых версий интерфейсов HAL, как описано в жизненном цикле FCM .
- Необходимые HAL-компоненты присутствуют на устройстве. Некоторые HAL-компоненты необходимы для корректной работы Android.
2. Проверьте ожидаемое поведение каждого HAL.
Каждый интерфейс HAL имеет собственные тесты VTS для проверки ожидаемого поведения клиентов. Тестовые случаи выполняются для каждого экземпляра объявленного интерфейса HAL и обеспечивают определенное поведение в зависимости от версии реализованного интерфейса.
В C++ список всех установленных в системе HAL можно получить с помощью функции android::getAidlHalInstanceNames в libaidlvintf_gtest_helper . В Rust используйте binder::get_declared_instances .
Эти тесты призваны охватить все аспекты реализации HAL, на которые опирается или может опираться в будущем платформа Android.
Эти тесты включают проверку поддержки функций, обработку ошибок и любое другое поведение, которое клиент может ожидать от сервиса.
Этапы внедрения системы VTS в разработку HAL
При создании или изменении интерфейсов HAL в Android необходимо постоянно обновлять тесты VTS (или любые другие тесты).
Тесты VTS должны быть завершены и готовы для проверки реализаций поставщиков до того, как они будут зафиксированы для выпуска Android Vendor API. Они должны быть готовы до фиксации интерфейсов, чтобы разработчики могли создать свои реализации, проверить их и предоставить обратную связь разработчикам интерфейсов HAL.
Тестирование на каракатице
Когда аппаратное обеспечение недоступно, Android использует Cuttlefish в качестве средства разработки интерфейсов HAL. Это позволяет масштабировать интеграционное тестирование Android.
Тест hal_implementation_test проверяет, что Cuttlefish использует реализации последних версий интерфейса HAL, чтобы убедиться, что Android готов к обработке новых интерфейсов, а тесты VTS готовы к проверке новых реализаций от поставщиков, как только станет доступно новое оборудование и устройства.