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 de l'IPC de classeur et répertorie la politique SELinux requise.

Modifications du 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 des classeurs, Android 8 inclut plusieurs améliorations conçues pour maintenir la rapidité de l'IPC des classeurs. Les fournisseurs de SoC et les OEM doivent fusionner directement à partir des branches concernées d'Android-4.4, Android-4.9 et supérieur du projet noyau/commun .

Plusieurs domaines de liaison (contextes)

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

Pour diviser proprement le trafic de classeur entre le code du framework (indépendant de l'appareil) et le code du fournisseur (spécifique à l'appareil), Android 8 a introduit le concept de contexte de classeur . Chaque contexte de classeur a son propre nœud de périphérique et son propre gestionnaire de contexte (service). Vous pouvez accéder au gestionnaire de contexte uniquement 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 .

Éparpiller-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 dans le processus d'appel
  • Une fois dans le pilote du noyau pour copier le Parcel dans le processus cible
  • Une fois pour désérialiser la Parcel dans le processus cible

Android 8 utilise l'optimisation scatter-gather pour réduire le nombre de copies de 3 à 1. Au lieu de sérialiser d'abord les données dans une Parcel , les données restent dans leur structure et 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 se protéger contre l'accès simultané 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é, il pouvait sérieusement retarder les threads de priorité supérieure devant obtenir le même verrou. Cela a provoqué un blocage dans la plate-forme.

Les premières tentatives pour résoudre ce problème impliquaient de désactiver la préemption tout en maintenant le verrou global. Cependant, il s'agissait plus d'un piratage que d'une véritable solution, et a finalement été rejeté par l'amont et abandonné. Les tentatives ultérieures visaient à 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 suivantes.

Après avoir identifié de petits problèmes dans l'implémentation de verrouillage à grain fin, 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 à 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 l'implémentation recommandée pour les appareils livrés avec Android 8.

Héritage des priorités en temps réel

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

Le pilote de classeur a toujours pris en charge l'héritage de priorité agréable. Comme un nombre croissant de processus sous Android s'exécutent en 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 en priorité en temps réel. . Pour prendre en charge ces cas d'utilisation, Android 8 implémente désormais l'héritage de priorité 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é des nœuds avec de belles valeurs, mais Android 8 ajoute la prise en charge de l'héritage des nœuds des politiques de planification en temps réel.

Modifications de l'espace utilisateur

Android 8 inclut toutes les modifications de l'espace utilisateur requises 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 fait passer le contrôle de l'héritage de priorité à une méthode plus fine qui est par mode de classeur (et non par contexte). Ainsi, l'ioctl n'est pas dans la branche commune d'Android et est plutôt soumis dans nos noyaux communs .

L'effet de cette modification est que l'héritage de priorité en temps réel est désactivé par défaut pour chaque nœud. L'équipe de performance Android a trouvé avantageux d'activer l'héritage de priorité 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 communs

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

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

Utilisation du classeur IPC

Historiquement, les processus des fournisseurs ont utilisé la communication interprocessus (IPC) du classeur 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 du fournisseur 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 fournisseur, Android prend en charge l'IPC de classeur 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é HIDL et /dev/hwbinder résolus. Pour savoir comment utiliser Stable AIDL, voir AIDL for HALs .

vndbinder

Android 8 prend en charge un nouveau domaine de classeur à utiliser par les 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 CIP La description
/dev/binder IPC entre les processus framework/app avec les interfaces AIDL
/dev/hwbinder IPC entre processus framework/fournisseur avec interfaces HIDL
IPC entre les processus du fournisseur avec des interfaces HIDL
/dev/vndbinder IPC entre les processus fournisseur/fournisseur avec les interfaces AIDL

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

Normalement, les processus du fournisseur n'ouvrent pas directement le pilote du classeur et se lient à la place à la bibliothèque de l'espace utilisateur libbinder , qui ouvre le pilote du 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 dans 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 de 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 de fournisseur 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 construit à partir des mêmes sources que le framework servicemanager . Les processus du fournisseur n'ont pas besoin d'apporter des modifications pour communiquer avec vndservicemanager ; lorsqu'un processus fournisseur ouvre / dev/vndbinder , les recherches de service vont automatiquement à 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 du fournisseur.
  4. Autorisation d' {add, find} 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 ont besoin de parler via le classeur peut rester en place et n'a pas besoin d'être renommé.

Pour répondre à l'exigence 4, vous devez modifier la manière 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 Security-Enhanced Linux in 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 service 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

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

Étiquettes de service

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;

