Caméra de véhicule HAL

Android contient une couche d'abstraction matérielle (HAL) HIDL automobile qui permet la capture et l'affichage d'images très tôt dans le processus de démarrage d'Android et continue de fonctionner pendant toute la durée de vie du système. Le HAL comprend la pile du système de vision extérieure (EVS) et est généralement utilisé pour prendre en charge les caméras de recul et les écrans à vue panoramique dans les véhicules équipés de systèmes d'infodivertissement embarqués (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 à 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 de caméra 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’un HAL dédié permet une interface rationalisée et indique clairement ce qu’un OEM doit mettre en œuvre pour prendre en charge la pile EVS.

Composants du système

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 SVE

Un exemple d'application C++ EVS ( /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 pour affichage au gestionnaire EVS. Il devrait être démarré par init dès que EVS et Car Service seront disponibles, dans les deux (2) secondes suivant la mise sous tension. Les OEM peuvent modifier ou remplacer l'application EVS à leur guise.

Responsable SVE

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, depuis un simple affichage de caméra de recul jusqu'à un rendu multi-caméras 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 Car Service) peuvent interroger l'état du gestionnaire EVS pour savoir quand le système EVS est actif.

Interface EVS-HIDL

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 exerce l'interface (génère des images de test synthétiques et valide les images lors de l'aller-retour) est fourni dans /hardware/interfaces/automotive/evs/1.0/default .

L'OEM est responsable de la mise en œuvre de l'API exprimée par les fichiers .hal dans /hardware/interfaces/automotive/evs . De telles implémentations sont chargées de configurer et de collecter les données des caméras physiques et de les transmettre via des tampons de mémoire partagée reconnaissables par Gralloc. Le côté affichage de l'implémentation est chargé de fournir un tampon de mémoire partagée qui peut être rempli par l'application (généralement via le rendu EGL) et de présenter les images terminées de préférence à tout ce qui pourrait vouloir apparaître sur l'écran physique. Les implémentations des fournisseurs de l'interface EVS peuvent être stockées sous /vendor/… /device/… ou hardware/… (par exemple, /hardware/[vendor]/[platform]/evs ).

Pilotes du noyau

Un périphérique prenant en charge 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 EVS via les pilotes de caméra et/ou d'affichage existants. La réutilisation des pilotes pourrait ê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 présenter l'image de sortie.

Description de l'interface matérielle EVS

La section décrit le HAL. Les fournisseurs doivent fournir des implémentations de cette API adaptées à leur matériel.

IEvsEnumérateur

Cet objet est chargé d'énumérer le matériel EVS disponible dans le système (une ou plusieurs caméras et le seul périphérique 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 connaissable au moment du démarrage. Pour plus de détails 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 un NULL en cas d'échec. Les tentatives de réouverture d’une caméra déjà ouverte ne peuvent é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 demande puisse être satisfaite. Une instance de caméra qui a été préemptée de cette manière doit être mise dans un état inactif, en attendant sa destruction finale et en répondant à toute demande visant à affecter l'état de la caméra avec un code retour 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'affichage EVS du système. Un seul client peut détenir une instance fonctionnelle de IEvsDisplay à la fois. Semblable au comportement d'ouverture agressif décrit dans openCamera , un nouvel objet IEvsDisplay peut être créé à tout moment et désactivera 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. Finalement, l'application client devrait remarquer les codes de retour d'erreur OWNERSHIP_LOST et 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 via getTargetBuffer() doivent être renvoyés à l'écran avant de fermer l'écran.

getDisplayState() generates (DisplayState state);

Obtient l’état d’affichage actuel. La mise en œuvre de HAL doit signaler l'état actuel réel, qui peut différer de l'état demandé le plus récemment. La logique responsable du changement des états d'affichage doit exister au-dessus de la couche de périphérique, ce qui rend indésirable que l'implémentation HAL change spontanément les états d'affichage. Si l'affichage n'est actuellement détenu par aucun client (par un appel à openDisplay), alors cette fonction renvoie NOT_OPEN . Sinon, il signale l'état actuel de l'affichage 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 du périphérique du noyau ou du nom du 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 . Une méthode permettant de transmettre de manière opaque des informations spécialisées sur la caméra du conducteur à une application EVS personnalisée. Il est transmis sans interprétation du conducteur à l'application EVS, qui est libre de l'ignorer.

