Interfacce

Ogni interfaccia definita in un pacchetto HIDL ha la propria classe C++ generata automaticamente all'interno dello spazio dei nomi del pacchetto. Client e server gestiscono le interfacce in diversi modi:

  • I server implementano le interfacce.
  • I client chiamano metodi sulle interfacce.

Le interfacce possono essere registrate per nome dal server o passate come parametri ai metodi definiti da HIDL. Ad esempio, il codice framework può servire un'interfaccia per ricevere messaggi asincroni dall'HAL e passare tale interfaccia direttamente all'HAL senza registrarlo.

Implementazione del server

Un server che implementa l'interfaccia IFoo deve includere il file di intestazione IFoo che è stato generato automaticamente:

#include <android/hardware/samples/1.0/IFoo.h>

L'intestazione viene automaticamente esportata dalla libreria condivisa dell'interfaccia IFoo per collegarsi. Esempio IFoo.hal :

// IFoo.hal
interface IFoo {
    someMethod() generates (vec<uint32_t>);
    ...
}

Scheletro di esempio per un'implementazione 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 a un client l'implementazione di un'interfaccia server, è possibile:

  1. Registrare l'implementazione dell'interfaccia con hwservicemanager (vedi dettagli di seguito),

    O

  2. Passa l'implementazione dell'interfaccia come argomento di un metodo di interfaccia (per i dettagli, vedi Callback asincroni ).

Durante la registrazione dell'implementazione dell'interfaccia, il processo hwservicemanager tiene traccia delle interfacce HIDL registrate in esecuzione sul dispositivo per nome e versione. I server possono registrare un'implementazione dell'interfaccia HIDL per nome e i client possono richiedere implementazioni del servizio 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 (come IFoo.h ) dispone di un metodo registerAsService() che può essere utilizzato per registrare l'implementazione dell'interfaccia con hwservicemanager . L'unico argomento richiesto è il nome delle implementazioni dell'interfaccia poiché i client utilizzeranno questo nome per recuperare l'interfaccia da hwservicemanager in seguito:

::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 versioni diverse della stessa interfaccia) di registrarsi con nomi di istanza identici senza conflitti. Se chiami registerAsService() con la stessa versione del pacchetto, interfaccia e nome di istanza, hwservicemanager elimina il riferimento al servizio precedentemente registrato e utilizza quello nuovo.

Implementazione del cliente

Proprio come fa 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:

  • Attraverso I<InterfaceName>::getService (tramite hwservicemanager )
  • Attraverso 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 del servizio da hwservicemanager :

// getService will return 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ò richiamare metodi come se fosse un'implementazione di classe locale. In realtà, l'implementazione può essere eseguita nello stesso processo, in un processo diverso o anche su un altro dispositivo (con servizi remoti HAL). Poiché il client chiamato getService su un oggetto IFoo incluso dalla versione 1.0 del pacchetto, hwservicemanager restituisce un'implementazione del server solo se tale implementazione è compatibile con i client 1.0 . In pratica, ciò significa che solo le implementazioni server con la versione 1.n (la versione x.(y+1) di un'interfaccia deve estendersi (ereditare da) xy ).

Inoltre viene fornito il metodo castFrom per eseguire il cast tra diverse interfacce. Questo metodo funziona effettuando una chiamata IPC all'interfaccia remota per assicurarsi che il tipo sottostante sia lo stesso del tipo 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 necessitano di un modo asincrono per notificare ai client i nuovi eventi che si sono verificati. Un'interfaccia HIDL può essere utilizzata come callback asincrono poiché 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 passarlo su 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 la richiamata e richiamarla nel client ogni volta che desidera utilizzare questa interfaccia.

Destinatari della morte

Poiché le implementazioni del servizio possono essere eseguite in un processo diverso, può accadere che il processo che implementa un'interfaccia muoia mentre il client rimane in vita. Qualsiasi chiamata su un oggetto interfaccia ospitato in un processo che è morto fallirà con un errore di trasporto ( isOK() restituirà false). L'unico modo per ripristinare un errore di questo tipo è richiedere una nuova istanza del servizio chiamando I<InterfaceName>::getService() . Funziona solo se il processo che si è bloccato è stato riavviato e registrato nuovamente i suoi servizi con servicemanager (il che è generalmente vero per le implementazioni HAL).

Invece di gestire la situazione in modo reattivo, i client di un'interfaccia possono anche registrare un destinatario di morte per ricevere una notifica quando un servizio muore. Per registrarsi per tali notifiche su un'interfaccia IFoo recuperata, un client può effettuare le seguenti operazioni:

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() che verrà chiamato da un thread nel pool di thread RPC quando il processo che ospita l'interfaccia muore:

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 fornita sopra, cookie è uguale a 1481 e who è uguale foo .

È anche possibile annullare la registrazione di un destinatario di morte dopo averlo registrato:

foo->unlinkToDeath(recipient);