Camera HAL工程实战:从移植到量产优化 - 第3节

3.1 交叉编译工具链配置

Camera HAL 模块最终运行在嵌入式设备(如手机、平板、IoT 摄像头)的 Linux 内核之上,而非开发主机(x86_64)。因此,必须使用交叉编译工具链,在主机上编译出目标架构(ARM64、ARM32、RISC-V 等)的可执行文件与共享库。

3.1.1 工具链选择与安装

Android 官方推荐使用 Clang/LLVM 作为默认编译器,GCC 已逐步弃用。在 AOSP 源码树中,预编译工具链位于 prebuilts/clang/host/linux-x86/ 目录下。对于 Camera HAL 开发,通常不需要手动安装独立工具链,而是通过 Android 构建系统(Soong / Make)自动调用。

工具链组件 说明 典型路径(AOSP 内)
Clang 编译器 C/C++ 前端,生成 LLVM IR prebuilts/clang/host/linux-x86/clang-r487747/bin/clang
LLVM 链接器 (lld) 替代 GNU ld,速度更快 同上目录下的 ld.lld
sysroot 目标系统的头文件与库文件根目录 prebuilts/ndk/current/platforms/android-34/arch-arm64
binutils 汇编器、反汇编器、strip 等 prebuilts/gas/linux-x86/
实战建议:在量产项目中,务必锁定 Clang 版本号。不同 Clang 版本对 Neon 内联汇编的优化策略不同,可能导致图像处理算法行为不一致。建议在 BoardConfig.mk 中显式指定 TARGET_CLANG_VERSION := clang-r487747。

3.1.2 环境变量与构建配置

在开始编译 Camera HAL 之前,需要正确设置以下环境变量(通常通过 source build/envsetup.sh 和 lunch 自动完成):

  • ANDROID_BUILD_TOP:AOSP 源码根目录。
  • ANDROID_PRODUCT_OUT:编译产物输出目录。
  • ANDROID_HOST_OUT:主机工具输出目录。
  • PATH:必须包含 $ANDROID_BUILD_TOP/prebuilts/clang/host/linux-x86/clang-版本/bin。
# 典型初始化流程
cd ~/aosp
source build/envsetup.sh
lunch aosp_arm64-userdebug
# 验证工具链
which clang
# 输出应指向 prebuilts/clang/.../clang
clang --version
# 确认目标 triple 为 aarch64-linux-android
常见陷阱:不要使用宿主机的 GCC 或 Clang 直接编译 HAL 模块。宿主机的 glibc 版本、动态链接器路径与 Android 目标系统完全不同,链接阶段会报 "cannot find crtbegin_so.o" 或运行时出现 "dlopen failed: cannot locate symbol" 错误。

3.2 Android 源码树结构

Camera HAL 开发人员必须熟悉 AOSP 源码树中与 Camera 相关的关键目录。错误的文件放置位置会导致编译系统找不到模块或依赖。

3.2.1 Camera 相关目录全景

Android 源码树 Camera 相关目录结构 AOSP 根目录 hardware/interfaces/ hardware/libhardware/ frameworks/av/ device/ camera/ graphics/ modules/camera/ camera/ vendor/<oem>/ 3.4/ (HIDL) provider/ CameraService CameraClient camera/ libcameraservice/ 图例说明: • 绿色:HAL 接口定义与 libhardware 框架层 • 紫色:HIDL / AIDL 接口描述文件(.hal / .aidl) • 青色:Framework 层 CameraService 与 Client • 红色:OEM/SoC 厂商实现的 Camera HAL 库与专属服务 • 橙色:legacy camera HAL (camera_module_t) 存放位置

3.2.2 关键目录职责

目录路径 职责 HAL 开发者关注点
hardware/interfaces/camera/ 定义 Camera HIDL/AIDL 接口(ICameraDevice、ICameraProvider 等) 实现接口时需严格遵循 .hal 文件中的方法签名
hardware/libhardware/modules/camera/ 存放 legacy Camera HAL (camera_module_t) 示例代码 参考 CameraHardwareInterface.h 实现 open/close/ops
frameworks/av/camera/ CameraService、CameraClient、Camera2Client 理解 Framework 如何调用 HAL 接口,调试 binder 调用
device/<vendor>/<board>/camera/ OEM 自定义 Camera HAL 实现、专属算法库、传感器驱动适配 主要开发工作目录,包含 Android.bp 和所有源文件
vendor/<oem>/ 闭源二进制库、专有图像处理库(如高通、联发科、三星) 链接 libmmcamera.so 或 libcamx.so 等闭源库

3.3 HAL 模块编译与集成流程

从零开始将一个 Camera HAL 模块集成到 AOSP 构建系统中,需要依次完成模块声明、依赖配置、编译脚本编写、产物打包四个步骤。

