Vehicle Camera HAL

Android contains an automotive HIDL Hardware Abstraction Layer (HAL) that provides for imagery capture and display very early in the Android boot process and continues functioning for the life of the system. The HAL includes the exterior view system (EVS) stack and is typically used to support rearview camera and surround view displays in vehicles with Android-based In-Vehicle Infotainment (IVI) systems. EVS also enables advanced features to be implemented in user applications.

Android also includes an EVS-specific capture and display driver interface (in /hardware/interfaces/automotive/evs/1.0). While it is possible to build a rearview camera application on top of existing Android camera and display services, such an application would likely run too late in the Android boot process. Using a dedicated HAL enables a streamlined interface and makes it clear what an OEM needs to implement to support the EVS stack.

System components

EVS includes the following system components:

EVS System
components diagram
Figure 1. EVS System components overview

EVS application

A sample C++ EVS application (/packages/services/Car/evs/app) serves as a reference implementation. This application is responsible for requesting video frames from the EVS Manager and sending finished frames for display back to the EVS Manager. It expects to be started by init as soon as EVS and Car Service are available, targeted within two (2) seconds of power on. OEMs can modify or replace the EVS application as desired.

EVS Manager

The EVS Manager (/packages/services/Car/evs/manager) provides the building blocks needed by an EVS application to implement anything from a simple rearview camera display to a 6DOF multi-camera rendering. Its interface is presented through HIDL and is built to accept multiple concurrent clients. Other applications and services (specifically the Car Service) can query the EVS Manager state to find out when the EVS system is active.

EVS HIDL interface

The EVS system, both the camera and the display elements, is defined in the android.hardware.automotive.evs package. A sample implementation that exercises the interface (generates synthetic test images and validates the images make the round trip) is provided in /hardware/interfaces/automotive/evs/1.0/default.

The OEM is responsible for implementing the API expressed by the .hal files in /hardware/interfaces/automotive/evs. Such implementations are responsible for configuring and gathering data from physical cameras and delivering it via shared memory buffers recognizable by Gralloc. The display side of the implementation is responsible for providing a shared memory buffer that can be filled by the application (usually via EGL rendering) and presenting the finished frames in preference to anything else that might want to appear on the physical display. Vendor implementations of the EVS interface may be stored under /vendor/… /device/… or hardware/… (e.g., /hardware/[vendor]/[platform]/evs).

Kernel drivers

A device that supports the EVS stack requires kernel drivers. Instead of creating new drivers, OEMs have the option to support EVS-required features via existing camera and/or display hardware drivers. Reusing drivers could be advantageous, especially for display drivers where image presentation may require coordination with other active threads. Android 8.0 includes a v4l2-based sample driver (in packages/services/Car/evs/sampleDriver) that depends on the kernel for v4l2 support and on SurfaceFlinger for presenting the output image.

EVS hardware interface description

The section describes the HAL. Vendors are expected to provide implementations of this API adapted for their hardware.

IEvsEnumerator

This object is responsible for enumerating the available EVS hardware in the system (one or more cameras and the single display device).

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

Returns a vector containing descriptions for all cameras in the system. It is assumed the set of cameras is fixed and knowable at boot time. For details on camera descriptions, see CameraDesc.

openCamera(string camera_id) generates (IEvsCamera camera);

Obtains an interface object used to interact with a specific camera identified by the unique camera_id string. Returns a NULL on failure. Attempts to reopen a camera that is already open cannot fail. To avoid race conditions associated with application startup and shutdown, reopening a camera should shut down the previous instance so the new request can be fulfilled. A camera instance that has been preempted in this way must be put in an inactive state, awaiting final destruction and responding to any request to affect the camera state with a return code of OWNERSHIP_LOST.

closeCamera(IEvsCamera camera);

Releases the IEvsCamera interface (and is the opposite of the openCamera() call). The camera video stream must be stopped by calling stopVideoStream() before calling closeCamera.

openDisplay() generates (IEvsDisplay display);

