HIDL C++

Android 8 wurde neu konzipiert, um klare Schnittstellen zwischen der geräteunabhängigen Android-Plattform und geräte- und anbieterspezifischem Code zu definieren. Android definiert bereits viele solcher Schnittstellen in Form von HAL-Schnittstellen, die in hardware/libhardware als C-Header definiert sind. HIDL ersetzt diese HAL-Schnittstellen durch stabile, versionierte Schnittstellen, die client- und serverseitige HIDL-Schnittstellen in C++ (siehe unten) oder Java sein können.

Auf den Seiten in diesem Abschnitt werden C++-Implementierungen von HIDL-Schnittstellen beschrieben. Dazu gehören Details zu den Dateien, die vom hidl-gen-Compiler automatisch aus den HIDL-.hal-Dateien generiert werden, wie diese Dateien verpackt werden und wie sie in den C++-Code eingebunden werden, in dem sie verwendet werden.

Client- und Serverimplementierungen

HIDL-Schnittstellen haben Client- und Serverimplementierungen:

  • Ein Client einer HIDL-Schnittstelle ist der Code, der die Schnittstelle verwendet, indem Methoden darauf aufgerufen werden.
  • Ein Server ist eine Implementierung einer HIDL-Schnittstelle, die Aufrufe von Clients empfängt und bei Bedarf Ergebnisse zurückgibt.

Bei der Umstellung von libhardware HALs auf HIDL HALs wird die HAL-Implementierung zum Server und der Prozess, der die HAL aufruft, zum Client. Standardimplementierungen können sowohl Passthrough- als auch Binder-HALs bereitstellen und sich im Laufe der Zeit ändern:

Abbildung 1: Entwicklungsfortschritt für ältere HALs.

HAL-Client erstellen

Fügen Sie zuerst die HAL-Bibliotheken in das Makefile ein:

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

Fügen Sie als Nächstes die HAL-Headerdateien ein:

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

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

HAL-Server erstellen

Zum Erstellen der HAL-Implementierung müssen Sie die .hal-Dateien haben, die Ihre HAL darstellen, und bereits Makefiles für Ihre HAL mit -Lmakefile oder -Landroidbp auf hidl-gen generiert haben. ./hardware/interfaces/update-makefiles.sh ist eine gute Referenz, da es dies für interne HAL-Dateien tut. Wenn Sie HALs von libhardware übertragen, können Sie einen Großteil dieser Arbeit mit c2hal erledigen.

So erstellen Sie die erforderlichen Dateien zur Implementierung Ihrer 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

Damit die HAL im Passthrough-Modus funktioniert, muss die Funktion HIDL_FETCH_IModuleName in /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so vorhanden sein. Dabei ist OPTIONAL_IDENTIFIER ein String, der die Passthrough-Implementierung identifiziert. Die Anforderungen für den Passthrough-Modus werden automatisch durch die oben genannten Befehle erfüllt, mit denen auch das android.hardware.nfc@1.0-impl-Ziel erstellt wird. Es kann jedoch jede Erweiterung verwendet werden. Beispielsweise verwendet android.hardware.nfc@1.0-impl-foo -foo, um sich abzuheben.

Wenn eine HAL eine Minorversion oder eine Erweiterung einer anderen HAL ist, sollte die Basis-HAL zum Benennen dieses Binärobjekts verwendet werden. Beispielsweise sollten android.hardware.graphics.mapper@2.1-Implementierungen weiterhin in einem Binärprogramm namens android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER) enthalten sein. Normalerweise enthält OPTIONAL_IDENTIFIER hier die tatsächliche HAL-Version. Wenn du das Binärprogramm so benennst, können 2.0-Clients es direkt abrufen und 2.1-Clients die Implementierung upcasten.

Füllen Sie als Nächstes die Stubs mit Funktionen aus und richten Sie einen Daemon ein. Beispiel für Daemon-Code (unterstützt Passthrough):

#include <hidl/LegacySupport.h>

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

defaultPassthroughServiceImplementation ruft dlopen() für die bereitgestellte -impl-Bibliothek auf und stellt sie als gebundenen Dienst bereit. Beispiel für Daemoncode (für reinen Binder-Dienst):

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
}

Dieser Daemon befindet sich normalerweise in $PACKAGE + "-service-suffix" (z. B. android.hardware.nfc@1.0-service), kann aber auch an anderer Stelle sein. Die sepolicy für eine bestimmte Klasse von HALs ist das Attribut hal_<module> (z. B. hal_nfc)). Dieses Attribut muss auf den Daemon angewendet werden, der eine bestimmte HAL ausführt. Wenn derselbe Prozess mehrere HALs bedient, können mehrere Attribute darauf angewendet werden.