Caméra de véhicule HAL

Android contient une couche d'abstraction matérielle (HAL) HIDL pour l'automobile qui permet de capturer et d'afficher des 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) 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é (IVI) 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 en plus des services d'appareil photo et d'affichage Android existants, une telle application s'exécuterait probablement trop tard dans le processus de démarrage d'Android. L'utilisation d'une solution HAL dédiée permet de simplifier l'interface et indique 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:

Schéma des composants du système EVS
Figure 1 Présentation des composants du système EVS.

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 finalisées à l'EVS Manager pour qu'elles soient affichées. Il doit être démarré par init dès que l'EVS et le service de voiture sont disponibles, ciblé dans les deux (2) secondes suivant la mise sous tension. Les OEM peuvent modifier ou remplacer l'application EVS selon leurs besoins.

EVS Manager

Le Gestionnaire EVS (/packages/services/Car/evs/manager) fournit les éléments de base dont une application EVS a besoin pour implémenter tout ce qui va d'un simple affichage de caméra de recul à un rendu multicaméra à 6 degrés de liberté. 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 de voiture) peuvent interroger l'état du Gestionnaire EVS pour savoir quand le système EVS est actif.

Interface HIDL de l'EVS

Le système EVS, à la fois les éléments de la caméra et de l'écran, est défini dans le package android.hardware.automotive.evs. Un exemple d'implémentation qui exécute l'interface (génère des images de test synthétiques et valide que les images effectuent un 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 chargées de configurer et de collecter les données des caméras physiques, puis de les transmettre via des tampons de mémoire partagée reconnaissables par Gralloc. Le côté écran de l'implémentation est chargé de fournir un tampon de mémoire partagée pouvant être rempli par l'application (généralement avec un rendu EGL) et de présenter les images finales de préférence à tout autre élément susceptible d'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 de kernel

Un appareil compatible avec la pile EVS nécessite des pilotes de noyau. Au lieu de créer de nouveaux pilotes, les OEM peuvent prendre en charge les fonctionnalités requises par l'EVS via les pilotes matériels d'appareil photo 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 la version v4l2 (dans packages/services/Car/evs/sampleDriver) qui dépend du noyau pour la compatibilité avec la version v4l2 et de SurfaceFlinger pour la présentation de l'image de sortie.

Description de l'interface matérielle EVS

Cette section décrit le HAL. Les fournisseurs doivent proposer 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 un seul appareil d'affichage).

getCameraList() generates (vec<CameraDesc> cameras);

Renvoie un vecteur contenant des descriptions pour toutes les caméras du système. On suppose que l'ensemble des caméras est fixe et connu au démarrage. Pour en savoir plus sur les descriptions des 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 unique camera_id. Renvoie une valeur NULL en cas d'échec. Les tentatives de réouverture d'un appareil photo déjà ouvert ne peuvent pas échouer. Pour éviter les conditions de course 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 passer à l'état inactif, attendre sa destruction finale et répondre à toute requête visant à modifier l'état de la caméra avec un code de retour OWNERSHIP_LOST.

closeCamera(IEvsCamera camera);

Libère l'interface IEvsCamera (et est l'opposé de l'appel openCamera()). Vous devez arrêter le flux vidéo de la caméra en appelant stopVideoStream() avant d'appeler closeCamera.

openDisplay() generates (IEvsDisplay display);

Récupère 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 inactives. À terme, l'application cliente doit remarquer les codes de retour d'erreur OWNERSHIP_LOST, 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 des appels getTargetBuffer() doivent être renvoyés à l'écran avant de le fermer.

getDisplayState() generates (DisplayState state);

