3、Camera HAL接口演进:从HAL1到HAL3再到HIDL/AIDL
Camera HAL(Hardware Abstraction Layer)是Android系统中连接上层Camera Framework与底层硬件驱动的关键桥梁。自Android诞生以来,Camera HAL接口经历了从HAL1到HAL3,再到HIDL(HAL Interface Definition Language)与AIDL(Android Interface Definition Language)化的重大演进。每一次演进都不仅仅是接口语法的变化,更深刻反映了移动影像从“拍照工具”向“计算摄影平台”的定位转变。对于驱动开发者而言,理解这一演进脉络,是进行新平台适配、老代码维护以及性能优化的基础。本章将系统梳理各版本接口的核心设计哲学、关键差异,并给出实际迁移中的操作要点与避坑指南。
上图清晰地展示了Camera HAL接口演进的三个关键阶段。HAL1时代,Camera被抽象为一个简单的“黑盒”,上层只能进行基础的预览和拍照操作;HAL3时代,接口被彻底重构为“请求-结果”模型,上层应用可以精细控制每一帧的曝光、对焦、ISP参数,为计算摄影打开了大门;而HIDL/AIDL阶段,则是在HAL3逻辑模型不变的基础上,将接口的物理形态从“同进程函数调用”转变为“跨进程RPC调用”,解决了系统升级与驱动解耦的工程难题。下面我们将逐一深入剖析。
3.1 HAL1:固定管线时代的简单抽象
核心原理:单一设备节点与固定处理流程
HAL1诞生于Android 4.x时代,其设计目标非常简单:为上层提供统一的Camera硬件访问入口。在HAL1中,整个Camera硬件被抽象为一个camera_device_t结构体,该结构体包含一组固定的函数指针,如start_preview()、stop_preview()、take_picture()等。硬件厂商需要实现这些函数,而Camera Service则通过调用这些函数来控制硬件。HAL1的底层处理流程是固定的:预览数据流经Sensor → ISP → 预览Buffer,拍照数据流经Sensor → ISP → JPEG编码器 → 图片Buffer。上层无法干预ISP的具体参数,只能通过简单的参数(如白平衡模式、场景模式)间接影响输出效果。
判断方法:如何识别一个驱动是否基于HAL1
在实际代码中,判断HAL版本最直接的方法是查看hardware/libhardware/include/hardware/camera.h头文件。如果该文件定义了camera_device_t且其ops字段指向camera_device_ops_t,并且该结构体中包含set_preview_window()、start_preview()等函数指针,那么基本可以确定是HAL1接口。此外,HAL1的camera_device_t中通常只有一个camera_device_ops_t实例,且没有request/result相关的数据结构。
案例拆解:老平台移植时的HAL1适配
假设我们需将一个基于Android 4.4的老款手机驱动移植到Android 7.0系统上。初始现象是:系统能识别到Camera设备,但打开相机应用时黑屏,且logcat中频繁出现start_preview failed的错误。分析发现,Android 7.0的Camera Service已经默认使用HAL3接口,而我们的驱动只实现了HAL1。关键日志如下:
E CameraService: Camera HAL module not HAL3.0, falling back to HAL1
E CameraClient: startPreview() failed: Invalid argument (-22)
此时,我们有两个选择:一是修改camera_module_t的get_number_of_cameras()和get_camera_info(),将device_version强制设为HAL_DEVICE_VERSION_3_0,但这会导致上层调用HAL3接口而底层没有实现,直接崩溃;二是继续使用HAL1,但需要修改系统属性persist.camera.HAL3.enabled为0,强制Camera Service走HAL1路径。最终我们选择了方案二,并额外修改了camera_module_t中的open()函数,确保返回的hw_device_t的close()函数正确释放资源。此案例的教训是:HAL1与HAL3在接口形态上完全不兼容,迁移时不能只改版本号,必须理解上层调用逻辑。
常见误区与总结
总结:HAL1是Android Camera接口的“史前时代”,它简单但僵化。对于驱动开发者,理解HAL1有助于维护老设备,但新项目不应再基于HAL1开发。
3.2 HAL3:请求-响应模型与全手动控制
核心原理:从“函数调用”到“请求/结果”的范式转变
HAL3(Android 5.0引入)彻底重构了接口模型。其核心是camera3_device_t,它不再暴露start_preview()等函数,而是通过process_capture_request()函数接收上层下发的camera3_capture_request_t请求。每个请求包含一个camera3_stream_buffer_t数组(用于指定输出Buffer)和一个camera_metadata_t(包含曝光、增益、对焦、ISP等全部控制参数)。HAL层处理完请求后,通过process_capture_result()回调返回结果,结果中包含填充好的Buffer和对应的结果元数据。这一模型使得上层可以对每一帧进行精细控制,并且支持多个Stream(如预览流、拍照流、视频流)同时存在,每个Stream可以有不同的分辨率、格式和帧率。
操作流程:HAL3设备打开与请求处理链路
一个标准的HAL3请求处理流程如下:
- 设备打开:Camera Service调用
open()打开设备,获取camera3_device_t实例。 - 配置Stream:上层调用
configure_streams(),传入一个camera3_stream_t数组,定义各Stream的格式、分辨率、用途(如CAMERA3_STREAM_PREVIEW)。HAL需要根据这些配置分配内部Buffer池。 - 下发请求:上层调用
process_capture_request(),传入请求。请求中必须包含有效的input_buffer(如果支持ZSL)和至少一个output_buffers。 - 硬件处理:HAL驱动解析请求中的元数据,配置Sensor、ISP、3A模块,并触发硬件捕获。
- 结果回调:硬件完成后,HAL填充Buffer,并调用
process_capture_result()返回。结果中的metadata必须包含实际使用的曝光时间、ISO等,以便上层统计。
案例拆解:实现“零快门延迟”的HAL3配置
某项目中,产品要求实现ZSL(Zero Shutter Lag)功能,即用户在按下快门前,后台始终有一组环形Buffer在持续捕获帧。初始现象是:使用HAL3接口后,虽然预览流畅,但拍照时仍有约200ms的延迟。分析发现,问题出在Stream配置上。我们最初只配置了一个CAMERA3_STREAM_PREVIEW流和一个CAMERA3_STREAM_STILL_CAPTURE流,且未设置CAMERA3_STREAM_IMPLEMENTATION_OPAQUE标志。关键参数如下:
camera3_stream_t preview_stream = {
.stream_type = CAMERA3_STREAM_OUTPUT,
.width = 1920, .height = 1080,
.format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
.usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_CAMERA_ZSL,
.max_buffers = 8
};
我们忽略了GRALLOC_USAGE_HW_CAMERA_ZSL标志,导致Gralloc分配的内存未标记为ZSL专用,HAL无法将其用于环形Buffer。修正后,我们在configure_streams()中增加了CAMERA3_STREAM_IMPLEMENTATION_OPAQUE类型的流,并设置max_buffers=8,同时确保process_capture_request()中请求的Buffer来自该环形池。最终,拍照延迟降低至30ms以内。此案例说明,HAL3的Stream配置是性能的关键,usage标志和max_buffers必须与硬件能力匹配。
常见误区与总结
ANDROID_SENSOR_EXPOSURE_TIME、ANDROID_SENSOR_SENSITIVITY等标签,否则上层应用无法正确计算3A统计信息。此外,process_capture_result()必须保证每个请求都有对应的结果回调,且回调顺序可以与请求顺序不同,但必须保证Buffer的完整性。
总结:HAL3是当前所有现代Android设备的基础,它赋予了上层极大的灵活性,但也对驱动的正确性提出了更高要求。驱动开发者需要深入理解请求-响应模型,并掌握Stream配置、Buffer管理、元数据填充等核心技能。
3.3 HIDL/AIDL:接口服务化与Treble架构
核心原理:从“同进程函数”到“跨进程RPC”
Android 8.0引入Treble架构,将系统框架(Framework)与厂商实现(Vendor)分离。为此,Camera HAL接口从传统的hardware/libhardware中的C结构体,迁移到了HIDL(HAL Interface Definition Language)定义的Binder接口。HIDL接口以.hal文件定义,编译后生成C++或Java的Binder代理与存根。Camera的HIDL接口位于android.hardware.camera.provider@2.4和android.hardware.camera.device@3.4等包中。从Android 13开始,Google进一步推动AIDL化,将Camera接口迁移到android.hardware.camera.provider AIDL包,以统一系统内所有HAL的接口风格。无论是HIDL还是AIDL,其核心逻辑模型仍然是HAL3的请求-响应模型,但接口的物理形态变成了跨进程的Binder调用。
判断方法:区分HIDL与AIDL实现
在代码中,判断当前系统使用HIDL还是AIDL,可以查看device_manifest.xml或vintf_fragments.xml中的<hal>标签。如果看到<name>android.hardware.camera.provider</name>且<version>2.4</version>,则是HIDL;如果看到<name>android.hardware.camera.provider</name>且<version>1.0</version>(AIDL版本从1.0开始),则是AIDL。此外,HIDL接口的包名通常以@2.4或@3.4结尾,而AIDL接口的包名是android.hardware.camera.provider.ICameraProvider。
案例拆解:从HAL3 C接口迁移到HIDL服务
我们曾将一个基于Android 7.0的HAL3 C接口驱动迁移到Android 10的HIDL架构。初始目标是保持所有功能不变,但需要将驱动封装为一个独立的Vendor进程。关键步骤包括:
- 编写HIDL接口:在
hardware/interfaces/camera/provider/2.4/default/下创建CameraProvider.cpp,实现ICameraProvider接口的getCameraDeviceInterface()方法。 - 封装设备回调:将原有的
camera3_device_ops_t回调函数(如process_capture_result)包装为HIDL的ICameraDeviceCallback实现。 - 处理Buffer句柄:HIDL使用
hidl_handle传递Buffer的native_handle_t,需要在process_capture_request中将hidl_handle转换为buffer_handle_t。 - VINTF配置:在
device_manifest.xml中声明Camera Provider的版本和实例名,确保Framework能发现服务。
迁移过程中遇到的最大问题是Buffer句柄的跨进程传递。由于Vendor进程与Framework进程不在同一地址空间,buffer_handle_t不能直接使用,必须通过hidl_memory或hidl_handle进行映射。我们通过IAllocator服务分配共享内存,并在HAL侧使用IMapper导入句柄,最终解决了问题。此案例的教训是:HIDL迁移不仅是接口语法转换,更是内存管理和进程间通信的重新设计。
常见误区与总结
service_manager自动重启机制。另外,HIDL接口的版本升级(如从2.4到3.4)需要同时更新Framework和Vendor侧的代码,否则会出现版本不匹配错误。
总结:HIDL/AIDL是Android系统架构演进的必然结果,它解决了系统升级与驱动解耦的问题。对于驱动开发者,需要掌握Binder通信、共享内存管理以及VINTF配置等新技能。
3.4 版本对比与迁移决策要点
| 对比维度 | HAL1 | HAL3 | HIDL/AIDL |
|---|---|---|---|
| 控制模型 | 固定函数调用 | 请求/结果(每帧控制) | 请求/结果(跨进程) |
| Stream支持 | 单路(预览或拍照) | 多路并行(预览+拍照+视频) | 多路并行(同HAL3) |
| 3A控制 | 厂商私有,上层不可控 | 上层可下发3A参数并获取状态 | 同HAL3 |
| Buffer管理 | 内部管理,无共享 | Gralloc共享,支持ZSL | 跨进程共享,需IMapper导入 |
| 部署形态 | 同进程库 | 同进程库 | 独立Vendor进程(Binder服务) |
| 升级维护 | 与系统强耦合 | 与系统强耦合 | 可独立升级(Treble) |
在进行接口迁移时,建议遵循以下决策路径:
- 新项目:直接采用AIDL接口(Android 13+),或至少采用HIDL 3.x版本,避免走HAL1弯路。
- 老项目维护:如果硬件能力较弱(如仅支持基本预览),可继续使用HAL1,但需在系统属性中强制禁用HAL3。
- 功能升级:如果需要在老硬件上支持HDR、夜景等,必须迁移到HAL3,并确保硬件ISP支持相应的参数控制。
- 架构解耦:如果目标是解决系统升级导致的驱动兼容问题,应优先迁移到HIDL/AIDL,并利用Treble的VINTF机制进行版本管理。