Questa pagina descrive come registrare e rilevare i servizi e come inviare
dati a un servizio chiamando i metodi definiti nelle interfacce nei file .hal
.
Registra servizi
I server di interfaccia HIDL (oggetti che implementano l'interfaccia) possono essere registrati come servizi denominati. Il nome registrato non deve necessariamente essere correlato al nome dell'interfaccia o del pacchetto. Se non viene specificato alcun nome, viene utilizzato il nome "predefinito". Questo valore deve essere utilizzato per gli HAL che non devono registrare due implementazioni della stessa interfaccia. Ad esempio, la chiamata C++ per la registrazione del servizio definita in ogni interfaccia è:
status_t status = myFoo->registerAsService(); status_t anotherStatus = anotherFoo->registerAsService("another_foo_service"); // if needed
La versione di un'interfaccia HIDL è inclusa nell'interfaccia stessa. È associato automaticamente alla registrazione del servizio e può essere recuperato tramite una chiamata al metodo (android::hardware::IInterface::getInterfaceVersion()
) su ogni interfaccia HIDL. Gli oggetti del server non devono essere registrati e possono essere trasmessi tramite i parametri del metodo HIDL a un altro processo che effettua chiamate al metodo HIDL nel server.
Scoprire i servizi
Le richieste tramite codice client vengono effettuate per una determinata interfaccia per nome e per versione, chiamando getService
sulla classe HAL desiderata:
// C++ sp<V1_1::IFooService> service = V1_1::IFooService::getService(); sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service"); // Java V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */); V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);
Ogni versione di un'interfaccia HIDL viene trattata come un'interfaccia separata. Pertanto,
IFooService
versione 1.1 e IFooService
versione 2.2
possono essere registrate come "foo_service" e
getService("foo_service")
su entrambe le interfacce ottiene il servizio registrato
per quella interfaccia. Ecco perché, nella maggior parte dei casi, non è necessario fornire alcun parametro di nome per la registrazione o il rilevamento (ovvero il nome "predefinito").
L'oggetto interfaccia fornitore svolge un ruolo anche nel metodo di trasporto dell'interfaccia restituita. Per un'interfaccia IFoo
nel pacchetto
android.hardware.foo@1.0
, l'interfaccia restituita da
IFoo::getService
utilizza sempre il metodo di trasporto dichiarato per
android.hardware.foo
nel manifest del dispositivo, se la voce esiste;
e se il metodo di trasporto non è disponibile, viene restituito nullptr.
In alcuni casi, potrebbe essere necessario continuare immediatamente anche senza ricevere il servizio. Questo può accadere, ad esempio, quando un client vuole gestire autonomamente le notifiche dei servizi o in un programma di diagnostica (comeatrace
) che deve recuperare tutti gli hwservices. In questo caso, vengono fornite API aggiuntive come tryGetService
in C++ o
getService("instance-name", false)
in Java. L'API legacygetService
fornita in Java deve essere utilizzata anche con le notifiche del servizio. L'utilizzo di questa API non evita la condizione di gara in cui un server si registra dopo la richiesta del client con una di queste API senza tentativi.
Notifiche relative al ritiro di un servizio
I clienti che vogliono ricevere una notifica quando un servizio non è più disponibile possono ricevere notifiche di ritiro del servizio inviate dal framework. Per ricevere le notifiche, il cliente deve:
- Crea una sottoclasse della classe/interfaccia HIDL
hidl_death_recipient
(nel codice C++, non in HIDL). - Sostituisci il metodo
serviceDied()
. - Crea un'istanza di un oggetto della sottoclasse
hidl_death_recipient
. - Chiama il metodo
linkToDeath()
sul servizio da monitorare, passando l'oggetto dell'interfaccia diIDeathRecipient
. Tieni presente che questo metodo non acquisisce la proprietà del destinatario del decesso o del proxy su cui viene chiamato.
Un esempio di pseudocodice (C++ e Java sono simili):
class IMyDeathReceiver : hidl_death_recipient { virtual void serviceDied(uint64_t cookie, wp<IBase>& service) override { log("RIP service %d!", cookie); // Cookie should be 42 } }; .... IMyDeathReceiver deathReceiver = new IMyDeathReceiver(); m_importantService->linkToDeath(deathReceiver, 42);
Lo stesso destinatario del decesso può essere registrato su più servizi diversi.
Trasferimento di dati
I dati possono essere inviati a un servizio chiamando i metodi definiti nelle interfacce nei file .hal
. Esistono due tipi di metodi:
- I metodi bloccanti aspettano che il server produca un risultato.
- I metodi Oneway inviano i dati in una sola direzione e non si bloccano. Se la quantità di dati in transito nelle chiamate RPC supera i limiti di implementazione, le chiamate potrebbero bloccarsi o restituire un'indicazione di errore (il comportamento non è ancora stato determinato).
Un metodo che non restituisce un valore, ma non è dichiarato come
oneway
, continua a bloccarsi.
Tutti i metodi dichiarati in un'interfaccia HIDL vengono chiamati in una sola direzione, dall'HAL o nell'HAL. L'interfaccia non specifica in quale direzione viene chiamata. Le architetture che richiedono chiamate provenienti dall'HAL devono fornire due (o più) interfacce nel pacchetto HAL e fornire l'interfaccia appropriata da ogni processo. Le parole client e server vengono utilizzate in base alla direzione di chiamata dell'interfaccia (ad es. l'HAL può essere un server di un'interfaccia e un client di un'altra interfaccia).
Callback
La parola callback si riferisce a due concetti diversi, distinti da callback sincrono e callback asincrono.
I richiami sincroni vengono utilizzati in alcuni metodi HIDL che restituiscono dati. Un metodo HIDL che restituisce più di un valore (o restituisce un valore di tipo non primitivo) restituisce i risultati tramite una funzione di callback. Se viene restituito un solo valore ed è di tipo primitivo, non viene utilizzato un callback e il valore viene restituito dal metodo. Il server implementa i metodi HIDL e il client implementa i callback.
I callback asincroni consentono al server di un'interfaccia HIDL di eseguire chiamate. Ciò viene fatto passando un'istanza di una seconda interfaccia
tramite la prima interfaccia. Il client della prima interfaccia deve fungere da
server della seconda. Il server della prima interfaccia può chiamare i metodi sull'oggetto della seconda interfaccia. Ad esempio, un'implementazione HAL può inviare informazioni
in modo asincrono al processo che le utilizza chiamando metodi su un
oggetto interfaccia creato e servito da quel processo. I metodi nelle interfacce utilizzati per il callback asincrono potrebbero essere bloccanti (e restituire valori all'autore della chiamata) o oneway
. Per un esempio, consulta "Richiami asincroni" in
HIDL C++.
Per semplificare la proprietà della memoria, le chiamate ai metodi e i callback accettano solo parametri in
e non supportano i parametri out
o inout
.
Limiti per transazione
I limiti per transazione non vengono imposti sulla quantità di dati inviati nei metodi e nei callback HIDL. Tuttavia, le chiamate che superano i 4 KB per transazione sono considerate eccessive. In questo caso, è consigliabile riprogettare l'interfaccia HIDL indicata. Un altro limite è costituito dalle risorse disponibili per l'infrastruttura HIDL per gestire più transazioni simultanee. Più
transazioni possono essere in corso contemporaneamente a causa di più thread o
processi che inviano chiamate a un processo o a più chiamate oneway
che
non vengono gestite rapidamente dal processo di ricezione. Per impostazione predefinita, lo spazio totale massimo disponibile per tutte le transazioni simultanee è 1 MB.
In un'interfaccia ben progettata, il superamento di queste limitazioni delle risorse non dovrebbe verificarsi. In caso contrario, la chiamata che le ha superate può bloccarsi fino a quando le risorse non diventano disponibili o segnalare un errore di trasporto. Ogni occorrenza di superamento dei limiti per transazione o di overflow delle risorse di implementazione HIDL da parte delle transazioni in-flight aggregate viene registrata per facilitare il debug.
Implementazioni dei metodi
HIDL genera file di intestazione che dichiarano i tipi, i metodi e i callback necessari nella lingua di destinazione (C++ o Java). Il prototipo dei metodi e dei callback definiti da HIDL è lo stesso sia per il codice client sia per quello del server. Il sistema HIDL fornisce implementazioni proxy dei metodi sul lato chiamante che organizzano i dati per il trasporto IPC e codice stub sul lato chiamato che li passa alle implementazioni dei metodi per gli sviluppatori.
L'autore di una chiamata a una funzione (metodo HIDL o callback) è il proprietario delle strutture di dati passate alla funzione e ne mantiene la proprietà dopo la chiamata. In tutti i casi, il chiamato non deve liberare o rilasciare lo spazio di archiviazione.
- In C++, i dati potrebbero essere di sola lettura (i tentativi di scrittura possono causare un errore di segmentazione) e sono validi per la durata della chiamata. Il client può eseguire una copia approfondita dei dati per propagarli oltre la chiamata.
- In Java, il codice riceve una copia locale dei dati (un normale oggetto Java), che potrebbe conservare e modificare o consentire di essere sottoposto a garbage collection.
Trasferimento di dati non RPC
HIDL offre due modi per trasferire dati senza utilizzare una chiamata RPC: memoria condivisa e coda di messaggi rapida (FMQ), entrambe supportate solo in C++.
- Memoria condivisa. Il tipo HIDL integrato
memory
viene utilizzato per passare un oggetto che rappresenta la memoria condivisa allocata. Può essere utilizzato in un processo di ricezione per mappare la memoria condivisa. - Fast Message Queue (FMQ). HIDL fornisce un tipo di coda di messaggi basata su modelli che implementa la trasmissione di messaggi senza attesa. Non utilizza il kernel o lo schedulatore in modalità passthrough o con binder (la comunicazione tra dispositivi non ha queste proprietà). In genere, l'HAL configura la fine della coda,
creando un oggetto che può essere passato tramite RPC tramite un parametro di tipo HIDL integrato
MQDescriptorSync
oMQDescriptorUnsync
. Questo oggetto può essere utilizzato dal processo di ricezione per configurare l'altra estremità della coda.- Le code di sincronizzazione non possono superare il limite e possono avere un solo lettore.
- Le code Disaccoppia possono superare il limite e possono avere molti lettori, ciascuno dei quali deve leggere i dati in tempo o perderli.
Per ulteriori dettagli su FMQ, consulta Fast Message Queue (FMQ).