Интерфейсы

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

Чтобы сделать реализацию серверного интерфейса доступной клиенту, вы можете:

  1. Зарегистрируйте реализацию интерфейса с помощью hwservicemanager (подробности см. ниже),

    ИЛИ

  2. Передайте реализацию интерфейса в качестве аргумента метода интерфейса (подробности см. в разделе Асинхронные обратные вызовы ).

При регистрации реализации интерфейса процесс 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);