Récupère l'état actuel de l'écran. L'implémentation HAL doit indiquer l'état actuel réel, qui peut différer de l'état le plus récemment demandé. La logique responsable de la modification des états d'affichage doit exister au-dessus de la couche de l'appareil. Il est donc déconseillé que l'implémentation du HAL modifie spontanément les états d'affichage. Si l'écran n'est actuellement détenu par aucun client (par un appel à openDisplay), cette fonction renvoie NOT_OPEN. Sinon, elle indique l'état actuel de l'écran EVS (voir API IEvsDisplay).

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 kernel ou d'un nom donné à l'appareil, tel que rearview (vue arrière). 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 des informations de caméra spécialisées de manière opaque du pilote à une application EVS personnalisée. Elle est transmise sans interprétation du pilote à l'application EVS, qui est libre de l'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 l'CameraDesc de cette caméra.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Indique la profondeur de la chaîne de tampons que la caméra doit prendre en charge. Le client d'IEvsCamera peut détenir jusqu'à ce nombre de frames simultanément. Si autant de frames ont été transmis au récepteur sans être renvoyés par doneWithFrame, le flux ignore des trames jusqu'à ce qu'un tampon soit renvoyé pour réutilisation. Cet appel peut être envoyé à tout moment, même lorsque des flux sont déjà en cours d'exécution. Dans ce cas, des tampons doivent être ajoutés ou supprimés de la chaîne selon les besoins. Si aucun appel n'est effectué vers ce point d'entrée, IEvsCamera accepte au moins une image par défaut, ce qui est plus acceptable.

Si le bufferCount demandé ne peut pas être accepté, la fonction renvoie BUFFER_NOT_AVAILABLE ou un autre code d'erreur approprié. Dans ce cas, le système continue de fonctionner avec la valeur précédemment définie.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Demande la diffusion des images de la caméra EVS à partir de cette caméra. IEvsCameraStream commence à recevoir des appels périodiques avec de nouveaux frames d'image jusqu'à ce que stopVideoStream() soit appelé. Les frames doivent commencer à être diffusés dans les 500 ms suivant l'appel startVideoStream et, après le début, à être générés à un minimum de 10 FPS. Le temps nécessaire pour démarrer le flux vidéo est pris en compte dans le délai de démarrage de la caméra arrière. Si le flux n'est pas démarré, un code d'erreur doit être renvoyé. Sinon, la valeur "OK" est renvoyée.

oneway doneWithFrame(BufferDesc buffer);