IEvsCaméra

Cet objet représente une seule caméra et constitue l'interface principale pour capturer des 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 tampon que la caméra doit prendre en charge. Jusqu'à présent, de nombreuses images peuvent être conservées simultanément par le client d'IEvsCamera. Si autant de trames ont été transmises au récepteur sans être renvoyées par doneWithFrame , le flux ignore les trames jusqu'à ce qu'un tampon soit renvoyé pour réutilisation. Il est légal que cet appel soit effectué à tout moment, même lorsque les flux sont déjà en cours d'exécution, auquel cas des 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 une image par défaut ; avec plus acceptable.

Si le bufferCount demandé ne peut pas être pris en compte, 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 précédemment définie.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Demande la livraison des images de caméra EVS à partir de cette caméra. IEvsCameraStream commence à recevoir des appels périodiques avec de nouvelles images jusqu'à ce que stopVideoStream() soit appelé. Les images doivent commencer à être livrées dans les 500 ms suivant l'appel startVideoStream et, après le démarrage, doivent être générées à un minimum de 10 FPS. Le temps requis pour démarrer le flux vidéo est effectivement pris en compte dans toute exigence de temps de démarrage de 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 une image qui a été transmise par à IEvsCameraStream. Une fois la consommation d’une image livrée à l’interface IEvsCameraStream terminée, la trame doit être renvoyée à IEvsCamera pour être réutilisée. Un petit nombre fini de tampons sont disponibles (peut-être aussi petit qu'un), et si la réserve est épuisée, aucune autre trame n'est délivrée jusqu'à ce qu'un tampon soit renvoyé, ce qui peut entraîner des trames sauté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 succès, ou un code d'erreur approprié incluant potentiellement INVALID_ARG ou BUFFER_NOT_AVAILABLE .

stopVideoStream();

Arrête la livraison des cadres de caméra EVS. La livraison étant asynchrone, les trames peuvent continuer à arriver pendant un certain temps après le retour de cet appel. Chaque trame doit être renvoyée jusqu'à ce que la fermeture du flux soit signalée à IEvsCameraStream. Il est légal d'appeler stopVideoStream sur un flux déjà arrêté ou jamais démarré, auquel cas il est ignoré.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Demande des informations spécifiques au pilote à l’implémentation de HAL. Les valeurs autorisées pour opaqueIdentifier sont spécifiques au pilote, mais aucune valeur transmise ne peut faire planter le 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 est fournie uniquement pour faciliter les extensions spécifiques au véhicule et aucune implémentation de HAL ne devrait exiger que cet appel fonctionne dans un état par défaut. Si le pilote reconnaît et accepte les valeurs, OK doit être renvoyé ; 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 , comme cela peut être nécessaire pour utiliser l'image avec EGL via l'extension eglCreateImageKHR() .

  • width . La largeur en pixels de l'image présentée.
  • height . La hauteur en pixels de l'image présentée.
  • stride . Nombre de pixels que chaque ligne occupe réellement en mémoire, prenant en compte tout remplissage nécessaire à 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 individuel, permettant 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 . Le format de pixel utilisé par l'image. Le format fourni doit être compatible avec l'implémentation OpenGL de la plateforme. Pour réussir les tests de compatibilité, HAL_PIXEL_FORMAT_YCRCB_420_SP doit être préféré pour l'utilisation de la caméra, et RGBA ou BGRA doit être préféré pour l'affichage.
  • usage . Indicateurs d'utilisation définis par l'implémentation HAL. Les clients HAL sont censés transmettre ces informations sans modification (pour plus de détails, reportez-vous aux indicateurs associés à Gralloc.h ).
  • bufferId . Une valeur unique spécifiée par l'implémentation de HAL pour permettre à un tampon d'être reconnu après un aller-retour via les API HAL. La valeur stockée dans ce champ peut être arbitrairement choisie par l'implémentation HAL.
  • memHandle . Handle de la mémoire tampon sous-jacente qui contient les données d’image. L'implémentation de HAL peut choisir de stocker ici un handle de tampon Gralloc.

IEvsCameraStream

Le client implémente cette interface pour recevoir des livraisons d'images vidéo asynchrones.

deliverFrame(BufferDesc buffer);

Reçoit les appels du HAL chaque fois qu'une image vidéo est prête à être inspectée. Les handles de tampon reçus par cette méthode doivent être renvoyés via des appels à IEvsCamera::doneWithFrame() . Lorsque le flux vidéo est arrêté via un appel à IEvsCamera::stopVideoStream() , ce rappel peut continuer pendant que le pipeline se vide. Chaque image doit encore être renvoyée ; lorsque la dernière trame du flux a été livrée, un bufferHandle NULL sera livré, signifiant la fin du flux et aucune autre livraison de trame n'aura lieu. Le NULL bufferHandle lui-même n'a pas besoin d'être renvoyé via 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é nécessitent 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 entrelacé), RGBA (32 bits R:G:B:x), 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 plateforme.

