Android 11 permet d'utiliser AIDL pour les HAL sous Android. Ainsi, possible d'implémenter certaines parties d'Android sans HIDL. Faire passer les HAL à l'utilisation d'AIDL exclusivement dans la mesure du possible (lorsque les HAL en amont utilisent HIDL, HIDL doit être utilisé).
Les HAL qui utilisent AIDL pour communiquer entre les composants du framework, tels que ceux de
system.img
et les composants matériels, tels que ceux de vendor.img
, doivent utiliser
AIDL stable. Toutefois, pour communiquer au sein d'une partition, par exemple,
HAL vers un autre, il n'y a aucune restriction
sur le mécanisme d'IPC à utiliser.
Motivation
AIDL existe depuis plus longtemps que HIDL et est utilisé dans de nombreux autres endroits, tels que entre les composants du framework Android ou dans les applications. Maintenant qu'AIDL est stable, il est possible d'implémenter une pile entière avec un seul environnement d'exécution IPC. AIDL dispose également d'un meilleur système de gestion des versions que HIDL.
- L’utilisation d’un seul langage IPC signifie avoir une seule chose à apprendre, à déboguer, à optimiser et à sécuriser.
- AIDL permet la gestion des versions sur place pour les propriétaires d'une interface:
<ph type="x-smartling-placeholder">
- </ph>
- Les propriétaires peuvent ajouter des méthodes à la fin des interfaces ou des champs aux parcelables. Cela signifie qu'il est plus facile de coder des versions au fil des années, mais aussi le coût annuel est moins élevé (les types peuvent être modifiés sur place et il n'y a pas besoin de bibliothèques supplémentaires pour chaque version de l'interface).
- Les interfaces d'extension peuvent être associées au moment de l'exécution plutôt que dans le type Il n'est donc pas nécessaire de redéfinir les extensions en aval sur de nouvelles versions d'interfaces.
- Une interface AIDL existante peut être utilisée directement lorsque son propriétaire le souhaite pour la stabiliser. Auparavant, une copie complète de l'interface devait être créés dans HIDL.
Compiler avec l'environnement d'exécution AIDL
AIDL dispose de trois backends différents: Java, NDK et CPP. Pour utiliser la version stable d'AIDL, vous devez
utilisez toujours la copie système de libbinder dans system/lib*/libbinder.so
et parlez
le /dev/binder
. Pour le code sur l'image du fournisseur, cela signifie que libbinder
.
(depuis le VNDK) ne peuvent pas être utilisées: cette bibliothèque possède une API C++ instable et
instables. À la place, le code de fournisseur natif doit utiliser le backend du NDK
AIDL, lien vers libbinder_ndk
(qui repose sur le système libbinder.so
)
et associez-les aux bibliothèques NDK créées par les entrées aidl_interface
. Pour
les noms exacts des modules, consultez
règles de dénomination des modules.
Écrire une interface HAL AIDL
Pour qu'une interface AIDL puisse être utilisée entre le système et le fournisseur, elle doit deux modifications:
- Chaque définition de type doit être annotée avec
@VintfStability
. - La déclaration
aidl_interface
doit inclurestability: "vintf",
.
Seul le propriétaire d'une interface peut effectuer ces modifications.
Lorsque vous effectuez ces modifications, l'interface doit se trouver dans le
fichier manifeste VINTF pour qu'elles fonctionnent. Tester ceci (et les
requises, comme vérifier que les interfaces publiées sont figées) à l'aide de la
Test VTS vts_treble_vintf_vendor_test
. Vous pouvez utiliser un @VintfStability
sans ces exigences en appelant l'une ou l'autre
AIBinder_forceDowngradeToLocalStability
dans le backend du NDK,
android::Stability::forceDowngradeToLocalStability
dans le backend C++,
ou android.os.Binder#forceDowngradeToSystemStability
dans le backend Java
sur un objet de liaison
avant qu'il ne soit envoyé à un autre processus. Revenir à une version antérieure d'un service
à la stabilité du fournisseur n'est pas prise en charge en Java, car toutes les applications s'exécutent sur un système.
le contexte.
De plus, pour une portabilité maximale du code et pour éviter des problèmes potentiels tels que comme bibliothèques supplémentaires inutiles, désactivez le backend CPP.
Notez que l'utilisation de backends
dans l'exemple de code ci-dessous est correcte, car
se trouvent trois backends (Java, NDK et CPP). Le code ci-dessous indique comment sélectionner
backend CPP pour le désactiver.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Rechercher des interfaces HAL AIDL
Les interfaces AIDL stables d'AOSP pour les HAL se trouvent dans les mêmes répertoires de base que
Interfaces HIDL, dans des dossiers aidl
.
- matériel/interfaces
- Frameworks/matériel/interfaces
- système/matériel/interfaces
Vous devez placer les interfaces d'extension dans d'autres hardware/interfaces
sous-répertoires de vendor
ou hardware
.
Interfaces des extensions
Android dispose d'un ensemble d'interfaces AOSP officielles avec chaque version. Quand Android souhaitent ajouter des fonctionnalités à ces interfaces, ils ne doivent pas modifier directement, car cela signifierait que leur environnement d'exécution Android est incompatible avec l'environnement d'exécution Android AOSP. Pour les appareils GMS, évitez ces interfaces est également ce qui garantit que l'image GSI peut continuer à fonctionner.
Les extensions peuvent s'enregistrer de deux manières différentes:
- au moment de l'exécution, consultez la section Extensions associées.
- autonome, enregistrée dans le monde entier et dans VINTF.
Cependant, une extension est enregistrée lorsqu'elle est spécifique au fournisseur (c'est-à-dire qu'elle ne fait pas partie les composants AOSP en amont) utilisent l'interface, aucune fusion n'est possible. conflit. Toutefois, lorsque les modifications en aval des composants AOSP en amont sont peut entraîner des conflits de fusion. Les stratégies suivantes sont recommandées:
- les ajouts d'interface peuvent être intégrés en amont vers AOSP dans la prochaine version.
- des ajouts d'interface pour une plus grande flexibilité, sans conflit de fusion peuvent être ajoutées en amont dans la prochaine version
Parcelables de l'extension: ParcelableHolder
ParcelableHolder
est un Parcelable
qui peut contenir un autre Parcelable
.
Le principal cas d'utilisation de ParcelableHolder
consiste à rendre Parcelable
extensible.
Par exemple, une image que les responsables de la mise en œuvre d'appareils s'attendent à pouvoir étendre une
Parcelable
défini par AOSP, AospDefinedParcelable
, pour inclure sa valeur ajoutée
caractéristiques.
Auparavant, sans ParcelableHolder
, les responsables de la mise en œuvre des appareils ne pouvaient pas modifier
une interface AIDL stable définie par AOSP, car ce serait une erreur d'ajouter
:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
Comme nous l'avons vu dans le code précédent, cette pratique est contraire à la réalité, car les champs ajouté par l'outil de mise en œuvre de l'appareil peut avoir un conflit lorsque le Parcelable est révisé dans les prochaines versions d'Android.
À l'aide de ParcelableHolder
, le propriétaire d'un parcelable peut définir une extension
point dans une Parcelable
.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Les responsables de la mise en œuvre de l'appareil peuvent ensuite définir leur propre Parcelable
pour leur
.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Enfin, le nouveau Parcelable
peut être associé au Parcelable
d'origine avec
le champ ParcelableHolder
.
// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;
ap.extension.setParcelable(op);
...
OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);
// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();
ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);
...
std::shared_ptr<OemDefinedParcelable> op_ptr;
ap.extension.getParcelable(&op_ptr);
// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);
...
std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);
// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });
ap.extension.set_parcelable(Rc::clone(&op));
...
let op = ap.extension.get_parcelable::<OemDefinedParcelable>();
Noms d'instances de serveur HAL AIDL
Par convention, les services AIDL HAL ont un nom d'instance au format
$package.$type/$instance
Par exemple, une instance du vibreur HAL est
enregistré sous le nom android.hardware.vibrator.IVibrator/default
.
Écrire un serveur HAL AIDL
@VintfStability
serveurs AIDL doivent être déclarés dans le fichier manifeste VINTF pour
exemple comme ceci:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
Sinon, ils doivent enregistrer un service AIDL normalement. Lors de l'exécution de VTS tests, il est normal que tous les HAL AIDL déclarés soient disponibles.
Écrire un client AIDL
Les clients AIDL doivent se déclarer dans la matrice de compatibilité, par exemple comme ceci:
<hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
<name>IVibrator</name>
<instance>default</instance>
</interface>
</hal>
Convertir une classe HAL existante de HIDL en AIDL
Utilisez l'outil hidl2aidl
pour convertir une interface HIDL en AIDL.
Fonctionnalités hidl2aidl
:
- Créer des fichiers
.aidl
en fonction des fichiers.hal
pour le package donné - Créer des règles de compilation pour le package AIDL que vous venez de créer avec tous les backends activé
- Créer des méthodes de traduction dans les backends Java, CPP et NDK pour la traduction des types HIDL aux types AIDL
- Créer des règles de compilation pour les bibliothèques de traduction avec les dépendances requises
- Créez des assertions statiques pour vous assurer que les énumérateurs HIDL et AIDL disposent du rôle les mêmes valeurs dans les backends CPP et NDK
Pour convertir un package de fichiers .hal en fichiers .aidl, procédez comme suit:
Créez l'outil situé dans
system/tools/hidl/hidl2aidl
.Créer cet outil à partir de la dernière source offre la solution la plus complète expérience. Vous pouvez utiliser la dernière version pour convertir les interfaces d'anciennes branches des versions précédentes.
m hidl2aidl
Exécutez l'outil avec un répertoire de sortie suivi du package à convertis.
Vous pouvez également utiliser l'argument
-l
pour ajouter le contenu d'un nouveau fichier de licence. en haut de tous les fichiers générés. Veillez à utiliser la licence et la date appropriées.hidl2aidl -o <output directory> -l <file with license> <package>
Exemple :
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Parcourez les fichiers générés et corrigez les problèmes de conversion.
conversion.log
contient tous les problèmes non gérés à résoudre en premier.- Le fichier
.aidl
généré peut contenir des avertissements et des suggestions susceptibles de nécessitent une action de votre part. Ces commentaires commencent par//
. - Profitez-en pour nettoyer le pack et y apporter des améliorations.
- Vérifiez le
@JavaDerive
pour les fonctionnalités qui pourraient être nécessaires, commetoString
ouequals
Ne créez que les cibles dont vous avez besoin.
- Désactivez les backends qui ne seront pas utilisés. Privilégier le backend du NDK plutôt que le CPP consultez la section Choisir un environnement d'exécution.
- Supprimez les bibliothèques de traduction ou tout code généré qui ne sera pas utilisé.
Consultez les différences majeures entre AIDL et HIDL.
- L'utilisation du
Status
intégré d'AIDL et des exceptions améliore généralement et évitent d'avoir à utiliser un autre type d'état spécifique à l'interface. - Les arguments d'interface AIDL dans les méthodes ne sont pas
@nullable
par défaut, étaient en HIDL.
- L'utilisation du
SEPolicy pour les HAL AIDL
Pour un type de service AIDL visible par le code du fournisseur,
hal_service_type
. Sinon, la configuration sepolicy est la même
que pour tout autre service AIDL (bien qu'il existe des attributs spéciaux pour les HAL). Ici,
Voici un exemple de définition d'un contexte de service HAL:
type hal_foo_service, service_manager_type, hal_service_type;
Pour la plupart des services définis par la plate-forme, le contexte du service
a déjà été ajouté (par exemple, android.hardware.foo.IFoo/default
est déjà marqué comme hal_foo_service
). Toutefois, si un client de framework prend en charge
plusieurs noms d'instances, vous devez ajouter des noms d'instances supplémentaires
fichiers service_contexts
spécifiques à l'appareil.
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
Les attributs HAL doivent être ajoutés lorsque nous créons un nouveau type de HAL. Un HAL spécifique
peut être associé à plusieurs types de services (chacun pouvant
plusieurs instances, comme nous venons de le voir). Pour un HAL, foo
, nous avons
hal_attribute(foo)
Cette macro définit les attributs hal_foo_client
et
hal_foo_server
Pour un domaine donné, les champs hal_client_domain
et
Les macros hal_server_domain
associent un domaine à un attribut HAL donné. Pour
exemple, si le serveur système est un client de ce HAL, cela correspond à la règle
hal_client_domain(system_server, hal_foo)
Un serveur HAL inclut également
hal_server_domain(my_hal_domain, hal_foo)
En général, pour un HAL donné,
nous créons également un domaine tel que hal_foo_default
pour référence ou
exemples de HAL. Cependant, certains appareils utilisent ces domaines pour leurs propres serveurs.
La distinction entre les domaines pour plusieurs serveurs n'a d'importance que si nous avons
plusieurs serveurs qui utilisent la même interface et qui nécessitent une autorisation différente
définies dans leurs implémentations. Dans toutes ces macros, hal_foo
n'est pas
un objet sepolicy. À la place, ce jeton est utilisé par ces macros pour faire référence à
le groupe d'attributs associé à
une paire client/serveur.
Toutefois, jusqu'à présent, nous n'avons pas associé hal_foo_service
ni hal_foo
(la paire d'attributs de hal_attribute(foo)
). Un attribut HAL est associé
avec les services AIDL HAL utilisant la macro hal_attribute_service
(les HAL HIDL utilisent
la macro hal_attribute_hwservice
). Par exemple :
hal_attribute_service(hal_foo, hal_foo_service)
Cela signifie que
Les processus hal_foo_client
peuvent accéder au HAL et hal_foo_server
peuvent enregistrer le HAL. L'application de ces règles d'enregistrement
effectuée par le gestionnaire de contexte (servicemanager
). Notez que les noms de service
ne correspondent pas toujours
aux attributs HAL. Par exemple, nous pouvons voir
hal_attribute_service(hal_foo, hal_foo2_service)
De manière générale, puisque
cela implique que les services sont toujours utilisés ensemble, nous pourrions envisager de supprimer
le hal_foo2_service
et en utilisant hal_foo_service
pour tous nos services
différents contextes. La plupart des HAL qui définissent plusieurs hal_attribute_service
sont :
le nom d'origine de l'attribut HAL n'est pas assez général et ne peut pas être modifié.
En résumé, voici un exemple d'HAL:
public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
Interfaces des extensions associées
Une extension peut être attachée à n'importe quelle interface de liaison, qu'il s'agisse d'une interface de niveau supérieur enregistrée directement auprès du gestionnaire de service ou il s'agit d'une sous-interface. Lorsque vous obtenez une extension, vous devez confirmer que le type d'extension est comme prévu. Les extensions ne peuvent être définies qu'à partir du processus servant une liaison.
Les extensions associées doivent être utilisées chaque fois qu'une extension modifie la d'un HAL existant. Lorsqu'une toute nouvelle fonctionnalité est nécessaire, ce mécanisme n'a pas besoin d'être utilisé, et une interface d'extension peut être directement auprès du gestionnaire de service. Interfaces des extensions associées sont plus logiques lorsqu'elles sont rattachées à des sous-interfaces, car elles peuvent être profondes ou multi-instances. Utiliser une extension globale pour la mise en miroir la hiérarchie d'interfaces de liaisons d'un autre service nécessiterait pour fournir des fonctionnalités équivalentes à celles des extensions directement associées.
Pour définir une extension sur la liaison, utilisez les API suivantes:
- Dans le backend du NDK:
AIBinder_setExtension
- Dans le backend Java:
android.os.Binder.setExtension
- Dans le backend CPP:
android::Binder::setExtension
- Dans le backend Rust:
binder::Binder::set_extension
Pour obtenir une extension sur une liaison, utilisez les API suivantes:
- Dans le backend du NDK:
AIBinder_getExtension
- Dans le backend Java:
android.os.IBinder.getExtension
- Dans le backend CPP:
android::IBinder::getExtension
- Dans le backend Rust:
binder::Binder::get_extension
Pour en savoir plus sur ces API, consultez la documentation
getExtension
dans le backend correspondant. Exemple d'utilisation
extensions se trouvent dans
hardware/interfaces/tests/extension/vibrator.
Différences majeures entre AIDL et HIDL
Lorsque vous utilisez des HAL AIDL ou des interfaces HAL AIDL, tenez compte des différences par rapport à l'écriture de HAL HIDL.
- La syntaxe du langage AIDL est plus proche de celle de Java. La syntaxe HIDL est semblable à C++.
- Toutes les interfaces AIDL ont des états d'erreur intégrés. Au lieu de créer
d'état, créer des entiers d'état constants dans les fichiers d'interface et utiliser
EX_SERVICE_SPECIFIC
dans les backends CPP/NDK etServiceSpecificException
dans le backend Java. Voir Erreur Manipulation : - AIDL ne démarre pas automatiquement les pools de threads lorsque des objets de liaison sont envoyés. Elles doivent être démarrées manuellement (voir fil de discussion (gestion de l'authentification et des accès)).
- AIDL n'annule pas l'opération en cas d'erreurs de transport décochées (HIDL
Return
abandonne le erreurs décochées). - AIDL ne peut déclarer qu'un seul type par fichier.
- Les arguments AIDL peuvent être spécifiés comme "in/out/inout" en plus de la sortie (il n'existe pas de "rappels synchrones").
- AIDL utilise un fd comme type primitif au lieu de handle.
- HIDL utilise des versions majeures pour les modifications incompatibles et des versions mineures pour
modifications compatibles. Dans AIDL, des modifications rétrocompatibles sont effectuées.
AIDL n'a pas de concept explicite de versions majeures. Il s'agit plutôt
incorporés dans les
noms de package. Par exemple, AIDL peut utiliser le nom de package
bluetooth2
- Par défaut, AIDL n'hérite pas de la priorité "Temps réel".
setInheritRt
doit être utilisée par liaison pour permettre l'héritage de priorité en temps réel.