Каждый интерфейс, определенный в пакете HIDL, имеет собственный автоматически сгенерированный класс C++ внутри пространства имен своего пакета. Клиенты и серверы взаимодействуют с интерфейсами по-разному:
- Серверы реализуют интерфейсы.
- Клиенты вызывают методы на интерфейсах.
Интерфейсы могут быть зарегистрированы сервером по имени или переданы в качестве параметров методам, определенным в HIDL. Например, код фреймворка может служить интерфейсом для получения асинхронных сообщений от HAL и передачи этого интерфейса непосредственно в HAL без его регистрации.
Реализация сервера
Сервер, реализующий интерфейс IFoo
, должен включать автоматически сгенерированный заголовочный файл IFoo
:
#include <android/hardware/samples/1.0/IFoo.h>
Заголовок автоматически экспортируется общей библиотекой интерфейса IFoo
для связывания. Пример IFoo.hal
:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
Пример скелета серверной реализации интерфейса IFoo:
// From the IFoo.h header using android::hardware::samples::V1_0::IFoo; class FooImpl : public IFoo { Return<void> someMethod(foo my_foo, someMethod_cb _cb) { vec<uint32_t> return_data; // Compute return_data _cb(return_data); return Void(); } ... };
Чтобы сделать реализацию интерфейса сервера доступной для клиента, вы можете:
- Зарегистрируйте реализацию интерфейса в
hwservicemanager
(подробности см. ниже),
ИЛИ ЖЕ - Передайте реализацию интерфейса в качестве аргумента метода интерфейса (подробности см. в разделе Асинхронные обратные вызовы ).
При регистрации реализации интерфейса процесс hwservicemanager
отслеживает зарегистрированные HIDL-интерфейсы, работающие на устройстве, по именам и версиям. Серверы могут зарегистрировать реализацию интерфейса HIDL по имени, а клиенты могут запросить реализацию службы по имени и версии. Этот процесс обслуживает интерфейс HIDL android.hidl.manager@1.0::IServiceManager
.
Каждый автоматически сгенерированный файл заголовка интерфейса HIDL (например, IFoo.h
) имеет метод registerAsService()
, который можно использовать для регистрации реализации интерфейса с помощью hwservicemanager
. Единственный обязательный аргумент — это имя реализации интерфейса, так как клиенты будут использовать это имя для получения интерфейса из hwservicemanager
позже:
::android::sp<IFoo> myFoo = new FooImpl(); ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); status_t status = myFoo->registerAsService(); status_t anotherStatus = mySecondFoo->registerAsService("another_foo");
hwservicemanager
обрабатывает комбинацию [package@version::interface, instance_name]
как уникальную, чтобы позволить различным интерфейсам (или разным версиям одного и того же интерфейса) регистрироваться с одинаковыми именами экземпляров без конфликтов. Если вы вызываете registerAsService()
с точно такой же версией пакета, интерфейсом и именем экземпляра, hwservicemanager
удаляет свою ссылку на ранее зарегистрированную службу и использует новую.
Реализация клиента
Как и сервер, клиент должен #include
каждый интерфейс, на который он ссылается:
#include <android/hardware/samples/1.0/IFoo.h>
Клиент может получить интерфейс двумя способами:
- Через
I<InterfaceName>::getService
(черезhwservicemanager
) - Через интерфейсный метод
Каждый автоматически сгенерированный заголовочный файл интерфейса имеет статический метод getService
, который можно использовать для получения экземпляра службы из hwservicemanager
:
// getService will return nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
Теперь у клиента есть интерфейс IFoo
, и он может вызывать к нему методы, как если бы это была реализация локального класса. На самом деле реализация может выполняться в том же процессе, в другом процессе или даже на другом устройстве (с удаленным взаимодействием HAL). Поскольку клиент вызвал getService
для объекта IFoo
, включенного в пакет версии 1.0
, hwservicemanager
возвращает реализацию сервера, только если эта реализация совместима с клиентами версии 1.0
. На практике это означает, что только реализации сервера с версией 1.n
(версия x.(y+1)
интерфейса должна расширяться (наследоваться от) xy
).
Кроме того, предоставляется метод castFrom
для приведения между различными интерфейсами. Этот метод работает, выполняя вызов IPC к удаленному интерфейсу, чтобы убедиться, что базовый тип совпадает с запрашиваемым типом. Если запрошенный тип недоступен, возвращается nullptr
.
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
Асинхронные обратные вызовы
Многие существующие реализации HAL взаимодействуют с асинхронным оборудованием, а это означает, что им нужен асинхронный способ уведомления клиентов о новых произошедших событиях. Интерфейс HIDL можно использовать в качестве асинхронного обратного вызова, поскольку функции интерфейса HIDL могут принимать объекты интерфейса HIDL в качестве параметров.
Пример файла интерфейса IFooCallback.hal
:
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
Пример нового метода в IFoo
, который принимает параметр IFooCallback
:
package android.hardware.samples@1.0; interface IFoo { struct Foo { int64_t someValue; handle myHandle; }; someMethod(Foo foo) generates (int32_t ret); anotherMethod() generates (vec<uint32_t>); registerCallback(IFooCallback callback); };
Клиент , использующий интерфейс IFoo
, является сервером интерфейса IFooCallback
; он обеспечивает реализацию IFooCallback
:
class FooCallback : public IFooCallback { Return<void> sendEvent(uint32_t event_id) { // process the event from the HAL } Return<void> sendData(const hidl_vec<uint8_t>& data) { // process data from the HAL } };
Он также может просто передать это существующему экземпляру интерфейса IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
Сервер, реализующий IFoo
получает это как объект sp<IFooCallback>
. Он может сохранить обратный вызов и выполнить обратный вызов клиенту всякий раз, когда он захочет использовать этот интерфейс.
Получатели смерти
Поскольку реализации службы могут выполняться в другом процессе, может случиться так, что процесс, реализующий интерфейс, умрет, а клиент останется в живых. Любые вызовы объекта интерфейса, размещенного в процессе, который умер, завершится с ошибкой транспорта ( isOK()
вернет false). Единственный способ восстановиться после такого сбоя — запросить новый экземпляр службы, вызвав I<InterfaceName>::getService()
. Это работает только в том случае, если процесс, в котором произошел сбой, был перезапущен и повторно зарегистрировал свои службы в servicemanager
служб (что обычно верно для реализаций HAL).
Вместо того, чтобы реагировать на это, клиенты интерфейса также могут зарегистрировать получателя смерти , чтобы получать уведомление, когда служба умирает. Чтобы зарегистрироваться для получения таких уведомлений на полученном интерфейсе IFoo
, клиент может сделать следующее:
foo->linkToDeath(recipient, 1481 /* cookie */);
Параметр recipient
должен быть реализацией интерфейса android::hardware::hidl_death_recipient
, предоставляемого HIDL, который содержит единственный метод serviceDied()
, который будет вызываться из потока в пуле потоков RPC, когда процесс, в котором размещен интерфейс, умирает:
class MyDeathRecipient : public android::hardware::hidl_death_recipient { virtual void serviceDied(uint64_t cookie, const android::wp<::android::hidl::base::V1_0::IBase>& who) { // Deal with the fact that the service died } }
Параметр cookie
содержит файл cookie, который был передан с помощью linkToDeath()
, тогда как параметр who
содержит слабый указатель на объект, представляющий службу в клиенте. В приведенном выше примере вызова cookie
равно 1481, а who
равно foo
.
Также можно отменить регистрацию получателя смерти после его регистрации:
foo->unlinkToDeath(recipient);