Utilisation de Binder IPC

Cette page décrit les modifications apportées au pilote de classeur dans Android 8, fournit des détails sur l'utilisation du classeur IPC et répertorie la politique SELinux requise.

Modifications apportées au pilote de classeur

À partir d'Android 8, le framework Android et les HAL communiquent désormais entre eux à l'aide d'un classeur. Comme cette communication augmente considérablement le trafic du classeur, Android 8 inclut plusieurs améliorations conçues pour maintenir la rapidité de l'IPC du classeur. Les fournisseurs de SoC et les OEM doivent fusionner directement à partir des branches pertinentes d'Android-4.4, Android-4.9 et versions ultérieures du projet noyau/commun .

Domaines de liaison multiples (contextes)

Common-4.4 et supérieur, y compris en amont

Pour diviser proprement le trafic du classeur entre le code du framework (indépendant du périphérique) et celui du fournisseur (spécifique au périphérique), Android 8 a introduit le concept de contexte de classeur . Chaque contexte de classeur possède son propre nœud de périphérique et son propre gestionnaire de contexte (service). Vous ne pouvez accéder au gestionnaire de contexte que via le nœud de périphérique auquel il appartient et, lors du passage d'un nœud de liaison à travers un certain contexte, il n'est accessible à partir de ce même contexte que par un autre processus, isolant ainsi complètement les domaines les uns des autres. Pour plus de détails sur l'utilisation, consultez vndbinder et vndservicemanager .

Dispersion-rassembler

Common-4.4 et supérieur, y compris en amont

Dans les versions précédentes d'Android, chaque élément de données d'un appel de classeur était copié trois fois :

  • Une fois pour le sérialiser dans un Parcel lors du processus d'appel
  • Une fois dans le pilote du noyau pour copier le Parcel vers le processus cible
  • Une fois pour désérialiser le Parcel dans le processus cible

Android 8 utilise l'optimisation par dispersion pour réduire le nombre de copies de 3 à 1. Au lieu de sérialiser d'abord les données dans un Parcel , les données restent dans leur structure et leur disposition de mémoire d'origine et le pilote les copie immédiatement dans le processus cible. Une fois les données dans le processus cible, la structure et la disposition de la mémoire sont les mêmes et les données peuvent être lues sans nécessiter une autre copie.

Verrouillage à grain fin

Common-4.4 et supérieur, y compris en amont

Dans les versions précédentes d'Android, le pilote de classeur utilisait un verrou global pour protéger contre les accès simultanés aux structures de données critiques. Bien qu'il y ait eu peu de conflits pour le verrou, le principal problème était que si un thread de faible priorité obtenait le verrou puis était préempté, cela pourrait sérieusement retarder les threads de priorité plus élevée devant obtenir le même verrou. Cela a provoqué des perturbations sur la plate-forme.

Les premières tentatives pour résoudre ce problème impliquaient la désactivation de la préemption tout en maintenant le verrouillage global. Cependant, il s’agissait plus d’un hack que d’une véritable solution, et a finalement été rejetée par l’amont et abandonnée. Les tentatives ultérieures ont consisté à rendre le verrouillage plus fin, dont une version fonctionne sur les appareils Pixel depuis janvier 2017. Bien que la majorité de ces modifications aient été rendues publiques, des améliorations substantielles ont été apportées aux versions ultérieures.

Après avoir identifié de petits problèmes dans l'implémentation du verrouillage à granularité fine, nous avons conçu une solution améliorée avec une architecture de verrouillage différente et soumis les modifications dans toutes les branches courantes du noyau. Nous continuons de tester cette implémentation sur un grand nombre d'appareils différents ; Comme nous n'avons connaissance d'aucun problème en suspens, il s'agit de la mise en œuvre recommandée pour les appareils livrés avec Android 8.

Héritage prioritaire en temps réel

Common-4.4 et common-4.9 (en amont à venir)

Le pilote de classeur a toujours pris en charge un bel héritage de priorité. Alors qu'un nombre croissant de processus dans Android s'exécutent avec une priorité en temps réel, dans certains cas, il est désormais logique que si un thread en temps réel effectue un appel de classeur, le thread du processus qui gère cet appel s'exécute également avec une priorité en temps réel. . Pour prendre en charge ces cas d'utilisation, Android 8 implémente désormais l'héritage des priorités en temps réel dans le pilote de classeur.

