HIDL C++

Android O gestaltet das Android-Betriebssystem neu, um klare Schnittstellen zwischen der geräteunabhängigen Android-Plattform und geräte- und herstellerspezifischem Code zu definieren. Android definiert bereits viele solcher Schnittstellen in Form von HAL-Schnittstellen, definiert als C-Header in hardware/libhardware . HIDL ersetzt diese HAL-Schnittstellen durch stabile, versionierte Schnittstellen, bei denen es sich um client- und serverseitige HIDL-Schnittstellen in C++ (unten beschrieben) oder Java handeln kann.

Auf den Seiten in diesem Abschnitt werden C++-Implementierungen von HIDL-Schnittstellen beschrieben, einschließlich Details zu den Dateien, die vom hidl-gen Compiler automatisch aus den HIDL- .hal Dateien generiert werden, wie diese Dateien gepackt werden und wie diese Dateien in den C++-Code integriert werden nutzt sie.

Client- und Server-Implementierungen

HIDL-Schnittstellen verfügen über Client- und Serverimplementierungen:

  • Ein Client einer HIDL-Schnittstelle ist der Code, der die Schnittstelle nutzt, indem er darauf Methoden aufruft.
  • Ein Server ist eine Implementierung einer HIDL-Schnittstelle, die Aufrufe von Clients empfängt und (falls erforderlich) Ergebnisse zurückgibt.

Beim Übergang von libhardware HALs zu HIDL-HALs wird die HAL-Implementierung zum Server und der Prozess, der die HAL aufruft, zum Client. Standardimplementierungen können sowohl Passthrough- als auch gebundene HALs bereitstellen und sich im Laufe der Zeit ändern:

Abbildung 1. Entwicklungsfortschritt für ältere HALs.

Erstellen des HAL-Clients

Beginnen Sie damit, die HAL-Bibliotheken in das Makefile aufzunehmen:

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

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

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

Erstellen des HAL-Servers

Um die HAL-Implementierung zu erstellen, müssen Sie über die .hal Dateien verfügen, die Ihr HAL darstellen, und Sie müssen bereits Makefiles für Ihr HAL generiert haben, indem Sie -Lmakefile oder -Landroidbp auf hidl-gen verwenden ( ./hardware/interfaces/update-makefiles.sh erledigt dies für interne HAL-Dateien und ist eine gute Referenz). Wenn Sie HALs von libhardware übertragen, können Sie einen Großteil dieser Arbeit problemlos mit c2hal erledigen.

So erstellen Sie die erforderlichen Dateien zur Implementierung Ihres 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 sich die Funktion HIDL_FETCH_IModuleName in /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl( OPTIONAL_IDENTIFIER ).so befinden Dabei ist OPTIONAL_IDENTIFIER eine Zeichenfolge, die die Passthrough-Implementierung identifiziert. Die Anforderungen des Passthrough-Modus werden automatisch durch die oben genannten Befehle erfüllt, die auch das Ziel android.hardware.nfc@1.0-impl erstellen, es kann jedoch jede beliebige Erweiterung verwendet werden. Zum Beispiel verwendet android.hardware.nfc@1.0-impl-foo -foo , um sich zu differenzieren.

Wenn eine HAL eine Nebenversion oder eine Erweiterung einer anderen HAL ist, sollte die Basis-HAL zur Benennung dieser Binärdatei verwendet werden. Beispielsweise sollten android.hardware.graphics.mapper@2.1 immer noch in einer Binärdatei mit dem Namen android.hardware.graphics.mapper@2.0-impl( OPTIONAL_IDENTIFIER ) vorliegen. Normalerweise würde der OPTIONAL_IDENTIFIER hier die tatsächliche HAL-Version enthalten. Durch die Benennung der Binärdatei auf diese Weise können 2.0-Clients sie direkt abrufen und 2.1-Clients können die Implementierung hochladen.

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

#include <hidl/LegacySupport.h>

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

defaultPassthroughServiceImplementation dlopen() die bereitgestellte -impl Bibliothek und stellt sie als binderisierten Dienst bereit. Beispiel-Daemon-Code (für rein binderisierten 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 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
}

Dieser Daemon befindet sich normalerweise in $PACKAGE + "-service-suffix" (z. B. android.hardware.nfc@1.0-service ), kann sich aber auch überall befinden. Die Sepolicy für eine bestimmte Klasse von HALs ist das Attribut hal_<module> (zum Beispiel 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).