Язык описания интерфейса HAL (HIDL) – это язык описания интерфейса (IDL), который позволяет задавать интерфейс между HAL и его пользователями. HIDL позволяет указывать типы и вызовы методов, собранные в интерфейсы и пакеты. В более широком смысле HIDL – это система для обмена данными между базами кода, которые могут компилироваться независимо друг от друга.
HIDL предназначен для межпроцессного взаимодействия (IPC). HAL, созданные с помощью HDL, называются binderized HAL, поскольку они могут взаимодействовать с другими уровнями архитектуры с помощью вызовов межпроцессного взаимодействия (IPC) связывателя. HAL-интерфейсы, использующие Binder, выполняются в отдельном процессе от клиента, который их использует. Для библиотек, которые должны быть связаны с процессом, также доступен режим сквозной передачи (не поддерживается в Java).
HIDL определяет структуры данных и сигнатуры методов, организованные в интерфейсы (похожие на классы), которые собираются в пакеты. Синтаксис HIDL похож на C++ и Java, но с другим набором ключевых слов. В HIDL также используются аннотации в стиле Java.
Терминология
В этом разделе используются следующие термины, связанные с HIDL:
| binderized | Указывает, что HIDL используется для вызовов удаленных процедур между процессами, реализованных с помощью механизма, похожего на Binder. См. также передачу. |
|---|---|
| обратный вызов, асинхронный | Интерфейс, предоставляемый пользователем HAL, передаваемый в HAL (с помощью метода HIDL) и вызываемый HAL для возврата данных в любое время. |
| callback, синхронный | Возвращает данные из реализации метода HIDL сервера клиенту. Не используется для методов, которые возвращают пустое значение или одно примитивное значение. |
| client | Процесс, который вызывает методы определенного интерфейса. Процесс HAL или фреймворка Android может быть клиентом одного интерфейса и сервером другого. См. также passthrough. |
| extends | Указывает на интерфейс, который добавляет методы и/или типы в другой интерфейс. Интерфейс может расширять только один другой интерфейс. Может использоваться для промежуточной версии в том же пакете или для нового пакета (например, расширения поставщика), созданного на основе старого. |
| генерирует | Указывает метод интерфейса, который возвращает значения клиенту. Чтобы вернуть одно непримитивное значение или несколько значений, генерируется синхронная функция обратного вызова. |
| интерфейс | Набор методов и типов. Преобразуется в класс на C++ или Java. Все методы в интерфейсе вызываются в одном направлении: клиентский процесс вызывает методы, реализованные серверным процессом. |
| oneway | При применении к методу HIDL указывает, что метод не возвращает значений и не блокирует работу. |
| пакет | Набор интерфейсов и типов данных, имеющих одну версию. |
| сквозная передача | Режим HIDL, в котором сервер является общей библиотекой, dlopenзагружаемой
клиентом. В режиме сквозной передачи клиент и сервер представляют собой один и тот же процесс, но разные базы кода. Используется только для переноса устаревших баз кода в модель HIDL.
См. также Binderized. |
| сервер | Процесс, реализующий методы интерфейса. См. также passthrough. |
| transport | Инфраструктура HIDL, которая передает данные между сервером и клиентом. |
| версия | Версия пакета. Состоит из двух целых чисел: основного и дополнительного. В промежуточных версиях могут добавляться (но не изменяться) типы и методы. |
Дизайн HIDL
Цель HIDL – обеспечить возможность замены фреймворка Android без пересборки HAL. HAL создаются поставщиками или производителями процессоров и помещаются в раздел /vendor на устройстве. Это позволяет заменять фреймворк Android, который находится в собственном разделе, с помощью OTA-обновлений без перекомпиляции HAL.
При разработке HIDL учитывались следующие факторы:
- Совместимость. Создавайте надежные совместимые интерфейсы между процессами, которые могут быть скомпилированы с использованием различных архитектур, цепочек инструментов и конфигураций сборки. Интерфейсы HIDL имеют версии и не могут быть изменены после публикации.
- Эффективность. HIDL пытается минимизировать количество операций копирования. Данные, определенные в HIDL, передаются в код C++ в виде структур данных стандартной раскладки C++, которые можно использовать без распаковки. HIDL также предоставляет интерфейсы общей памяти, а поскольку RPC по своей сути довольно медленные, HIDL поддерживает два способа передачи данных без использования вызова RPC: общую память и очередь быстрых сообщений (FMQ).
- Интуитивно понятный. HIDL позволяет избежать сложных проблем с владением памятью, используя для RPC только параметры
in(см. Android Interface Definition Language (AIDL)). Значения, которые нельзя эффективно вернуть из методов, возвращаются с помощью функций обратного вызова. Передача данных в HIDL и получение данных из HIDL не меняют права собственности на данные. Они всегда остаются у вызывающей функции. Данные должны сохраняться только на время выполнения вызванной функции и могут быть удалены сразу после ее завершения.
Как использовать режим трансляции
Чтобы обновить устройства с более ранними версиями Android до Android O, вы можете упаковать как обычные, так и устаревшие HAL в новый интерфейс HIDL, который обслуживает HAL в режимах связывания и сквозной передачи. Такая упаковка прозрачна как для HAL, так и для фреймворка Android.
Режим сквозной передачи доступен только для клиентов и реализаций на языке C++. Устройства с более ранними версиями Android не имеют HAL, написанных на Java, поэтому HAL Java по своей природе связаны.
Проходные заголовочные файлы
При компиляции файла .hal hidl-gen создает дополнительный сквозной заголовочный файл BsFoo.h в дополнение к заголовкам, используемым для связи с Binder. Этот заголовок определяет функции, которые должны быть dlopen. Поскольку сквозные HAL выполняются в том же процессе, в котором они вызываются, в большинстве случаев сквозные методы вызываются прямым вызовом функции (в том же потоке). Методы oneway выполняются в собственном потоке, поскольку они не предназначены для ожидания их обработки HAL (это означает, что любой HAL, использующий методы oneway в режиме сквозной передачи, должен быть потокобезопасным).
Используя IFoo.hal, BsFoo.h обертывает методы, сгенерированные HIDL, чтобы предоставить дополнительные функции (например, чтобы транзакции oneway выполнялись в другом потоке). Этот файл похож на BpFoo.h, но вместо передачи вызовов IPC с помощью binder вызываются нужные функции. В будущих реализациях HAL может быть несколько реализаций, например FooFast HAL и FooAccurate HAL. В таких случаях создается файл для каждой дополнительной реализации (например, PTFooFast.cpp и PTFooAccurate.cpp).
Как перевести HAL со сквозной передачей на Binder
Вы можете использовать binder для реализаций HAL, поддерживающих режим сквозной передачи. Для интерфейса HAL a.b.c.d@M.N::IFoo создаются два пакета:
a.b.c.d@M.N::IFoo-impl– содержит реализацию HAL и предоставляет функциюIFoo* HIDL_FETCH_IFoo(const char* name). На устройствах устаревших версий этот пакетdlopen, а реализация создается с помощьюHIDL_FETCH_IFoo. Вы можете сгенерировать базовый код с помощьюhidl-gen,-Lc++-implи-Landroidbp-impl.a.b.c.d@M.N::IFoo-service. Открывает HAL сквозной передачи и регистрирует себя как сервис Binder, позволяя использовать одну и ту же реализацию HAL как для сквозной передачи, так и для Binder.
Учитывая тип IFoo, вы можете вызвать sp<IFoo>
IFoo::getService(string name, bool getStub), чтобы получить доступ к экземпляру IFoo. Если getStub имеет значение true, getService пытается открыть HAL только в режиме сквозной передачи. Если значение getStub – false, getService пытается найти сервис, связанный с Binder, а если это не удается, то сервис с передачей данных. Параметр getStub никогда не следует использовать, кроме как в defaultPassthroughServiceImplementation. (Устройства, выпущенные с Android O, полностью поддерживают Binder, поэтому открывать сервис в режиме сквозной передачи запрещено.)
Грамматика HIDL
По замыслу язык HIDL похож на C (но не использует препроцессор C). Все знаки препинания, не описанные ниже (кроме очевидного использования = и |), относятся к грамматике.
Примечание. Подробные сведения о стиле кода HIDL можно найти в руководстве по стилю кода.
/** */– комментарий к документации. Их можно применять только к объявлениям типа, метода, поля и значения перечисления./* */– многострочный комментарий.//указывает на комментарий в конце строки. Помимо//, символы новой строки ничем не отличаются от других пробельных символов.- В примере грамматики ниже текст от
//до конца строки не является частью грамматики, а представляет собой комментарий к ней. [empty]означает, что термин может быть пустым.?после литерала или термина означает, что он необязателен....– последовательность, содержащая ноль или более элементов с указанными знаками препинания. В HIDL нет аргументов переменной длины.- Элементы последовательности разделяются запятыми.
- Точка с запятой ставится после каждого элемента, включая последний.
- ВЕРХНИЙ РЕГИСТР – нетерминальный символ.
italics– это семейство токенов, напримерintegerилиidentifier(стандартные правила синтаксического анализа C).constexpr– это константное выражение в стиле C (например,1 + 1и1L << 3).import_name– название пакета или интерфейса, соответствующее требованиям, описанным в разделе Версии HIDL.- Токены в нижнем регистре
wordsявляются литеральными.
Пример:
ROOT = PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... } // not for types.hal | PACKAGE IMPORTS ITEM ITEM... // only for types.hal; no method definitions ITEM = ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?; | safe_union identifier { UFIELD; UFIELD; ...}; | struct identifier { SFIELD; SFIELD; ...}; // Note - no forward declarations | union identifier { UFIELD; UFIELD; ...}; | enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar | typedef TYPE identifier; VERSION = integer.integer; PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION; PREAMBLE = interface identifier EXTENDS EXTENDS = <empty> | extends import_name // must be interface, not package GENERATES = generates (FIELD, FIELD ...) // allows the Binder interface to be used as a type // (similar to typedef'ing the final identifier) IMPORTS = [empty] | IMPORTS import import_name; TYPE = uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t | float | double | bool | string | identifier // must be defined as a typedef, struct, union, enum or import // including those defined later in the file | memory | pointer | vec<TYPE> | bitfield<TYPE> // TYPE is user-defined enum | fmq_sync<TYPE> | fmq_unsync<TYPE> | TYPE[SIZE] FIELD = TYPE identifier UFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SFIELD = TYPE identifier | safe_union identifier { FIELD; FIELD; ...}; | struct identifier { FIELD; FIELD; ...}; | union identifier { FIELD; FIELD; ...}; | safe_union identifier { FIELD; FIELD; ...} identifier; | struct identifier { FIELD; FIELD; ...} identifier; | union identifier { FIELD; FIELD; ...} identifier; SIZE = // Must be greater than zero constexpr ANNOTATIONS = [empty] | ANNOTATIONS ANNOTATION ANNOTATION = | @identifier | @identifier(VALUE) | @identifier(ANNO_ENTRY, ANNO_ENTRY ...) ANNO_ENTRY = identifier=VALUE VALUE = "any text including \" and other escapes" | constexpr | {VALUE, VALUE ...} // only in annotations ENUM_ENTRY = identifier | identifier = constexpr