HIDL

Язык определения интерфейса HAL или HIDL — это язык описания интерфейса (IDL), определяющий интерфейс между HAL и его пользователями. HIDL позволяет указывать типы и вызовы методов, собранные в интерфейсы и пакеты. В более широком смысле HIDL — это система связи между базами кода, которые могут компилироваться независимо.

HIDL предназначен для использования для межпроцессного взаимодействия (IPC). HAL, созданные с помощью HDL, называются HAL с привязкой, поскольку они могут взаимодействовать с другими уровнями архитектуры с помощью вызовов межпроцессного взаимодействия (IPC). HAL с привязкой выполняются в отдельном процессе от клиента, который их использует. Для библиотек, которые необходимо связать с процессом, также доступен сквозной режим (не поддерживается в Java).

HIDL определяет структуры данных и сигнатуры методов, организованные в интерфейсы (аналогично классу), которые собираются в пакеты. Синтаксис HIDL выглядит знакомым программистам C++ и Java, но с другим набором ключевых слов. HIDL также использует аннотации в стиле Java.

Терминология

В этом разделе используются следующие термины, связанные с HIDL:

связующий Указывает, что HIDL используется для удаленных вызовов процедур между процессами, реализованных с помощью механизма, подобного Binder. См. также транзит .
обратный вызов, асинхронный Интерфейс, обслуживаемый пользователем HAL, передаваемый в HAL (с использованием метода HIDL) и вызываемый HAL для возврата данных в любое время.
обратный вызов, синхронный Возвращает данные из реализации метода HIDL сервера клиенту. Не используется для методов, возвращающих void или одно примитивное значение.
клиент Процесс, вызывающий методы определенного интерфейса. Процесс платформы HAL или Android может быть клиентом одного интерфейса и сервером другого. См. также транзит .
простирается Указывает интерфейс, который добавляет методы и/или типы к другому интерфейсу. Интерфейс может расширять только один другой интерфейс. Может использоваться для увеличения дополнительной версии в том же имени пакета или для нового пакета (например, расширения поставщика) для создания более старого пакета.
генерирует Указывает метод интерфейса, который возвращает значения клиенту. Чтобы вернуть одно не примитивное значение или несколько значений, генерируется синхронная функция обратного вызова.
интерфейс Коллекция методов и типов. Переводится в класс на C++ или Java. Все методы в интерфейсе вызываются в одном направлении: клиентский процесс вызывает методы, реализованные серверным процессом.
Одностороннее движение При применении к методу HIDL указывает, что метод не возвращает значений и не блокируется.
упаковка Коллекция интерфейсов и типов данных, имеющих одну версию.
сквозной Режим HIDL, в котором сервер представляет собой общую библиотеку, dlopen клиентом. В транзитном режиме клиент и сервер представляют собой один и тот же процесс, но с разными базами кода. Используется только для включения устаревших кодовых баз в модель HIDL. См. также Binderized .
сервер Процесс, реализующий методы интерфейса. См. также транзит .
транспорт HIDL-инфраструктура, которая перемещает данные между сервером и клиентом.
версия Версия пакета. Состоит из двух целых чисел: старшего и младшего. Незначительные приращения версии могут добавлять (но не изменять) типы и методы.

HIDL-дизайн

Цель HIDL состоит в том, чтобы инфраструктуру Android можно было заменить без необходимости пересборки HAL. HAL создаются поставщиками или производителями SOC и помещаются в раздел /vendor на устройстве, что позволяет заменить платформу Android в ее собственном разделе на 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.

Режим Passthrough доступен только для клиентов и реализаций C++. Устройства под управлением более ранних версий Android не имеют HAL, написанных на Java, поэтому HAL Java по своей сути связаны с привязкой.

При компиляции файла .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) . На устаревших устройствах этот пакет dlopen , и его реализация создается с помощью HIDL_FETCH_IFoo . Вы можете сгенерировать базовый код, используя 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 .
  • 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