Android contiene una capa de abstracción de hardware (HAL) de HIDL automotriz que permite capturar y mostrar imágenes en las primeras etapas del proceso de inicio de Android, y continúa 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 pantallas de cámara posterior y vistas envolventes en vehículos con sistemas de infoentretenimiento en el vehículo (IVI) basados en Android. EVS también permite implementar funciones avanzadas en las apps de los usuarios.
Android también incluye una interfaz de controlador de captura y visualización específica de 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 una app de este tipo se ejecute demasiado tarde en el proceso de inicio de Android. El uso de un HAL dedicado permite una interfaz optimizada y deja en claro lo que un OEM debe implementar para admitir la pila de EVS.
Componentes del sistema
EVS incluye los siguientes componentes del sistema:
App de EVS
Una app de EVS de C++ de muestra (/packages/services/Car/evs/app
) sirve como implementación de referencia. Esta app es responsable de solicitar fotogramas de video al Administrador de EVS y enviar fotogramas terminados para su visualización al Administrador de EVS.
Se espera que se inicie con init en cuanto estén disponibles EVS y Car Service, que se orienta dentro de los dos (2) segundos posteriores al encendido. Los OEMs pueden modificar o reemplazar
la app de EVS según lo deseen.
Administrador de EVS
EVS Manager (/packages/services/Car/evs/manager
) proporciona los componentes básicos que necesita una app de EVS para implementar cualquier cosa, desde una simple pantalla de cámara posterior hasta una renderización de varias cámaras 6DOF. Su interfaz se presenta a través de HIDL y está diseñada para aceptar varios clientes simultáneos.
Otras apps y servicios (en particular, el servicio de vehículos) pueden consultar el estado del administrador de EVS para saber cuándo está activo el sistema de EVS.
Interfaz de 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 recorrido 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 cámaras físicas y entregarlos a través de búferes de memoria compartida que Gralloc puede reconocer. El lado de la pantalla de la implementación es responsable de proporcionar un búfer de memoria compartida que la app puede completar (por lo general, con renderización de EGL) y presentar los fotogramas terminados en preferencia a cualquier otra cosa que pueda aparecer en la pantalla física. Las implementaciones de la interfaz de EVS de los proveedores se pueden almacenar en /vendor/… /device/…
o hardware/…
(p.ej.,
/hardware/[vendor]/[platform]/evs
).
Controladores de 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 de EVS a través de controladores existentes de hardware de pantalla o cámara. Reutilizar controladores podría 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 la HAL. Se espera que los proveedores proporcionen implementaciones de esta API adaptadas para 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);
Muestra 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 al momento del inicio. Para obtener detalles sobre las descripciones de cámaras, 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 única camera_id. Muestra un valor NULL si se produce un error.
Los intentos de volver a abrir una cámara que ya está abierta no pueden fallar. Para evitar 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 entregar la solicitud nueva. Una instancia de cámara que se haya usurpado de esta manera debe colocarse en un estado inactivo, a la espera de la 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 IEvsCamera (es lo opuesto a la llamada a openCamera()
). Se debe llamar a stopVideoStream()
para detener el flujo de video de la cámara antes de llamar a closeCamera
.
openDisplay() generates (IEvsDisplay display);
Obtiene un objeto de interfaz que se usa para interactuar de forma exclusiva con la pantalla de EVS del sistema. Solo un cliente puede tener una instancia funcional de IEvsDisplay a la vez. Al igual que el comportamiento de apertura agresivo que se describe en openCamera
, se puede crear un nuevo objeto IEvsDisplay en cualquier momento y se inhabilitan las instancias anteriores. Las instancias invalidadas siguen existiendo y responden a llamadas a 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 note los códigos de retorno de error OWNERSHIP_LOST
y cierre y libere la interfaz inactiva.
closeDisplay(IEvsDisplay display);
Libera la interfaz IEvsDisplay (es lo opuesto a la llamada 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 visualización actual. La implementación de 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 encima de la capa del dispositivo, por lo que no desea que la implementación de HAL cambie espontáneamente los estados de la pantalla. Si ningún cliente mantiene la pantalla (con una llamada a openDisplay), esta función muestra NOT_OPEN
. De lo contrario, informa el estado actual de la pantalla 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 inequívoca una cámara determinada. Puede ser el nombre del dispositivo de kernel o un nombre para el dispositivo, como revisión. La implementación de HAL elige el valor de esta cadena y la pila anterior la 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 ignorarla.
IEvsCamera
Este objeto representa una sola cámara y es la interfaz principal para capturar imágenes.
getCameraInfo() generates (CameraDesc info);
Muestra 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 mantener hasta esta cantidad de fotogramas de forma simultánea. Si se entregaron muchos fotogramas al receptor sin que doneWithFrame
los devolviera, la transmisión omite fotogramas hasta que se devuelve un búfer para su reutilización. Es legal que esta llamada llegue en cualquier momento, incluso mientras las transmisiones ya están en ejecución, 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, IEvsCamera admite al menos un fotograma de forma predeterminada, con más aceptable.
Si no se puede admitir el bufferCount solicitado, la función muestra BUFFER_NOT_AVAILABLE
o algún otro código de error relevante. En este caso, el sistema continúa 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. IEvsCameraStream comienza a recibir llamadas periódicas con nuevos fotogramas de imagen hasta que se llama a stopVideoStream()
. Las tramas deben comenzar a entregarse en un plazo de 500 ms a partir de la llamada a startVideoStream
y, después de iniciarse, deben generarse con un mínimo de 10 FPS. El tiempo necesario para iniciar la transmisión de video por Internet se considera eficazmente contra cualquier requisito de tiempo de inicio de la cámara retrovisora. Si no se inicia la transmisión, se debe mostrar un código de error; de lo contrario, se muestra OK.
oneway doneWithFrame(BufferDesc buffer);
Devuelve un fotograma que se envió a IEvsCameraStream. Cuando se termina de consumir un fotograma entregado a la interfaz IEvsCameraStream, se debe devolver a IEvsCamera para su reutilización. Hay una cantidad pequeña y finita de búferes disponibles (posiblemente uno solo) y, si se agota el suministro, no se entregan más tramas hasta que se muestra un búfer, lo que podría provocar que se omitan tramas (un búfer con un control nulo indica el final de una transmisión y no es necesario que se muestre a través de esta función). Muestra OK si la operación se realiza de forma correcta o el código de error adecuado puede incluir INVALID_ARG
o BUFFER_NOT_AVAILABLE
.
stopVideoStream();
Detiene la publicación de fotogramas de la cámara EVS. Debido a que la entrega es asíncrona, es posible que las tramas sigan llegando durante un tiempo después de que se devuelva esta llamada. Cada fotograma se debe mostrar hasta que se indique el cierre de la transmisión a IEvsCameraStream. Es legal llamar a stopVideoStream
en una transmisión que ya se detuvo o nunca se inició, en cuyo caso se ignora.
getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);
Solicita información específica del controlador de la implementación de HAL. Los valores permitidos para opaqueIdentifier
son específicos del controlador, pero ningún valor pasado puede hacer que el controlador falle. El controlador debe mostrar 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 se proporciona solo para facilitar las extensiones específicas de los vehículos y ninguna implementación de HAL debe requerir esta llamada para funcionar en un estado predeterminado. Si el controlador reconoce y acepta los valores, se debe mostrar OK. De lo contrario, se debe mostrar 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 pasa a través de la API. La unidad de HAL es responsable de completar esta estructura para describir el búfer de imágenes, y el cliente de HAL debe tratar esta estructura como de solo lectura. Los campos contienen suficiente información para permitir que el cliente reconstruya un objeto ANativeWindowBuffer
, como puede ser 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 cada fila ocupa en la memoria, teniendo en cuenta cualquier padding para la alineación de filas. Se expresa en píxeles para que coincida con la convención que adoptó 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 filas en la imagen (stride
en bytes =stride
en píxeles *pixelSize
).format
: Es el formato de píxeles que usa la imagen. El formato proporcionado debe ser compatible con la implementación de OpenGL de la plataforma. Para aprobar las pruebas de compatibilidad, se debe preferirHAL_PIXEL_FORMAT_YCRCB_420_SP
para el uso de la cámara yRGBA
oBGRA
para la pantalla.usage
. Marcas de uso establecidas por la implementación de HAL. Se espera que los clientes de HAL pasen estos elementos sin modificar (para obtener más información, consulta las marcas relacionadas conGralloc.h
).bufferId
: Es un valor único que especifica la implementación de 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 HAL. La implementación de 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 de HAL podría optar por almacenar un control de búfer de Gralloc aquí.
IEvsCameraStream
El cliente implementa esta interfaz para recibir entregas de fotogramas de video asíncronas.
deliverFrame(BufferDesc buffer);
Recibe llamadas del sistema HAL cada vez que un fotograma está listo para inspeccionarse.
Los controladores del búfer que recibe este método se deben mostrar a través de llamadas a IEvsCamera::doneWithFrame()
. Cuando se detiene la transmisión de video por Internet 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 mostrar cada fotograma. Cuando se entrega el último fotograma de la transmisión, se entrega una bufferHandle
NULL, lo que indica el final de la transmisión y no se realizan más entregas de fotogramas. No es necesario que se devuelva el bufferHandle
NULL con doneWithFrame()
, pero se deben devolver todos los demás controladores.
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 semiplano), YV12 (YCrCb 4:2:0 plano), 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 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, en esencia, privados para la implementación del controlador HAL y pueden usarlos (y reutilizarlos) según lo considere conveniente.
IEvsDisplay
Este objeto representa la pantalla Evs, controla el estado de la pantalla y controla la presentación real de las imágenes.
getDisplayInfo() generates (DisplayDesc info);
Muestra información básica sobre la pantalla del EVS que proporciona el sistema (consulta DisplayDesc).
setDisplayState(DisplayState state) generates (EvsResult result);
Establece el estado de la pantalla. Los clientes pueden establecer el estado de la pantalla para expresar el estado deseado, y la implementación de HAL debe aceptar de forma fluida una solicitud para cualquier estado mientras se encuentra en cualquier otro estado, aunque la respuesta puede ser ignorar la solicitud.
Durante la inicialización, se define que la pantalla comience en el estado NOT_VISIBLE
, después del cual se espera que el cliente solicite el estado VISIBLE_ON_NEXT_FRAME
y comience a proporcionar video. Cuando ya no se requiera la visualización, 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 es visible, debería seguir siéndolo si se establece en VISIBLE_ON_NEXT_FRAME
. Siempre muestra OK, a menos que el estado solicitado sea un valor de enumeración no reconocido, en cuyo caso se muestra INVALID_ARG
.
getDisplayState() generates (DisplayState state);
Obtiene el estado de visualización. La implementación de 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 encima de la capa del dispositivo, por lo que no desea que la implementación de HAL cambie espontáneamente los estados de la pantalla.
getTargetBuffer() generates (handle bufferHandle);
Muestra un controlador de un búfer de tramas asociado con la pantalla. El software o GL pueden bloquear este búfer y escribir en él. Este búfer se debe mostrar con una llamada a returnTargetBufferForDisplay()
, incluso si la pantalla ya no es 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 semiplano), YV12 (YCrCb 4:2:0 plano), 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 un destino de renderización GL válido en la implementación de GLES de la plataforma.
En caso de error, se muestra un búfer con un identificador nulo, pero no es necesario pasarlo a returnTargetBufferForDisplay
.
returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);
Le 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 usar con esta llamada, y la app cliente no puede modificar el contenido de BufferDesc
. Después de esta llamada, el búfer ya no es válido para que lo use el cliente. Muestra OK si 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 EVS y las que requiere una implementación de EVS. El HAL es responsable de completar esta estructura para describir la pantalla de 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 inequívoca la pantalla. 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 anterior la 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 ignorarla.
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 sistema de advertencia de punto ciego, que puede estar inhabilitada (no es visible para el conductor) o habilitada (muestra una imagen al conductor).
Incluye un estado transitorio en el que la pantalla aún no es visible, pero está preparada para que lo sea cuando se entregue el siguiente fotograma de imágenes con la llamada a returnTargetBufferForDisplay()
.
Administrador de EVS
El administrador de EVS proporciona la interfaz pública al sistema de EVS para recopilar y presentar vistas de cámaras externas. Cuando los controladores de hardware solo permiten una interfaz activa por recurso (cámara o pantalla), el Administrador de EVS facilita el acceso compartido a las cámaras. Una sola app de EVS principal es el primer cliente del administrador de EVS y es el único cliente que puede escribir datos de visualización (se puede otorgar acceso de solo lectura a imágenes de la cámara a clientes adicionales).
El administrador de EVS implementa la misma API que los controladores de 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 administrador de EVS y recibir una transmisión de video).
Las apps no ven diferencias cuando funcionan a través de la implementación de 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 administrador de EVS es, en sí, el único cliente permitido de la capa de HAL de hardware de EVS y actúa como proxy para el HAL de hardware de EVS.
En las siguientes secciones, se describen solo aquellas llamadas que tienen un comportamiento diferente (extendido) en la implementación de EVS Manager. Las llamadas restantes son idénticas a las descripciones de la 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 mediante la cadena única de camera_id. Muestra un valor NULL si se produce un error.
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 que la transmisión de video se divida en varias apps para consumidores. Las cadenas camera_id
en la capa del administrador de EVS son las mismas que las que se informan a la capa de hardware de EVS.
IEvsCamera
La implementación de IEvsCamera proporcionada por el administrador de EVS está virtualizada de forma interna, de modo que las operaciones que realiza un cliente en una cámara no afecten 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 transmisiones de video de forma independiente 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);
Muestra un fotograma. Cada cliente debe devolver sus tramas cuando termine, pero puede conservarlas todo el tiempo que desee. Cuando el recuento de fotogramas que retiene un cliente alcanza su límite configurado, no recibirá más fotogramas hasta que muestre uno. La omisión de este fotograma no afecta a otros clientes, que continúan recibiendo todos los fotogramas como se espera.
stopVideoStream();
Detiene una transmisión de video. Cada cliente puede detener su transmisión de video en cualquier momento sin afectar a otros clientes. La transmisión subyacente de la cámara en la capa de hardware se detiene cuando el último cliente de una cámara determinada detiene su transmisión.
setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);
Envía un valor específico del controlador, lo que puede permitir que un cliente afecte a otro. Debido a que el administrador de EVS no puede comprender las implicaciones de las palabras de control definidas por el proveedor, no se virtualizan y los efectos secundarios se aplican 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 velocidad nueva.
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 de IEvsDisplay directamente a la implementación de la HAL subyacente.
App de EVS
Android incluye una implementación de referencia nativa de C++ de una app de EVS que se comunica con el administrador de EVS y el HAL del vehículo para proporcionar funciones básicas de la cámara retrovisor. Se espera que la app se inicie en las primeras etapas del proceso de inicio del sistema, y se mostrará un video adecuado según las cámaras disponibles y el estado del vehículo (estado de los cambios y de la señal de giro). Los OEMs pueden modificar o reemplazar la app de EVS con su propia lógica y presentación específicas del vehículo.
Debido a que los datos de 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 para que la app renderice la imagen en el búfer de visualización de la forma que desee.
Por ejemplo, la app puede optar por mover los datos de píxeles, posiblemente con una operación de escala o rotación intercalada. La app también podría usar la imagen de origen como una textura de OpenGL y renderizar una escena compleja en el búfer de salida, incluidos elementos virtuales, como íconos, lineamientos 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 de arriba abajo del entorno del vehículo).
Usar el EGL/SurfaceFlinger en la HAL de la pantalla de EVS
En esta sección, se explica cómo usar EGL para renderizar una implementación de HAL de pantalla de EVS en Android 10.
Una implementación de referencia de 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 objetivo. En Android 8 (y versiones posteriores), libgui
se clasifica como VNDK-private, que hace referencia a un grupo de bibliotecas disponibles para las bibliotecas del VNDK que los procesos del proveedor no pueden usar.
Debido a 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 los procesos de los proveedores
El uso de libgui
es la única opción para usar EGL/SurfaceFlinger en 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 objetivo es exactamente el mismo que el objetivo 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. Debido a 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 condició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 muestra. Recibirás 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 Binder en la implementación de HAL de EVS
En Android 8 (y versiones posteriores), el nodo del dispositivo /dev/binder
se volvió exclusivo de los procesos del framework y, por lo tanto, no era accesible 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 quieran seguir usando interfaces AIDL entre procesos de proveedores, deben usar el dominio de Binder, /dev/vndbinder
.
Dominio IPC | Descripción |
---|---|
/dev/binder |
IPC entre procesos de framework o app con interfaces de AIDL |
/dev/hwbinder |
IPC entre procesos de framework o proveedor con interfaces HIDL IPC entre procesos de proveedores con interfaces HIDL |
/dev/vndbinder |
La IPC entre los procesos del proveedor con interfaces AIDL |
Si bien SurfaceFlinger define las interfaces del AIDL, los procesos del proveedor solo pueden usar interfaces HIDL para comunicarse con los procesos del framework. Se requiere una cantidad de trabajo no trivial para convertir las interfaces AIDL existentes en HIDL. Afortunadamente, Android proporciona un método con el que se puede seleccionar el controlador de Binder para libbinder
, al que están vinculados los procesos de la biblioteca del espacio de 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 de proveedores deben llamar a esto antes de llamar a Process
o IPCThreadState
, o antes de realizar llamadas a Binder.
Políticas de SELinux
Si la implementación del dispositivo es de agudo completo, SELinux evita que los procesos del proveedor usen /dev/binder
. Por ejemplo, una implementación de muestra de HAL de EVS se asigna al dominio hal_evs_driver
y requiere permisos de lectura y escritura en 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 causa un error de compilación porque infringe las siguientes reglas de neverallow definidas en system/sepolicy/domain.te
para un dispositivo de treble completo.
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 descrito 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 de EVS como un proceso de 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;