L'application ne doit s'appuyer sur aucune correspondance entre le champ bufferId et le memHandle dans la structure BufferDesc . Les valeurs bufferId sont essentiellement privées de l’implémentation du pilote HAL, et celle-ci peut les utiliser (et les réutiliser) comme bon lui semble.

IEvsDisplay

Cet objet représente l'affichage Evs, contrôle l'état de l'affichage et gère la présentation réelle 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é, et la mise en œuvre de HAL doit accepter gracieusement une demande pour n'importe quel état alors qu'elle se trouve dans n'importe quel autre état, bien que la réponse puisse être d'ignorer la demande.

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 de la vidéo. Lorsque l'affichage n'est plus nécessaire, le client doit demander l'état NOT_VISIBLE après avoir passé la dernière image vidéo.

Il est valable pour n’importe quel état et peut être demandé à tout moment. Si l'affichage est déjà visible, il doit rester visible 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);

Obtient l'état d'affichage. La mise en œuvre de HAL doit signaler l'état actuel réel, qui peut différer de l'état demandé le plus récemment. La logique responsable du changement des états d'affichage doit exister au-dessus de la couche de périphérique, ce qui rend indésirable que l'implémentation de HAL change spontanément les états d'affichage.

getTargetBuffer() generates (handle bufferHandle);

Renvoie un handle vers un tampon de trame associé à l’affichage. Ce tampon peut être verrouillé et écrit par un logiciel et/ou GL. Ce tampon doit être renvoyé via un appel à returnTargetBufferForDisplay() même si l'affichage 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 pris en charge : NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4: 2:2 entrelacé), RGBA (32 bits R:G:B:x), 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 plateforme.

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 via un appel à getTargetBuffer() sont valides pour une utilisation avec cet appel, et le contenu de BufferDesc ne peut pas être modifié par l'application client. Après cet appel, le tampon n'est plus valide pour une utilisation par le client. Renvoie OK en cas de succès, ou un code d'erreur approprié incluant 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 affichage 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 affichage physique ou d'un affichage virtuel superposé ou mélangé à un autre périphérique de présentation.

  • display_id . Chaîne qui identifie de manière unique l'affichage. Il peut s'agir du nom du périphérique du noyau ou du nom du 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 . Une méthode permettant de transmettre de manière opaque des informations spécialisées sur la caméra du conducteur à une application EVS personnalisée. Il est transmis sans interprétation du conducteur à 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
}

Décrit l'état de l'affichage EVS, qui peut être désactivé (non visible pour 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 à devenir visible avec la livraison de la prochaine image d'imagerie via l'appel returnTargetBufferForDisplay() .

Responsable SVE

Le gestionnaire EVS fournit l'interface publique au système EVS pour collecter et présenter les vues des caméras externes. Là où 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 est le seul client autorisé à écrire des données d'affichage (des clients supplémentaires peuvent bénéficier d'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 (plus d'un client peut ouvrir une caméra via le gestionnaire EVS et recevoir un flux vidéo).

Diagramme de l'API EVS Manager et EVS Hardware.

Figure 2. EVS Manager reflète l'API matérielle EVS sous-jacente.

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é au flux de 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 couche HAL matérielle EVS.

Les sections suivantes décrivent uniquement les appels qui ont un comportement différent (étendu) dans l'implémentation du gestionnaire EVS ; les appels restants sont identiques aux descriptions EVS HAL.

IEvsEnumérateur

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 un NULL en cas d'échec. Au niveau de la couche EVS Manager, tant que des ressources système suffisantes sont disponibles, une caméra déjà ouverte peut être rouverte par un autre processus, permettant ainsi la transmission du flux vidéo vers plusieurs applications grand public. Les chaînes camera_id au niveau de la couche EVS Manager sont les mêmes que celles signalées à la couche matérielle EVS.

IEvsCaméra

Le gestionnaire EVS fourni par l'implémentation d'IEvsCamera est virtualisé en interne afin que les opérations sur une caméra par un client n'affectent pas 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 au démarrage du premier client.

doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);