3.3.1 模块声明与 Android.bp

Android 新构建系统使用 Blueprint/Soong,配置文件为 Android.bp(取代旧式的 Android.mk)。以下是一个典型的 Camera HAL 模块 Android.bp 示例:

// device/vendor/board/camera/Android.bp
cc_library_shared {
    name: "camera.<vendor>.<board>",
    vendor: true,
    relative_install_path: "hw",
    srcs: [
        "CameraHAL.cpp",
        "CameraDevice.cpp",
        "CameraStream.cpp",
    ],
    shared_libs: [
        "libhardware",
        "libcutils",
        "libutils",
        "liblog",
        "libcamera_metadata",
        "libsync",
    ],
    header_libs: [
        "camera_metadata_headers",
    ],
    cflags: [
        "-Wall",
        "-Werror",
        "-O2",
        "-DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)",
    ],
    include_dirs: [
        "device/vendor/board/include",
    ],
    sanitize: {
        hwaddress: true,  // 量产前开启 HWASan 检测内存错误
    },
}
关键字段解释:
• vendor: true:产物安装到 /vendor/lib64/hw/ 分区,而非 system 分区。
• relative_install_path: "hw":HAL 模块必须放在 hw 子目录下,HAL 加载器通过 /vendor/lib64/hw/camera.<board>.so 路径查找。
• shared_libs:必须链接 libhardware(提供 hw_module_t 结构体)和 libcamera_metadata(操作 camera_metadata_t)。

3.3.2 编译与产物验证

执行以下命令编译 Camera HAL 模块:

cd ~/aosp
source build/envsetup.sh
lunch aosp_arm64-userdebug
# 编译指定模块
make camera.<vendor>.<board> -j$(nproc)
# 或者编译整个 vendor 分区
make vendorimage -j$(nproc)

编译成功后,产物位于 $ANDROID_PRODUCT_OUT/vendor/lib64/hw/camera.<vendor>.<board>.so。使用以下命令验证模块结构:

# 检查 ELF 架构
readelf -h camera.<vendor>.<board>.so | grep Machine
# 输出应为 Machine: AArch64

# 检查 HAL 模块符号
readelf -s camera.<vendor>.<board>.so | grep HAL_MODULE_INFO_SYM
# 必须存在 HAL_MODULE_INFO_SYM_AS_STR 符号,否则 HAL 加载器无法识别

# 检查依赖库
readelf -d camera.<vendor>.<board>.so | grep NEEDED

3.3.3 集成到系统镜像

将 HAL 模块集成到最终系统镜像中有两种方式:

集成方式 适用场景 操作要点
直接编译进 vendor image 量产固件、完整 OTA 包 在 device/vendor/board/device.mk 中添加 PRODUCT_PACKAGES += camera.<vendor>.<board>
通过 adb push 单独调试 开发阶段快速迭代 adb root && adb remount && adb push camera.<vendor>.<board>.so /vendor/lib64/hw/ && adb reboot
量产集成检查清单:
✓ SELinux policy 是否允许 HAL 进程访问 /dev/video* 节点?
✓ 是否在 manifest.xml 中注册了 Camera HAL 实例?
✓ 是否关闭了调试日志(LOG_NDEBUG)以避免性能开销?
✓ 是否使用 strip 去除调试符号以减少 .so 体积?

3.3.4 编译集成流程图

Camera HAL 编译与集成流程 编写 Android.bp source & lunch make camera.<vendor>.<board> 编译成功? 是 集成到 vendor image 否 检查错误日志 虚线回退路径:修正编译错误后重新执行 make 命令,无需再次 source/lunch

3.4 本章小结

Camera HAL 开发环境搭建的核心要点可归纳为:

  • 工具链:使用 AOSP 预置的 Clang/LLVM,通过 lunch 自动配置交叉编译环境,避免手动设置 sysroot。
  • 源码结构:HAL 接口定义在 hardware/interfaces/camera/,Framework 服务在 frameworks/av/camera/,OEM 实现放在 device/<vendor>/<board>/camera/。
  • 编译集成:使用 Android.bp 声明模块,通过 make 编译,产物放入 /vendor/lib64/hw/,并在 device.mk 中注册。
  • 验证:使用 readelf 检查架构、符号表、依赖库,确保 HAL_MODULE_INFO_SYM 符号存在。
量产红线:严禁在未验证 SELinux 策略和 Treble 兼容性测试(VTS)的情况下将 HAL 模块合入量产分支。VTS 中的 VtsHalCameraProviderV2_4TargetTest 会严格检查 HAL 接口实现的完整性,任何未实现的回调方法都会导致测试失败,阻塞 OTA 发布。