HIDL C++

Android 8 riprogetta il sistema operativo Android per definire interfacce chiare tra piattaforma Android indipendente dal dispositivo e codice specifico del dispositivo e del fornitore. Android ha già definito molte interfacce di questo tipo sotto forma di interfacce HAL, definita come intestazioni C in hardware/libhardware. HIDL le sostituisce Interfacce HAL con interfacce stabili con controllo delle versioni, che possono essere interfacce HIDL lato server in C++ (descritte di seguito) o Java.

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

Implementazioni di client e server

Le interfacce HIDL hanno implementazioni client e server:

  • Un client di un'interfaccia HIDL è il codice che utilizza la proprietà richiamando metodi su quest'ultima.
  • Un server è un'implementazione di un'interfaccia HIDL che riceve chiamate dai clienti e restituisce i risultati (se necessario).

Durante la transizione dagli HAL libhardware agli HAL HIDL, l'HAL diventa il server e il processo che chiama all'HAL diventa il cliente. Le implementazioni predefinite possono gestire sia passthrough che binderizzate HAL e possono cambiare nel tempo:

Figura 1. Avanzamento dello sviluppo per gli HAL precedenti.

Crea il client HAL

Inizia includendo le librerie HAL nel makefile:

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

Quindi, 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 il tuo HAL e che hanno già generato makefile per l'HAL utilizzando -Lmakefile o -Landroidbp su hidl-gen (./hardware/interfaces/update-makefiles.sh lo fa per uso interno HAL ed è un buon riferimento). Durante il trasferimento su HAL da libhardware, puoi svolgere facilmente molte di queste attività usando 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 all'interno /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so dove OPTIONAL_IDENTIFIER è una stringa che identifica il passthrough implementazione. I requisiti della modalità passthrough vengono soddisfatti automaticamente precedenti, che creano anche 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'altra HAL, per assegnare un nome a questo programma binario deve essere utilizzato l'HAL di base. Ad esempio, android.hardware.graphics.mapper@2.1 implementazioni devono comunque essere in un file binario chiamato android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER). Solitamente, il valore OPTIONAL_IDENTIFIER qui include l'HAL effettivo completamente gestita. Assegnando al programma binario il nome seguente, i client 2.0 possono recuperarlo direttamente, e 2.1 possono aggiornare l'implementazione.

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

#include <hidl/LegacySupport.h>

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

defaultPassthroughServiceImplementation chiamate dlopen() per la libreria -impl fornita e lo fornisce come un servizio binderizzato. Esempio di codice daemon (per servizio binderized puro):

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 daemon solitamente risiede in $PACKAGE + "-service-suffix" (per esempio, android.hardware.nfc@1.0-service), ma potrebbe essere ovunque. Il sepolicy per uno specifico HAL è l'attributo hal_<module> (ad esempio, hal_nfc). Questo attributo deve essere applicato al daemon che esegue un un determinato HAL (se la stessa procedura pubblica più HAL, più attributi possono essere applicate a quest'ultima).