HAL de la cámara del vehículo

Android contiene una capa de abstracción de hardware (HAL) de HIDL para automóviles que permite la captura y visualización de imágenes muy al principio del proceso de arranque de Android y sigue funcionando durante la vida útil del sistema. La HAL incluye la pila del sistema de vista exterior (EVS) y, por lo general, se usa para admitir la cámara de vista posterior y las pantallas de vista envolvente en vehículos con sistemas de infoentretenimiento (IVI) basados en Android. El EVS también permite implementar funciones avanzadas en las apps para usuarios.

Android también incluye una interfaz de controlador de captura y pantalla específica para el EVS (en /hardware/interfaces/automotive/evs/1.0). Si bien es posible compilar una app de cámara retrovisora sobre los servicios de cámara y pantalla existentes de Android, es probable que esa app se ejecute demasiado tarde en el proceso de arranque de Android. El uso de una HAL dedicada permite una interfaz optimizada y deja en claro lo que un OEM debe implementar para admitir la pila de EVS.

Componentes del sistema

El EVS incluye los siguientes componentes del sistema:

Diagrama de los componentes del sistema de EVS
Figura 1: Descripción general de los componentes del sistema EVS.

App de EVS

Una app de EVS de C++ de ejemplo (/packages/services/Car/evs/app) sirve como implementación de referencia. Esta app es responsable de solicitar fotogramas de video al EVS Manager y enviar los fotogramas terminados para que se muestren de nuevo en el EVS Manager. Se espera que init lo inicie en cuanto estén disponibles los servicios de EVS y Car, y que se ejecute en un plazo de dos (2) segundos después del encendido. Los OEM pueden modificar o reemplazar la app de EVS como deseen.

Administrador de EVS

El EVS Manager (/packages/services/Car/evs/manager) proporciona los componentes básicos que necesita una app de EVS para implementar cualquier cosa, desde una pantalla simple de cámara retrovisora hasta una renderización multicámara de 6 DOF. Su interfaz se presenta a través de HIDL y está diseñada para aceptar varios clientes simultáneos. Otras apps y servicios (específicamente el servicio de automóvil) pueden consultar el estado del EVS Manager para saber cuándo está activo el sistema EVS.

Interfaz HIDL de EVS

El sistema EVS, tanto la cámara como los elementos de la pantalla, se define en el paquete android.hardware.automotive.evs. En /hardware/interfaces/automotive/evs/1.0/default, se proporciona una implementación de muestra que ejercita la interfaz (genera imágenes de prueba sintéticas y valida que las imágenes realicen el viaje de ida y vuelta).

El OEM es responsable de implementar la API que expresan los archivos .hal en /hardware/interfaces/automotive/evs. Estas implementaciones son responsables de configurar y recopilar datos de las cámaras físicas, y de entregarlos a través de búferes de memoria compartida reconocibles por Gralloc. El lado de la implementación de la pantalla es responsable de proporcionar un búfer de memoria compartida que la app puede completar (por lo general, con renderización de EGL) y de presentar los fotogramas terminados con preferencia a cualquier otra cosa que pueda aparecer en la pantalla física. Las implementaciones del proveedor de la interfaz de EVS se pueden almacenar en /vendor/… /device/… o hardware/… (p.ej., /hardware/[vendor]/[platform]/evs).

Controladores del kernel

Un dispositivo que admite la pila de EVS requiere controladores de kernel. En lugar de crear controladores nuevos, los OEM tienen la opción de admitir las funciones requeridas por el EVS a través de los controladores de hardware de cámara o pantalla existentes. Reutilizar los controladores puede ser ventajoso, en especial para los controladores de pantalla, en los que la presentación de imágenes puede requerir coordinación con otros subprocesos activos. Android 8.0 incluye un controlador de muestra basado en v4l2 (en packages/services/Car/evs/sampleDriver) que depende del kernel para la compatibilidad con v4l2 y de SurfaceFlinger para presentar la imagen de salida.

Descripción de la interfaz de hardware de EVS

