Услуги и усилители; Обмен данными

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

Пример псевдокода (C++ и 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) .