In Android 8.0 wurde die Architektur des Android-Betriebssystems neu gestaltet, um klare Schnittstellen zwischen der geräteunabhängigen Android-Plattform und geräte- und herstellerspezifischem Code zu definieren. Android hat bereits viele solcher Schnittstellen in Form von HAL-Schnittstellen definiert, die als C-Header in hardware/libhardware
definiert sind. HIDL hat diese HAL-Schnittstellen durch stabile, versionierte Schnittstellen ersetzt, die entweder in Java (unten beschrieben) oder client- und serverseitige HIDL-Schnittstellen in C++ sein können.
HIDL-Schnittstellen sollen hauptsächlich aus nativem Code verwendet werden. Daher konzentriert sich HIDL auf die automatische Generierung von effizientem Code in C++. Allerdings müssen HIDL-Schnittstellen auch für die Nutzung direkt aus Java verfügbar sein, da einige Android-Subsysteme (z. B. Telefonie) über Java HIDL-Schnittstellen verfügen.
Auf den Seiten in diesem Abschnitt wird das Java-Frontend für HIDL-Schnittstellen beschrieben, detailliert beschrieben, wie Dienste erstellt, registriert und verwendet werden, und erläutert, wie in Java geschriebene HALs und HAL-Clients mit dem HIDL-RPC-System interagieren.
Kunde sein
Dies ist ein Beispiel für einen Client für eine Schnittstelle IFoo
im Paket android.hardware.foo@1.0
, das als Dienstname default
und einen zusätzlichen Dienst mit dem benutzerdefinierten Dienstnamen second_impl
registriert ist.
Bibliotheken hinzufügen
Sie müssen Abhängigkeiten zur entsprechenden HIDL-Stub-Bibliothek hinzufügen, wenn Sie sie verwenden möchten. Normalerweise ist dies eine statische Bibliothek:
// in Android.bp static_libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
Wenn Sie wissen, dass Sie bereits Abhängigkeiten von diesen Bibliotheken ziehen, können Sie auch die gemeinsame Verknüpfung verwenden:
// in Android.bp libs: [ "android.hardware.foo-V1.0-java", ], // in Android.mk LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java
Zusätzliche Überlegungen zum Hinzufügen von Bibliotheken in Android 10
Wenn Sie über ein System oder eine Anbieter-App verfügen, die auf Android 10 oder höher abzielt, können Sie diese Bibliotheken statisch einbinden. Sie können auch (nur) HIDL-Klassen aus benutzerdefinierten JARs verwenden, die auf dem Gerät installiert sind, mit stabilen Java-APIs, die über den vorhandenen uses-library
Mechanismus für System-Apps verfügbar gemacht werden. Letzterer Ansatz spart Platz auf dem Gerät. Weitere Einzelheiten finden Sie unter Implementieren der Java SDK-Bibliothek . Bei älteren Apps bleibt das alte Verhalten erhalten.
Ab Android 10 sind auch „flache“ Versionen dieser Bibliotheken verfügbar. Dazu gehört die betreffende Klasse, jedoch keine der abhängigen Klassen. Beispielsweise enthält android.hardware.foo-V1.0-java-shallow
Klassen im foo-Paket, jedoch keine Klassen in android.hidl.base-V1.0-java
, das die Basisklasse aller HIDL enthält Schnittstellen. Wenn Sie eine Bibliothek erstellen, in der die Basisklassen der bevorzugten Schnittstelle bereits als Abhängigkeit verfügbar sind, können Sie Folgendes verwenden:
// in Android.bp static_libs: [ "android.hardware.foo-V1.0-java-shallow", ], // in Android.mk LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow
HIDL-Basis- und Manager-Bibliotheken sind auch nicht mehr im Boot-Klassenpfad für Apps verfügbar (zuvor wurden sie aufgrund des Delegat-First-Klassenladers von Android manchmal als versteckte API verwendet). Stattdessen wurden sie mit jarjar
in einen neuen Namensraum verschoben, und Apps, die diese verwenden (notwendigerweise private Apps), müssen ihre eigenen separaten Kopien haben. Module im Boot-Klassenpfad, die HIDL verwenden, müssen die flachen Varianten dieser Java-Bibliotheken verwenden und jarjar_rules: ":framework-jarjar-rules"
zu ihrer Android.bp
hinzufügen, um die Version dieser Bibliotheken zu verwenden, die im Boot-Klassenpfad vorhanden ist.
Ändern Ihrer Java-Quelle
Es gibt nur eine Version ( @1.0
) dieses Dienstes, daher ruft dieser Code nur diese Version ab. Informationen zum Umgang mit mehreren verschiedenen Versionen des Dienstes finden Sie unter Schnittstellenerweiterungen .
import android.hardware.foo.V1_0.IFoo; ... // retry to wait until the service starts up if it is in the manifest IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available IFoo anotherServer = IFoo.getService("second_impl", true /* retry */); server.doSomething(…);
Bereitstellung einer Dienstleistung
Framework-Code in Java muss möglicherweise Schnittstellen bereitstellen, um asynchrone Rückrufe von HALs zu empfangen.
Für die IFooCallback
Schnittstelle in Version 1.0 des Pakets android.hardware.foo
können Sie Ihre Schnittstelle mit den folgenden Schritten in Java implementieren:
- Definieren Sie Ihre Schnittstelle in HIDL.
- Öffnen Sie
/tmp/android/hardware/foo/IFooCallback.java
als Referenz. - Erstellen Sie ein neues Modul für Ihre Java-Implementierung.
- Untersuchen Sie die abstrakte Klasse
android.hardware.foo.V1_0.IFooCallback.Stub
und schreiben Sie dann eine neue Klasse, um sie zu erweitern und die abstrakten Methoden zu implementieren.
Automatisch generierte Dateien anzeigen
Um die automatisch generierten Dateien anzuzeigen, führen Sie Folgendes aus:
hidl-gen -o /tmp -Ljava \ -randroid.hardware:hardware/interfaces \ -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0
Diese Befehle generieren das Verzeichnis /tmp/android/hardware/foo/1.0
. Für die Datei hardware/interfaces/foo/1.0/IFooCallback.hal
wird dadurch die Datei /tmp/android/hardware/foo/1.0/IFooCallback.java
generiert, die die Java-Schnittstelle, den Proxy-Code und die Stubs (beide Proxy) kapselt und Stubs entsprechen der Schnittstelle).
-Lmakefile
generiert die Regeln, die diesen Befehl zur Erstellungszeit ausführen und es Ihnen ermöglichen android.hardware.foo-V1.0-java
einzuschließen und mit den entsprechenden Dateien zu verknüpfen. Ein Skript, das dies automatisch für ein Projekt voller Schnittstellen durchführt, finden Sie unter hardware/interfaces/update-makefiles.sh
. Die Pfade in diesem Beispiel sind relativ; hardware/interfaces kann ein temporäres Verzeichnis unter Ihrem Codebaum sein, damit Sie vor der Veröffentlichung eine HAL entwickeln können.
Einen Dienst ausführen
Die HAL stellt die IFoo
Schnittstelle bereit, die über die IFooCallback
Schnittstelle asynchrone Rückrufe an das Framework durchführen muss. Die IFooCallback
Schnittstelle ist nicht namentlich als erkennbarer Dienst registriert; Stattdessen muss IFoo
eine Methode wie setFooCallback(IFooCallback x)
enthalten.
Um IFooCallback
ab Version 1.0 des Pakets android.hardware.foo
einzurichten, fügen Sie android.hardware.foo-V1.0-java
zu Android.mk
hinzu. Der Code zum Ausführen des Dienstes lautet:
import android.hardware.foo.V1_0.IFoo; import android.hardware.foo.V1_0.IFooCallback.Stub; .... class FooCallback extends IFooCallback.Stub { // implement methods } .... // Get the service from which you will be receiving callbacks. // This also starts the threadpool for your callback service. IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available .... // This must be a persistent instance variable, not local, // to avoid premature garbage collection. FooCallback mFooCallback = new FooCallback(); .... // Do this once to create the callback service and tell the "foo-bar" service server.setFooCallback(mFooCallback);
Schnittstellenerweiterungen
Unter der Annahme, dass ein bestimmter Dienst die IFoo
Schnittstelle auf allen Geräten implementiert, ist es möglich, dass der Dienst auf einem bestimmten Gerät zusätzliche Funktionen bereitstellt, die in der Schnittstellenerweiterung IBetterFoo
implementiert sind, wie folgt:
interface IFoo { ... }; interface IBetterFoo extends IFoo { ... };
Durch Aufrufen von Code, der die erweiterte Schnittstelle kennt, kann die Java-Methode castFrom()
verwendet werden, um die Basisschnittstelle sicher in die erweiterte Schnittstelle umzuwandeln:
IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available IBetterFoo extendedService = IBetterFoo.castFrom(baseService); if (extendedService != null) { // The service implements the extended interface. } else { // The service implements only the base interface. }