HIDL C++

Android 8 riprogetta il sistema operativo Android per definire interfacce chiare tra la piattaforma Android indipendente dal dispositivo e il codice specifico per il dispositivo e il 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 lato server in C++ (descritte di seguito) o in Java.

Le pagine di questa sezione descrivono le implementazioni C++ delle interfacce HIDL, inclusi i dettagli sui file generati automaticamente dai file .hal HIDL dal compilatore hidl-gen, sulla modalità di pacchettizzamento di questi file e su come integrarli con il codice C++ che li utilizza.

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 i relativi metodi.
  • Un server è un'implementazione di un'interfaccia HIDL che riceve chiamate dai client e restituisce risultati (se necessario).

Durante la transizione dalle HAL libhardware alle HAL HIDL, l'implementazione HAL diventa il server e il processo che chiama l'HAL diventa il client. Le implementazioni predefinite possono servire sia HAL passthrough che con binder e possono cambiare nel tempo:

Figura 1. Progressione dello sviluppo per gli HAL legacy.

Crea il client HAL

Inizia includendo le librerie HAL nel file makefile:

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

Poi, includi i file di intestazione HAL:

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

// in code:
sp<IFoo> client = IFoo::getService();
client->doThing();

Crea il server HAL

Per creare l'implementazione HAL, devi disporre dei file .hal che rappresentano l'HAL e aver già generato i file make per l'HAL utilizzando -Lmakefile o -Landroidbp su hidl-gen (./hardware/interfaces/update-makefiles.sh esegue questa operazione per i file HAL interni ed è un buon riferimento). Quando trasferisci gli HAL da libhardware, puoi svolgere gran parte di questo lavoro facilmente utilizzando c2hal.

Per creare i file necessari per implementare l'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, devi avere la funzione HIDL_FETCH_IModuleName 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 riportati sopra, che creano anche la destinazione android.hardware.nfc@1.0-impl, ma è possibile utilizzare qualsiasi estensione. Ad esempio, android.hardware.nfc@1.0-impl-foo utilizza -foo per distinguersi.

Se un HAL è una versione minore o un'estensione di un altro HAL, deve essere utilizzato l'HAL di base per assegnare un nome a questo file binario. Ad esempio, le implementazioni di android.hardware.graphics.mapper@2.1 dovrebbero essere ancora in un file binario chiamato android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER). Di solito, OPTIONAL_IDENTIFIER qui include la versione HAL effettiva. Se il file binario viene denominato 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 la funzionalità e configura un demone. Codice del daemon di esempio (supporta il passthrough):

#include <hidl/LegacySupport.h>

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

defaultPassthroughServiceImplementation chiama dlopen() per la libreria -impl fornita e la fornisce come un servizio incapsulato. Codice di esempio del demone (per un servizio completamente incapsulato):

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 never exceeds
    // 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 di solito si trova in $PACKAGE + "-service-suffix" (ad es. android.hardware.nfc@1.0-service), ma potrebbe trovarsi ovunque. sepolicy per una specifica classe di HAL è l'attributo hal_<module> (ad esempio, hal_nfc). Questo attributo deve essere applicato al daemon che esegue un determinato HAL (se lo stesso processo serve più HAL, è possibile applicare più attributi).