Android O gestaltet das Android-Betriebssystem neu, 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 als C-Header in hardware/libhardware
definiert sind. HIDL ersetzt diese HAL-Schnittstellen durch stabile, versionierte Schnittstellen, die client- und serverseitige HIDL-Schnittstellen in C++ (unten beschrieben) oder Java sein können.
Die Seiten in diesem Abschnitt beschreiben C++-Implementierungen von HIDL-Schnittstellen, einschließlich Details zu den Dateien, die vom hidl-gen
Compiler automatisch aus den HIDL- .hal
-Dateien generiert werden, wie diese Dateien verpackt werden und wie diese Dateien in den C++-Code integriert werden verwendet sie.
Client- und Serverimplementierungen
HIDL-Schnittstellen haben Client- und Serverimplementierungen:
- Ein Client einer HIDL-Schnittstelle ist der Code, der die Schnittstelle verwendet, indem er Methoden darauf 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 binderisierte HALs bedienen und können sich im Laufe der Zeit ändern:
Abbildung 1. Entwicklungsfortschritt für Legacy-HALs.
Erstellen des HAL-Clients
Beginnen Sie damit, die HAL-Bibliotheken in das Makefile aufzunehmen:
- Machen 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 hinzu:
#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 Ihre HAL darstellen, und Sie müssen bereits Makefiles für Ihre HAL mit -Lmakefile
oder -Landroidbp
auf hidl-gen
generiert haben ( ./hardware/interfaces/update-makefiles.sh
tut dies für internen HAL-Dateien und ist eine gute Referenz). Beim Übertragen von HALs von libhardware
können Sie einen Großteil dieser Arbeit einfach mit c2hal erledigen.
So erstellen Sie die erforderlichen Dateien zum Implementieren 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, müssen Sie die Funktion HIDL_FETCH_IModuleName in /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl( OPTIONAL_IDENTIFIER ).so
wobei OPTIONAL_IDENTIFIER eine Zeichenfolge ist, die die Passthrough-Implementierung identifiziert. Die Anforderungen für den Passthrough-Modus werden automatisch von den obigen Befehlen erfüllt, die auch das Ziel android.hardware.nfc@1.0-impl
erstellen, aber es kann jede beliebige Erweiterung verwendet werden. Zum Beispiel verwendet android.hardware.nfc@1.0-impl-foo
-foo
, um sich zu unterscheiden.
Wenn eine HAL eine Nebenversion oder eine Erweiterung einer anderen HAL ist, sollte die Basis-HAL verwendet werden, um diese Binärdatei zu benennen. Zum Beispiel sollten sich android.hardware.graphics.mapper@2.1
immer noch in einer Binärdatei namens android.hardware.graphics.mapper@2.0-impl( OPTIONAL_IDENTIFIER )
. Normalerweise würde der OPTIONAL_IDENTIFIER hier die tatsächliche HAL-Version enthalten. Indem Sie die Binärdatei so benennen, können 2.0-Clients sie direkt abrufen, und 2.1-Clients können die Implementierung upcasten.
Als nächstes füllen Sie die Stubs mit Funktionalität aus und richten 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 gebundenen Dienst bereit. Beispiel-Daemon-Code (für reinen gebundenen 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
), aber er könnte überall sein. Die Trennungsrichtlinie 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 ihm mehrere Attribute zugewiesen werden).