HIDLJava

Dans Android 8.0, le système d'exploitation Android a été repensé pour définir des interfaces claires entre la plate-forme Android indépendante de l'appareil et le code spécifique à l'appareil et au fournisseur. Android a déjà défini de nombreuses interfaces de ce type sous la forme d'interfaces HAL, définies comme des en-têtes C dans hardware/libhardware . HIDL a remplacé ces interfaces HAL par des interfaces stables et versionnées, qui peuvent être soit en Java (décrites ci-dessous), soit être des interfaces HIDL côté client et serveur en C++ .

Les interfaces HIDL sont destinées à être utilisées principalement à partir de code natif et, par conséquent, HIDL se concentre sur la génération automatique de code efficace en C++. Cependant, les interfaces HIDL doivent également être disponibles pour une utilisation directement à partir de Java, car certains sous-systèmes Android (tels que la téléphonie) disposent d'interfaces Java HIDL.

Les pages de cette section décrivent l'interface Java pour les interfaces HIDL, détaillent comment créer, enregistrer et utiliser les services, et expliquent comment les HAL et les clients HAL écrits en Java interagissent avec le système HIDL RPC.

Être client

Ceci est un exemple de client pour une interface IFoo dans le package android.hardware.foo@1.0 qui est enregistré comme nom de service default et un service supplémentaire avec le nom de service personnalisé second_impl .

Ajout de bibliothèques

Vous devez ajouter des dépendances sur la bibliothèque stub HIDL correspondante si vous souhaitez l'utiliser. Il s'agit généralement d'une bibliothèque statique :

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

Si vous savez que vous créez déjà des dépendances sur ces bibliothèques, vous pouvez également utiliser des liens partagés :

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

Considérations supplémentaires pour l'ajout de bibliothèques dans Android 10

Si vous disposez d’un système ou d’une application fournisseur qui cible Android 10 ou version ultérieure, vous pouvez inclure ces bibliothèques de manière statique. Vous pouvez également utiliser (uniquement) des classes HIDL à partir de fichiers JAR personnalisés installés sur l'appareil avec des API Java stables mises à disposition à l'aide du mécanisme uses-library existant pour les applications système. Cette dernière approche permet d'économiser de l'espace sur l'appareil. Pour plus de détails, consultez Implémentation de la bibliothèque SDK Java . Pour les applications plus anciennes, l’ancien comportement est conservé.

À partir d'Android 10, des versions « superficielles » de ces bibliothèques sont également disponibles. Ceux-ci incluent la classe en question mais n’incluent aucune des classes dépendantes. Par exemple, android.hardware.foo-V1.0-java-shallow inclut des classes dans le package foo, mais n'inclut pas de classes dans android.hidl.base-V1.0-java , qui contient la classe de base de tous les HIDL. interfaces. Si vous créez une bibliothèque dans laquelle les classes de base de l'interface préférée sont déjà disponibles en tant que dépendance, vous pouvez utiliser ce qui suit :

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

Les bibliothèques de base et de gestionnaire HIDL ne sont également plus disponibles sur le chemin de classe de démarrage pour les applications (auparavant, elles étaient parfois utilisées comme API cachée, en raison du chargeur de classe délégué d'abord d'Android). Au lieu de cela, ils ont été déplacés vers un nouvel espace de noms avec jarjar , et les applications qui les utilisent (nécessairement des applications privées) doivent avoir leurs propres copies distinctes. Les modules sur le chemin de classe de démarrage utilisant HIDL doivent utiliser les variantes superficielles de ces bibliothèques Java et ajouter jarjar_rules: ":framework-jarjar-rules" à leur Android.bp pour utiliser la version de ces bibliothèques qui existe dans le chemin de classe de démarrage.

Modification de votre source Java

Il n'existe qu'une seule version ( @1.0 ) de ce service, donc ce code récupère uniquement cette version. Consultez les extensions d’interface pour savoir comment gérer plusieurs versions différentes du service.

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

Fournir un service

Le code framework en Java devra peut-être servir des interfaces pour recevoir des rappels asynchrones des HAL.

Pour l'interface IFooCallback dans la version 1.0 du package android.hardware.foo , vous pouvez implémenter votre interface en Java en suivant les étapes suivantes :

  1. Définissez votre interface en HIDL.
  2. Ouvrez /tmp/android/hardware/foo/IFooCallback.java comme référence.
  3. Créez un nouveau module pour votre implémentation Java.
  4. Examinez la classe abstraite android.hardware.foo.V1_0.IFooCallback.Stub , puis écrivez une nouvelle classe pour l'étendre et implémenter les méthodes abstraites.

Affichage des fichiers générés automatiquement

Pour afficher les fichiers générés automatiquement, exécutez :

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

Ces commandes génèrent le répertoire /tmp/android/hardware/foo/1.0 . Pour le fichier hardware/interfaces/foo/1.0/IFooCallback.hal , cela génère le fichier /tmp/android/hardware/foo/1.0/IFooCallback.java , qui encapsule l'interface Java, le code proxy et les stubs (les deux proxy et les stubs sont conformes à l'interface).

-Lmakefile génère les règles qui exécutent cette commande au moment de la construction et vous permettent d'inclure android.hardware.foo-V1.0-java et de créer un lien avec les fichiers appropriés. Un script qui fait automatiquement cela pour un projet rempli d'interfaces peut être trouvé sur hardware/interfaces/update-makefiles.sh . Les chemins dans cet exemple sont relatifs ; hardware/interfaces peut être un répertoire temporaire sous votre arborescence de code pour vous permettre de développer un HAL avant de le publier.

Exécuter un service

Le HAL fournit l'interface IFoo , qui doit effectuer des rappels asynchrones au framework via l'interface IFooCallback . L'interface IFooCallback n'est pas enregistrée nommément en tant que service détectable ; à la place, IFoo doit contenir une méthode telle que setFooCallback(IFooCallback x) .

Pour configurer IFooCallback à partir de la version 1.0 du package android.hardware.foo , ajoutez android.hardware.foo-V1.0-java à Android.mk . Le code pour exécuter le service est :

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

Extensions d'interface

En supposant qu'un service donné implémente l'interface IFoo sur tous les appareils, il est possible que sur un appareil particulier, le service fournisse des fonctionnalités supplémentaires implémentées dans l'extension d'interface IBetterFoo , comme suit :

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

L’appel de code conscient de l’interface étendue peut utiliser la méthode Java castFrom() pour convertir en toute sécurité l’interface de base en interface étendue :

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