Obtains an interface object used to exclusively interact with the system's EVS display. Only one client may hold a functional instance of IEvsDisplay at time. Similar to the aggressive open behavior described in openCamera, a new IEvsDisplay object may be created at any time and will disable any previous instances. Invalidated instances continue to exist and respond to function calls from their owners, but must perform no mutating operations when dead. Eventually, the client application is expected to notice the OWNERSHIP_LOST error return codes and close and release the inactive interface.

closeDisplay(IEvsDisplay display);

Releases the IEvsDisplay interface (and is the opposite of the openDisplay() call). Outstanding buffers received via getTargetBuffer() calls must be returned to the display before closing the display.

getDisplayState() generates (DisplayState state);

Gets the current display state. The HAL implementation should report the actual current state, which might differ from the most recently requested state. The logic responsible for changing display states should exist above the device layer, making it undesirable for the HAL implementation to spontaneously change display states. If the display is not currently held by any client (by a call to openDisplay), then this function returns NOT_OPEN. Otherwise, it reports the current state of the EVS Display (see IEvsDisplay API).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id. A string that uniquely identifies a given camera. Can be the kernel device name of the device or a name for the device, such as rearview. The value for this string is chosen by the HAL implementation and used opaquely by the stack above.
  • vendor_flags. A method for passing specialized camera information opaquely from the driver to a custom EVS application. It is passed uninterpreted from the driver up to the EVS application, which is free to ignore it.

IEvsCamera

This object represents a single camera and is the primary interface for capturing images.

getCameraInfo() generates (CameraDesc info);

Returns CameraDesc of this camera.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

Specifies the depth of the buffer chain the camera is asked to support. Up to this many frames may be held concurrently by the client of IEvsCamera. If this many frames have been delivered to the receiver without being returned by doneWithFrame, the stream skips frames until a buffer is returned for reuse. It is legal for this call to come at any time, even while streams are already running, in which case buffers should be added or removed from the chain as appropriate. If no call is made to this entry point, the IEvsCamera supports at least one frame by default; with more acceptable.

If the requested bufferCount cannot be accommodated, the function returns BUFFER_NOT_AVAILABLE or other relevant error code. In this case, the system continues to operate with the previously-set value.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Requests delivery of EVS camera frames from this camera. The IEvsCameraStream begins receiving periodic calls with new image frames until stopVideoStream() is called. Frames must begin being delivered within 500ms of the startVideoStream call and after starting, must generated at a minimum of 10 FPS. The time required to start the video stream effectively counts against any rearview camera startup time requirement. If the stream is not started, an error code must be returned; otherwise OK is returned.

oneway doneWithFrame(BufferDesc buffer);

Returns a frame that was delivered by to the IEvsCameraStream. When done consuming a frame delivered to the IEvsCameraStream interface, the frame must be returned to the IEvsCamera for reuse. A small, finite number of buffers are available (possibly as small as one), and if the supply is exhausted, no further frames are delivered until a buffer is returned, potentially resulting in skipped frames (a buffer with a null handle denotes the end of a stream and does not need to be returned through this function). Returns OK on success, or appropriate error code potentially including INVALID_ARG or BUFFER_NOT_AVAILABLE.

stopVideoStream();

Stops the delivery of EVS camera frames. Because delivery is asynchronous, frames may continue to arrive for some time after this call returns. Each frame must be returned until the closure of the stream is signaled to the IEvsCameraStream. It is legal to call stopVideoStream on a stream that has already been stopped or never started, in which cases it is ignored.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

Requests driver-specific information from the HAL implementation. Values allowed for opaqueIdentifier are driver-specific, but no value passed may crash the driver. The driver should return 0 for any unrecognized opaqueIdentifier.

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

Sends a driver-specific value to the HAL implementation. This extension is provided only to facilitate vehicle-specific extensions and no HAL implementation should require this call to function in a default state. If the driver recognizes and accepts the values, OK should be returned; otherwise INVALID_ARG or other representative error code should be returned.

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
}

