HIDL Java

In Android 8.0 il sistema operativo Android è stato riprogettato per definire interfacce chiare tra la piattaforma Android indipendente dal dispositivo e il codice specifico del dispositivo e del fornitore. Android ha già definito molte di queste interfacce sotto forma di interfacce HAL, definite come header C in hardware/libhardware . HIDL ha sostituito queste interfacce HAL con interfacce stabili e con versione, che possono essere in Java (descritte di seguito) o interfacce HIDL lato client e server in C++ .

Le interfacce HIDL devono essere utilizzate principalmente dal codice nativo e, di conseguenza, HIDL si concentra sulla generazione automatica di codice efficiente in C++. Tuttavia, le interfacce HIDL devono essere disponibili anche per l'uso direttamente da Java, poiché alcuni sottosistemi Android (come Telefonia) dispongono di interfacce HIDL Java.

Le pagine in questa sezione descrivono il frontend Java per le interfacce HIDL, descrivono in dettaglio come creare, registrare e utilizzare i servizi e spiegano come HAL e client HAL scritti in Java interagiscono con il sistema HIDL RPC.

Essere un cliente

Questo è un esempio di client per un'interfaccia IFoo nel pacchetto android.hardware.foo@1.0 che è registrato come nome del servizio default e un servizio aggiuntivo con il nome del servizio personalizzato second_impl .

Aggiunta di librerie

È necessario aggiungere dipendenze sulla libreria stub HIDL corrispondente se si desidera utilizzarla. Di solito, questa è una libreria statica:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Se sai che stai già inserendo dipendenze da queste librerie, puoi anche utilizzare il collegamento condiviso:

// in Android.bp
libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Considerazioni aggiuntive per l'aggiunta di librerie in Android 10

Se disponi di un'app di sistema o del fornitore destinata ad Android 10 o versioni successive, puoi includere staticamente queste librerie. Puoi anche usare (solo) classi HIDL da JAR personalizzati installati nel dispositivo con API Java stabili rese disponibili usando il meccanismo di uses-library esistente per le app di sistema. Quest'ultimo approccio consente di risparmiare spazio sul dispositivo. Per maggiori dettagli, consulta Implementazione della libreria Java SDK . Per le app meno recenti, il vecchio comportamento viene mantenuto.

A partire da Android 10, sono disponibili anche versioni "superficiali" di queste librerie. Questi includono la classe in questione ma non includono nessuna delle classi dipendenti. Ad esempio, android.hardware.foo-V1.0-java-shallow include classi nel pacchetto foo, ma non include classi in android.hidl.base-V1.0-java , che contiene la classe base di tutti gli HIDL interfacce. Se stai creando una libreria che ha già le classi base dell'interfaccia preferita disponibili come dipendenza, puoi usare quanto segue:

// 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

Anche le librerie di base e manager HIDL non sono più disponibili nel percorso di classe di avvio per le app (in precedenza, a volte venivano utilizzate come API nascoste, a causa del primo classloader delegato di Android). Invece, sono stati spostati in un nuovo spazio dei nomi con jarjar e le app che le utilizzano (necessariamente app private) devono avere le proprie copie separate. I moduli sul percorso di classe di avvio che utilizzano HIDL devono utilizzare le varianti superficiali di queste librerie Java e aggiungere jarjar_rules: ":framework-jarjar-rules" al proprio Android.bp per utilizzare la versione di queste librerie che esiste nel percorso di classe di avvio.

Modifica della tua sorgente Java

Esiste solo una versione ( @1.0 ) di questo servizio, quindi questo codice recupera solo quella versione. Vedi le estensioni dell'interfaccia per come gestire più versioni differenti del servizio.

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

Fornire un servizio

Il codice Framework in Java potrebbe dover servire interfacce per ricevere callback asincrone da HAL.

Per l'interfaccia IFooCallback nella versione 1.0 del pacchetto android.hardware.foo , puoi implementare la tua interfaccia in Java seguendo i seguenti passaggi:

  1. Definisci la tua interfaccia in HIDL.
  2. Apri /tmp/android/hardware/foo/IFooCallback.java come riferimento.
  3. Crea un nuovo modulo per la tua implementazione Java.
  4. Esaminare la classe astratta android.hardware.foo.V1_0.IFooCallback.Stub , quindi scrivere una nuova classe per estenderla e implementare i metodi astratti.

Visualizzazione dei file generati automaticamente

Per visualizzare i file generati automaticamente, eseguire:

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

Questi comandi generano la directory /tmp/android/hardware/foo/1.0 . Per il file hardware/interfaces/foo/1.0/IFooCallback.hal , questo genera il file /tmp/android/hardware/foo/1.0/IFooCallback.java , che incapsula l'interfaccia Java, il codice proxy e gli stub (entrambi proxy e gli stub sono conformi all'interfaccia).

-Lmakefile genera le regole che eseguono questo comando in fase di compilazione e consentono di includere android.hardware.foo-V1.0-java e collegarsi ai file appropriati. Uno script che esegue automaticamente questa operazione per un progetto pieno di interfacce può essere trovato in hardware/interfaces/update-makefiles.sh . I percorsi in questo esempio sono relativi; hardware/interfaces può essere una directory temporanea nell'albero del codice per consentirti di sviluppare un HAL prima di pubblicarlo.

Esecuzione di un servizio

L'HAL fornisce l'interfaccia IFoo , che deve effettuare callback asincroni al framework tramite l'interfaccia IFooCallback . L'interfaccia IFooCallback non è registrata per nome come servizio rilevabile; invece, IFoo deve contenere un metodo come setFooCallback(IFooCallback x) .

Per configurare IFooCallback dalla versione 1.0 del pacchetto android.hardware.foo , aggiungi android.hardware.foo-V1.0-java ad Android.mk . Il codice per eseguire il servizio è:

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

Estensioni dell'interfaccia

Supponendo che un determinato servizio implementi l'interfaccia IFoo su tutti i dispositivi, è possibile che su un particolare dispositivo il servizio fornisca funzionalità aggiuntive implementate nell'estensione dell'interfaccia IBetterFoo , come segue:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

Il codice chiamante a conoscenza dell'interfaccia estesa può utilizzare il metodo Java castFrom() per eseguire il cast sicuro dell'interfaccia di base sull'interfaccia estesa:

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