Dans 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 service

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

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

Dans 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;
,

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

Modifications du 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 des classeurs, Android 8 inclut plusieurs améliorations conçues pour maintenir la rapidité de l'IPC des classeurs. Les fournisseurs de SoC et les OEM doivent fusionner directement à partir des branches concernées d'Android-4.4, Android-4.9 et supérieur du projet noyau/commun .

Plusieurs domaines de liaison (contextes)

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

Pour diviser proprement le trafic de classeur entre le code du framework (indépendant de l'appareil) et le code du fournisseur (spécifique à l'appareil), Android 8 a introduit le concept de contexte de classeur . Chaque contexte de classeur a son propre nœud de périphérique et son propre gestionnaire de contexte (service). Vous pouvez accéder au gestionnaire de contexte uniquement 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 .

Éparpiller-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 dans le processus d'appel
  • Une fois dans le pilote du noyau pour copier le Parcel dans le processus cible
  • Une fois pour désérialiser la Parcel dans le processus cible

Android 8 utilise l'optimisation scatter-gather pour réduire le nombre de copies de 3 à 1. Au lieu de sérialiser d'abord les données dans une Parcel , les données restent dans leur structure et 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 se protéger contre l'accès simultané 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é, il pouvait sérieusement retarder les threads de priorité supérieure devant obtenir le même verrou. Cela a provoqué un blocage dans la plate-forme.

Les premières tentatives pour résoudre ce problème impliquaient de désactiver la préemption tout en maintenant le verrou global. Cependant, il s'agissait plus d'un piratage que d'une véritable solution, et a finalement été rejeté par l'amont et abandonné. Les tentatives ultérieures visaient à 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 suivantes.

Après avoir identifié de petits problèmes dans l'implémentation de verrouillage à grain fin, 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 à 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 l'implémentation recommandée pour les appareils livrés avec Android 8.

Héritage des priorités en temps réel

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

Le pilote de classeur a toujours pris en charge l'héritage de priorité agréable. Comme un nombre croissant de processus sous Android s'exécutent en 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 en priorité en temps réel. . Pour prendre en charge ces cas d'utilisation, Android 8 implémente désormais l'héritage de priorité 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é des nœuds avec de belles valeurs, mais Android 8 ajoute la prise en charge de l'héritage des nœuds des politiques de planification en temps réel.

Modifications de l'espace utilisateur

Android 8 inclut toutes les modifications de l'espace utilisateur requises 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 fait passer le contrôle de l'héritage de priorité à une méthode plus fine qui est par mode de classeur (et non par contexte). Ainsi, l'ioctl n'est pas dans la branche commune d'Android et est plutôt soumis dans nos noyaux communs .

L'effet de cette modification est que l'héritage de priorité en temps réel est désactivé par défaut pour chaque nœud. L'équipe de performance Android a trouvé avantageux d'activer l'héritage de priorité 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 communs

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

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

Utilisation du classeur IPC

Historiquement, les processus des fournisseurs ont utilisé la communication interprocessus (IPC) du classeur 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 du fournisseur 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 fournisseur, Android prend en charge l'IPC de classeur 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é HIDL et /dev/hwbinder résolus. Pour savoir comment utiliser Stable AIDL, voir AIDL for HALs .

vndbinder

Android 8 prend en charge un nouveau domaine de classeur à utiliser par les 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 CIP La description
/dev/binder IPC entre les processus framework/app avec les interfaces AIDL
/dev/hwbinder IPC entre processus framework/fournisseur avec interfaces HIDL
IPC entre les processus du fournisseur avec des interfaces HIDL
/dev/vndbinder IPC entre les processus fournisseur/fournisseur avec les interfaces AIDL

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

Normalement, les processus du fournisseur n'ouvrent pas directement le pilote du classeur et se lient à la place à la bibliothèque de l'espace utilisateur libbinder , qui ouvre le pilote du 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 dans 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 de 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 de fournisseur 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 construit à partir des mêmes sources que le framework servicemanager . Les processus du fournisseur n'ont pas besoin d'apporter des modifications pour communiquer avec vndservicemanager ; lorsqu'un processus fournisseur ouvre / dev/vndbinder , les recherches de service vont automatiquement à 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 du fournisseur.
  4. Autorisation d' {add, find} 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 ont besoin de parler via le classeur peut rester en place et n'a pas besoin d'être renommé.

Pour répondre à l'exigence 4, vous devez modifier la manière 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 Security-Enhanced Linux in 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 service 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

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

Étiquettes de service

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;

Dans 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 service

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

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

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