Describes an image passed through the API. The HAL drive is responsible for filling out this structure to describe the image buffer and the HAL client should treat this structure as read-only. The fields contain enough information to allow the client to reconstruct an ANativeWindowBuffer object, as may be required to use the image with EGL via the eglCreateImageKHR() extension.

  • width. The width in pixels of the presented image.
  • height. The height in pixels of the presented image.
  • stride. Number of pixels each row actually occupies in memory, accounting for any padding for alignment of rows. Expressed in pixels to match the convention adopted by gralloc for its buffer descriptions.
  • pixelSize. Number of bytes occupied by each individual pixel, enabling computation of the size in bytes necessary to step between rows in the image (stride in bytes = stride in pixels * pixelSize).
  • format. The pixel format used by the image. The format provided must be compatible with the platform's OpenGL implementation. To pass compatibility testing, HAL_PIXEL_FORMAT_YCRCB_420_SP should be preferred for camera usage, and RGBA or BGRA should be preferred for display.
  • usage. Usage flags set by the HAL implementation. HAL clients are expected to pass these unmodified (for details, refer to Gralloc.h related flags).
  • bufferId. A unique value specified by the HAL implementation to allow a buffer to be recognized after a round trip through the HAL APIs. The value stored in this field may be arbitrarily chosen by the HAL implementation.
  • memHandle. The handle for the underlying memory buffer that contains the image data. The HAL implementation might choose to store a Gralloc buffer handle here.

IEvsCameraStream

The client implements this interface to receive asynchronous video frame deliveries.

deliverFrame(BufferDesc buffer);

Receives calls from the HAL each time a video frame is ready for inspection. Buffer handles received by this method must be returned via calls to IEvsCamera::doneWithFrame(). When the video stream is stopped via a call to IEvsCamera::stopVideoStream(), this callback might continue as the pipeline drains. Each frame must still be returned; when the last frame in the stream has been delivered, a NULL bufferHandle will be delivered, signifying the end of the stream and no further frame deliveries occur. The NULL bufferHandle itself does not need to be sent back via doneWithFrame(), but all other handles must be returned

While proprietary buffer formats are technically possible, compatibility testing requires the buffer be in one of four supported formats: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 bit R:G:B:x), BGRA (32 bit B:G:R:x). The selected format must be a valid GL texture source on the platform's GLES implementation.

The application should not rely on any correspondence between the bufferId field and the memHandle in the BufferDesc structure. The bufferId values are essentially private to the HAL driver implementation, and it may use (and reuse) them as it sees fit.

IEvsDisplay

This object represents the Evs display, controls the state of the display, and handles the actual presentation of images.

getDisplayInfo() generates (DisplayDesc info);

Returns basic information about the EVS display provided by the system (see DisplayDesc).

setDisplayState(DisplayState state) generates (EvsResult result);

Sets the display state. Clients may set the display state to express the desired state, and the HAL implementation must gracefully accept a request for any state while in any other state, although the response may be to ignore the request.

Upon initialization, the display is defined to start in the NOT_VISIBLE state, after which the client is expected to request the VISIBLE_ON_NEXT_FRAME state and begin providing video. When the display is no longer required, the client is expected to request the NOT_VISIBLE state after passing the last video frame.

It is valid for any state to be requested at any time. If the display is already visible, it should remain visible if set to VISIBLE_ON_NEXT_FRAME. Always returns OK unless the requested state is an unrecognized enum value, in which case INVALID_ARG is returned.

getDisplayState() generates (DisplayState state);

Gets the display state. The HAL implementation should report the actual current state, which might differ from the most recently requested state. The logic responsible for changing display states should exist above the device layer, making it undesirable for the HAL implementation to spontaneously change display states.

getTargetBuffer() generates (handle bufferHandle);

Returns a handle to a frame buffer associated with the display. This buffer may be locked and written to by software and/or GL. This buffer must be returned via a call to returnTargetBufferForDisplay() even if the display is no longer visible.

