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 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 sein können (unten beschrieben) oder client- und serverseitige HIDL-Schnittstellen in C++ sein können.

HIDL-Schnittstellen sollen hauptsächlich von nativem Code verwendet werden, und daher konzentriert sich HIDL auf die automatische Generierung von effizientem Code in C++. Allerdings müssen auch HIDL-Schnittstellen für die Verwendung direkt aus Java verfügbar sein, da einige Android-Subsysteme (z. B. Telefonie) über Java-HIDL-Schnittstellen verfügen.

Die Seiten in diesem Abschnitt beschreiben das Java-Frontend für HIDL-Schnittstellen, beschreiben ausführlich, wie Dienste erstellt, registriert und verwendet werden, und erläutern, 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 , die als Dienstname default registriert ist, und einen zusätzlichen Dienst mit dem benutzerdefinierten second_impl .

Bibliotheken hinzufügen

Sie müssen Abhängigkeiten von der 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 Shared Linkage 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 eine System- oder Anbieter-App haben, die auf Android 10 oder höher ausgerichtet ist, können Sie diese Bibliotheken statisch einschließen. Sie können auch (nur) HIDL-Klassen aus benutzerdefinierten JARs verwenden, die auf dem Gerät mit stabilen Java-APIs installiert sind, die mithilfe des vorhandenen uses-library Mechanismus für System-Apps verfügbar gemacht werden. Der letztere 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, aber keine der abhängigen Klassen. Beispielsweise android.hardware.foo-V1.0-java-shallow Klassen im foo -Paket, aber keine Klassen in android.hidl.base-V1.0-java , das die Basisklasse aller HIDL enthält Schnittstellen. Wenn Sie eine Bibliothek erstellen, die bereits die Basisklassen der bevorzugten Schnittstelle als Abhängigkeit verfügbar hat, 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 (früher wurden sie aufgrund des Delegate-First-Klassenladers von Android manchmal als versteckte API verwendet). Stattdessen wurden sie mit jarjar in einen neuen Namespace 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 , 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 android.hardware.foo -Pakets können Sie Ihre Schnittstelle in Java mit den folgenden Schritten implementieren:

  1. Definieren Sie Ihre Schnittstelle in HIDL.
  2. Öffnen /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 , schreiben Sie dann eine neue Klasse, um sie zu erweitern, und implementieren Sie die abstrakten Methoden.

Anzeigen von automatisch generierten Dateien

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 erzeugen das Verzeichnis /tmp/android/hardware/foo/1.0 . Für die Datei hardware/interfaces/foo/1.0/IFooCallback.hal erzeugt dies die Datei /tmp/android/hardware/foo/1.0/IFooCallback.java , die die Java-Schnittstelle, den Proxy-Code und die Stubs (beide Proxy und Stubs entsprechen der Schnittstelle).

-Lmakefile generiert die Regeln, die diesen Befehl zur Build-Zeit ausführen und es Ihnen ermöglichen, android.hardware.foo-V1.0-java und mit den entsprechenden Dateien zu verknüpfen. Ein Skript, das dies für ein Projekt voller Schnittstellen automatisch erledigt, 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 eine HAL entwickeln können, bevor Sie sie veröffentlichen.

Ausführen eines Dienstes

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 von Version 1.0 des Pakets android.hardware.foo , fügen android.hardware.foo-V1.0-java zu Android.mk . 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

Angenommen, ein bestimmter Dienst implementiert die IFoo Schnittstelle auf allen Geräten, ist es möglich, dass der Dienst auf einem bestimmten Gerät zusätzliche Funktionen bereitstellt, die in der Schnittstellenerweiterung IBetterFoo wie folgt implementiert sind:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

Aufrufender Code, der die erweiterte Schnittstelle kennt, kann die Java-Methode castFrom() verwenden, 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.
}