Интерфейсы

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