车载摄像头HAL

Android 包含一个汽车 HIDL 硬件抽象层 (HAL),可在 Android 启动过程的早期提供图像捕获和显示,并在系统生命周期内持续运行。 HAL 包括外视系统 (EVS) 堆栈,通常用于支持配备基于 Android 的车载信息娱乐 (IVI) 系统的车辆中的后视摄像头和环视显示器。 EVS 还支持在用户应用程序中实现高级功能。

Android 还包括 EVS 特定的捕获和显示驱动程序接口(位于/hardware/interfaces/automotive/evs/1.0中)。虽然可以在现有 Android 摄像头和显示服务之上构建后视摄像头应用程序,但此类应用程序可能会在 Android 启动过程中运行得太晚。使用专用 HAL 可实现简化的接口,并明确 OEM 需要实现什么来支持 EVS 堆栈。

系统组成

EVS 包括以下系统组件:

EVS系统组成图

图 1.EVS系统组件概览。

电动车应用程序

示例 C++ EVS 应用程序 ( /packages/services/Car/evs/app ) 用作参考实现。此应用程序负责从 EVS 管理器请求视频帧并将完成的帧发送回 EVS 管理器进行显示。一旦 EVS 和汽车服务可用,它就会由 init 启动,目标是在开机后两 (2) 秒内。 OEM 可以根据需要修改或替换 EVS 应用程序。

电动车管理器

EVS 管理器 ( /packages/services/Car/evs/manager ) 提供 EVS 应用程序所需的构建块,以实现从简单的后视摄像头显示到 6DOF 多摄像头渲染的任何功能。其接口通过 HIDL 呈现,旨在接受多个并发客户端。其他应用程序和服务(特别是汽车服务)可以查询 EVS 管理器状态以了解 EVS 系统何时处于活动状态。

EVS HIDL 接口

EVS 系统(包括摄像头和显示元素)在android.hardware.automotive.evs包中定义。 /hardware/interfaces/automotive/evs/1.0/default中提供了练习该接口的示例实现(生成合成测试图像并验证图像进行往返)。

OEM 负责实现由/hardware/interfaces/automotive/evs中的 .hal 文件表示的 API。此类实现负责配置和收集来自物理相机的数据,并通过 Gralloc 可识别的共享内存缓冲区传递数据。实现的显示端负责提供可由应用程序填充的共享内存缓冲区(通常通过 EGL 渲染),并优先于可能希望出现在物理显示器上的任何其他内容呈现完成的帧。 EVS 接口的供应商实现可以存储在/vendor/… /device/…hardware/…下(例如/hardware/[vendor]/[platform]/evs )。

内核驱动程序

支持 EVS 堆栈的设备需要内核驱动程序。 OEM 无需创建新驱动程序,而是可以选择通过现有摄像头和/或显示硬件驱动程序来支持 EVS 所需的功能。重用驱动程序可能是有利的,特别是对于图像呈现可能需要与其他活动线程协调的显示驱动程序。 Android 8.0 包含一个基于 v4l2 的示例驱动程序(位于packages/services/Car/evs/sampleDriver中),该驱动程序依赖于内核来提供 v4l2 支持,并依赖于 SurfaceFlinger 来呈现输出图像。

EVS硬件接口说明

本节介绍 HAL。供应商应提供适合其硬件的 API 实现。

IEvs枚举器

该对象负责枚举系统中可用的 EVS 硬件(一个或多个摄像机和单个显示设备)。

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

返回一个向量,其中包含系统中所有摄像机的描述。假设相机组是固定的并且在启动时是已知的。有关相机描述的详细信息,请参阅CameraDesc

openCamera(string camera_id) generates (IEvsCamera camera);

获取用于与由唯一的camera_id字符串标识的特定相机交互的接口对象。失败时返回 NULL。尝试重新打开已打开的相机不会失败。为了避免与应用程序启动和关闭相关的竞争条件,重新打开相机应关闭先前的实例,以便可以满足新的请求。以这种方式被抢占的相机实例必须置于非活动状态,等待最终销毁并以返回码OWNERSHIP_LOST响应任何影响相机状态的请求。

closeCamera(IEvsCamera camera);

释放 IEvsCamera 接口(与openCamera()调用相反)。在调用closeCamera之前,必须通过调用stopVideoStream()来停止摄像头视频流。

openDisplay() generates (IEvsDisplay display);

