Услуги и передача данных

В этом разделе описывается, как регистрировать и обнаруживать службы, а также как отправлять данные в службу, вызывая методы, определенные в интерфейсах в файлах .hal .

Регистрация услуг

Серверы интерфейса HIDL (объекты, реализующие интерфейс) могут быть зарегистрированы как именованные службы. Зарегистрированное имя не обязательно должно быть связано с именем интерфейса или пакета. Если имя не указано, используется имя «по умолчанию»; это следует использовать для HAL, которым не нужно регистрировать две реализации одного и того же интерфейса. Например, вызов C++ для регистрации службы, определенный в каждом интерфейсе, выглядит следующим образом:

status_t status = myFoo->registerAsService();
status_t anotherStatus = anotherFoo->registerAsService("another_foo_service");  // if needed

Версия интерфейса HIDL включена в сам интерфейс. Он автоматически связывается с регистрацией службы и может быть получен с помощью вызова метода ( android::hardware::IInterface::getInterfaceVersion() ) на каждом интерфейсе HIDL. Объекты сервера не нужно регистрировать, и их можно передать через параметры метода HIDL другому процессу, который будет выполнять вызовы метода HIDL на сервере.

Обнаружение услуг

Запросы по клиентскому коду выполняются для заданного интерфейса по имени и по версии, вызывая getService для нужного класса HAL:

// C++
sp<V1_1::IFooService> service = V1_1::IFooService::getService();
sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service");
// Java
V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */);
V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);

Каждая версия интерфейса HIDL рассматривается как отдельный интерфейс. Таким образом, IFooService версии 1.1 и IFooService версии 2.2 могут быть зарегистрированы как «foo_service», а getService("foo_service") на любом интерфейсе получает зарегистрированную службу для этого интерфейса. Вот почему в большинстве случаев для регистрации или обнаружения не требуется указывать параметр имени (что означает имя «по умолчанию»).

Объект интерфейса поставщика также играет роль в транспортном методе возвращаемого интерфейса. Для интерфейса IFoo в пакете android.hardware.foo@1.0 интерфейс, возвращаемый IFoo::getService всегда использует транспортный метод, объявленный для android.hardware.foo в манифесте устройства, если запись существует; и если метод транспорта недоступен, возвращается nullptr.

В некоторых случаях может потребоваться немедленное продолжение даже без получения услуги. Это может произойти (например), когда клиент хочет сам управлять уведомлениями службы или в диагностической программе (такой как atrace ), которая должна получить все hwservices и получить их. В этом случае предоставляются дополнительные API, такие как tryGetService в C++ или getService("instance-name", false) в Java. Устаревший API getService предоставляемый в Java, также должен использоваться со служебными уведомлениями. Использование этого API не позволяет избежать состояния гонки, когда сервер регистрируется после того, как клиент запрашивает его с помощью одного из этих API без повторных попыток.

Уведомления о смерти службы

Клиенты, которые хотят получать уведомления о смерти службы, могут получать уведомления о смерти, доставляемые платформой. Для получения уведомлений клиент должен:

  1. Подкласс класса/интерфейса hidl_death_recipient (в коде C++, а не в HIDL).
  2. Переопределите его метод serviceDied() .
  3. Создайте экземпляр объекта подкласса hidl_death_recipient .
  4. Вызовите метод linkToDeath() в службе для мониторинга, передав объект интерфейса IDeathRecipient . Обратите внимание, что этот метод не становится владельцем получателя смерти или прокси-сервера, на котором он вызывается.

Пример псевдокода (С++ и Java похожи):

class IMyDeathReceiver : hidl_death_recipient {
  virtual void serviceDied(uint64_t cookie,
                           wp<IBase>& service) override {
    log("RIP service %d!", cookie);  // Cookie should be 42
  }
};
....
IMyDeathReceiver deathReceiver = new IMyDeathReceiver();
m_importantService->linkToDeath(deathReceiver, 42);

Один и тот же получатель смерти может быть зарегистрирован в нескольких разных сервисах.

Передача данных

