HIDL Java

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

HIDL-Schnittstellen sind in erster Linie für die Verwendung in nativem Code vorgesehen. Daher liegt der Schwerpunkt von HIDL auf der automatischen Generierung effizienten Codes in C++. HIDL-Schnittstellen müssen jedoch auch direkt in Java verwendet werden können, da einige Android-Subsysteme (z. B. Telephony) Java-HIDL-Schnittstellen haben.

Auf den Seiten in diesem Abschnitt wird das Java-Frontend für HIDL-Schnittstellen beschrieben. Außerdem erfahren Sie, wie Sie Dienste erstellen, registrieren und verwenden. Außerdem wird erläutert, wie HALs und HAL-Clients, die in Java geschrieben sind, mit dem HIDL-RPC-System interagieren.

Beispiel für einen Kunden

Dies ist ein Beispiel für einen Client für eine Schnittstelle IFoo im Paket android.hardware.foo@1.0, der als Dienstname default und als zusätzlicher Dienst mit dem benutzerdefinierten Dienstnamen second_impl registriert ist.

Bibliotheken hinzufügen

Wenn Sie sie verwenden möchten, müssen Sie Abhängigkeiten von der entsprechenden HIDL-Stub-Bibliothek hinzufügen. 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 einbinden, 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

Weitere Hinweise zum Hinzufügen von Bibliotheken in Android 10

Wenn Sie eine System- oder Anbieter-App haben, die auf Android 10 oder höher ausgerichtet ist, können Sie diese Bibliotheken statisch einbinden. Sie können auch (nur) HIDL-Klassen aus benutzerdefinierten JARs verwenden, die auf dem Gerät mit stabilen Java APIs installiert sind, die über den vorhandenen uses-library-Mechanismus für System-Apps verfügbar gemacht werden. Letzteres spart Speicherplatz auf dem Gerät. Weitere Informationen finden Sie unter Java SDK-Bibliothek implementieren. Bei älteren Apps bleibt das alte Verhalten erhalten.

Ab Android 10 sind auch „flache“ Versionen dieser Bibliotheken verfügbar. Dazu gehören die betreffende Klasse, aber keine der abhängigen Klassen. Beispielsweise enthält android.hardware.foo-V1.0-java-shallow Klassen im Paket „foo“, aber keine Klassen in android.hidl.base-V1.0-java, das die Basisklasse aller HIDL-Schnittstellen enthält. 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 ‑Managerbibliotheken sind auch nicht mehr im Boot-Classpath für Apps verfügbar. Bisher wurden sie aufgrund des delegierten Classloaders von Android manchmal als ausgeblendete API verwendet. Stattdessen wurden sie in einen neuen Namespace mit jarjar verschoben. Apps, die diese verwenden (zwingend private Apps), müssen eigene Kopien haben. Module im Boot-Klassenpfad, die HIDL verwenden, müssen die schlanken 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.

Java-Quellcode ändern

Es gibt nur eine Version (@1.0) dieses Dienstes. Daher wird mit diesem Code nur diese Version abgerufen. Informationen zum Umgang mit mehreren verschiedenen Versionen des Dienstes finden Sie unter Benutzeroberflächenerweiterungen.

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(…);

Einen Dienst bereitstellen

Framework-Code in Java muss möglicherweise Schnittstellen bereitstellen, um asynchrone Rückrufe von HALs zu empfangen.

Für die IFooCallback-Oberfläche in Version 1.0 des android.hardware.foo-Pakets können Sie die Benutzeroberfläche mit den folgenden Schritten in Java implementieren:

  1. Definieren Sie die Schnittstelle in HIDL.
  2. Öffnen Sie /tmp/android/hardware/foo/IFooCallback.java als Referenz.
  3. Erstellen Sie ein neues Modul für Ihre Java-Implementierung.
  4. Sehen Sie sich die abstrakte Klasse android.hardware.foo.V1_0.IFooCallback.Stub an und schreiben Sie dann eine neue Klasse, um sie zu erweitern und die abstrakten Methoden zu implementieren.

Automatisch erstellte Dateien ansehen

Führen Sie Folgendes aus, um die automatisch generierten Dateien aufzurufen:

hidl-gen -o /tmp -Ljava \
  -randroid.hardware:hardware/interfaces \
  -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0

Mit diesen Befehlen wird das Verzeichnis /tmp/android/hardware/foo/1.0 erstellt. Für die Datei hardware/interfaces/foo/1.0/IFooCallback.hal wird die Datei /tmp/android/hardware/foo/1.0/IFooCallback.java generiert, die die Java-Schnittstelle, den Proxycode und die Stubs kapselt (sowohl Proxy als auch Stubs entsprechen der Schnittstelle).

-Lmakefile generiert die Regeln, mit denen dieser Befehl zum Zeitpunkt des Builds ausgeführt wird. So können Sie android.hardware.foo-V1.0-java einbinden und mit den entsprechenden Dateien verknüpfen. Ein Script, das dies automatisch für ein Projekt mit vielen Oberflächen erledigt, finden Sie unter hardware/interfaces/update-makefiles.sh. Die Pfade in diesem Beispiel sind relativ. „hardware/interfaces“ kann ein temporäres Verzeichnis in Ihrem Codebaum sein, damit Sie eine HAL entwickeln können, bevor Sie sie veröffentlichen.

Dienst ausführen

Die HAL stellt die IFoo-Schnittstelle bereit, die asynchrone Rückrufe an das Framework über die IFooCallback-Schnittstelle ausführen muss. Die IFooCallback-Schnittstelle wird nicht per Namen als auffindbarer Dienst registriert. Stattdessen muss IFoo eine Methode wie setFooCallback(IFooCallback x) enthalten.

Wenn Sie IFooCallback mit Version 1.0 des android.hardware.foo-Pakets einrichten möchten, fügen Sie Android.mk android.hardware.foo-V1.0-java 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);

Benutzeroberflächenerweiterungen

Angenommen, ein bestimmter Dienst implementiert die IFoo-Oberfläche auf allen Geräten, ist es möglich, dass der Dienst auf einem bestimmten Gerät zusätzliche Funktionen bietet, die in der Schnittstellenerweiterung IBetterFoo implementiert sind. Das kann so aussehen:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

Code, der die erweiterte Schnittstelle kennt, kann die Java-Methode castFrom() verwenden, um die Basisschnittstelle sicher in die erweiterte Schnittstelle zu casten:

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.
}