Как создать интерфейс HAL

Для описания всех флагов сборки, используемых для условной компиляции фреймворка, необходимо использовать HIDL. Соответствующие флаги сборки должны быть сгруппированы и включены в один файл .hal. Использование HIDL для указания элементов конфигурации включает следующие преимущества:

  • Версионность (чтобы добавить новые элементы конфигурации, поставщики и производители должны явно расширить HAL).
  • Хорошо документировано
  • Контроль доступа с помощью SELinux
  • Проверка работоспособности элементов конфигурации с помощью Vendor Test Suite (проверка диапазона, взаимозависимости между элементами и т. д.).
  • Автоматически созданные API на C++ и Java

Определять флаги сборки, используемые фреймворком

Сначала определите конфигурации сборки, которые используются для условной компиляции фреймворка, а затем откажитесь от устаревших конфигураций, чтобы уменьшить их количество. Например, для surfaceflinger определен следующий набор флагов сборки:

  • TARGET_USES_HWC2
  • TARGET_BOARD_PLATFORM
  • TARGET_DISABLE_TRIPLE_BUFFERING
  • TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS
  • NUM_FRAMEBUFFER_SURFACE_BUFFERS
  • TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK
  • VSYNC_EVENT_PHASE_OFFSET_NS
  • SF_VSYNC_EVENT_PHASE_OFFSET_NS
  • PRESENT_TIME_OFFSET_FROM_VSYNC_NS
  • MAX_VIRTUAL_DISPLAY_DIMENSION

Как создать интерфейс HAL

Конфигурации сборки для подсистемы доступны через интерфейс HAL, а интерфейсы для передачи значений конфигурации сгруппированы в пакете HAL android.hardware.configstore (сейчас используется версия 1.0). Например, чтобы создать файл интерфейса HAL для surfaceflinger, в hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

package android.hardware.configstore@1.0;

interface ISurfaceFlingerConfigs {
    // TO-BE-FILLED-BELOW
};

После создания файла .hal запустите hardware/interfaces/update-makefiles.sh, чтобы добавить новый файл .hal в файлы Android.bp и Android.mk.

Добавление функций для флагов сборки

Для каждого флага сборки добавьте в интерфейс новую функцию. Например, в следующем коде: hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal

interface ISurfaceFlingerConfigs {
    disableTripleBuffering() generates(OptionalBool ret);
    forceHwcForVirtualDisplays() generates(OptionalBool ret);
    enum NumBuffers: uint8_t {
        USE_DEFAULT = 0,
        TWO = 2,
        THREE = 3,
    };
    numFramebufferSurfaceBuffers() generates(NumBuffers ret);
    runWithoutSyncFramework() generates(OptionalBool ret);
    vsyncEventPhaseOffsetNs generates (OptionalUInt64 ret);
    presentTimeOffsetFromSyncNs generates (OptionalUInt64 ret);
    maxVirtualDisplayDimension() generates(OptionalInt32 ret);
};

При добавлении функции:

  • Будьте лаконичны. Не преобразуйте названия переменных makefile в названия функций и помните, что префиксы TARGET_ и BOARD_ больше не нужны.
  • Добавлять комментарии. Помогите разработчикам понять назначение элемента конфигурации, как он влияет на поведение фреймворка, какие у него допустимые значения и другую важную информацию.

Функция может возвращать значения следующих типов:Optional[Bool|String|Int32|UInt32|Int64|UInt64]. Типы определяются в файле types.hal в том же каталоге и заключают примитивные значения в поле, которое указывает, задано ли значение HAL. Если нет, используется значение по умолчанию.

struct OptionalString {
    bool specified;
    string value;
};

Если это необходимо, определите перечисление, которое лучше всего представляет тип элемента конфигурации, и используйте его в качестве типа возвращаемого значения. В примере выше перечисление NumBuffers ограничивает количество допустимых значений. При определении таких типов данных добавьте поле или значение перечисления (например, USE_DEFAULT), чтобы указать, задано ли значение в HAL.

Необязательно, чтобы один флаг сборки становился одной функцией в HIDL. Владельцы модулей могут объединить связанные флаги сборки в структуру и создать функцию, которая возвращает эту структуру. Это позволит уменьшить количество вызовов функций.

Например, вот как можно объединить два флага сборки в одну структуру в hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

 interface ISurfaceFlingerConfigs {
    // other functions here
    struct SyncConfigs {
        OptionalInt64 vsyncEventPhaseoffsetNs;
        OptionalInt64 presentTimeoffsetFromSyncNs;
    };
    getSyncConfigs() generates (SyncConfigs ret);
    // other functions here
};

Альтернативы для одной функции HAL

Вместо того чтобы использовать одну функцию HAL для всех флагов сборки, интерфейс HAL также предоставляет простые функции, такие как getBoolean(string key) и getInteger(string key). Фактические пары key=value хранятся в отдельных файлах, и сервис HAL предоставляет значения, считывая и анализируя эти файлы.

Этот подход прост в реализации, но не дает преимуществ, которые предоставляет HIDL (принудительное управление версиями, простота документирования, контроль доступа), поэтому мы не рекомендуем его использовать.

Один или несколько интерфейсов

При разработке интерфейса HAL для элементов конфигурации можно выбрать один из двух вариантов:

  • Единый интерфейс для всех элементов конфигурации.
  • Несколько интерфейсов, каждый из которых содержит набор связанных элементов конфигурации.

Один интерфейс проще, но при добавлении в один файл большого количества элементов конфигурации его становится сложно поддерживать. Кроме того, контроль доступа не детализирован, поэтому процесс, которому предоставлен доступ к интерфейсу, может читать все элементы конфигурации (доступ к части элементов конфигурации предоставить нельзя). Если доступ не предоставлен, элементы конфигурации не могут быть прочитаны.

Из-за этих проблем в Android используется несколько интерфейсов с одним интерфейсом HAL для группы связанных элементов конфигурации. Например, ISurfaceflingerConfigs для элементов конфигурации, связанных с surfaceflinger, и IBluetoothConfigs для элементов конфигурации, связанных с Bluetooth.