En esta sección, se describe el HAL. Se espera que los proveedores proporcionen implementaciones de esta API adaptadas a su hardware.

IEvsEnumerator

Este objeto es responsable de enumerar el hardware de EVS disponible en el sistema (una o más cámaras y el único dispositivo de pantalla).

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

Devuelve un vector que contiene descripciones de todas las cámaras del sistema. Se supone que el conjunto de cámaras es fijo y se puede conocer en el momento del inicio. Para obtener detalles sobre las descripciones de la cámara, consulta CameraDesc.

openCamera(string camera_id) generates (IEvsCamera camera);

Obtiene un objeto de interfaz que se usa para interactuar con una cámara específica identificada por la cadena camera_id única. Devuelve NULL si falla. Los intentos de volver a abrir una cámara que ya está abierta no pueden fallar. Para evitar las condiciones de carrera asociadas con el inicio y el cierre de la app, la reapertura de una cámara debe cerrar la instancia anterior para que se pueda satisfacer la nueva solicitud. Una instancia de cámara que se haya interrumpido de esta manera debe ponerse en un estado inactivo, a la espera de su destrucción final y responder a cualquier solicitud para afectar el estado de la cámara con un código de retorno de OWNERSHIP_LOST.

closeCamera(IEvsCamera camera);

Libera la interfaz de IEvsCamera (y es lo opuesto a la llamada openCamera()). Se debe detener el flujo de video de la cámara llamando a stopVideoStream() antes de llamar a closeCamera.

openDisplay() generates (IEvsDisplay display);

Obtiene un objeto de interfaz que se usa para interactuar exclusivamente con la pantalla del EVS del sistema. Solo un cliente puede tener una instancia funcional de IEvsDisplay a la vez. De manera similar al comportamiento de apertura agresiva que se describe en openCamera, se puede crear un nuevo objeto IEvsDisplay en cualquier momento, lo que inhabilita cualquier instancia anterior. Las instancias invalidadas siguen existiendo y responden a las llamadas de funciones de sus propietarios, pero no deben realizar operaciones de mutación cuando están inactivas. Con el tiempo, se espera que la app cliente detecte los códigos de error de OWNERSHIP_LOST, cierre y libere la interfaz inactiva.

closeDisplay(IEvsDisplay display);

Libera la interfaz de IEvsDisplay (y es lo opuesto a la llamada de openDisplay()). Los búferes pendientes recibidos con llamadas a getTargetBuffer() se deben devolver a la pantalla antes de cerrarla.

getDisplayState() generates (DisplayState state);

Obtiene el estado de pantalla actual. La implementación del HAL debe informar el estado actual real, que puede diferir del estado solicitado más recientemente. La lógica responsable de cambiar los estados de la pantalla debe existir por encima de la capa del dispositivo, lo que hace que no sea conveniente que la implementación de la HAL cambie espontáneamente los estados de la pantalla. Si ningún cliente mantiene la pantalla actualmente (mediante una llamada a openDisplay), esta función devuelve NOT_OPEN. De lo contrario, informa el estado actual de la pantalla del EVS (consulta la API de IEvsDisplay).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id: Es una cadena que identifica de forma única una cámara determinada. Puede ser el nombre del dispositivo del kernel o un nombre para el dispositivo, como rearview. La implementación de HAL elige el valor de esta cadena y la pila superior lo usa de forma opaca.
  • vendor_flags. Es un método para pasar información especializada de la cámara de forma opaca desde el controlador a una app de EVS personalizada. Se pasa sin interpretar desde el controlador hasta la app de EVS, que puede ignorarlo.

IEvsCamera

Este objeto representa una sola cámara y es la interfaz principal para capturar imágenes.

getCameraInfo() generates (CameraDesc info);

