Cada interface definida em um pacote HIDL tem sua própria classe C++ gerada automaticamente dentro do namespace de seu pacote. Clientes e servidores lidam com interfaces de diferentes maneiras:
- Servidores implementam interfaces.
- Os clientes chamam métodos em interfaces.
As interfaces podem ser registradas pelo nome pelo servidor ou passadas como parâmetros para métodos definidos por HIDL. Por exemplo, o código do framework pode servir uma interface para receber mensagens assíncronas do HAL e passar essa interface diretamente para o HAL sem registrá-lo.
Implementação do servidor
Um servidor que implementa a interface do IFoo
deve incluir o arquivo de cabeçalho do IFoo
que foi gerado automaticamente:
#include <android/hardware/samples/1.0/IFoo.h>
O cabeçalho é exportado automaticamente pela biblioteca compartilhada da interface do IFoo
para vincular. Exemplo IFoo.hal
:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
Exemplo de esqueleto para uma implementação de servidor da interface 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(); } ... };
Para disponibilizar a implementação de uma interface de servidor para um cliente, você pode:
- Registre a implementação da interface com o
hwservicemanager
(veja detalhes abaixo),
OU - Passe a implementação da interface como um argumento de um método de interface (para obter detalhes, consulte retornos de chamada assíncronos ).
Ao registrar a implementação da interface, o processo hwservicemanager
acompanha as interfaces HIDL registradas em execução no dispositivo por nome e versão. Os servidores podem registrar uma implementação de interface HIDL por nome e os clientes podem solicitar implementações de serviço por nome e versão. Esse processo atende à interface HIDL android.hidl.manager@1.0::IServiceManager
.
Cada arquivo de cabeçalho de interface HIDL gerado automaticamente (como IFoo.h
) tem um método registerAsService()
que pode ser usado para registrar a implementação da interface com o hwservicemanager
. O único argumento necessário é o nome das implementações da interface, pois os clientes usarão esse nome para recuperar a interface do hwservicemanager
posteriormente:
::android::sp<IFoo> myFoo = new FooImpl(); ::android::sp<IFoo> mySecondFoo = new FooAnotherImpl(); status_t status = myFoo->registerAsService(); status_t anotherStatus = mySecondFoo->registerAsService("another_foo");
O hwservicemanager
trata a combinação de [package@version::interface, instance_name]
como exclusiva para permitir que diferentes interfaces (ou diferentes versões da mesma interface) sejam registradas com nomes de instância idênticos sem conflitos. Se você chamar registerAsService()
com exatamente a mesma versão de pacote, interface e nome de instância, o hwservicemanager
descarta sua referência ao serviço registrado anteriormente e usa o novo.
Implementação do cliente
Assim como o servidor, um cliente deve #include
todas as interfaces às quais se refere:
#include <android/hardware/samples/1.0/IFoo.h>
Um cliente pode obter uma interface de duas maneiras:
- Através
I<InterfaceName>::getService
(viahwservicemanager
) - Através de um método de interface
Cada arquivo de cabeçalho de interface gerado automaticamente tem um método estático getService
que pode ser usado para recuperar uma instância de serviço do hwservicemanager
:
// getService will return nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
Agora o cliente tem uma interface IFoo
e pode chamar métodos para ela como se fosse uma implementação de classe local. Na realidade, a implementação pode ser executada no mesmo processo, em um processo diferente ou até em outro dispositivo (com comunicação remota HAL). Como o cliente chamou getService
em um objeto IFoo
incluído na versão 1.0
do pacote, o hwservicemanager
retornará uma implementação de servidor somente se essa implementação for compatível com clientes 1.0
. Na prática, isso significa que apenas implementações de servidor com versão 1.n
(versão x.(y+1)
de uma interface devem estender (herdar de) xy
).
Além disso, o método castFrom
é fornecido para converter entre diferentes interfaces. Esse método funciona fazendo uma chamada IPC para a interface remota para garantir que o tipo subjacente seja o mesmo que o tipo que está sendo solicitado. Se o tipo solicitado não estiver disponível, nullptr
será retornado.
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
Retornos de chamada assíncronos
Muitas implementações de HAL existentes falam com hardware assíncrono, o que significa que precisam de uma maneira assíncrona de notificar os clientes sobre novos eventos que ocorreram. Uma interface HIDL pode ser usada como um retorno de chamada assíncrono porque as funções de interface HIDL podem receber objetos de interface HIDL como parâmetros.
Exemplo de arquivo de interface IFooCallback.hal
:
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
Exemplo de novo método no IFoo
que recebe um parâmetro 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); };
O cliente que usa a interface IFoo
é o servidor da interface IFooCallback
; ele fornece uma implementação de 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 } };
Ele também pode simplesmente passar isso por uma instância existente da interface do IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
O servidor que implementa o IFoo
recebe isso como um objeto sp<IFooCallback>
. Ele pode armazenar o retorno de chamada e chamar de volta para o cliente sempre que quiser usar essa interface.
Destinatários da morte
Como as implementações de serviço podem ser executadas em um processo diferente, pode acontecer que o processo que implementa uma interface morra enquanto o cliente permanece ativo. Quaisquer chamadas em um objeto de interface hospedado em um processo que morreu falhará com um erro de transporte ( isOK()
retornará false). A única maneira de se recuperar dessa falha é solicitar uma nova instância do serviço chamando I<InterfaceName>::getService()
. Isso funciona apenas se o processo que travou foi reiniciado e registrou novamente seus serviços com o servicemanager
(o que geralmente é verdade para implementações HAL).
Em vez de lidar com isso de forma reativa, os clientes de uma interface também podem registrar um destinatário de falecimento para receber uma notificação quando um serviço morre. Para se registrar para essas notificações em uma interface IFoo
recuperada, um cliente pode fazer o seguinte:
foo->linkToDeath(recipient, 1481 /* cookie */);
O parâmetro de recipient
deve ser uma implementação da interface android::hardware::hidl_death_recipient
fornecida pelo HIDL, que contém um único método serviceDied()
que será chamado de um thread no threadpool RPC quando o processo que hospeda a interface morrer:
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 } }
O parâmetro cookie
contém o cookie que foi passado com linkToDeath()
, enquanto o parâmetro who
contém um ponteiro fraco para o objeto que representa o serviço no cliente. Com a chamada de amostra fornecida acima, cookie
é igual a 1481 e who
é igual a foo
.
Também é possível cancelar o registro de um beneficiário de falecimento após registrá-lo:
foo->unlinkToDeath(recipient);