HIDL Java

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:

  1. Definieren Sie Ihre 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. 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.
}