1. 课程导入
在 Android 系统开发中,Vendor Module(厂商模块)是连接 AOSP 通用框架与具体硬件平台的关键桥梁。随着 Android 8.0 Project Treble 的引入,Google 将系统框架与厂商实现彻底解耦,Vendor 分区、VINTF、HAL 接口、Vendor Module 驱动等概念成为每一位 Android 底层工程师必须掌握的核心知识。本课程围绕“Vendor Module 驱动工程”这一主线,从驱动加载机制、内核模块编译、HAL 接口实现,到 Vendor 分区打包与系统集成,构建一条完整的工程化学习路径。
一、学习目标:从“会写驱动”到“会交付模块”
本课程的学习目标不是停留在“能写一个字符设备驱动”,而是建立一套面向 Android 产品交付的 Vendor Module 工程能力。具体包括四个层次:
- 认知层:理解 Treble 架构下 System / Vendor / Product 分区的职责边界,明确 Vendor Module 在其中的位置。
- 机制层:掌握 Linux Kernel Module(LKM)的加载、卸载、依赖与符号导出机制,理解 Android 特有的模块加载策略。
- 接口层:能够基于 HIDL / AIDL 定义并实现 HAL 接口,完成驱动到框架的贯通。
- 工程层:能够使用 Android.bp、Kernel Makefile、VINTF Manifest 等工具完成模块编译、打包与集成验证。
二、核心概念:五个必须厘清的术语
在进入实操之前,需要先建立清晰的概念坐标系。以下五个概念贯穿整个课程:
| 概念 | 含义 | 典型载体 |
|---|---|---|
| Vendor Module | 运行在 Vendor 分区、由厂商维护的驱动或服务模块 | vendor/lib/modules/*.ko |
| HAL | 硬件抽象层,定义框架与硬件之间的标准接口 | android.hardware.xxx@1.0-service |
| VINTF | Vendor Interface,描述框架与 Vendor 之间的接口契约 | manifest.xml / compatibility_matrix.xml |
| KMI | Kernel Module Interface,内核模块与内核之间的稳定接口 | Module.symvers / abi_gki_aarch64 |
| GKI | Generic Kernel Image,统一内核镜像,Vendor 模块以 .ko 形式加载 | boot.img + vendor_boot.img |
其中,GKI 与 KMI 是 Android 11 之后驱动工程的最大变量。传统“把驱动编进内核”的方式逐渐被“内核模块动态加载”取代,这要求工程师必须理解模块与内核之间的 ABI 约束。
三、整体知识框架:四层结构一张图
为了把零散知识点组织成体系,本节用一张结构关系图展示 Vendor Module 驱动工程的整体框架。图中自下而上分为硬件层、内核层、HAL 层与框架层,Vendor Module 横跨内核层与 HAL 层,是整条链路的交付核心。
这张图揭示了三个关键关系:第一,Vendor Module 不是孤立代码,而是同时向下依赖内核接口、向上提供 HAL 接口;第二,VINTF 是框架与 Vendor 之间的“合同”,任何接口变更都必须同步更新;第三,GKI 让内核与驱动分离,驱动工程的重心从“改内核”转向“写模块 + 配接口”。
四、实战路线图:从环境搭建到系统验证
本课程后续章节将按照一条可执行的路线推进,建议按顺序学习,每一步都对应可验证的产物:
- 环境搭建:下载 AOSP 源码、配置 lunch 目标、编译 GKI 内核,产出可启动的 boot.img。
- 驱动开发:编写第一个字符设备驱动,理解 file_operations、ioctl、sysfs 等核心结构。
- 模块编译:使用 Android.bp 与 Kernel Makefile 将驱动编译为 .ko,处理 KMI 符号依赖。
- HAL 实现:定义 HIDL/AIDL 接口,实现 binderized 服务,完成驱动到框架的调用链。
- VINTF 配置:编写 manifest.xml 与 compatibility_matrix.xml,通过 vintf_fixer 校验。
- 打包集成:将 .ko 与 HAL 服务放入 vendor 分区,配置 init.rc 与 SELinux 策略。
- 验证排障:通过 dmesg、logcat、lshal、vintf 等工具定位加载失败与接口不匹配问题。
五、典型场景与常见误区
以一个真实场景为例:某项目需要新增一颗 I2C 接口的温湿度传感器。传统做法是直接修改内核源码,把驱动编进内核。但在 GKI 架构下,正确做法是:将驱动编译为独立 .ko,通过 vendor_boot 或 vendor 分区加载;同时实现一个 Sensor HAL 服务,将传感器数据通过 AIDL 上报给 SensorService;最后在 VINTF Manifest 中声明该 HAL 接口。
# 典型模块加载日志
[ 3.421] vendor_module: loading out-of-tree module taints kernel
[ 3.425] vendor_module: probe i2c-1 addr=0x48 success
[ 3.430] vendor_module: registered sensor hal interface v1.0
[ 3.435] init: starting service 'vendor.sensor-hal-1-0'
[ 3.440] vintf: manifest check passed for android.hardware.sensors@1.0
常见误区集中在三点:
- 误区一:把驱动当应用写。内核模块运行在内核态,不能调用标准 C 库,必须使用内核 API,且要注意并发与内存安全。
- 误区二:忽略 KMI 约束。直接使用未导出的内核符号,导致模块加载时报 “Unknown symbol”,必须通过 GKI 允许的符号列表。
- 误区三:VINTF 与实现不同步。HAL 接口版本升级后忘记更新 manifest,导致框架启动时找不到服务,表现为 “HAL not found”。