获取用于专门与系统的 EVS 显示交互的接口对象。同一时间只有一个客户端可以持有 IEvsDisplay 的功能实例。与openCamera中描述的主动打开行为类似,可以随时创建新的 IEvsDisplay 对象,并将禁用任何先前的实例。无效的实例继续存在并响应其所有者的函数调用,但在死亡时不得执行任何变异操作。最终,客户端应用程序预计会注意到OWNERSHIP_LOST错误返回代码并关闭并释放非活动界面。

closeDisplay(IEvsDisplay display);

释放 IEvsDisplay 接口(与openDisplay()调用相反)。在关闭显示之前,必须将通过getTargetBuffer()调用接收到的未完成缓冲区返回到显示。

getDisplayState() generates (DisplayState state);

获取当前显示状态。 HAL 实现应报告实际的当前状态,该状态可能与最近请求的状态不同。负责改变显示状态的逻辑应该存在于设备层之上,这使得 HAL 实现不希望自发地改变显示状态。如果当前没有任何客户端持有该显示(通过调用 openDisplay),则此函数返回NOT_OPEN 。否则,它会报告 EVS Display 的当前状态(请参阅IEvsDisplay API )。

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id 。唯一标识给定相机的字符串。可以是设备的内核设备名称或设备的名称,例如后视镜。该字符串的值由 HAL 实现选择,并由上面的堆栈不透明地使用。
  • vendor_flags 。一种将专用摄像头信息从驱动程序不透明地传递到自定义 EVS 应用程序的方法。它未经解释地从驱动程序传递到 EVS 应用程序,EVS 应用程序可以随意忽略它。

电动车相机

该对象代表单个相机,是捕获图像的主要界面。

getCameraInfo() generates (CameraDesc info);

返回该相机的CameraDesc

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

指定要求相机支持的缓冲链的深度。 IEvsCamera 的客户端最多可以同时保存这么多帧。如果这么多帧已传递到接收器而未由doneWithFrame返回,则流会跳过帧,直到返回缓冲区以供重用。即使流已经在运行,此调用在任何时间都是合法的,在这种情况下,应根据需要在链中添加或删除缓冲区。如果不调用该入口点,IEvsCamera默认至少支持一帧;更能接受。

如果无法满足请求的 bufferCount,则该函数返回BUFFER_NOT_AVAILABLE或其他相关错误代码。在这种情况下,系统继续以先前设置的值运行。

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

请求从此摄像机传送 EVS 摄像机帧。 IEvsCameraStream 开始接收带有新图像帧的定期调用,直到调用stopVideoStream()为止。帧必须在startVideoStream调用后 500 毫秒内开始传送,并且在启动后必须以至少 10 FPS 生成。启动视频流所需的时间有效地计入任何后视摄像头启动时间要求。如果流没有启动,必须​​返回错误码;否则返回OK。

oneway doneWithFrame(BufferDesc buffer);

返回传送到 IEvsCameraStream 的帧。当使用完传递到 IEvsCameraStream 接口的帧后,该帧必须返回到 IEvsCamera 以供重用。可用的缓冲区数量有限(可能只有一个),如果供应耗尽,则在返回缓冲区之前不会再传送任何帧,这可能会导致帧跳过(带有空句柄的缓冲区表示结束)流的并且不需要通过此函数返回)。成功时返回 OK,或者可能包含INVALID_ARGBUFFER_NOT_AVAILABLE的适当错误代码。

stopVideoStream();

停止传送 EVS 相机帧。由于传递是异步的,因此在此调用返回后,帧可能会在一段时间内继续到达。必须返回每个帧,直到向 IEvsCameraStream 发出流关闭信号。在已停止或从未启动的流上调用stopVideoStream是合法的,在这种情况下,它将被忽略。

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

从 HAL 实现请求特定于驱动程序的信息。 opaqueIdentifier允许的值是特定于驱动程序的,但传递的任何值都可能导致驱动程序崩溃。对于任何无法识别的opaqueIdentifier ,驱动程序应返回 0。

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

将特定于驱动程序的值发送到 HAL 实现。提供此扩展只是为了促进特定于车辆的扩展,并且 HAL 实现不应要求此调用在默认状态下运行。如果驱动程序识别并接受这些值,则应返回 OK;否则应返回INVALID_ARG或其他有代表性的错误代码。

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
}