Renvoie un frame qui a été transmis à l'IEvsCameraStream. Lorsque vous avez terminé d'utiliser un frame envoyé à l'interface IEvsCameraStream, vous devez le renvoyer à IEvsCamera pour le réutiliser. Un petit nombre fini de tampons est disponible (peut-être aussi petit qu'un seul), et si l'approvisionnement est épuisé, aucun autre frame n'est envoyé tant qu'un tampon n'est pas renvoyé, ce qui peut entraîner des frames ignorés (un tampon avec un gestionnaire 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é pouvant inclure INVALID_ARG ou BUFFER_NOT_AVAILABLE.

stopVideoStream();

Arrêt de la diffusion des images de la caméra EVS. Étant donné que la diffusion est asynchrone, les trames 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 été démarré, auquel cas il est ignoré.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Demande des informations spécifiques au pilote à l'implémentation du HAL. Les valeurs autorisées pour opaqueIdentifier sont spécifiques au pilote, mais aucune valeur transmise ne peut provoquer le plantage du pilote. Le pilote doit renvoyer 0 pour toute valeur opaqueIdentifier non reconnue.

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 au véhicule. Aucune implémentation HAL ne doit nécessiter cet appel pour fonctionner dans un état par défaut. Si le pilote reconnaît et accepte les valeurs, la valeur "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 lecteur HAL est chargé de remplir cette structure pour décrire le tampon d'image, et 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, ce qui peut être nécessaire pour utiliser l'image avec EGL avec l'extension eglCreateImageKHR().

  • width : largeur en pixels de l'image présentée.
  • height : hauteur en pixels de l'image présentée.
  • stride : nombre de pixels que chaque ligne occupe réellement en mémoire, en tenant compte de tout espace réservé pour l'alignement des lignes. Exprimé en pixels pour correspondre à la convention adoptée par gralloc pour ses descriptions de tampons.
  • pixelSize : nombre d'octets occupés par chaque pixel, ce qui permet de calculer la taille en octets nécessaire pour passer entre les lignes de 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 est à privilégier pour l'utilisation de l'appareil photo, et RGBA ou BGRA pour l'affichage.
  • usage : options d'utilisation définies par l'implémentation HAL. Les clients HAL doivent transmettre ces éléments sans modification (pour en savoir plus, consultez les indicateurs associés à Gralloc.h).
  • bufferId. Valeur unique spécifiée par l'implémentation HAL pour permettre la reconnaissance d'une mémoire tampon 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. Poignée du tampon de mémoire sous-jacent contenant les données d'image. L'implémentation du HAL peut choisir de stocker ici un gestionnaire de tampon Gralloc.

IEvsCameraStream

Le client met en œuvre cette interface pour recevoir des images vidéo asynchrones.

deliverFrame(BufferDesc buffer);

Reçoit des appels du HAL chaque fois qu'une image vidéo est prête à être inspectée. Les poignées de tampon reçues par cette méthode doivent être renvoyées via des appels à IEvsCamera::doneWithFrame(). Lorsque le flux vidéo est arrêté avec un appel à IEvsCamera::stopVideoStream(), ce rappel peut se poursuivre à mesure que le pipeline se vide. Chaque trame doit toujours être renvoyée. Lorsque la dernière trame du flux a été délivrée, une valeur bufferHandle NULL est délivrée, indiquant la fin du flux et qu'aucune autre livraison de trame n'a lieu. La valeur NULL bufferHandle elle-même n'a pas besoin d'être renvoyée avec doneWithFrame(), mais tous les autres identifiants doivent être renvoyés.

Bien que les formats de tampon propriétaires soient techniquement possibles, les tests de compatibilité nécessitent que le tampon soit dans l'un des quatre formats compatibles : NV21 (YCrCb 4:2:0 semi-plan), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 entrelacé), RVBA (32 bits R:G:B:G: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 le memHandle dans la structure BufferDesc. Les valeurs bufferId sont essentiellement privées à l'implémentation du pilote HAL, et elles peuvent être utilisées (et réutilisées) comme bon lui semble.

IEvsDisplay

Cet objet représente l'écran Evs, contrôle l'état de l'écran et gère la présentation réelle des images.

getDisplayInfo() generates (DisplayDesc info);

Renvoie des informations de base sur l'affichage EVS fournies 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 de manière appropriée une requête pour n'importe quel état, même si la réponse consiste à ignorer la requête.

Lors de l'initialisation, l'écran est défini pour démarrer dans l'état NOT_VISIBLE, après quoi le client doit demander l'état VISIBLE_ON_NEXT_FRAME et commencer à fournir la vidéo. Lorsque l'affichage n'est plus nécessaire, le client doit demander l'état NOT_VISIBLE après avoir transmis le dernier frame vidéo.

Il est valide pour n'importe quel État à demander à tout moment. Si l'écran est déjà visible, il doit le rester s'il est défini 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);

Récupère l'état d'affichage. L'implémentation de HAL doit indiquer l'état actuel réel, qui peut différer de l'état le plus récemment demandé. La logique chargée de modifier les états d'affichage doit se trouver au-dessus de la couche de l'appareil. Il est donc déconseillé que l'implémentation du HAL modifie spontanément les états d'affichage.

getTargetBuffer() generates (handle bufferHandle);

Renvoie un handle vers un tampon d'affichage associé à l'écran. Ce tampon peut être verrouillé et utilisé par un logiciel et/ou GL. Ce tampon doit être renvoyé avec un appel à returnTargetBufferForDisplay(), même si l'écran n'est plus visible.

Bien que les formats de tampon propriétaires soient techniquement possibles, les tests de compatibilité nécessitent que le tampon soit dans l'un des quatre formats compatibles : NV21 (YCrCb 4:2:0 semi-plan), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 entrelacé), RVBA (32 bits R:G:B:G:x), 32 bits R:G:B:G. 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 gestionnaire 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 via un appel à getTargetBuffer() sont valides pour 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. En cas de réussite, renvoie "OK", ou un code d'erreur approprié peut inclure 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'affichage EVS. Il peut s'agir d'un écran physique ou virtuel qui est superposé ou mélangé à un autre appareil de présentation.

  • display_id : chaîne qui identifie l'affichage de manière unique. Il peut s'agir du nom de l'appareil du kernel ou d'un nom donné à l'appareil, comme rearview (vue arrière). 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 des informations de caméra spécialisées de manière opaque du pilote à une application EVS personnalisée. Elle est transmise sans interprétation du pilote à l'application EVS, qui est libre de l'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
}

