Android contient une couche d'abstraction matérielle (HAL, Hardware Abstraction Layer) HIDL automobile qui permet la capture et l'affichage d'images très tôt dans le processus de démarrage d'Android et qui continue de fonctionner pendant toute la durée de vie du système. Le HAL inclut la pile du système de vue extérieure (EVS, Exterior View System) et est généralement utilisé pour prendre en charge la caméra de recul et les écrans de vue panoramique dans les véhicules équipés de systèmes d'infoloisirs embarqués (IVI, In-Vehicle Infotainment) basés sur Android. EVS permet également d'implémenter des fonctionnalités avancées dans les applications utilisateur.
Android inclut également une interface de pilote de capture et d'affichage spécifique à l'EVS (dans /hardware/interfaces/automotive/evs/1.0
). Bien qu'il soit possible de créer une application de caméra de recul par-dessus les services d'affichage et de caméra Android existants, une telle application s'exécuterait probablement trop tard dans le processus de démarrage d'Android. L'utilisation d'une HAL dédiée permet de simplifier l'interface et d'indiquer clairement ce qu'un OEM doit implémenter pour prendre en charge la pile EVS.
Composants système
L'EVS comprend les composants système suivants :

Application EVS
Un exemple d'application EVS C++ (/packages/services/Car/evs/app
) sert d'implémentation de référence. Cette application est chargée de demander des images vidéo au gestionnaire EVS et de renvoyer les images terminées au gestionnaire EVS pour affichage.
Il s'attend à être démarré par init dès que l'EVS et le service automobile sont disponibles, dans les deux (2) secondes suivant la mise sous tension. Les OEM peuvent modifier ou remplacer l'application EVS comme ils le souhaitent.
EVS Manager
Le gestionnaire EVS (/packages/services/Car/evs/manager
) fournit les éléments de base nécessaires à une application EVS pour implémenter n'importe quoi, d'un simple affichage de caméra de recul à un rendu multicaméra 6DOF. Son interface est présentée via HIDL et est conçue pour accepter plusieurs clients simultanés.
D'autres applications et services (en particulier le service automobile) peuvent interroger l'état du gestionnaire EVS pour savoir quand le système EVS est actif.
Interface HIDL EVS
Le système EVS, à la fois la caméra et les éléments d'affichage, est défini dans le package android.hardware.automotive.evs
. Un exemple d'implémentation qui utilise l'interface (génère des images de test synthétiques et valide que les images font l'aller-retour) est fourni dans /hardware/interfaces/automotive/evs/1.0/default
.
L'OEM est responsable de l'implémentation de l'API exprimée par les fichiers .hal dans /hardware/interfaces/automotive/evs
. Ces implémentations sont responsables de la configuration et de la collecte des données des caméras physiques, et de leur transmission via des tampons de mémoire partagée reconnaissables par Gralloc. Le côté affichage de l'implémentation est responsable de la fourniture d'un tampon de mémoire partagée qui peut être rempli par l'application (généralement avec le rendu EGL) et de la présentation des frames finaux de préférence à tout autre élément qui pourrait apparaître sur l'écran physique. Les implémentations de l'interface EVS par les fournisseurs peuvent être stockées sous /vendor/… /device/…
ou hardware/…
(par exemple,
/hardware/[vendor]/[platform]/evs
).
Pilotes du noyau
Un appareil compatible avec la pile EVS nécessite des pilotes de noyau. Au lieu de créer de nouveaux pilotes, les OEM ont la possibilité de prendre en charge les fonctionnalités requises par l'EVS via les pilotes matériels de caméra ou d'écran existants. La réutilisation des pilotes peut être avantageuse, en particulier pour les pilotes d'affichage où la présentation des images peut nécessiter une coordination avec d'autres threads actifs. Android 8.0 inclut un exemple de pilote basé sur v4l2 (dans packages/services/Car/evs/sampleDriver
) qui dépend du noyau pour la prise en charge de v4l2 et de SurfaceFlinger pour la présentation de l'image de sortie.
Description de l'interface matérielle EVS
Cette section décrit la HAL. Les fournisseurs sont censés fournir des implémentations de cette API adaptées à leur matériel.
IEvsEnumerator
Cet objet est chargé d'énumérer le matériel EVS disponible dans le système (une ou plusieurs caméras et l'écran unique).
getCameraList() generates (vec<CameraDesc> cameras);
Renvoie un vecteur contenant les descriptions de toutes les caméras du système. Il est supposé que l'ensemble de caméras est fixe et connu au moment du démarrage. Pour en savoir plus sur les descriptions de caméras, consultez CameraDesc
.
openCamera(string camera_id) generates (IEvsCamera camera);
Obtient un objet d'interface utilisé pour interagir avec une caméra spécifique identifiée par la chaîne camera_id unique. Renvoie une valeur NULL en cas d'échec.
Les tentatives de réouverture d'une caméra déjà ouverte ne peuvent pas échouer. Pour éviter les conditions de concurrence associées au démarrage et à l'arrêt de l'application, la réouverture d'une caméra doit arrêter l'instance précédente afin que la nouvelle requête puisse être traitée. Une instance de caméra préemptée de cette manière doit être mise à l'état inactif, en attendant sa destruction finale et en répondant à toute demande d'affectation de l'état de la caméra avec un code de retour de OWNERSHIP_LOST
.
closeCamera(IEvsCamera camera);
Libère l'interface IEvsCamera (et est l'opposé de l'appel openCamera()
). Le flux vidéo de la caméra doit être arrêté en appelant stopVideoStream()
avant d'appeler closeCamera
.
openDisplay() generates (IEvsDisplay display);
Obtient un objet d'interface utilisé pour interagir exclusivement avec l'écran EVS du système. Un seul client peut détenir une instance fonctionnelle d'IEvsDisplay à la fois. Comme pour le comportement d'ouverture agressif décrit dans openCamera
, un nouvel objet IEvsDisplay peut être créé à tout moment et désactive toutes les instances précédentes. Les instances invalidées continuent d'exister et de répondre aux appels de fonction de leurs propriétaires, mais ne doivent effectuer aucune opération de mutation lorsqu'elles sont mortes. À terme, l'application cliente devrait remarquer les codes de retour d'erreur OWNERSHIP_LOST
, puis fermer et libérer l'interface inactive.
closeDisplay(IEvsDisplay display);
Libère l'interface IEvsDisplay (et est l'opposé de l'appel openDisplay()
). Les tampons en attente reçus avec les appels getTargetBuffer()
doivent être renvoyés à l'écran avant de le fermer.
getDisplayState() generates (DisplayState state);
Obtient l'état d'affichage actuel. L'implémentation HAL doit indiquer l'état actuel réel, qui peut être différent de l'état demandé le plus récemment.
La logique responsable de la modification des états d'affichage doit exister au-dessus de la couche de l'appareil, ce qui rend indésirable l'implémentation HAL pour modifier spontanément les états d'affichage. Si l'affichage n'est actuellement détenu par aucun client (par un appel à openDisplay), cette fonction renvoie NOT_OPEN
. Sinon, il indique l'état actuel de l'écran EVS (voir IEvsDisplay API).
struct CameraDesc { string camera_id; int32 vendor_flags; // Opaque value }
camera_id
: chaîne qui identifie de manière unique une caméra donnée. Il peut s'agir du nom de l'appareil du noyau ou d'un nom pour l'appareil, tel que rearview. La valeur de cette chaîne est choisie par l'implémentation HAL et utilisée de manière opaque par la pile ci-dessus.vendor_flags
. Méthode permettant de transmettre de manière opaque des informations spécialisées sur la caméra du pilote à une application EVS personnalisée. Ces informations sont transmises sans interprétation du pilote à l'application EVS, qui est libre de les ignorer.
IEvsCamera
Cet objet représente une seule caméra et constitue l'interface principale pour la capture d'images.
getCameraInfo() generates (CameraDesc info);
Renvoie CameraDesc
de cette caméra.
setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);
Spécifie la profondeur de la chaîne de tampon que la caméra est invitée à prendre en charge. Le client IEvsCamera peut contenir simultanément jusqu'à ce nombre d'images. Si ce nombre d'images a été envoyé au récepteur sans être renvoyé par doneWithFrame
, le flux ignore les images jusqu'à ce qu'un tampon soit renvoyé pour être réutilisé. Cet appel peut être effectué à tout moment, même lorsque des flux sont déjà en cours d'exécution. Dans ce cas, les tampons doivent être ajoutés ou supprimés de la chaîne, selon le cas. Si aucun appel n'est effectué vers ce point d'entrée, IEvsCamera prend en charge au moins un frame par défaut, et plus si possible.
Si le bufferCount demandé ne peut pas être pris en charge, la fonction renvoie BUFFER_NOT_AVAILABLE
ou un autre code d'erreur pertinent. Dans ce cas, le système continue de fonctionner avec la valeur définie précédemment.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Demande la diffusion des images de la caméra EVS. IEvsCameraStream commence à recevoir des appels périodiques avec de nouvelles images jusqu'à ce que stopVideoStream()
soit appelé. Les frames doivent commencer à être distribuées dans les 500 ms suivant l'appel startVideoStream
et, une fois démarrées, doivent être générées à un minimum de 10 FPS. Le temps nécessaire pour démarrer le flux vidéo est effectivement pris en compte dans le temps de démarrage requis pour la caméra de recul. Si le flux n'est pas démarré, un code d'erreur doit être renvoyé. Sinon, OK est renvoyé.
oneway doneWithFrame(BufferDesc buffer);
Renvoie un frame qui a été fourni à IEvsCameraStream. Une fois qu'un frame fourni à l'interface IEvsCameraStream a été consommé, il doit être renvoyé à IEvsCamera pour être réutilisé. Un nombre limité de tampons est disponible (voire un seul). Si l'offre est épuisée, aucune autre trame n'est fournie tant qu'un tampon n'est pas renvoyé, ce qui peut entraîner des trames ignorées (un tampon avec un handle nul indique la fin d'un flux et n'a pas besoin d'être renvoyé via cette fonction). Renvoie OK en cas de réussite ou le code d'erreur approprié, y compris potentiellement INVALID_ARG
ou BUFFER_NOT_AVAILABLE
.
stopVideoStream();
Arrête la diffusion des images de la caméra EVS. Étant donné que la diffusion est asynchrone, des frames peuvent continuer à arriver pendant un certain temps après le retour de cet appel. Chaque frame doit être renvoyé jusqu'à ce que la fermeture du flux soit signalée à IEvsCameraStream. Il est légal d'appeler stopVideoStream
sur un flux qui a déjà été arrêté ou qui n'a jamais démarré, auquel cas il est ignoré.
getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);
Demande des informations spécifiques au pilote à partir de l'implémentation HAL. Les valeurs autorisées pour opaqueIdentifier
sont spécifiques au pilote, mais l'absence de valeur peut entraîner un plantage du pilote. Le pilote doit renvoyer 0 pour tout opaqueIdentifier
non reconnu.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Envoie une valeur spécifique au pilote à l'implémentation HAL. Cette extension n'est fournie que pour faciliter les extensions spécifiques aux véhicules. Aucune implémentation HAL ne devrait nécessiter cet appel pour fonctionner dans un état par défaut. Si le pilote reconnaît et accepte les valeurs, la réponse OK doit être renvoyée. Sinon, INVALID_ARG
ou un autre code d'erreur représentatif doit être renvoyé.
struct BufferDesc { uint32 width; // Units of pixels uint32 height; // Units of pixels uint32 stride; // Units of pixels uint32 pixelSize; // Size of single pixel in bytes uint32 format; // May contain values from android_pixel_format_t uint32 usage; // May contain values from Gralloc.h uint32 bufferId; // Opaque value handle memHandle; // gralloc memory buffer handle }
Décrit une image transmise via l'API. Le pilote HAL est chargé de remplir cette structure pour décrire le tampon d'image. Le client HAL doit traiter cette structure en lecture seule. Les champs contiennent suffisamment d'informations pour permettre au client de reconstruire un objet ANativeWindowBuffer
, comme cela peut être nécessaire pour utiliser l'image avec EGL et l'extension eglCreateImageKHR()
.
width
: largeur en pixels de l'image présentée.height
: hauteur de l'image présentée en pixels.stride
: nombre de pixels que chaque ligne occupe réellement en mémoire, en tenant compte de tout remplissage pour l'alignement des lignes. Exprimée en pixels pour correspondre à la convention adoptée par gralloc pour ses descriptions de tampon.pixelSize
. Nombre d'octets occupés par chaque pixel, ce qui permet de calculer la taille en octets nécessaire pour passer d'une ligne à l'autre dans l'image (stride
en octets =stride
en pixels *pixelSize
).format
: format de pixel utilisé par l'image. Le format fourni doit être compatible avec l'implémentation OpenGL de la plate-forme. Pour réussir les tests de compatibilité,HAL_PIXEL_FORMAT_YCRCB_420_SP
doit être préféré pour l'utilisation de l'appareil photo, etRGBA
ouBGRA
doivent être préférés pour l'affichage.usage
. Indicateurs d'utilisation définis par l'implémentation HAL. Les clients HAL sont censés les transmettre sans les modifier (pour en savoir plus, consultez les indicateurs associés àGralloc.h
).bufferId
: valeur unique spécifiée par l'implémentation HAL pour permettre à une mémoire tampon d'être reconnue après un aller-retour via les API HAL. La valeur stockée dans ce champ peut être choisie arbitrairement par l'implémentation HAL.memHandle
: handle du tampon de mémoire sous-jacent contenant les données d'image. L'implémentation HAL peut choisir de stocker un handle de tampon Gralloc ici.
IEvsCameraStream
Le client implémente cette interface pour recevoir des images vidéo de manière asynchrone.
deliverFrame(BufferDesc buffer);
Reçoit des appels du HAL chaque fois qu'une image vidéo est prête à être inspectée.
Les tampons reçus par cette méthode doivent être renvoyés par des appels à IEvsCamera::doneWithFrame()
. Lorsque le flux vidéo est arrêté avec un appel à IEvsCamera::stopVideoStream()
, ce rappel peut se poursuivre pendant la vidange du pipeline. Chaque frame doit toujours être renvoyé. Lorsque le dernier frame du flux a été distribué, un bufferHandle
NULL est distribué, ce qui signifie la fin du flux et qu'aucun autre frame ne sera distribué. Le bufferHandle
NULL lui-même n'a pas besoin d'être renvoyé avec doneWithFrame()
, mais tous les autres handles doivent être renvoyés.
Bien que les formats de tampon propriétaires soient techniquement possibles, les tests de compatibilité exigent que le tampon soit dans l'un des quatre formats pris en charge : NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 bits R:G:B:x) et BGRA (32 bits B:G:R:x). Le format sélectionné doit être une source de texture GL valide sur l'implémentation GLES de la plate-forme.
L'application ne doit pas s'appuyer sur une correspondance entre le champ bufferId
et memHandle
dans la structure BufferDesc
. Les valeurs bufferId
sont essentiellement privées à l'implémentation du pilote HAL, qui peut les utiliser (et les réutiliser) comme bon lui semble.
IEvsDisplay
Cet objet représente l'affichage EVS, contrôle son état et gère la présentation des images.
getDisplayInfo() generates (DisplayDesc info);
Renvoie des informations de base sur l'affichage EVS fourni par le système (voir DisplayDesc).
setDisplayState(DisplayState state) generates (EvsResult result);
Définit l'état d'affichage. Les clients peuvent définir l'état d'affichage pour exprimer l'état souhaité. L'implémentation HAL doit accepter correctement une requête pour n'importe quel état alors qu'elle se trouve dans un autre état, même si la réponse peut être d'ignorer la requête.
Lors de l'initialisation, l'affichage est défini pour démarrer dans l'état NOT_VISIBLE
, après quoi le client est censé demander l'état VISIBLE_ON_NEXT_FRAME
et commencer à fournir la vidéo. Lorsque l'affichage n'est plus nécessaire, le client est censé demander l'état NOT_VISIBLE
après avoir passé le dernier frame vidéo.
Il est valable pour tout état et peut être demandé à tout moment. Si l'affichage est déjà visible, il doit le rester si la valeur est définie sur VISIBLE_ON_NEXT_FRAME
. Renvoie toujours OK, sauf si l'état demandé est une valeur d'énumération non reconnue, auquel cas INVALID_ARG
est renvoyé.
getDisplayState() generates (DisplayState state);
Obtient l'état de l'affichage. L'implémentation HAL doit indiquer l'état actuel réel, qui peut être différent de l'état demandé le plus récemment. La logique responsable du changement d'état de l'écran doit se trouver au-dessus de la couche de l'appareil. Il n'est donc pas souhaitable que l'implémentation HAL modifie spontanément les états de l'écran.
getTargetBuffer() generates (handle bufferHandle);
Renvoie un handle à un tampon de frame associé à l'écran. Ce tampon peut être verrouillé et écrit par un logiciel et/ou GL. Ce tampon doit être renvoyé avec un appel à returnTargetBufferForDisplay()
, même si l'affichage n'est plus visible.
Bien que des formats de tampon propriétaires soient techniquement possibles, les tests de compatibilité exigent que le tampon soit dans l'un des quatre formats compatibles : NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 bits R:G:B:x) ou BGRA (32 bits B:G:R:x). Le format sélectionné doit être une cible de rendu GL valide sur l'implémentation GLES de la plate-forme.
En cas d'erreur, un tampon avec un handle nul est renvoyé, mais un tel tampon n'a pas besoin d'être renvoyé à returnTargetBufferForDisplay
.
returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);
Indique à l'écran que le tampon est prêt à être affiché. Seuls les tampons récupérés par un appel à getTargetBuffer()
peuvent être utilisés avec cet appel, et le contenu de BufferDesc
ne peut pas être modifié par l'application cliente. Après cet appel, le tampon n'est plus valide pour l'utilisation par le client. Renvoie "OK" en cas de réussite ou le code d'erreur approprié, y compris potentiellement INVALID_ARG
ou BUFFER_NOT_AVAILABLE
.
struct DisplayDesc { string display_id; int32 vendor_flags; // Opaque value }
Décrit les propriétés de base d'un écran EVS et requises par une implémentation EVS. Le HAL est chargé de remplir cette structure pour décrire l'écran EVS. Il peut s'agir d'un écran physique ou virtuel superposé ou combiné à un autre appareil de présentation.
display_id
: chaîne qui identifie de manière unique l'écran. Il peut s'agir du nom de périphérique du noyau ou d'un nom pour le périphérique, tel que rearview. La valeur de cette chaîne est choisie par l'implémentation HAL et utilisée de manière opaque par la pile ci-dessus.vendor_flags
. Méthode permettant de transmettre de manière opaque des informations spécialisées sur la caméra du pilote à une application EVS personnalisée. Ces informations sont transmises sans interprétation du pilote à l'application EVS, qui est libre de les ignorer.
enum DisplayState : uint32 { NOT_OPEN, // Display has not been “opened” yet NOT_VISIBLE, // Display is inhibited VISIBLE_ON_NEXT_FRAME, // Will become visible with next frame VISIBLE, // Display is currently active DEAD, // Display is not available. Interface should be closed }
Décrit l'état de l'affichage EVS, qui peut être désactivé (non visible par le conducteur) ou activé (affichant une image au conducteur).
Inclut un état transitoire dans lequel l'affichage n'est pas encore visible, mais est prêt à le devenir avec la diffusion du prochain frame d'images avec l'appel returnTargetBufferForDisplay()
.
EVS Manager
Le gestionnaire EVS fournit l'interface publique au système EVS pour collecter et présenter les vues de caméras externes. Lorsque les pilotes matériels n'autorisent qu'une seule interface active par ressource (caméra ou écran), le gestionnaire EVS facilite l'accès partagé aux caméras. Une seule application EVS principale est le premier client du gestionnaire EVS. Il s'agit du seul client autorisé à écrire des données d'affichage (d'autres clients peuvent se voir accorder un accès en lecture seule aux images de la caméra).
Le gestionnaire EVS implémente la même API que les pilotes HAL sous-jacents et fournit un service étendu en prenant en charge plusieurs clients simultanés (plusieurs clients peuvent ouvrir une caméra via le gestionnaire EVS et recevoir un flux vidéo).

Les applications ne voient aucune différence lorsqu'elles fonctionnent via l'implémentation EVS Hardware HAL ou l'API EVS Manager, sauf que l'API EVS Manager permet un accès simultané aux flux de caméras. Le gestionnaire EVS est lui-même le seul client autorisé de la couche HAL matérielle EVS et sert de proxy pour la couche HAL matérielle EVS.
Les sections suivantes décrivent uniquement les appels qui ont un comportement différent (étendu) dans l'implémentation EVS Manager. Les appels restants sont identiques aux descriptions EVS HAL.
IEvsEnumerator
openCamera(string camera_id) generates (IEvsCamera camera);
Obtient un objet d'interface utilisé pour interagir avec une caméra spécifique identifiée par la chaîne camera_id unique. Renvoie une valeur NULL en cas d'échec.
Au niveau du gestionnaire EVS, tant que des ressources système suffisantes sont disponibles, une caméra déjà ouverte peut être rouverte par un autre processus, ce qui permet de rediriger le flux vidéo vers plusieurs applications consommatrices. Les chaînes camera_id
au niveau du gestionnaire EVS sont identiques à celles signalées au niveau du matériel EVS.
IEvsCamera
L'implémentation IEvsCamera fournie par EVS Manager est virtualisée en interne. Les opérations effectuées sur une caméra par un client n'ont donc aucune incidence sur les autres clients, qui conservent un accès indépendant à leurs caméras.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
Démarre les flux vidéo. Les clients peuvent démarrer et arrêter indépendamment les flux vidéo sur la même caméra sous-jacente. La caméra sous-jacente démarre lorsque le premier client démarre.
doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);
Renvoie un frame. Chaque client doit renvoyer ses montures lorsqu'il n'en a plus besoin, mais il est autorisé à les conserver aussi longtemps qu'il le souhaite. Lorsque le nombre de frames détenus par un client atteint la limite configurée, il ne reçoit plus de frames tant qu'il n'en a pas renvoyé un. Cette perte d'images n'affecte pas les autres clients, qui continuent de recevoir toutes les images comme prévu.
stopVideoStream();
Arrête un flux vidéo. Chaque client peut arrêter son flux vidéo à tout moment sans affecter les autres clients. Le flux de caméras sous-jacent au niveau matériel est arrêté lorsque le dernier client d'une caméra donnée arrête son flux.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Envoie une valeur spécifique au pilote, ce qui permet potentiellement à un client d'affecter un autre client. Étant donné que le gestionnaire EVS ne peut pas comprendre les implications des mots de contrôle définis par le fournisseur, ils ne sont pas virtualisés et tous les effets secondaires s'appliquent à tous les clients d'une caméra donnée. Par exemple, si un fournisseur utilisait cet appel pour modifier la fréquence d'images, tous les clients de la caméra de la couche matérielle concernée recevraient des images à la nouvelle fréquence.
IEvsDisplay
Un seul propriétaire de l'écran est autorisé, même au niveau de l'administrateur EVS. Le gestionnaire n'ajoute aucune fonctionnalité et transmet simplement l'interface IEvsDisplay directement à l'implémentation HAL sous-jacente.
Application EVS
Android inclut une implémentation de référence C++ native d'une application EVS qui communique avec le gestionnaire EVS et le HAL du véhicule pour fournir des fonctions de caméra de recul de base. L'application doit démarrer très tôt dans le processus de démarrage du système, avec une vidéo appropriée affichée en fonction des caméras disponibles et de l'état de la voiture (état de la vitesse et des clignotants). Les OEM peuvent modifier ou remplacer l'application EVS par leur propre logique et présentation spécifiques au véhicule.


Étant donné que les données d'image sont présentées à l'application dans un tampon graphique standard, l'application est responsable du déplacement de l'image du tampon source vers le tampon de sortie. Bien que cela entraîne le coût d'une copie de données, cela permet également à l'application d'afficher l'image dans le tampon d'affichage de la manière souhaitée.
Par exemple, l'application peut choisir de déplacer les données de pixels elles-mêmes, potentiellement avec une opération de mise à l'échelle ou de rotation intégrée. L'application peut également choisir d'utiliser l'image source en tant que texture OpenGL et d'afficher une scène complexe dans le tampon de sortie, y compris des éléments virtuels tels que des icônes, des consignes et des animations. Une application plus sophistiquée peut également sélectionner plusieurs caméras d'entrée simultanées et les fusionner dans un seul frame de sortie (par exemple, pour une vue virtuelle de l'environnement du véhicule).
Utiliser EGL/SurfaceFlinger dans le HAL d'affichage EVS
Cette section explique comment utiliser EGL pour afficher une implémentation EVS Display HAL dans Android 10.
Une implémentation de référence HAL EVS utilise EGL pour afficher l'aperçu de la caméra sur l'écran et libgui
pour créer la surface de rendu EGL cible. Dans Android 8 (et versions ultérieures), libgui
est classé comme VNDK-private, qui fait référence à un groupe de bibliothèques disponibles pour les bibliothèques VNDK que les processus du fournisseur ne peuvent pas utiliser.
Étant donné que les implémentations HAL doivent résider dans la partition du fournisseur, les fournisseurs ne peuvent pas utiliser Surface dans les implémentations HAL.
Compiler libgui pour les processus du fournisseur
L'utilisation de libgui
est la seule option pour utiliser EGL/SurfaceFlinger dans les implémentations EVS Display HAL. Le moyen le plus simple d'implémenter libgui
consiste à passer directement par frameworks/native/libs/gui en utilisant une cible de compilation supplémentaire dans le script de compilation. Cette cible est exactement la même que la cible libgui
, à l'exception de l'ajout de deux champs :
name
vendor_available
cc_library_shared { name: "libgui_vendor", vendor_available: true, vndk: { enabled: false, }, double_loadable: true,
defaults: ["libgui_bufferqueue-defaults"],
srcs: [ … // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: [ "-DNO_BUFFERHUB", "-DNO_INPUT", ], …
Remarque : Les cibles de fournisseur sont créées avec la macro NO_INPUT
, qui supprime un mot de 32 bits des données du colis. Étant donné que SurfaceFlinger s'attend à ce champ qui a été supprimé, il ne parvient pas à analyser le paquet. Cela se traduit par un échec fcntl
:
W Parcel : Attempt to read object from Parcel 0x78d9cffad8 at offset 428 that is not in the object list E Parcel : fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 0, fds[i] is 0, fd_count is 20, error: Unknown error 2147483647 W Parcel : Attempt to read object from Parcel 0x78d9cffad8 at offset 544 that is not in the object list
Pour résoudre ce problème :
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 6066421fa..25cf5f0ce 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -54,6 +54,9 @@ status_t layer_state_t::write(Parcel& output) const output.writeFloat(color.b); #ifndef NO_INPUT inputInfo.write(output); +#else + // Write a dummy 32-bit word. + output.writeInt32(0); #endif output.write(transparentRegion); output.writeUint32(transform);
Vous trouverez ci-dessous des instructions de compilation. Vous devriez recevoir un $(ANDROID_PRODUCT_OUT)/system/lib64/libgui_vendor.so
.
$ cd <your_android_source_tree_top> $ . ./build/envsetup. $ lunch <product_name>-<build_variant> ============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=10 TARGET_PRODUCT=<product_name> TARGET_BUILD_VARIANT=<build_variant> TARGET_BUILD_TYPE=release TARGET_ARCH=arm64 TARGET_ARCH_VARIANT=armv8-a TARGET_CPU_VARIANT=generic TARGET_2ND_ARCH=arm TARGET_2ND_ARCH_VARIANT=armv7-a-neon TARGET_2ND_CPU_VARIANT=cortex-a9 HOST_ARCH=x86_64 HOST_2ND_ARCH=x86 HOST_OS=linux HOST_OS_EXTRA=<host_linux_version> HOST_CROSS_OS=windows HOST_CROSS_ARCH=x86 HOST_CROSS_2ND_ARCH=x86_64 HOST_BUILD_TYPE=release BUILD_ID=QT OUT_DIR=out ============================================
$ m -j libgui_vendor … $ find $ANDROID_PRODUCT_OUT/system -name "libgui_vendor*" .../out/target/product/hawk/system/lib64/libgui_vendor.so .../out/target/product/hawk/system/lib/libgui_vendor.so
Utiliser le binder dans l'implémentation EVS HAL
Dans Android 8 (et versions ultérieures), le nœud de périphérique /dev/binder
est devenu exclusif aux processus du framework et, par conséquent, inaccessible aux processus du fournisseur. À la place, les processus du fournisseur doivent utiliser /dev/hwbinder
et convertir toutes les interfaces AIDL en HIDL. Pour ceux qui souhaitent continuer à utiliser les interfaces AIDL entre les processus du fournisseur, utilisez le domaine Binder, /dev/vndbinder
.
Domaine IPC | Description |
---|---|
/dev/binder |
IPC entre les processus de framework/d'application avec des interfaces AIDL |
/dev/hwbinder |
IPC entre les processus du framework/fournisseur avec les interfaces HIDL IPC entre les processus du fournisseur avec les interfaces HIDL |
/dev/vndbinder |
IPC entre les processus du fournisseur avec les interfaces AIDL |
Alors que SurfaceFlinger définit des interfaces AIDL, les processus du fournisseur ne peuvent utiliser que des interfaces HIDL pour communiquer avec les processus du framework. La conversion des interfaces AIDL existantes en HIDL nécessite un travail non négligeable. Heureusement, Android fournit une méthode permettant de sélectionner le pilote Binder pour libbinder
, auquel les processus de la bibliothèque userspace sont liés.
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp index d8fb3166..5fd02935 100644 --- a/evs/sampleDriver/service.cpp +++ b/evs/sampleDriver/service.cpp @@ -21,6 +21,7 @@ #include <utils/Errors.h> #include <utils/StrongPointer.h> #include <utils/Log.h> +#include <binder/ProcessState.h> #include "ServiceNames.h" #include "EvsEnumerator.h" @@ -43,6 +44,9 @@ using namespace android; int main() { ALOGI("EVS Hardware Enumerator service is starting"); + // Use /dev/binder for SurfaceFlinger + ProcessState::initWithDriver("/dev/binder"); + // Start a thread to listen to video device addition events. std::atomic<bool> running { true }; std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));
Remarque : Les processus du fournisseur doivent appeler cette méthode avant d'appeler Process
ou IPCThreadState
, ou avant d'effectuer des appels Binder.
Règles SELinux
Si l'implémentation de l'appareil est entièrement Treble, SELinux empêche les processus du fournisseur d'utiliser /dev/binder
. Par exemple, un exemple d'implémentation EVS HAL est attribué au domaine hal_evs_driver
et nécessite des autorisations de lecture/écriture pour le domaine binder_device
.
W ProcessState: Opening '/dev/binder' failed: Permission denied F ProcessState: Binder driver could not be opened. Terminating. F libc : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 9145 (android.hardwar), pid 9145 (android.hardwar) W android.hardwar: type=1400 audit(0.0:974): avc: denied { read write } for name="binder" dev="tmpfs" ino=2208 scontext=u:r:hal_evs_driver:s0 tcontext=u:object_r:binder_device:s0 tclass=chr_file permissive=0
Toutefois, l'ajout de ces autorisations entraîne un échec de compilation, car il enfreint les règles neverallow suivantes définies dans system/sepolicy/domain.te
pour un appareil full-treble.
libsepol.report_failure: neverallow on line 631 of system/sepolicy/public/domain.te (or line 12436 of policy.conf) violated by allow hal_evs_driver binder_device:chr_file { read write }; libsepol.check_assertions: 1 neverallow failures occurred
full_treble_only(` neverallow { domain -coredomain -appdomain -binder_in_vendor_violators } binder_device:chr_file rw_file_perms; ')
binder_in_vendor_violators
est un attribut fourni pour détecter un bug et guider le développement. Il peut également être utilisé pour résoudre le problème de non-respect d'Android 10 décrit ci-dessus.
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te index f1f31e9fc..6ee67d88e 100644 --- a/evs/sepolicy/evs_driver.te +++ b/evs/sepolicy/evs_driver.te @@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain; hal_server_domain(hal_evs_driver, hal_evs) hal_client_domain(hal_evs_driver, hal_evs) +# Allow to use /dev/binder +typeattribute hal_evs_driver binder_in_vendor_violators; + # allow init to launch processes in this context type hal_evs_driver_exec, exec_type, file_type, system_file_type; init_daemon_domain(hal_evs_driver)
Créer l'implémentation de référence EVS HAL en tant que processus fournisseur
Pour référence, vous pouvez appliquer les modifications suivantes à packages/services/Car/evs/Android.mk
. Veillez à vérifier que toutes les modifications décrites fonctionnent pour votre implémentation.
diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk index 734feea7d..0d257214d 100644 --- a/evs/sampleDriver/Android.mk +++ b/evs/sampleDriver/Android.mk @@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \ LOCAL_SHARED_LIBRARIES := \ android.hardware.automotive.evs@1.0 \ libui \ - libgui \ + libgui_vendor \ libEGL \ libGLESv2 \ libbase \ @@ -33,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample +LOCAL_PROPRIETARY_MODULE := true LOCAL_MODULE_TAGS := optional LOCAL_STRIP_MODULE := keep_symbols @@ -40,6 +41,7 @@ LOCAL_STRIP_MODULE := keep_symbols LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code +LOCAL_CFLAGS += -Iframeworks/native/include #NOTE: It can be helpful, while debugging, to disable optimizations #LOCAL_CFLAGS += -O0 -g diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp index d8fb31669..5fd029358 100644 --- a/evs/sampleDriver/service.cpp +++ b/evs/sampleDriver/service.cpp @@ -21,6 +21,7 @@ #include <utils/Errors.h> #include <utils/StrongPointer.h> #include <utils/Log.h> +#include <binder/ProcessState.h> #include "ServiceNames.h" #include "EvsEnumerator.h" @@ -43,6 +44,9 @@ using namespace android; int main() { ALOGI("EVS Hardware Enumerator service is starting"); + // Use /dev/binder for SurfaceFlinger + ProcessState::initWithDriver("/dev/binder"); + // Start a thread to listen video device addition events. std::atomic<bool> running { true }; std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running)); diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te index f1f31e9fc..632fc7337 100644 --- a/evs/sepolicy/evs_driver.te +++ b/evs/sepolicy/evs_driver.te @@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain; hal_server_domain(hal_evs_driver, hal_evs) hal_client_domain(hal_evs_driver, hal_evs) +# allow to use /dev/binder +typeattribute hal_evs_driver binder_in_vendor_violators; + # allow init to launch processes in this context type hal_evs_driver_exec, exec_type, file_type, system_file_type; init_daemon_domain(hal_evs_driver) @@ -22,3 +25,7 @@ allow hal_evs_driver ion_device:chr_file r_file_perms; # Allow the driver to access kobject uevents allow hal_evs_driver self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl; + +# Allow the driver to use the binder device +allow hal_evs_driver binder_device:chr_file rw_file_perms;