Devuelve el CameraDesc de esta cámara.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Especifica la profundidad de la cadena de búferes que se le solicita a la cámara que admita. El cliente de IEvsCamera puede contener hasta esta cantidad de fotogramas de forma simultánea. Si se entregaron esta cantidad de fotogramas al receptor sin que doneWithFrame los devolviera, la transmisión omitirá fotogramas hasta que se devuelva un búfer para su reutilización. Es legal que esta llamada se realice en cualquier momento, incluso mientras ya se están ejecutando transmisiones, en cuyo caso se deben agregar o quitar búferes de la cadena según corresponda. Si no se realiza ninguna llamada a este punto de entrada, la IEvsCamera admite al menos un fotograma de forma predeterminada, y se aceptan más.

Si no se puede satisfacer el bufferCount solicitado, la función devuelve BUFFER_NOT_AVAILABLE o algún otro código de error pertinente. En este caso, el sistema sigue funcionando con el valor establecido anteriormente.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Solicita la entrega de fotogramas de la cámara de EVS desde esta cámara. El IEvsCameraStream comienza a recibir llamadas periódicas con nuevos fotogramas de imagen hasta que se llama a stopVideoStream(). Los fotogramas deben comenzar a entregarse en un plazo de 500 ms después de la llamada a startVideoStream y, una vez que comiencen, deben generarse a un mínimo de 10 FPS. El tiempo necesario para iniciar la transmisión de video se cuenta de forma efectiva en contra de cualquier requisito de tiempo de inicio de la cámara retrovisora. Si no se inicia la transmisión, se debe devolver un código de error; de lo contrario, se devuelve OK.

oneway doneWithFrame(BufferDesc buffer);

Devuelve un fotograma que se entregó a IEvsCameraStream. Cuando se termina de consumir un fotograma entregado a la interfaz IEvsCameraStream, el fotograma se debe devolver a IEvsCamera para su reutilización. Hay una pequeña cantidad finita de búferes disponibles (posiblemente, solo uno), y, si se agota la cantidad, no se entregan más fotogramas hasta que se devuelva un búfer, lo que puede provocar que se omitan fotogramas (un búfer con un identificador nulo denota el final de una transmisión y no es necesario devolverlo a través de esta función). Devuelve OK si la operación se realiza correctamente o el código de error apropiado, que puede incluir INVALID_ARG o BUFFER_NOT_AVAILABLE.

stopVideoStream();

Detiene la entrega de los fotogramas de la cámara de EVS. Debido a que la entrega es asíncrona, es posible que los fotogramas sigan llegando durante un tiempo después de que se devuelva esta llamada. Cada fotograma se debe devolver hasta que se señale el cierre de la transmisión a IEvsCameraStream. Es legal llamar a stopVideoStream en una transmisión que ya se detuvo o que nunca se inició, en cuyo caso se ignora.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Solicita información específica del controlador a la implementación de HAL. Los valores permitidos para opaqueIdentifier son específicos del controlador, pero si no se pasa ningún valor, es posible que el controlador falle. El controlador debe devolver 0 para cualquier opaqueIdentifier no reconocido.

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

Envía un valor específico del controlador a la implementación de HAL. Esta extensión solo se proporciona para facilitar las extensiones específicas del vehículo, y ninguna implementación de HAL debería requerir esta llamada para funcionar en un estado predeterminado. Si el controlador reconoce y acepta los valores, se debe devolver OK. De lo contrario, se debe devolver INVALID_ARG o algún otro código de error representativo.

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
}

