HIDL

Язык определения интерфейса 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, которая перемещает данные между сервером и клиентом.
версия Версия пакета. Состоит из двух целых чисел, старшего и младшего. Второстепенные приращения версии могут добавлять (но не изменять) типы и методы.