Данные могут быть отправлены в службу путем вызова методов, определенных в интерфейсах в файлах .hal . Существует два вида методов:

  • Методы блокировки ждут, пока сервер не выдаст результат.
  • Односторонние методы отправляют данные только в одном направлении и не блокируют. Если объем передаваемых данных в вызовах RPC превышает ограничения реализации, вызовы могут либо блокироваться, либо возвращать индикацию ошибки (поведение еще не определено).

Метод, который не возвращает значение, но не объявлен как oneway , по-прежнему блокируется.

Все методы, объявленные в интерфейсе HIDL, вызываются в одном направлении, либо из HAL, либо в HAL. Интерфейс не указывает, в каком направлении он будет вызываться. Архитектуры, которым требуется, чтобы вызовы исходили из HAL, должны предоставлять два (или более) интерфейса в пакете HAL и обслуживать соответствующий интерфейс от каждого процесса. Слова клиент и сервер используются по отношению к вызывающему направлению интерфейса (т. е. HAL может быть сервером одного интерфейса и клиентом другого интерфейса).

Обратные вызовы

Слово обратный вызов относится к двум различным понятиям, различаемым синхронным обратным вызовом и асинхронным обратным вызовом .

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

Асинхронные обратные вызовы позволяют серверу интерфейса HIDL инициировать вызовы. Это делается путем передачи экземпляра второго интерфейса через первый интерфейс. Клиент первого интерфейса должен действовать как сервер второго. Сервер первого интерфейса может вызывать методы объекта второго интерфейса. Например, реализация HAL может асинхронно отправлять информацию обратно в процесс, который ее использует, вызывая методы объекта интерфейса, созданного и обслуживаемого этим процессом. Методы в интерфейсах, используемые для асинхронного обратного вызова, могут быть блокирующими (и могут возвращать значения вызывающей стороне) или oneway . Пример см. в разделе «Асинхронные обратные вызовы» в HIDL C++ .

Чтобы упростить владение памятью, вызовы методов и обратные вызовы принимают только in и не поддерживают параметры out или inout .

Лимиты на транзакцию

Ограничения для каждой транзакции не накладываются на объем данных, отправляемых в методах HIDL и обратных вызовах. Однако вызовы, превышающие 4 КБ на транзакцию, считаются чрезмерными. Если это видно, рекомендуется изменить архитектуру данного интерфейса HIDL. Еще одним ограничением являются ресурсы, доступные инфраструктуре HIDL для обработки нескольких одновременных транзакций. Несколько транзакций могут выполняться одновременно из-за того, что несколько потоков или процессов отправляют вызовы процессу или несколько oneway вызовов, которые не обрабатываются быстро принимающим процессом. Максимальное общее пространство, доступное для всех одновременных транзакций, по умолчанию составляет 1 МБ.

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

Реализации метода

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

Вызывающий функцию (метод HIDL или обратный вызов) владеет структурами данных, переданными в функцию, и сохраняет право собственности после вызова; во всех случаях вызываемому объекту не нужно освобождать или освобождать память.

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

Передача данных без RPC

В HIDL есть два способа передачи данных без использования вызова RPC: разделяемая память и быстрая очередь сообщений (FMQ), оба из которых поддерживаются только в C++.

  • Общая память . Встроенная memory типа HIDL используется для передачи объекта, представляющего выделенную разделяемую память. Может использоваться в принимающем процессе для сопоставления общей памяти.
  • Быстрая очередь сообщений (FMQ) . HIDL предоставляет шаблонный тип очереди сообщений, который реализует передачу сообщений без ожидания. Он не использует ядро ​​или планировщик в режиме сквозной передачи или связывания (между устройствами эти свойства отсутствуют). Обычно HAL устанавливает свой конец очереди, создавая объект, который можно передать через RPC через параметр встроенного HIDL типа MQDescriptorSync или MQDescriptorUnsync . Этот объект может использоваться принимающим процессом для настройки другого конца очереди.
    • Очереди синхронизации не могут переполняться и могут иметь только один считыватель.
    • Очереди несинхронизации могут переполняться и могут иметь множество читателей, каждый из которых должен вовремя считывать данные или терять их.
    Ни одному из типов не разрешено недополнение (чтение из пустой очереди не будет выполнено), и у каждого типа может быть только один модуль записи.

Дополнительные сведения о FMQ см. в разделе Быстрая очередь сообщений (FMQ) .