En plus de l'héritage de priorité au niveau de la transaction, l'héritage de priorité de nœud permet à un nœud (objet de service de classeur) de spécifier une priorité minimale à laquelle les appels vers ce nœud doivent être exécutés. Les versions précédentes d'Android prenaient déjà en charge l'héritage de priorité de nœud avec des valeurs intéressantes, mais Android 8 ajoute la prise en charge de l'héritage de nœud de politiques de planification en temps réel.

Modifications de l'espace utilisateur

Android 8 inclut toutes les modifications de l'espace utilisateur nécessaires pour fonctionner avec le pilote de classeur actuel dans le noyau commun, à une exception près : l'implémentation d'origine pour désactiver l'héritage de priorité en temps réel pour /dev/binder utilisait un ioctl . Le développement ultérieur a basculé le contrôle de l'héritage des priorités vers une méthode plus fine qui s'effectue par mode classeur (et non par contexte). Ainsi, l'ioctl ne se trouve pas dans la branche commune d'Android et est plutôt soumis dans nos noyaux communs .

L'effet de ce changement est que l'héritage des priorités en temps réel est désactivé par défaut pour chaque nœud. L'équipe chargée des performances d'Android a trouvé avantageux d'activer l'héritage des priorités en temps réel pour tous les nœuds du domaine hwbinder . Pour obtenir le même effet, sélectionnez ce changement dans l'espace utilisateur.

SHA pour les noyaux courants

Pour obtenir les modifications nécessaires sur le pilote de classeur, synchronisez avec le SHA approprié :

  • Commun-3.18
    cc8b90c121de ANDROID : binder : ne vérifie pas les autorisations prioritaires lors de la restauration.
  • Commun-4.4
    76b376eac7a2 ANDROID : classeur : ne vérifie pas les autorisations prioritaires lors de la restauration.
  • Commun-4.9
    ecd972d4f9b5 ANDROID : binder : ne vérifie pas les autorisations prioritaires lors de la restauration.

Utilisation du classeur IPC

Historiquement, les processus des fournisseurs ont utilisé la communication interprocessus de liaison (IPC) pour communiquer. Dans Android 8, le nœud de périphérique /dev/binder devient exclusif aux processus du framework, ce qui signifie que les processus du fournisseur n'y ont plus accès. Les processus des fournisseurs peuvent accéder à /dev/hwbinder , mais doivent convertir leurs interfaces AIDL pour utiliser HIDL. Pour les fournisseurs qui souhaitent continuer à utiliser les interfaces AIDL entre les processus des fournisseurs, Android prend en charge le classeur IPC comme décrit ci-dessous. Dans Android 10, Stable AIDL permet à tous les processus d'utiliser /dev/binder tout en résolvant les garanties de stabilité résolues par HIDL et /dev/hwbinder . Pour savoir comment utiliser Stable AIDL, consultez AIDL for HALs .

vndbinder

Android 8 prend en charge un nouveau domaine de classeur destiné aux services du fournisseur, accessible à l'aide de /dev/vndbinder au lieu de /dev/binder . Avec l'ajout de /dev/vndbinder , Android dispose désormais des trois domaines IPC suivants :

Domaine CIB Description
/dev/binder IPC entre les processus framework/application avec les interfaces AIDL
/dev/hwbinder IPC entre processus framework/fournisseur avec interfaces HIDL
IPC entre processus fournisseurs avec interfaces HIDL
/dev/vndbinder IPC entre processus fournisseur/fournisseur avec les interfaces AIDL

Pour que /dev/vndbinder apparaisse, assurez-vous que l'élément de configuration du noyau CONFIG_ANDROID_BINDER_DEVICES est défini sur "binder,hwbinder,vndbinder" (c'est la valeur par défaut dans les arborescences courantes du noyau Android).