描述通过 API 传递的图像。 HAL 驱动器负责填写此结构来描述图像缓冲区,HAL 客户端应将此结构视为只读。这些字段包含足够的信息,允许客户端重建ANativeWindowBuffer对象,这可能需要通过eglCreateImageKHR()扩展将图像与 EGL 一起使用。

  • width 。所呈现图像的宽度(以像素为单位)。
  • height 。所呈现图像的高度(以像素为单位)。
  • stride 。每行在内存中实际占用的像素数,考虑行对齐的任何填充。以像素表示,以匹配 gralloc 对其缓冲区描述采用的约定。
  • pixelSize 。每个单独像素占用的字节数,允许计算在图像中的行之间步进所需的字节大小(以字节为单位的stride = 以像素为单位的stride * pixelSize )。
  • format 。图像使用的像素格式。提供的格式必须与平台的 OpenGL 实现兼容。为了通过兼容性测试,相机使用应首选HAL_PIXEL_FORMAT_YCRCB_420_SP ,显示应首选RGBABGRA
  • usage 。由 HAL 实现设置的使用标志。 HAL 客户端应传递这些未修改的内容(有关详细信息,请参阅Gralloc.h相关标志)。
  • bufferId 。 HAL 实现指定的唯一值,允许在通过 HAL API 往返之后识别缓冲区。该字段中存储的值可以由 HAL 实现任意选择。
  • memHandle 。包含图像数据的底层内存缓冲区的句柄。 HAL 实现可能会选择在此处存储 Gralloc 缓冲区句柄。

IEvsCameraStream

客户端实现该接口来接收异步视频帧传输。

deliverFrame(BufferDesc buffer);

每次视频帧准备好进行检查时,都会接收来自 HAL 的调用。此方法接收的缓冲区句柄必须通过调用IEvsCamera::doneWithFrame()返回。当通过调用IEvsCamera::stopVideoStream()停止视频流时,此回调可能会随着管道耗尽而继续。每一帧仍必须返回;当流中的最后一帧被传送时,将传送 NULL bufferHandle,表示流的结束并且不会发生进一步的帧传送。 NULL bufferHandle 本身不需要通过doneWithFrame()发回,但必须返回所有其他句柄

虽然专有缓冲区格式在技术上是可行的,但兼容性测试要求缓冲区采用四种受支持格式之一:NV21 (YCrCb 4:2:0 半平面)、YV12 (YCrCb 4:2:0 平面)、YUYV (YCrCb 4: 2:2 交错)、RGBA(32 位 R:G:B:x)、BGRA(32 位 B:G:R:x)。所选格式必须是平台 GLES 实现上的有效 GL 纹理源。

应用程序不应依赖bufferId字段和BufferDesc结构中的memHandle之间的任何对应关系。 bufferId值本质上是 HAL 驱动程序实现私有的,它可以根据需要使用(和重用)它们。

电动车显示

该对象代表 Evs 显示器,控制显示器的状态,并处理图像的实际呈现。

getDisplayInfo() generates (DisplayDesc info);

返回系统提供的有关 EVS 显示的基本信息(请参阅DisplayDesc )。

setDisplayState(DisplayState state) generates (EvsResult result);

设置显示状态。客户端可以设置显示状态来表达所需的状态,并且 HAL 实现必须在处于任何其他状态时优雅地接受任何状态的请求,尽管响应可能是忽略该请求。

初始化后,显示被定义为以NOT_VISIBLE状态启动,之后客户端应请求VISIBLE_ON_NEXT_FRAME状态并开始提供视频。当不再需要显示时,客户端应在传递最后一个视频帧后请求NOT_VISIBLE状态。

任何时候请求的任何状态都是有效的。如果显示已经可见,并且设置为VISIBLE_ON_NEXT_FRAME ,则它应该保持可见。始终返回 OK,除非请求的状态是无法识别的枚举值,在这种情况下返回INVALID_ARG

getDisplayState() generates (DisplayState state);

获取显示状态。 HAL 实现应报告实际的当前状态,该状态可能与最近请求的状态不同。负责改变显示状态的逻辑应该存在于设备层之上,这使得 HAL 实现不希望自发地改变显示状态。

getTargetBuffer() generates (handle bufferHandle);

返回与显示器关联的帧缓冲区的句柄。该缓冲区可以由软件和/或 GL 锁定和写入。即使显示不再可见,也必须通过调用returnTargetBufferForDisplay()返回此缓冲区。

虽然专有缓冲区格式在技术上是可行的,但兼容性测试要求缓冲区采用四种受支持格式之一:NV21 (YCrCb 4:2:0 半平面)、YV12 (YCrCb 4:2:0 平面)、YUYV (YCrCb 4: 2:2 交错)、RGBA(32 位 R:G:B:x)、BGRA(32 位 B:G:R:x)。所选格式必须是平台 GLES 实现上的有效 GL 渲染目标。

