Ogni interfaccia definita in un pacchetto HIDL ha una propria classe C++ generata automaticamente all'interno dello spazio dei nomi del pacchetto. I client e i server gestiscono le interfacce in modi diversi:
- I server implementano le interfacce.
- I client chiamano i metodi sulle interfacce.
Le interfacce possono essere registrate per nome dal server o trasmesse come parametri ai metodi definiti da HIDL. Ad esempio, il codice del framework può fornire un'interfaccia per ricevere messaggi asincroni dall'HAL e passarla direttamente all'HAL senza registrarla.
Implementazione del server
Un server che implementa l'interfaccia IFoo
deve includere il
file di intestazione IFoo
generato automaticamente:
#include <android/hardware/samples/1.0/IFoo.h>
L'intestazione viene esportata automaticamente dalla libreria condivisa dell'interfaccia IFoo
a cui eseguire il collegamento. Esempio IFoo.hal
:
// IFoo.hal interface IFoo { someMethod() generates (vec<uint32_t>); ... }
Skeleton di esempio per un'implementazione del server dell'interfaccia 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(); } ... };
Per rendere disponibile l'implementazione di un'interfaccia di server a un client, puoi:
- Registra l'implementazione dell'interfaccia con il
hwservicemanager
(vedi i dettagli di seguito),
OPPURE
- Passa l'implementazione dell'interfaccia come argomento di un metodo dell'interfaccia (per maggiori dettagli, consulta Richiami metodi asincroni).
Quando registri l'implementazione dell'interfaccia, il processo hwservicemanager
tiene traccia delle interfacce HIDL registrate in esecuzione sul dispositivo in base al nome e alla versione. I server possono registrare un'implementazione dell'interfaccia HIDL per nome e i client possono richiedere implementazioni dei servizi per nome e versione. Questo processo serve l'interfaccia HIDL
android.hidl.manager@1.0::IServiceManager
.
Ogni file di intestazione dell'interfaccia HIDL generato automaticamente (ad esempio IFoo.h
)
ha un metodo registerAsService()
che può essere utilizzato per registrare l'implementazione dell'interfaccia con il hwservicemanager
. L'unico
argomento obbligatorio è il nome delle implementazioni dell'interfaccia, poiché i client
lo utilizzano per recuperare l'interfaccia dal hwservicemanager
in un secondo momento:
::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
tratta la combinazione di
[package@version::interface, instance_name]
come univoca per consentire a diverse interfacce (o a diverse versioni della stessa interfaccia) di registrarsi
con nomi di istanze identici senza conflitti. Se chiami
registerAsService()
con la stessa versione, interfaccia e
nome dell'istanza del pacchetto, hwservicemanager
elimina il riferimento al
servizio registrato in precedenza e utilizza quello nuovo.
Implementazione del client
Come il server, un client deve #include
ogni interfaccia
a cui fa riferimento:
#include <android/hardware/samples/1.0/IFoo.h>
Un client può ottenere un'interfaccia in due modi:
- Fino al giorno
I<InterfaceName>::getService
(tramitehwservicemanager
) - Tramite un metodo di interfaccia
Ogni file di intestazione dell'interfaccia generato automaticamente ha un metodo getService
statico che può essere utilizzato per recuperare un'istanza di servizio dal hwservicemanager
:
// getService returns nullptr if the service can't be found sp<IFoo> myFoo = IFoo::getService(); sp<IFoo> myAlternateFoo = IFoo::getService("another_foo");
Ora il client ha un'interfaccia IFoo
e può chiamare i metodi come se fosse un'implementazione di classe locale. In realtà, l'implementazione può essere eseguita nello stesso processo, in un processo diverso o addirittura su un altro dispositivo (con il remote HAL). Poiché il client ha chiamato getService
su un oggetto IFoo
incluso dalla versione 1.0
del pacchetto, hwservicemanager
restituisce un'implementazione del server solo se è compatibile con i client 1.0
. In pratica, questo significa che solo le implementazioni del server con la versione 1.n
(la versione x.(y+1)
di un'interfaccia deve estendere (eredita da) x.y
).
Inoltre, è fornito il metodo castFrom
per eseguire il trasferimento tra diverse interfacce. Questo metodo funziona effettuando una chiamata IPC all'interfaccia remota per assicurarsi che il tipo sottostante sia uguale a quello richiesto. Se il tipo richiesto non è disponibile, viene restituito nullptr
.
sp<V1_0::IFoo> foo1_0 = V1_0::IFoo::getService(); sp<V1_1::IFoo> foo1_1 = V1_1::IFoo::castFrom(foo1_0);
Callback asincroni
Molte implementazioni HAL esistenti comunicano con hardware asincrono, il che significa che hanno bisogno di un modo asincrono per notificare ai client i nuovi eventi che si sono verificati. Un'interfaccia HIDL può essere utilizzata come callback asincrono perché le funzioni dell'interfaccia HIDL possono accettare oggetti dell'interfaccia HIDL come parametri.
File di interfaccia di esempio IFooCallback.hal
:
package android.hardware.samples@1.0; interface IFooCallback { sendEvent(uint32_t event_id); sendData(vec<uint8_t> data); }
Esempio di nuovo metodo in IFoo
che accetta un parametro 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); };
Il client che utilizza l'interfaccia IFoo
è il
server dell'interfaccia IFooCallback
; fornisce un'implementazione di 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 } };
Può anche semplicemente passare questo valore a un'istanza esistente dell'interfaccia IFoo
:
sp<IFooCallback> myFooCallback = new FooCallback(); myFoo.registerCallback(myFooCallback);
Il server che implementa IFoo
lo riceve come
oggetto sp<IFooCallback>
. Può memorizzare il callback e richiamare il client ogni volta che vuole utilizzare questa interfaccia.
Destinatari di annunci di decesso
Poiché le implementazioni dei servizi possono essere eseguite in un processo diverso, può accadere che il processo che implementa un'interfaccia venga interrotto mentre il client rimane attivo.
Qualsiasi chiamata a un oggetto interfaccia ospitato in un processo non attivo non va a buon fine con un errore di trasporto (isOK()
restituisce false
). L'unico modo per recuperare da questo errore è richiedere una nuova istanza del servizio chiamando I<InterfaceName>::getService()
. Questo funziona solo se il processo che ha avuto un arresto anomalo è stato riavviato e ha registrato di nuovo i suoi servizi con il servicemanager
(il che è generalmente vero per le implementazioni HAL).
Invece di gestire questo problema in modo reattivo, i client di un'interfaccia possono anche registrare un destinatario di avviso di morte per ricevere una notifica quando un servizio non è più disponibile.
Per registrarsi a queste notifiche su un'interfaccia IFoo
recuperata, un
client può:
foo->linkToDeath(recipient, 1481 /* cookie */);
Il parametro recipient
deve essere un'implementazione dell'interfaccia android::hardware::hidl_death_recipient
fornita da HIDL, che contiene un singolo metodo serviceDied()
chiamato da un thread nel threadpool RPC quando il processo che ospita l'interfaccia termina:
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 } }
Il parametro cookie
contiene il cookie passato con
linkToDeath()
, mentre il parametro who
contiene un
puntatore debole all'oggetto che rappresenta il servizio nel client. Con la chiamata di esempio riportata sopra, cookie
è uguale a 1481 e who
è uguale a foo
.
È anche possibile annullare la registrazione di un destinatario del decesso dopo averlo registrato:
foo->unlinkToDeath(recipient);