Describe una imagen que se pasó a través de la API. El controlador HAL es responsable de completar esta estructura para describir el búfer de imagen, y el cliente HAL debe tratar esta estructura como de solo lectura. Los campos contienen suficiente información para permitir que el cliente reconstruya un objeto ANativeWindowBuffer, según sea necesario para usar la imagen con EGL con la extensión eglCreateImageKHR().

  • width: Es el ancho en píxeles de la imagen presentada.
  • height: Es la altura en píxeles de la imagen presentada.
  • stride: Es la cantidad de píxeles que ocupa cada fila en la memoria, teniendo en cuenta el padding para la alineación de las filas. Se expresa en píxeles para que coincida con la convención adoptada por gralloc para sus descripciones de búfer.
  • pixelSize: Es la cantidad de bytes que ocupa cada píxel individual, lo que permite calcular el tamaño en bytes necesario para avanzar entre las filas de la imagen (stride en bytes = stride en píxeles * pixelSize).
  • format: Es el formato de píxel que usa la imagen. El formato proporcionado debe ser compatible con la implementación de OpenGL de la plataforma. Para superar las pruebas de compatibilidad, se debe preferir HAL_PIXEL_FORMAT_YCRCB_420_SP para el uso de la cámara y RGBA o BGRA para la pantalla.
  • usage: Marcas de uso establecidas por la implementación de HAL. Se espera que los clientes de HAL pasen estos parámetros sin modificar (para obtener detalles, consulta las marcas relacionadas con Gralloc.h).
  • bufferId: Es un valor único especificado por la implementación de la HAL para permitir que se reconozca un búfer después de un viaje de ida y vuelta a través de las APIs de la HAL. La implementación del HAL puede elegir de forma arbitraria el valor almacenado en este campo.
  • memHandle: Es el identificador del búfer de memoria subyacente que contiene los datos de la imagen. La implementación del HAL puede optar por almacenar un identificador de búfer de Gralloc aquí.

IEvsCameraStream

El cliente implementa esta interfaz para recibir entregas asíncronas de fotogramas de video.

deliverFrame(BufferDesc buffer);

Recibe llamadas del HAL cada vez que un fotograma de video está listo para la inspección. Los identificadores de búfer que recibe este método se deben devolver a través de llamadas a IEvsCamera::doneWithFrame(). Cuando se detiene la transmisión de video con una llamada a IEvsCamera::stopVideoStream(), es posible que esta devolución de llamada continúe mientras se vacía la canalización. Se debe devolver cada fotograma. Cuando se entrega el último fotograma de la transmisión, se entrega un bufferHandle NULL, lo que indica el final de la transmisión y que no se producirán más entregas de fotogramas. El bufferHandle NULL en sí no necesita enviarse de vuelta con doneWithFrame(), pero se deben devolver todos los demás identificadores.

Si bien los formatos de búfer propietarios son técnicamente posibles, las pruebas de compatibilidad requieren que el búfer esté en uno de los cuatro formatos admitidos: NV21 (YCrCb 4:2:0 semiplanar), YV12 (YCrCb 4:2:0 planar), YUYV (YCrCb 4:2:2 intercalado), RGBA (32 bits R:G:B:x) y BGRA (32 bits B:G:R:x). El formato seleccionado debe ser una fuente de textura de GL válida en la implementación de GLES de la plataforma.

La app no debe depender de ninguna correspondencia entre el campo bufferId y el memHandle en la estructura BufferDesc. Los valores de bufferId son esencialmente privados para la implementación del controlador de HAL, y este puede usarlos (y reutilizarlos) según lo considere adecuado.

IEvsDisplay

Este objeto representa la pantalla de EVS, controla el estado de la pantalla y controla la presentación real de las imágenes.

getDisplayInfo() generates (DisplayDesc info);

Devuelve información básica sobre la pantalla del EVS proporcionada por el sistema (consulta DisplayDesc).

setDisplayState(DisplayState state) generates (EvsResult result);

Establece el estado de visualización. Los clientes pueden establecer el estado de pantalla para expresar el estado deseado, y la implementación del HAL debe aceptar correctamente una solicitud para cualquier estado mientras se encuentra en cualquier otro estado, aunque la respuesta puede ser ignorar la solicitud.

Tras la inicialización, se define que la pantalla comienza en el estado NOT_VISIBLE, después de lo cual se espera que el cliente solicite el estado VISIBLE_ON_NEXT_FRAME y comience a proporcionar video. Cuando ya no se requiere la pantalla, se espera que el cliente solicite el estado NOT_VISIBLE después de pasar el último fotograma de video.

Es válido solicitar cualquier estado en cualquier momento. Si la pantalla ya está visible, debería seguir visible si se establece en VISIBLE_ON_NEXT_FRAME. Siempre devuelve OK, a menos que el estado solicitado sea un valor de enumeración no reconocido, en cuyo caso se devuelve INVALID_ARG.