Renvoie une image. Chaque client doit rendre ses cadres une fois qu'il a terminé, mais est autorisé à conserver ses cadres aussi longtemps qu'il le souhaite. Lorsque le nombre de trames détenu par un client atteint sa limite configurée, il ne recevra plus de trames jusqu'à ce qu'il en renvoie une. 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, permettant 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 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 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 EVS Manager. Le gestionnaire n'ajoute aucune fonctionnalité et transmet simplement l'interface IEvsDisplay directement à l'implémentation HAL sous-jacente.

Application SVE

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 les 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, avec une vidéo appropriée affichée en fonction des caméras disponibles et de l'état de la voiture (état du rapport de vitesse et des clignotants). Les constructeurs OEM peuvent modifier ou remplacer l'application EVS par leur propre logique et présentation spécifiques au véhicule.

Figure 3. Exemple de logique de l'application EVS, obtenez la liste des caméras.



Figure 4. Exemple de logique de l'application EVS, réception d'un rappel de trame.

É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, cela offre également à l'application la possibilité de restituer l'image dans le tampon d'affichage de la manière qu'elle souhaite.

Par exemple, l'application peut choisir de déplacer elle-même les données de pixels, éventuellement avec une opération d'échelle ou de rotation en ligne. L'application peut également choisir d'utiliser l'image source comme texture OpenGL et de restituer une scène complexe dans le tampon de sortie, comprenant des éléments virtuels tels que des icônes, des lignes directrices et des animations. Une application plus sophistiquée peut également sélectionner plusieurs caméras d'entrée simultanées et les fusionner dans une seule image de sortie (par exemple pour une utilisation dans une vue virtuelle descendante de l'environnement du véhicule).

Utilisez l'EGL/SurfaceFlinger dans le HAL d'affichage EVS

Cette section explique comment utiliser EGL pour restituer une implémentation EVS Display HAL dans Android 10.

Une implémentation de référence EVS HAL utilise EGL pour afficher l'aperçu de la caméra sur l'écran et utilise libgui pour créer la surface de rendu EGL cible. Dans 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.

Construire libgui pour les processus des fournisseurs

L'utilisation de libgui constitue la seule option pour utiliser EGL/SurfaceFlinger dans les implémentations EVS Display HAL. Le moyen le plus simple d'implémenter libgui consiste à utiliser directement frameworks/native/libs/gui en utilisant une cible de construction supplémentaire dans le script de construction. 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 des fournisseurs sont créées avec la macro NO_INPUT , qui supprime un mot de 32 bits des données de parcelle. Étant donné que SurfaceFlinger s'attend à ce que ce champ ait été supprimé, SurfaceFlinger ne parvient pas à analyser la parcelle. Ceci est observé comme 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 cette condition :

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 construction sont fournis ci-dessous. Attendez-vous à 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 classeur dans une 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 des fournisseurs. Au lieu de cela, les processus du fournisseur doivent utiliser /dev/hwbinder et doivent convertir toutes les interfaces AIDL en HIDL. Pour ceux qui souhaitent continuer à utiliser les interfaces AIDL entre les processus des fournisseurs, utilisez le domaine de classeur, /dev/vndbinder .

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

Alors que SurfaceFlinger définit les interfaces AIDL, les processus des fournisseurs ne peuvent utiliser que les interfaces HIDL pour communiquer avec les processus du framework. Une quantité de travail non négligeable est nécessaire pour convertir les interfaces AIDL existantes en HIDL. Heureusement, Android fournit une méthode permettant de sélectionner le pilote de classeur pour libbinder , auquel les processus de la bibliothèque de l'espace utilisateur 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 l'appeler avant d'appeler Process ou IPCThreadState , ou avant d'effectuer des appels de classeur.

Politiques SELinux

Si l'implémentation du périphérique est complète, 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 r/w sur 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 provoque un échec de construction car il enfreint les règles Neverallow suivantes définies dans system/sepolicy/domain.te pour un périphérique à triple triple.

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 la violation d'Android 10 décrite 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 de confirmer 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;