HIDL C++

Android O riprogetta il sistema operativo Android per definire interfacce chiare tra la piattaforma Android indipendente dal dispositivo e il codice specifico del dispositivo e del fornitore. Android definisce già molte di queste interfacce sotto forma di interfacce HAL, definite come intestazioni C in hardware/libhardware . HIDL sostituisce queste interfacce HAL con interfacce stabili e con versione, che possono essere interfacce HIDL lato client e server in C++ (descritte di seguito) o Java .

Le pagine in questa sezione descrivono le implementazioni C++ delle interfacce HIDL, inclusi i dettagli sui file generati automaticamente dai file HIDL .hal dal compilatore hidl-gen , come questi file vengono confezionati e come integrare questi file con il codice C++ che li usa.

Implementazioni client e server

Le interfacce HIDL hanno implementazioni client e server:

  • Un client di un'interfaccia HIDL è il codice che utilizza l'interfaccia chiamando metodi su di essa.
  • Un server è un'implementazione di un'interfaccia HIDL che riceve chiamate dai client e restituisce risultati (se necessario).

Nella transizione dagli HAL libhardware agli HAL HIDL, l'implementazione dell'HAL diventa il server e il processo che chiama l'HAL diventa il client. Le implementazioni predefinite possono servire sia HAL passthrough che binderizzati e possono cambiare nel tempo:

Figura 1. Progressione dello sviluppo per HAL legacy.

Creazione del client HAL

Inizia includendo le librerie HAL nel makefile:

  • Crea: LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
  • Presto: shared_libs: [ …, android.hardware.nfc@1.0 ]

Successivamente, includi i file di intestazione HAL:

#include <android/hardware/nfc/1.0/IFoo.h>
…
// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

Creazione del server HAL

Per creare l'implementazione HAL, devi avere i file .hal che rappresentano il tuo HAL e aver già generato makefile per il tuo HAL utilizzando -Lmakefile o -Landroidbp su hidl-gen ( ./hardware/interfaces/update-makefiles.sh fa questo per file HAL interni ed è un buon riferimento). Quando si trasferiscono HAL da libhardware , è possibile eseguire facilmente gran parte di questo lavoro utilizzando c2hal.

Per creare i file necessari per implementare il tuo HAL:

PACKAGE=android.hardware.nfc@1.0
LOC=hardware/interfaces/nfc/1.0/default/
m -j hidl-gen
hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \
    -randroid.hidl:system/libhidl/transport $PACKAGE

Affinché l'HAL funzioni in modalità passthrough, è necessario che la funzione HIDL_FETCH_IModuleName risieda in /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl( OPTIONAL_IDENTIFIER ).so dove OPTIONAL_IDENTIFIER è una stringa che identifica l'implementazione passthrough. I requisiti della modalità passthrough vengono soddisfatti automaticamente dai comandi precedenti, che creano anche il target android.hardware.nfc@1.0-impl , ma è possibile utilizzare qualsiasi estensione. Ad esempio android.hardware.nfc@1.0-impl-foo utilizza -foo per differenziarsi.

Se un HAL è una versione secondaria o un'estensione di un altro HAL, è necessario utilizzare l'HAL di base per denominare questo file binario. Ad esempio, le implementazioni di android.hardware.graphics.mapper@2.1 dovrebbero essere ancora in un binario chiamato android.hardware.graphics.mapper@2.0-impl( OPTIONAL_IDENTIFIER ) . Di solito, OPTIONAL_IDENTIFIER qui includerebbe la versione effettiva dell'HAL. Denominando il file binario in questo modo, i client 2.0 possono recuperarlo direttamente e i client 2.1 possono eseguire l'upcast dell'implementazione.

Successivamente, compila gli stub con le funzionalità e configura un demone. Esempio di codice demone (che supporta il passthrough):

#include <hidl/LegacySupport.h>

int main(int /* argc */, char* /* argv */ []) {
    return defaultPassthroughServiceImplementation<INfc>("nfc");
}

defaultPassthroughServiceImplementation dlopen() la libreria -impl fornita e la fornirà come servizio binderizzato. Esempio di codice demone (per puro servizio binder):

int main(int /* argc */, char* /* argv */ []) {
    // This function must be called before you join to ensure the proper
    // number of threads are created. The threadpool will never exceed
    // size one because of this call.
    ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/);

    sp<INfc> nfc = new Nfc();
    const status_t status = nfc->registerAsService();
    if (status != ::android::OK) {
        return 1; // or handle error
    }

    // Adds this thread to the threadpool, resulting in one total
    // thread in the threadpool. We could also do other things, but
    // would have to specify 'false' to willJoin in configureRpcThreadpool.
    ::android::hardware::joinRpcThreadpool();
    return 1; // joinRpcThreadpool should never return
}

Questo demone solitamente si trova in $PACKAGE + "-service-suffix" (ad esempio, android.hardware.nfc@1.0-service ), ma potrebbe essere ovunque. La sepolicy per una classe specifica di HAL è l'attributo hal_<module> (ad esempio, hal_nfc) . Questo attributo deve essere applicato al demone che esegue un particolare HAL (se lo stesso processo serve più HAL, è possibile applicargli più attributi).