Каждый интерфейс, определенный в пакете 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 returns 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);