getDisplayState() generates (DisplayState state);

Obtiene el estado de la pantalla. La implementación del HAL debe informar el estado actual real, que puede diferir del estado solicitado más recientemente. La lógica responsable de cambiar los estados de la pantalla debe existir por encima de la capa del dispositivo, lo que hace que no sea deseable que la implementación de la HAL cambie espontáneamente los estados de la pantalla.

getTargetBuffer() generates (handle bufferHandle);

Devuelve un identificador para un búfer de fotogramas asociado con la pantalla. El software o GL pueden bloquear este búfer y escribir en él. Este búfer debe devolverse con una llamada a returnTargetBufferForDisplay(), incluso si la pantalla ya no está visible.

Si bien los formatos de búfer propietarios son técnicamente posibles, las pruebas de compatibilidad requieren que el búfer esté en uno de los cuatro formatos admitidos: 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) y BGRA (32 bits B:G:R:x). El formato seleccionado debe ser un destino de renderización de GL válido en la implementación de GLES de la plataforma.

Si se produce un error, se devuelve un búfer con un identificador nulo, pero no es necesario devolver ese búfer a returnTargetBufferForDisplay.

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Indica a la pantalla que el búfer está listo para mostrarse. Solo los búferes recuperados a través de una llamada a getTargetBuffer() son válidos para usarse con esta llamada, y la app cliente no puede modificar el contenido de BufferDesc. Después de esta llamada, el cliente ya no puede usar el búfer. Devuelve OK si la operación se realiza correctamente o el código de error apropiado, que puede incluir INVALID_ARG o BUFFER_NOT_AVAILABLE.

struct DisplayDesc {
    string  display_id;
    int32   vendor_flags;  // Opaque value
}

Describe las propiedades básicas de una pantalla de EVS y es necesaria para la implementación de EVS. El HAL es responsable de completar esta estructura para describir la pantalla del EVS. Puede ser una pantalla física o virtual que se superpone o se mezcla con otro dispositivo de presentación.

  • display_id: Es una cadena que identifica de forma única la pantalla. Puede ser el nombre del dispositivo del kernel o un nombre para el dispositivo, como rearview. La implementación del HAL elige el valor de esta cadena y la pila superior lo usa de forma opaca.
  • vendor_flags: Es un método para pasar información especializada de la cámara de forma opaca desde el controlador a una app de EVS personalizada. Se pasa sin interpretar desde el controlador hasta la app de EVS, que puede ignorarlo.
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
}

Describe el estado de la pantalla del EVS, que puede ser inhabilitado (no visible para el conductor) o habilitado (muestra una imagen al conductor). Incluye un estado transitorio en el que la pantalla aún no es visible, pero está preparada para volverse visible con la entrega del siguiente fotograma de imágenes con la llamada returnTargetBufferForDisplay().

Administrador de EVS

El EVS Manager proporciona la interfaz pública al sistema EVS para recopilar y presentar vistas de cámaras externas. En los casos en que los controladores de hardware permiten solo una interfaz activa por recurso (cámara o pantalla), el EVS Manager facilita el acceso compartido a las cámaras. Una sola app de EVS principal es el primer cliente del EVS Manager y es el único cliente al que se le permite escribir datos de visualización (se puede otorgar acceso de solo lectura a las imágenes de la cámara a clientes adicionales).

El EVS Manager implementa la misma API que los controladores HAL subyacentes y proporciona un servicio expandido, ya que admite varios clientes simultáneos (más de un cliente puede abrir una cámara a través del EVS Manager y recibir una transmisión de video).

Diagrama de la API de hardware y de EVS Manager.
Figura 2: EVS Manager refleja la API de hardware de EVS subyacente.

Las apps no ven diferencias cuando operan a través de la implementación del HAL de hardware de EVS o la API de EVS Manager, excepto que la API de EVS Manager permite el acceso simultáneo a la transmisión de la cámara. El EVS Manager es, en sí mismo, el único cliente permitido de la capa de HAL de hardware del EVS y actúa como proxy para el HAL de hardware del EVS.

