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.