While proprietary buffer formats are technically possible, compatibility testing requires the buffer be in one of four supported formats: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 Planar), YUYV (YCrCb 4:2:2 Interleaved), RGBA (32 bit R:G:B:x), BGRA (32 bit B:G:R:x). The selected format must be a valid GL render target on the platform's GLES implementation.

On error, a buffer with a null handle is returned, but such a buffer does not need to be passed back to returnTargetBufferForDisplay.

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

Tells the display the buffer is ready for display. Only buffers retrieved through a call to getTargetBuffer() are valid for use with this call, and the contents of the BufferDesc may not be modified by the client application. After this call, the buffer is no longer valid for use by the client. Returns OK on success, or appropriate error code potentially including INVALID_ARG or BUFFER_NOT_AVAILABLE.

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

Describes the basic properties of an EVS display and required by an EVS implementation. The HAL is responsible for filling out this structure to describe the EVS display. Can be a physical display or a virtual display that is overlaid or mixed with another presentation device.

  • display_id. A string that uniquely identifies the display. This could be the kernel device name of the device, or a name for the device, such as rearview. The value for this string is chosen by the HAL implementation and used opaquely by the stack above.
  • vendor_flags. A method for passing specialized camera information opaquely from the driver to a custom EVS Application. It is passed uninterpreted from the driver up to the EVS Application, which is free to ignore it.
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
}

Describes the state of the EVS display, which can be disabled (not visible to the driver) or enabled (showing an image to the driver). Includes a transient state where the display is not visible yet but is prepared to become visible with the delivery of the next frame of imagery via the returnTargetBufferForDisplay() call.

EVS Manager

The EVS Manager provides the public interface to the EVS system for collecting and presenting external camera views. Where hardware drivers allow only one active interface per resource (camera or display), the EVS Manager facilitates shared access to the cameras. A single primary EVS application is the first client of the EVS Manager, and is the only client permitted to write display data (additional clients can be granted read-only access to camera images).

The EVS Manager implements the same API as the underlying HAL drivers and provides expanded service by supporting multiple concurrent clients (more than one client can open a camera through the EVS Manager and receive a video stream).

EVS Manager and
EVS Hardware API diagram.
Figure 2. EVS Manager mirrors underlying EVS Hardware API

Applications see no differences when operating through the EVS Hardware HAL implementation or the EVS Manager API except that the EVS Manager API allows concurrent camera stream access. The EVS Manager is, itself, the one allowed client of the EVS Hardware HAL layer, and acts as a proxy for the EVS Hardware HAL.

The following sections describe only those calls that have a different (extended) behavior in the EVS Manager implementation; remaining calls are identical to EVS HAL descriptions.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

Obtains an interface object used to interact with a specific camera identified by the unique camera_id string. Returns a NULL on failure. At the EVS Manager layer, as long as sufficient system resources are available, a camera that is already open may be opened again by another process, allowing teeing of the video stream to multiple consumer applications. The camera_id strings at the EVS Manager layer are the same as those reported to the EVS Hardware layer.

IEvsCamera

The EVS Manager provided IEvsCamera implementation is internally virtualized so operations on a camera by one client do not affect other clients, which retain independent access to their cameras.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

Starts video streams. Clients may independently start and stop video streams on the same underlying camera. The underlying camera starts when the first client starts.

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

Returns a frame. Each client must return their frames when they are done, but are permitted to hold onto their frames for as long as they desire. When the frame count held by a client reaches its configured limit, it will not receive any more frames until it returns one. This frame skipping does not affect other clients, which continue to receive all frames as expected.

stopVideoStream();

Stops a video stream. Each client can stop its video stream any time without affecting other clients. The underlying camera stream at the hardware layer is stopped when the last client of a given camera stops its stream.

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

Sends a driver-specific value, potentially enabling one client to affect another client. Because the EVS Manager cannot understand the implications of vendor-defined control words, they are not virtualized and any side effects apply to all clients of a given camera. For example, if a vendor used this call to change frame rates, all clients of the affected hardware layer camera would receive frames at the new rate.

IEvsDisplay