出错时,返回带有空句柄的缓冲区,但不需要将这样的缓冲区传递回returnTargetBufferForDisplay

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

告诉显示器缓冲区已准备好显示。只有通过调用getTargetBuffer()检索的缓冲区才可与此调用一起使用,并且客户端应用程序不得修改BufferDesc的内容。此调用之后,缓冲区不再可供客户端使用。成功时返回 OK,或者可能包含INVALID_ARGBUFFER_NOT_AVAILABLE的适当错误代码。

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

描述 EVS 显示的基本属性以及 EVS 实现所需的基本属性。 HAL 负责填写此结构来描述 EVS 显示。可以是物理显示器或与其他演示设备重叠或混合的虚拟显示器。

  • display_id 。唯一标识显示的字符串。这可以是设备的内核设备名称,也可以是设备的名称,例如后视镜。该字符串的值由 HAL 实现选择,并由上面的堆栈不透明地使用。
  • vendor_flags 。一种将专用摄像头信息从驱动程序不透明地传递到自定义 EVS 应用程序的方法。它未经解释地从驱动程序传递到 EVS 应用程序,EVS 应用程序可以随意忽略它。
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
}

描述 EVS 显示屏的状态,可以禁用(驾驶员不可见)或启用(向驾驶员显示图像)。包括暂时状态,其中显示尚不可见,但准备通过returnTargetBufferForDisplay()调用传递下一帧图像而变得可见。

电动车管理器

EVS 管理器为 EVS 系统提供公共接口,用于收集和呈现外部摄像机视图。如果硬件驱动程序仅允许每个资源(摄像头或显示器)有一个活动接口,则 EVS 管理器可促进对摄像头的共享访问。单个主 EVS 应用程序是 EVS 管理器的第一个客户端,也是唯一允许写入显示数据的客户端(可以授予其他客户端对摄像机图像的只读访问权限)。

EVS 管理器实现与底层 HAL 驱动程序相同的 API,并通过支持多个并发客户端(多个客户端可以通过 EVS 管理器打开摄像头并接收视频流)来提供扩展服务。

EVS 管理器和 EVS 硬件 API 图。

图 2.EVS Manager 镜像底层 EVS 硬件 API。

通过 EVS 硬件 HAL 实现或 EVS 管理器 API 进行操作时,应用程序看不到任何差异,只是 EVS 管理器 API 允许并发摄像头流访问。 EVS 管理器本身就是 EVS 硬件 HAL 层允许的客户端,并充当 EVS 硬件 HAL 的代理。

以下部分仅描述在 EVS Manager 实现中具有不同(扩展)行为的那些调用;其余调用与 EVS HAL 描述相同。

IEvs枚举器

openCamera(string camera_id) generates (IEvsCamera camera);

获取用于与由唯一的camera_id字符串标识的特定相机交互的接口对象。失败时返回 NULL。在 EVS 管理层,只要有足够的系统资源可用,已经打开的摄像头就可以由另一个进程再次打开,从而允许将视频流发送到多个消费者应用程序。 EVS Manager层的camera_id字符串与报告给EVS Hardware层的相同。

电动车相机

EVS Manager 提供的 IEvsCamera 实现是内部虚拟化的,因此一个客户端对摄像机的操作不会影响其他客户端,而其他客户端保留对其摄像机的独立访问。

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

开始视频流。客户端可以在同一底层摄像机上独立启动和停止视频流。当第一个客户端启动时,底层相机就会启动。

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

返回一个帧。每个客户在完成后都必须归还他们的相框,但可以根据需要保留他们的相框。当客户端持有的帧计数达到其配置的限制时,它将不会再接收任何帧,直到返回一帧。这种跳帧不会影响其他客户端,它们会继续按预期接收所有帧。

stopVideoStream();

停止视频流。每个客户端都可以随时停止其视频流,而不会影响其他客户端。当给定相机的最后一个客户端停止其流时,硬件层的底层相机流也会停止。

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

发送特定于驱动程序的值,可能使一个客户端影响另一个客户端。由于 EVS 管理器无法理解供应商定义的控制字的含义,因此它们不会被虚拟化,并且任何副作用都会适用于给定摄像机的所有客户端。例如,如果供应商使用此调用来更改帧速率,则受影响的硬件层相机的所有客户端都将以新速率接收帧。

电动车显示

