Язык определения интерфейса HAL или HIDL (произносится как «hide-l») — это язык описания интерфейса (IDL), определяющий интерфейс между HAL и его пользователями. Он позволяет указывать типы и вызовы методов, собранные в интерфейсы и пакеты. В более широком смысле HIDL — это система для связи между кодовыми базами, которые могут быть скомпилированы независимо друг от друга. Начиная с Android 10, HIDL устарел, и Android переходит на повсеместное использование AIDL.
HIDL предназначен для межпроцессного взаимодействия (IPC). Связь между процессами называется Binderized . Для библиотек, которые должны быть связаны с процессом, также доступен сквозной режим (не поддерживается в Java).
HIDL определяет структуры данных и сигнатуры методов, организованные в интерфейсы (аналогично классу), которые собираются в пакеты. Синтаксис HIDL покажется программистам на C++ и Java знакомым, хотя и с другим набором ключевых слов. HIDL также использует аннотации в стиле Java.
HIDL-дизайн
Цель HIDL состоит в том, что инфраструктуру можно заменить без перестройки HAL. HAL будут созданы поставщиками или производителями SOC и помещены в раздел /vendor
на устройстве, что позволит заменить инфраструктуру в ее собственном разделе на OTA без перекомпиляции HAL.
Дизайн HIDL уравновешивает следующие проблемы:
- Интероперабельность . Создавайте надежно взаимодействующие интерфейсы между процессами, которые могут быть скомпилированы с различными архитектурами, наборами инструментов и конфигурациями сборки. Интерфейсы HIDL имеют версии и не могут быть изменены после публикации.
- Эффективность . HIDL пытается минимизировать количество операций копирования. Данные, определенные HIDL, доставляются в код C++ в стандартных структурах данных компоновки C++, которые можно использовать без распаковки. HIDL также предоставляет интерфейсы с общей памятью, и, поскольку RPC по своей природе несколько медленны, HIDL поддерживает два способа передачи данных без использования вызова RPC: общую память и быструю очередь сообщений (FMQ).
- Интуитивно понятный . HIDL позволяет избежать сложных вопросов владения памятью, используя только параметры
in
для RPC (см. Язык определения интерфейса Android (AIDL) ); значения, которые не могут быть эффективно возвращены из методов, возвращаются через функции обратного вызова. Ни передача данных в HIDL для передачи, ни получение данных из HIDL не меняют владельца данных — право собственности всегда остается за вызывающей функцией. Данные должны сохраняться только на время работы вызываемой функции и могут быть уничтожены сразу после возврата из вызванной функции.
Использование сквозного режима
Чтобы обновить устройства с более ранними версиями Android до Android O, вы можете обернуть как обычные (и устаревшие) HAL в новый интерфейс HIDL, который обслуживает HAL в режимах связывания и того же процесса (сквозной). Эта оболочка прозрачна как для HAL, так и для платформы Android.
Транзитный режим доступен только для клиентов и реализаций C++. Устройства, работающие под управлением более ранних версий Android, не имеют HAL, написанных на Java, поэтому Java HAL по своей сути связаны.
Сквозные заголовочные файлы
Когда файл .hal
компилируется, hidl-gen
создает дополнительный сквозной заголовочный файл BsFoo.h
в дополнение к заголовкам, используемым для связи с связующим; этот заголовок определяет функции, которые необходимо dlopen
. Поскольку сквозные HAL выполняются в том же процессе, в котором они вызываются, в большинстве случаев сквозные методы вызываются прямым вызовом функции (тот же поток). oneway
методы выполняются в своем собственном потоке, поскольку они не предназначены для ожидания обработки их HAL (это означает, что любой HAL, использующий oneway
методы в сквозном режиме, должен быть потокобезопасным).
Учитывая IFoo.hal
, BsFoo.h
оборачивает методы, сгенерированные HIDL, чтобы предоставить дополнительные функции (например, выполнение oneway
транзакций в другом потоке). Этот файл похож на BpFoo.h
, однако вместо того, чтобы передавать вызовы IPC с помощью связующего, нужные функции вызываются напрямую. Будущие реализации HAL могут предоставлять несколько реализаций, таких как FooFast HAL и FooAccurate HAL. В таких случаях будет создан файл для каждой дополнительной реализации (например, PTFooFast.cpp
и PTFooAccurate.cpp
).
Связывание сквозных HAL
Вы можете связать реализации HAL, которые поддерживают сквозной режим. Учитывая интерфейс HAL abcd@MN::IFoo
, создаются два пакета:
-
abcd@MN::IFoo-impl
. Содержит реализацию HAL и предоставляет функциюIFoo* HIDL_FETCH_IFoo(const char* name)
. На устаревших устройствах этот пакет открывается, и реализацияHIDL_FETCH_IFoo
dlopen
Вы можете сгенерировать базовый код, используяhidl-gen
и-Lc++-impl
и-Landroidbp-impl
. -
abcd@MN::IFoo-service
. Открывает сквозной HAL и регистрирует себя в качестве службы связывания, позволяя использовать одну и ту же реализацию HAL как для сквозного, так и для связывания.
Учитывая тип IFoo
, вы можете вызвать sp<IFoo> IFoo::getService(string name, bool getStub)
, чтобы получить доступ к экземпляру IFoo
. Если getStub
имеет значение true, getService
пытается открыть HAL только в сквозном режиме. Если getStub
имеет значение false, getService
пытается найти службу с привязкой; если это не удается, он пытается найти транзитную службу. Параметр getStub
никогда не должен использоваться, кроме как в defaultPassthroughServiceImplementation
. (Устройства, запускаемые с Android O, являются полностью привязанными устройствами, поэтому открытие службы в транзитном режиме запрещено.)
HIDL-грамматика
По своей структуре язык HIDL похож на C (но не использует препроцессор C). Все знаки препинания, не описанные ниже (кроме очевидного использования =
и |
), являются частью грамматики.
Примечание. Дополнительные сведения о стиле кода HIDL см. в Руководстве по стилю кода .
-
/** */
указывает на комментарий к документации. Их можно применять только к объявлениям типов, методов, полей и значений перечисления. -
/* */
указывает на многострочный комментарий. -
//
указывает комментарий до конца строки. Помимо//
, новые строки такие же, как и любые другие пробелы. - В приведенном ниже примере грамматики текст от
//
до конца строки не является частью грамматики, а вместо этого является комментарием к грамматике. -
[empty]
означает, что термин может быть пустым. -
?
после литерала или термина означает, что это необязательно. -
...
указывает на последовательность, содержащую ноль или более элементов с разделительной пунктуацией, как указано. В HIDL нет аргументов с переменным числом аргументов. - Запятые разделяют элементы последовательности.
- Точка с запятой завершает каждый элемент, включая последний элемент.
- ВЕРХНИЙ РЕГИСТР — это нетерминал.
-
italics
— это семейство токенов, такое какinteger
илиidentifier
(стандартные правила синтаксического анализа C). -
constexpr
— это константное выражение в стиле C (например,1 + 1
и1L << 3
). -
import_name
— это имя пакета или интерфейса, определяемое, как описано в HIDL Versioning . -
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
Терминология
В этом разделе используются следующие термины, относящиеся к HIDL:
связующий | Указывает, что HIDL используется для удаленных вызовов процедур между процессами, реализованными с помощью механизма, подобного Binder. См. также прохождение . |
---|---|
обратный вызов, асинхронный | Интерфейс, обслуживаемый пользователем HAL, передаваемый в HAL (через метод HIDL) и вызываемый HAL для возврата данных в любое время. |
обратный вызов, синхронный | Возвращает данные из реализации метода HIDL сервера клиенту. Не используется для методов, возвращающих void или одно примитивное значение. |
клиент | Процесс, вызывающий методы определенного интерфейса. Процесс HAL или фреймворка может быть клиентом одного интерфейса и сервером другого. См. также прохождение . |
расширяет | Указывает интерфейс, который добавляет методы и/или типы к другому интерфейсу. Интерфейс может расширять только один другой интерфейс. Может использоваться для дополнительного приращения версии в пакете с тем же именем или для нового пакета (например, расширения поставщика) для создания более старого пакета. |
генерирует | Указывает метод интерфейса, возвращающий значения клиенту. Чтобы вернуть одно не примитивное значение или более одного значения, генерируется синхронная функция обратного вызова. |
интерфейс | Коллекция методов и типов. Переведен в класс на C++ или Java. Все методы в интерфейсе вызываются в одном направлении: клиентский процесс вызывает методы, реализованные серверным процессом. |
в одну сторону | При применении к методу HIDL указывает, что метод не возвращает значений и не блокируется. |
упаковка | Коллекция интерфейсов и типов данных, разделяющих версию. |
пройти через | Режим dlopen , в котором сервер является разделяемой библиотекой, открытой клиентом. В сквозном режиме клиент и сервер — это один и тот же процесс, но разные кодовые базы. Используется только для переноса устаревших кодовых баз в модель HIDL. См. также Связанный . |
сервер | Процесс, реализующий методы интерфейса. См. также прохождение . |
транспорт | Инфраструктура HIDL, которая перемещает данные между сервером и клиентом. |
версия | Версия пакета. Состоит из двух целых чисел, старшего и младшего. Второстепенные приращения версии могут добавлять (но не изменять) типы и методы. |