Normalement, les processus des fournisseurs n'ouvrent pas directement le pilote de classeur mais établissent plutôt un lien avec la bibliothèque d'espace utilisateur libbinder , qui ouvre le pilote de classeur. L'ajout d'une méthode pour ::android::ProcessState() sélectionne le pilote de classeur pour libbinder . Les processus du fournisseur doivent appeler cette méthode avant d'appeler ProcessState, IPCThreadState ou avant d'effectuer des appels de classeur en général. Pour l'utiliser, placez l'appel suivant après le main() d'un processus fournisseur (client et serveur) :

ProcessState::initWithDriver("/dev/vndbinder");

vndservicemanager

Auparavant, les services de classeur étaient enregistrés auprès servicemanager , où ils pouvaient être récupérés par d'autres processus. Dans Android 8, servicemanager est désormais utilisé exclusivement par les processus de framework et d'application et les processus des fournisseurs ne peuvent plus y accéder.

Cependant, les services des fournisseurs peuvent désormais utiliser vndservicemanager , une nouvelle instance de servicemanager qui utilise /dev/vndbinder au lieu de /dev/binder et qui est construite à partir des mêmes sources que le framework servicemanager . Les processus des fournisseurs n'ont pas besoin d'être modifiés pour communiquer avec vndservicemanager ; lorsqu'un processus fournisseur s'ouvre / dev/vndbinder , les recherches de services sont automatiquement transférées vers vndservicemanager .

Le binaire vndservicemanager est inclus dans les makefiles par défaut des appareils Android.

Politique SELinux

Les processus fournisseur qui souhaitent utiliser la fonctionnalité de classeur pour communiquer entre eux ont besoin des éléments suivants :

  1. Accès à /dev/vndbinder .
  2. Binder {transfer, call} se connecte à vndservicemanager .
  3. binder_call(A, B) pour tout domaine de fournisseur A qui souhaite appeler le domaine de fournisseur B via l'interface de classeur de fournisseur.
  4. Autorisation d' {add, find} des services dans vndservicemanager .

Pour répondre aux exigences 1 et 2, utilisez la macro vndbinder_use() :

vndbinder_use(some_vendor_process_domain);

Pour répondre à l'exigence 3, le binder_call(A, B) pour les processus fournisseur A et B qui doivent communiquer via binder peut rester en place et n'a pas besoin d'être renommé.

Pour répondre à l'exigence 4, vous devez apporter des modifications à la façon dont les noms de service, les étiquettes de service et les règles sont gérés.

Pour plus de détails sur SELinux, consultez Linux à sécurité améliorée sous Android . Pour plus de détails sur SELinux dans Android 8.0, consultez SELinux pour Android 8.0 .

Noms des services

Auparavant, le fournisseur traitait les noms de services enregistrés dans un fichier service_contexts et ajoutait les règles correspondantes pour accéder à ce fichier. Exemple de fichier service_contexts de device/google/marlin/sepolicy :

AtCmdFwd                              u:object_r:atfwd_service:s0
cneservice                            u:object_r:cne_service:s0
qti.ims.connectionmanagerservice      u:object_r:imscm_service:s0
rcs                                   u:object_r:radio_service:s0
uce                                   u:object_r:uce_service:s0
vendor.qcom.PeripheralManager         u:object_r:per_mgr_service:s0

Sous Android 8, vndservicemanager charge à la place le fichier vndservice_contexts . Les services du fournisseur migrant vers vndservicemanager (et qui se trouvent déjà dans l'ancien fichier service_contexts ) doivent être ajoutés au nouveau fichier vndservice_contexts .

Étiquettes de services

Auparavant, les étiquettes de service telles que u:object_r:atfwd_service:s0 étaient définies dans un fichier service.te . Exemple:

type atfwd_service,      service_manager_type;

Sous Android 8, vous devez modifier le type en vndservice_manager_type et déplacer la règle vers le fichier vndservice.te . Exemple:

type atfwd_service,      vndservice_manager_type;

Règles du gestionnaire de services

Auparavant, les règles accordaient aux domaines l'accès pour ajouter ou rechercher des services à partir servicemanager . Exemple:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;

Sous Android 8, ces règles peuvent rester en place et utiliser la même classe. Exemple:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;