En las siguientes secciones, se describen solo las llamadas que tienen un comportamiento diferente (extendido) en la implementación de EVS Manager. Las llamadas restantes son idénticas a las descripciones del HAL de EVS.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Obtiene un objeto de interfaz que se usa para interactuar con una cámara específica identificada por la cadena camera_id única. Devuelve NULL si falla. En la capa del administrador de EVS, siempre que haya suficientes recursos del sistema disponibles, otro proceso puede volver a abrir una cámara que ya está abierta, lo que permite poner en cola la transmisión de video para varias apps de consumo. Las cadenas camera_id en la capa del EVS Manager son las mismas que las que se informan a la capa del hardware del EVS.

IEvsCamera

La implementación de IEvsCamera que proporciona EVS Manager se virtualiza internamente, por lo que las operaciones en una cámara por parte de un cliente no afectan a otros clientes, que conservan el acceso independiente a sus cámaras.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Inicia transmisiones de video. Los clientes pueden iniciar y detener de forma independiente las transmisiones de video en la misma cámara subyacente. La cámara subyacente se inicia cuando se inicia el primer cliente.

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

Devuelve un fotograma. Cada cliente debe devolver sus fotogramas cuando termine, pero puede conservarlos durante el tiempo que desee. Cuando el recuento de fotogramas que mantiene un cliente alcanza su límite configurado, no recibirá más fotogramas hasta que devuelva uno. Este salto de fotogramas no afecta a otros clientes, que siguen recibiendo todos los fotogramas según lo esperado.

stopVideoStream();

Detiene una transmisión de video. Cada cliente puede detener su transmisión de video en cualquier momento sin afectar a los demás clientes. El flujo de cámara subyacente en la capa de hardware se detiene cuando el último cliente de una cámara determinada detiene su flujo.

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

Envía un valor específico del controlador, lo que podría permitir que un cliente afecte a otro. Dado que el administrador de EVS no puede comprender las implicaciones de las palabras de control definidas por el proveedor, no se virtualizan y cualquier efecto secundario se aplica a todos los clientes de una cámara determinada. Por ejemplo, si un proveedor usara esta llamada para cambiar las velocidades de fotogramas, todos los clientes de la cámara de la capa de hardware afectada recibirían fotogramas a la nueva velocidad.

IEvsDisplay

Solo se permite un propietario de la pantalla, incluso a nivel del Administrador de EVS. El administrador no agrega ninguna funcionalidad y simplemente pasa la interfaz IEvsDisplay directamente a la implementación de HAL subyacente.

App de EVS

Android incluye una implementación de referencia nativa en C++ de una app de EVS que se comunica con el EVS Manager y la HAL del vehículo para proporcionar funciones básicas de la cámara de visión trasera. Se espera que la app se inicie muy temprano en el proceso de inicio del sistema, con el video adecuado según las cámaras disponibles y el estado del automóvil (estado de la marcha y de las luces de giro). Los OEM pueden modificar o reemplazar la app de EVS con su propia lógica y presentación específicas del vehículo.

Figura 3: Lógica de muestra de la app de EVS, obtén la lista de cámaras.


Figura 4: Lógica de muestra de la app de EVS, devolución de llamada de recepción de fotogramas.

Dado que los datos de la imagen se presentan a la app en un búfer de gráficos estándar, la app es responsable de mover la imagen del búfer de origen al búfer de salida. Si bien esto introduce el costo de una copia de datos, también ofrece la oportunidad de que la app renderice la imagen en el búfer de pantalla de la forma que desee.

Por ejemplo, la app puede optar por mover los datos de píxeles por sí misma, posiblemente con una operación de rotación o escala intercalada. La app también podría usar la imagen fuente como una textura de OpenGL y renderizar una escena compleja en el búfer de salida, incluidos elementos virtuales como íconos, guías y animaciones. Una app más sofisticada también puede seleccionar varias cámaras de entrada simultáneas y combinarlas en el único fotograma de salida (por ejemplo, para usar en una vista virtual desde arriba del entorno del vehículo).

