Cada interface definida em um pacote HIDL tem a própria classe C++ gerada automaticamente dentro do namespace do pacote. Clientes e servidores lidam com interfaces de maneiras diferentes:
- Servidores implementam interfaces.
- Os clientes chamam métodos em interfaces.
As interfaces podem ser registradas por nome pelo servidor ou transmitidas 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 transmitir essa interface diretamente para o HAL sem registrá-la.
Implementação do servidor
Um servidor que implementa a interface IFoo
precisa incluir o
arquivo de cabeçalho IFoo
que foi gerado automaticamente:
#include <android/hardware/samples/1.0/IFoo.h>
O cabeçalho é exportado automaticamente pela biblioteca compartilhada da
interface 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 do servidor a um cliente, você pode:
- Registre a implementação da interface com o
hwservicemanager
(confira os detalhes abaixo),
OU
- Transmita a implementação da interface como um argumento de um método de interface. Para detalhes, consulte Callbacks assíncronos.
Ao registrar a implementação da interface, o
processo hwservicemanager
rastreia as interfaces HIDL registradas
que estão 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 obrigatório é o nome das implementações da interface, já que os clientes
usam esse nome para recuperar a interface do hwservicemanager
mais tarde:
::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 a mesma versão de pacote, interface
e nome de instância, o hwservicemanager
vai descartar a referência ao
serviço registrado anteriormente e usar o novo.
Implementação do cliente
Assim como o servidor, um cliente precisa #include
todas as interfaces
a que ele se refere:
#include <android/hardware/samples/1.0/IFoo.h>
Um cliente pode receber uma interface de duas maneiras:
- Por
I<InterfaceName>::getService
(usando ohwservicemanager
) - Por um método de interface
Cada arquivo de cabeçalho de interface gerado automaticamente tem um método getService
estático que pode ser usado para extrair uma instância de serviço do
hwservicemanager
:
// getService returns 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 verdade, a implementação
pode ser executada no mesmo processo, em um processo diferente ou até em outro dispositivo
(com acesso remoto HAL). Como o cliente chamou getService
em um
objeto IFoo
incluído na versão 1.0
do pacote,
o hwservicemanager
só retorna uma implementação do servidor se essa
implementação for compatível com clientes 1.0
. Na prática, isso
significa que apenas implementações de servidor com a versão 1.n
(a versão
x.(y+1)
de uma interface precisa estender (herdar de)
x.y
).
Além disso, o método castFrom
é fornecido para transmitir entre
interfaces diferentes. 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);
Callbacks assíncronos
Muitas implementações de HAL existentes se comunicam com hardware assíncrono, o que significa que elas precisam de uma maneira assíncrona para notificar os clientes sobre novos eventos que ocorreram. Uma interface HIDL pode ser usada como um callback assíncrono porque as funções de interface HIDL podem usar 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 em IFoo
que usa 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 transmitir isso sobre uma instância existente da
interface IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
O servidor que implementa IFoo
recebe isso como um
objeto sp<IFooCallback>
. Ele pode armazenar o callback e chamar
de volta ao cliente sempre que quiser usar essa interface.
Destinatários de falecimento
Como as implementações de serviço podem ser executadas em um processo diferente, pode acontecer
que o processo que implementa uma interface seja encerrado enquanto o cliente permanece ativo.
Todas as chamadas em um objeto de interface hospedado em um processo que foi encerrado falham
com um erro de transporte (isOK()
retorna false
). A única maneira de
recuperar essa falha é solicitar uma nova instância do serviço chamando
I<InterfaceName>::getService()
. Isso só funciona se
o processo que travou for reiniciado e registrar novamente os serviços com o
servicemanager
(o que geralmente é verdade para implementações do HAL).
Em vez de lidar com isso de forma reativa, os clientes de uma interface também podem
registrar um destinatário de morte para receber uma notificação quando um serviço for encerrado.
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 recipient
precisa ser uma implementação da
interface android::hardware::hidl_death_recipient
fornecida pelo HIDL,
que contém um único método serviceDied()
chamado
de uma linha de execução no pool de linhas de execução do RPC quando o processo que hospeda a interface é encerrado:
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 transmitido 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 exemplo acima, cookie
é igual a 1481 e who
é igual a foo
.
Também é possível cancelar o registro de um beneficiário falecido:
foo->unlinkToDeath(recipient);