即使在 EVS 管理员级别,也仅允许一名显示器所有者。 Manager 不添加任何功能,只是将 IEvsDisplay 接口直接传递到底层 HAL 实现。

电动车应用程序

Android 包含 EVS 应用程序的本机 C++ 参考实现,该应用程序与 EVS 管理器和车辆 HAL 进行通信,以提供基本的后视摄像头功能。该应用程序预计将在系统启动过程的早期启动,并根据可用摄像头和汽车状态(档位和转向灯状态)显示合适的视频。 OEM 可以使用自己的车辆特定逻辑和演示来修改或替换 EVS 应用程序。

图 3. EVS 应用程序示例逻辑,获取摄像机列表。



图 4. EVS 应用程序示例逻辑,接收帧回调。

由于图像数据在标准图形缓冲区中呈现给应用程序,因此应用程序负责将图像从源缓冲区移动到输出缓冲区。虽然这会带来数据复制的成本,但它也为应用程序提供了以所需的任何方式将图像渲染到显示缓冲区的机会。

例如,应用程序可能会选择移动像素数据本身,可能会使用内联缩放或旋转操作。应用程序还可以选择使用源图像作为 OpenGL 纹理,并将复杂的场景渲染到输出缓冲区,包括图标、指南和动画等虚拟元素。更复杂的应用程序还可以选择多个并发输入摄像头并将它们合并到单个输出帧中(例如用于车辆周围环境的自上而下的虚拟视图)。

在 EVS 显示 HAL 中使用 EGL/SurfaceFlinger

本部分介绍如何使用 EGL 在 Android 10 中渲染 EVS Display HAL 实现。

EVS HAL 参考实现使用 EGL 在屏幕上渲染相机预览,并使用libgui创建目标 EGL 渲染表面。在 Android 8(及更高版本)中, libgui被归类为VNDK-private ,它指的是一组可供 VNDK 库使用但供应商进程无法使用的库。由于 HAL 实现必须驻留在供应商分区中,因此供应商无法在 HAL 实现中使用 Surface。

为供应商进程构建 libgui

使用libgui是在 EVS Display HAL 实现中使用 EGL/SurfaceFlinger 的唯一选项。实现libgui最​​直接的方法是直接通过框架/native/libs/gui,在构建脚本中使用额外的构建目标。该目标与libgui目标完全相同,除了添加了两个字段:

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

注意:供应商目标是使用NO_INPUT宏构建的,该宏从地块数据中删除一个 32 位字。由于 SurfaceFlinger 预计该字段已被删除,因此 SurfaceFlinger 无法解析该包裹。这被视为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

要解决此情况:

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

下面提供了示例构建说明。期望收到$(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

在 EVS HAL 实现中使用绑定器

在 Android 8(及更高版本)中, /dev/binder设备节点成为框架进程独占的,因此供应商进程无法访问。相反,供应商进程应使用/dev/hwbinder ,并且必须将所有 AIDL 接口转换为 HIDL。对于那些想要继续在供应商进程之间使用 AIDL 接口的人,请使用活页夹域/dev/vndbinder

IPC域描述
/dev/binder具有 AIDL 接口的框架/应用程序进程之间的 IPC
/dev/hwbinder具有 HIDL 接口的框架/供应商进程之间的 IPC
具有 HIDL 接口的供应商进程之间的 IPC
/dev/vndbinder使用 AIDL 接口在供应商/供应商进程之间进行 IPC

虽然SurfaceFlinger定义了AIDL接口,但供应商进程只能使用HIDL接口与框架进程进行通信。将现有 AIDL 接口转换为 HIDL 需要大量工作。幸运的是,Android 提供了一种方法来为libbinder选择绑定器驱动程序,用户空间库进程链接到该驱动程序。

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

注意:供应商进程应该在调用ProcessIPCThreadState之前或在进行任何绑定程序调用之前调用此方法。

SELinux 政策

如果设备实现是全高音,SELinux 会阻止供应商进程使用/dev/binder 。例如,EVS HAL 示例实现分配给hal_evs_driver域,并需要对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

但是,添加这些权限会导致构建失败,因为它违反了system/sepolicy/domain.te中为 full-treble 设备定义的以下 neverallow 规则。

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是一个用于捕获错误并指导开发的属性。它还可用于解决上述 Android 10 违规问题。

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)

将 EVS HAL 参考实现构建为供应商流程

作为参考,您可以将以下更改应用于packages/services/Car/evs/Android.mk 。请务必确认所有描述的更改适用于您的实施。

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;