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