Usa EGL/SurfaceFlinger en el HAL de pantalla de EVS

En esta sección, se explica cómo usar EGL para renderizar una implementación del HAL de pantalla de EVS en Android 10.

Una implementación de referencia del HAL de EVS usa EGL para renderizar la vista previa de la cámara en la pantalla y usa libgui para crear la superficie de renderización de EGL de destino. En Android 8 (y versiones posteriores), libgui se clasifica como privado de VNDK, lo que hace referencia a un grupo de bibliotecas disponibles para las bibliotecas de VNDK que los procesos del proveedor no pueden usar. Dado que las implementaciones de HAL deben residir en la partición del proveedor, los proveedores no pueden usar Surface en las implementaciones de HAL.

Cómo compilar libgui para procesos del proveedor

El uso de libgui es la única opción para usar EGL/SurfaceFlinger en las implementaciones de HAL de pantalla de EVS. La forma más directa de implementar libgui es a través de frameworks/native/libs/gui directamente con un destino de compilación adicional en la secuencia de comandos de compilación. Este destino es exactamente igual al destino libgui, excepto por la adición de dos campos:

  • 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", ],

Nota: Los objetivos de proveedores se compilan con la macro NO_INPUT, que quita una palabra de 32 bits de los datos del paquete. Dado que SurfaceFlinger espera este campo que se quitó, no puede analizar el paquete. Esto se observa como una falla de 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

Para resolver esta situación, haz lo siguiente:

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

A continuación, se proporcionan instrucciones de compilación de ejemplo. Espera recibir 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

Usa el binder en la implementación del HAL de EVS

En Android 8 (y versiones posteriores), el nodo del dispositivo /dev/binder se volvió exclusivo para los procesos del framework y, por lo tanto, inaccesible para los procesos del proveedor. En su lugar, los procesos del proveedor deben usar /dev/hwbinder y deben convertir cualquier interfaz de AIDL a HIDL. Para quienes deseen seguir usando interfaces de AIDL entre procesos del proveedor, usen el dominio de Binder, /dev/vndbinder.

Dominio de IPC Descripción
/dev/binder IPC entre procesos de framework/app con interfaces de AIDL
/dev/hwbinder IPC entre procesos de framework y proveedores con interfaces HIDL
IPC entre procesos de proveedores con interfaces HIDL
/dev/vndbinder IPC entre procesos de proveedor/proveedor con interfaces de AIDL

Si bien SurfaceFlinger define interfaces AIDL, los procesos del proveedor solo pueden usar interfaces HIDL para comunicarse con los procesos del framework. Se requiere una cantidad no trivial de trabajo para convertir las interfaces AIDL existentes en HIDL. Afortunadamente, Android proporciona un método con el que se puede seleccionar el controlador del binder para libbinder, al que se vinculan los procesos de la biblioteca del espacio del usuario.

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

Nota: Los procesos del proveedor deben llamar a este método antes de llamar a Process o IPCThreadState, o antes de realizar cualquier llamada del vinculador.

Políticas de SELinux

Si la implementación del dispositivo es full treble, SELinux impide que los procesos del proveedor usen /dev/binder. Por ejemplo, una implementación de muestra del HAL de EVS se asigna al dominio hal_evs_driver y requiere permisos de lectura y escritura para el dominio 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

Sin embargo, agregar estos permisos provoca un error de compilación porque incumple las siguientes reglas de neverallow definidas en system/sepolicy/domain.te para un dispositivo 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 es un atributo que se proporciona para detectar un error y guiar el desarrollo. También se puede usar para resolver el incumplimiento de Android 10 que se describió anteriormente.

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)

Compila la implementación de referencia de HAL del EVS como un proceso del proveedor

Como referencia, puedes aplicar los siguientes cambios a packages/services/Car/evs/Android.mk. Asegúrate de confirmar que todos los cambios descritos funcionen para tu implementación.

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;