Only one owner of the display is allowed, even at the EVS Manager level. The Manager adds no functionality and simply passes the IEvsDisplay interface directly through to the underlying HAL implementation.

EVS application

Android includes a native C++ reference implementation of an EVS application that communicates with the EVS Manager and the Vehicle HAL to provide basic rearview camera functions. The application is expected to start very early in the system boot process, with suitable video shown depending on the available cameras and the state of the car (gear and turn signal state). OEMs can modify or replace the EVS application with their own vehicle-specific logic and presentation.

Figure 3. EVS application sample logic, get camera list.


Figure 4. EVS application sample logic, receive frame callback.

Because image data is presented to the application in a standard graphics buffer, the application is responsible for moving the image from the source buffer into the output buffer. While this introduces the cost of a data copy, it also offers the opportunity for the application to render the image into the display buffer in any fashion it desires.

For example, the application may choose to move the pixel data itself, potentially with an inline scale or rotation operation. The application could also choose to use the source image as an OpenGL texture and render a complex scene to the output buffer, including virtual elements such as icons, guidelines, and animations. A more sophisticated application may also select multiple concurrent input cameras and merge them into the single output frame (such as for use in a top-down, virtual view of vehicle surroundings).

Use the EGL/SurfaceFlinger in the EVS Display HAL

This section explains how to use EGL to render an EVS Display HAL implementation in Android 10.

An EVS HAL reference implementation uses EGL to render the camera preview on the screen and uses libgui to create the target EGL render surface. In Android 8 (and higher), libgui is classified as VNDK-private, which refers to a group of libraries available to VNDK libraries that vendor processes cannot use. Because HAL implementations must reside in the vendor partition, vendors are prevented from using Surface in HAL implementations.

Building libgui for vendor processes

The use of libgui serves as the only option to use EGL/SurfaceFlinger in EVS Display HAL implementations. The most straightforward way to implement libgui is through frameworks/native/libs/gui directly by using an additional build target in the build script. This target is exactly the same as the libgui target except for the addition of two fields:

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

Note: Vendor targets are built with the NO_INPUT macro, which removes one 32-bit word from the parcel data. Because SurfaceFlinger expects this field that has been removed, SurfaceFlinger fails to parse the parcel. This is observed as a fcntl failure:

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

To resolve this 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);

Sample build instructions are provided below. Expect to receive a $(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

Using binder in an EVS HAL implementation

In Android 8 (and higher), the /dev/binder device node became exclusive to framework processes and, therefore, inaccessible to vendor processes. Instead, vendor processes should use /dev/hwbinder and must convert any AIDL interfaces to HIDL. For those who want to continue using AIDL interfaces between vendor processes, use the binder domain, /dev/vndbinder.

IPC Domain Description
/dev/binder IPC between framework/app processes with AIDL interfaces
/dev/hwbinder IPC between framework/vendor processes with HIDL interfaces
IPC between vendor processes with HIDL interfaces
/dev/vndbinder IPC between vendor/vendor processes with AIDL Interfaces

While SurfaceFlinger defines AIDL interfaces, vendor processes can only use HIDL interfaces to communicate with framework processes. A non-trivial amount of work is required to convert existing AIDL interfaces into HIDL. Fortunately, Android provides a method with which to select the binder driver for libbinder, to which the userspace library processes are linked.

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

Note: Vendor processes should call this before calling into Process or IPCThreadState, or before making any binder calls.

SELinux Policies

If the device implementation is full treble, SELinux prevents vendor processes from using /dev/binder. For example, an EVS HAL sample implementation is assigned to the hal_evs_driver domain and requires r/w permissions to the binder_device domain.

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

Adding these permissions, however, causes a build failure because it violates the following neverallow rules defined in system/sepolicy/domain.te for a full-treble device.

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 is an attribute provided to catch a bug and guide development. It can also be used to resolve the Android 10 violation described above.

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)

Building EVS HAL reference implementation as a vendor process

As a reference, you can apply the following changes to packages/services/Car/evs/Android.mk. Be sure to confirm that all described changes work for your implementation.

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;