Indique l'état de l'écran de l'EVS, qui peut être désactivé (non visible par le conducteur) ou activé (affiche une image au conducteur). Inclut un état transitoire où l'écran 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

EVS Manager fournit l'interface publique au système EVS pour collecter et présenter des 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 et le seul client autorisé à écrire des données d'affichage (des clients supplémentaires peuvent être autorisés à accéder en lecture seule aux images de la caméra).

EVS Manager 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 EVS Manager et recevoir un flux vidéo).

Schéma de l&#39;EVS Manager et de l&#39;API EVS Hardware.
Figure 2. Le gestionnaire EVS reflète l'API matérielle EVS sous-jacente.

Les applications ne voient aucune différence lorsqu'elles fonctionnent via l'implémentation du HAL matériel EVS ou l'API EVS Manager, sauf que l'API EVS Manager permet un accès simultané au flux de la caméra. Le gestionnaire EVS est, lui-même, le seul client autorisé de la couche HAL matérielle EVS et agit en tant que proxy pour la HAL matérielle EVS.

Les sections suivantes ne décrivent que les appels dont le comportement (étendu) est différent dans l'implémentation du Gestionnaire EVS. Les autres appels sont identiques aux descriptions HAL EVS.

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 unique camera_id. Renvoie une valeur NULL en cas d'échec. Au niveau de la couche EVS Manager, tant que suffisamment de ressources système sont disponibles, une caméra déjà ouverte peut être ouverte à nouveau par un autre processus, ce qui permet le transfert du flux vidéo vers plusieurs applications grand public. Les chaînes camera_id au niveau de la couche EVS Manager sont identiques à celles signalées à la couche matérielle EVS.

IEvsCamera

L'implémentation IEvsCamera fournie par le Gestionnaire EVS est virtualisée en interne afin que les opérations effectuées par un client sur une caméra n'affectent pas les autres clients, qui conservent un accès indépendant à leurs caméras.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Lance 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 frames lorsqu'il a terminé, mais il est autorisé à les conserver aussi longtemps qu'il le souhaite. Lorsque le nombre de frames détenu par un client atteint sa limite configurée, il ne reçoit plus de frames tant qu'il n'en renvoie pas un. Ce saut de trame n'affecte pas les autres clients, qui continuent de recevoir toutes les trames 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éra sous-jacent au niveau de la couche matérielle 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 peut permettre à 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 les effets secondaires s'appliquent à tous les clients d'une caméra donnée. Par exemple, si un fournisseur utilisait cet appel pour modifier les fréquences d'images, tous les clients de la caméra de la couche matérielle concernée recevraient des images au nouveau débit.

IEvsDisplay

Un seul propriétaire de l'écran est autorisé, même au niveau du gestionnaire 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 native C++ d'une application EVS qui communique avec le Gestionnaire EVS et le HAL du véhicule pour fournir des fonctions de base de la caméra de recul. L'application devrait démarrer très tôt dans le processus de démarrage du système, et la vidéo appropriée s'affiche en fonction des caméras disponibles et de l'état de la voiture (état des vitesses et des clignotants). Les OEM peuvent modifier ou remplacer l'application EVS par leur propre logique et leur propre présentation spécifiques au véhicule.

