HIDL C++

В Android 8 была изменена архитектура ОС Android, чтобы четко разграничить независимую от устройства платформу Android и код, относящийся к устройству и поставщику. В Android уже определено множество таких интерфейсов в виде интерфейсов HAL, которые задаются в виде заголовков C в hardware/libhardware. HIDL заменяет эти интерфейсы HAL стабильными интерфейсами с версиями, которые могут быть клиентскими и серверными интерфейсами HIDL на C++ (описаны ниже) или Java.

На страницах этого раздела описаны реализации интерфейсов HIDL на C++, в том числе сведения о файлах, созданных автоматически из файлов HIDL .hal компилятором hidl-gen, о том, как эти файлы упаковываются и как интегрировать их с кодом C++, который их использует.

Реализации на стороне клиента и сервера

Интерфейсы HIDL имеют клиентские и серверные реализации:

  • Клиент интерфейса HIDL – это код, который использует интерфейс, вызывая его методы.
  • Сервер – это реализация интерфейса HIDL, которая принимает вызовы от клиентов и возвращает результаты (при необходимости).

При переходе с HAL-интерфейсов libhardware на HIDL HAL реализация HAL становится сервером, а процесс, вызывающий HAL, – клиентом. Реализации по умолчанию могут обслуживать как HAL с передачей данных, так и HAL с использованием Binder, и могут меняться со временем:

Рисунок 1. Этапы разработки устаревших HAL.

Как создать клиент HAL

Для начала включите библиотеки HAL в make-файл:

  • Марка: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Сун: shared_libs: [ …, android.hardware.nfc@1.0 ]

Затем добавьте заголовочные файлы HAL:

#include <android/hardware/nfc/1.0/IFoo.h>
…
// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

Как создать сервер HAL

Чтобы создать реализацию HAL, вам понадобятся файлы .hal, представляющие HAL, и сгенерированные для HAL файлы makefile с помощью -Lmakefile или -Landroidbp на устройстве hidl-gen (./hardware/interfaces/update-makefiles.sh делает это для внутренних файлов HAL и является хорошим примером). При переносе HAL из libhardware многие задачи можно легко выполнить с помощью c2hal.

Чтобы создать необходимые файлы для реализации HAL:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

Чтобы HAL работал в режиме сквозной передачи, функция HIDL_FETCH_IModuleName должна находиться в /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so, где OPTIONAL_IDENTIFIER – это строка, идентифицирующая реализацию сквозной передачи. Требования к режиму сквозной передачи выполняются автоматически с помощью приведенных выше команд, которые также создают целевой объект android.hardware.nfc@1.0-impl, но можно использовать любое расширение. Например, в названии android.hardware.nfc@1.0-impl-foo используется -foo.

Если HAL является промежуточной версией или расширением другого HAL, для названия этого двоичного файла следует использовать базовый HAL. Например, реализации android.hardware.graphics.mapper@2.1 по-прежнему должны находиться в двоичном файле с названием android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER). Обычно в OPTIONAL_IDENTIFIER указывается фактическая версия HAL. Если назвать двоичный файл таким образом, клиенты версии 2.0 смогут получить его напрямую, а клиенты версии 2.1 – преобразовать реализацию.

Затем заполните заглушки функциями и настройте демон. Пример кода демона (с поддержкой сквозной передачи):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

defaultPassthroughServiceImplementation вызывает dlopen() для предоставленной библиотеки -impl и предоставляет ее как сервис binderized. Пример кода демона (для сервиса, использующего только Binder):

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool never exceeds
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

Этот демон обычно находится в каталоге $PACKAGE + "-service-suffix" (например, android.hardware.nfc@1.0-service), но может быть и в другом месте. sepolicy для определенного класса HAL – это атрибут hal_<module> (например, hal_nfc)). Этот атрибут должен быть применен к демону, который запускает определенный HAL (если один и тот же процесс обслуживает несколько HAL, к нему можно применить несколько атрибутов).