Figure 3 : Exemple de logique d'application EVS, obtenir la liste des caméras.


Figure 4. Logique d'exemple d'application EVS, rappel de frame reçu.

Étant donné que les données d'image sont présentées à l'application dans un tampon graphique standard, l'application est chargée de déplacer l'image du tampon source vers le tampon de sortie. Bien que cela entraîne le coût d'une copie de données, l'application peut également afficher l'image dans le tampon d'affichage de la manière qu'elle souhaite.

Par exemple, l'application peut choisir de déplacer les données de pixel elle-même, éventuellement avec une opération d'échelle ou de rotation intégrée. L'application peut également choisir d'utiliser l'image source comme 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 du dessus de l'environnement du véhicule).

Utiliser EGL/SurfaceFlinger dans l'HAL de l'écran EVS

Cette section explique comment utiliser EGL pour afficher une implémentation HAL d'écran EVS sous Android 10.

Une implémentation de référence HAL EVS utilise EGL pour afficher l'aperçu de l'appareil photo à l'écran et utilise libgui pour créer la surface de rendu EGL cible. Sous Android 8 (et versions ultérieures), libgui est classé comme VNDK-private, ce qui fait référence à un groupe de bibliothèques disponibles pour les bibliothèques VNDK que les processus des fournisseurs 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.

Créer libgui pour les processus du fournisseur

L'utilisation de libgui constitue la seule option permettant d'utiliser EGL/SurfaceFlinger dans les implémentations HAL d'affichage EVS. Le moyen le plus simple d'implémenter libgui consiste à utiliser directement frameworks/native/libs/gui en utilisant une cible de compilation supplémentaire dans le script de compilation. Cette cible est identique à la cible libgui, à l'exception de deux champs ajoutés:

  • 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 fournisseurs sont créées avec la macro NO_INPUT, qui supprime un mot de 32 bits des données de lot. Étant donné que SurfaceFlinger s'attend à ce champ qui a été supprimé, il ne parvient pas à analyser le parcel. Cela se traduit par une défaillance 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);

Des exemples d'instructions de compilation sont fournis ci-dessous. 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 un liaisonneur dans l'implémentation du HAL EVS

Sous Android 8 (et versions ultérieures), le nœud d'appareil /dev/binder est devenu exclusif aux processus de 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 de liaison, /dev/vndbinder.

Domaine IPC Description
/dev/binder Communication inter-processus entre les processus de framework/application avec des interfaces AIDL
/dev/hwbinder Communication inter-processus entre les processus de framework/fournisseur avec des interfaces HIDL
Communication inter-processus entre les processus de fournisseur avec des interfaces HIDL
/dev/vndbinder Communication interprocessus entre les fournisseurs et les processus avec des interfaces AIDL

Bien que SurfaceFlinger définisse des interfaces AIDL, les processus du fournisseur ne peuvent utiliser que des interfaces HIDL pour communiquer avec les processus du framework. Une quantité non négligeable de travail est requise pour convertir les interfaces AIDL existantes en HIDL. Heureusement, Android fournit une méthode permettant de sélectionner le pilote de liaison pour libbinder, auquel les processus de bibliothèque d'espace utilisateur sont associé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 de liaison.

Règles SELinux

Si l'implémentation de l'appareil est complète, SELinux empêche les processus du fournisseur d'utiliser /dev/binder. Par exemple, une implémentation d'exemple HAL EVS est attribuée au domaine hal_evs_driver et nécessite des autorisations en 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

Cependant, 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 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 une implémentation de référence EVS HAL en tant que processus fournisseur

À titre de référence, vous pouvez appliquer les modifications suivantes à packages/services/Car/evs/Android.mk. Assurez-vous 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;