先把结论说在前面:
在 Android 里,HAL(Hardware Abstraction Layer,硬件抽象层)
就是夹在系统框架(Framework)和具体硬件/驱动中间的那层“翻译 + 适配 + 封装”。
你可以把 HAL 想象成:
- 一个会双语的“翻译官”:上面懂 Android 规定的“官方术语”,
- 下面懂自家摄像头、音频芯片、传感器那一套“土话”,
- 所有上层想用硬件的需求,都必须先跟它打交道。
这篇文章就用大白话,从几个角度讲清楚 HAL:
- HAL 存在的意义:为什么非要搞这么一层?
- HAL 在整个 Android 架构里的位置(和内核、驱动、Framework、App 的关系)
- HAL 的主要职责可以拆成哪几块?
- 具体举几个典型例子:
- 音频 HAL(Audio HAL)
- 摄像头 HAL(Camera HAL)
- 传感器 HAL(Sensor HAL)
- 显示 / 指纹等简单看一眼
- HAL 的实现大致流程:从接口定义,到 C/C++ 实现,到被系统加载调用
- Project Treble 后新一代 HAL(HIDL / AIDL)的变化
一、先占个位:HAL 在 Android 体系里到底在哪儿?
先用一段通俗的“楼房模型”,帮你把 HAL 放在脑子里:
最底层:硬件 + Linux 内核驱动
- CPU、GPU、摄像头、音频芯片、传感器、指纹头……
- 各种内核 Driver:camera driver、audio driver、input driver 等
往上一层:HAL(硬件抽象层)
- 一堆用 C/C++ 写的模块:camera HAL、audio HAL、sensor HAL…
- 它只认“Android 规定的统一接口”,不直接暴露厂商驱动细节给上层
再往上:Native Framework / System Services
- CameraService、AudioFlinger、SensorService 等
- 这些是系统服务进程里的 C++/Java 混合部分,主要通过 Binder 和 App 交互
再上一层:Java/Kotlin Framework API & Apps
- 开发者使用的 Camera API、MediaRecorder、AudioTrack、SensorManager 等
- 普通 App,从来没见过 HAL 长啥样,只知道有个系统服务给它能力
如果把 Android 比作一家大公司:
- 硬件 + 驱动:搬砖干活的一线工人(不会说人话,只懂寄存器和 IO)。
- HAL:懂技术的中层经理,一边会跟工人沟通,一边会跟老板/产品聊需求。
- Framework 服务:高层部门(摄像头部、音频部、传感器部),制定统一流程和对外接口。
- App:外部客户,通过前台(系统 API)提交需求,底下整套组织帮他干完。
关键点:
App 不会直接碰 HAL,
HAL 也不会直接和 App 聊天,
它只跟“系统服务 / Native 框架”说话。
二、HAL 存在的意义:为啥要多整这一层?
直觉上的问题很自然:
“既然有驱动了,系统服务直接调内核驱动不行吗?
为啥中间又加一层 HAL,看起来很啰嗦啊?”
你可以从三方面来理解:解耦、兼容、统一接口。
2.1 解耦:让“Android 系统”和“具体硬件实现”分手
不加 HAL 会怎样?
- CameraService 直接调用某家厂商摄像头驱动提供的 ioctl、sysfs、设备节点。
- AudioFlinger 直接操作某家音频芯片的寄存器地址。
结果是:
- Framework 一改,驱动也得改;
- 换个芯片厂,整个系统的服务代码要重写一半;
- 这对“要跑在各种硬件上”的 Android 来说很致命。
加了 HAL 之后变成:
Google 规定一套统一的 HAL 接口标准,
只要硬件厂按这套接口标准写一层 C/C++ 模块,
上层 CameraService/AudioFlinger/其他服务就不用改。
换芯片,只要厂商提供新的 HAL 实现,接口不变,上层 Android 基本不用动。
这就相当于:
- “标准电源插座 + 转换头”的思路:
- 上层只认“插头形状”,
- 具体里面是 220V 还是转了 110V、是国标还是美标,全由转换头(HAL)搞定。
2.2 兼容:控制 Android 碎片化
Android 当年最大的问题之一就是:碎片化。
- 各家的 ROM、自带系统服务、一堆私有接口,大杂烩。
- Google 想统一升级很难:
- 底下驱动是厂商的私有实现
- 上层 Framework 是 Google 控制的
- 中间没有清晰边界,就难以统一升级。
HAL 的出现和标准化,其实是在做一件事:
明确“系统上层”和“供应商底层”的分界线:
上面我管,下面你们玩,但是中间的接口必须听我的。
这在后来 Project Treble 里表现得更彻底:
- 系统分为System Image和Vendor Image
- HAL 接口被进一步规范成 HIDL/AIDL 模型
- 系统升级可以不动 vendor 分区,减少升级成本
2.3 统一接口:把五花八门的硬件能力整理成“一套 API”
比如摄像头:
- 有的设备只有一个后摄像头
- 有的有前后双摄、广角 + 长焦
- 有的还有 TOF、红外、夜视模块
如果没有 HAL 标准,各家 CameraService 要支持这些功能,写法会乱成一锅粥:
每家厂商一套 API,调用方式千奇百怪,
系统服务为了兼容所有厂商,代码变得极其复杂,也根本没法对外提供统一 Camera API。
有 HAL 之后,Google 可以规定:
- 摄像头 HAL 必须实现这些方法:
open_camera()get_camera_info()configure_streams()process_capture_request()
- 支持多个摄像头?那就通过
camera_id区分。 - 要支持特殊能力(如 RAW 输出、HDR 模式)?定义能力标志。
上层代码就变成:
- 不管下面的摄像头厂商是索尼、三星、OV 还是自研的,只要你按 HAL 标准实现,系统服务就能“统一调度”。
这就是 HAL 的核心价值之一:
统一 API、屏蔽差异、让上层有一个干净的世界。
三、HAL 的主要职责,可以粗暴总结成 4 句
用最接地气的说法:
- 翻译:把 Framework 的“标准化调用”,翻译成操作具体硬件的底层动作。
- 适配:屏蔽硬件差异,让上层不用关心你家是啥芯片、啥寄存器。
- 封装:把一堆复杂流程(上电、初始化、缓存、错误处理)封在内部,对外只暴露干净函数。
- 保障:负责跟硬件通信的安全性、稳定性,尽量不要把脏东西泄露到上层。
稍微正式一点,可以拆成几块职责:
- 定义“硬件能力接口”:比如摄像头能拍照、预览、录像,音频能播放/录制。
- 实现这些接口时,完成:
- 初始化 / 反初始化
- 参数配置
- 数据 IO(读写 buffer)
- 状态和错误报告
- 与底层驱动交互:
- ioctl
- 读写 /dev 设备节点
- 使用 sysfs 节点获取状态
- 做补丁:
- 有些底层驱动设计不理想,HAL 里做 workaround
- 在不动上层的前提下,修 bug、做兼容
四、典型 HAL 示例一:Audio HAL(音频硬件抽象层)
音频是 HAL 的经典示例。
我们先从大流程看,再稍微摸一下实现细节。
4.1 音频系统大致链路
从应用视角:
- App 调用
AudioTrack/MediaPlayer播放声音 - Java 层的 AudioTrack → JNI → C++ AudioTrack 实现
- C++ AudioTrack 向 AudioFlinger(系统音频服务)注册播放流
- AudioFlinger 负责混音、多路音频合成、音量控制等
- 最终 AudioFlinger 把混合后的音频数据,交给Audio HAL
- Audio HAL 再通过驱动,把数据送到真正的音频芯片播放
可以理解为:
App → Framework → AudioFlinger → Audio HAL → 音频驱动 → 声卡 → 喇叭
4.2 Audio HAL 做了哪几件关键事?
打开音频输出设备
- 告诉驱动:“我要以 44100Hz、16bit、立体声 使用你。”
- 可能涉及到 mixer 路由、时钟配置。
写音频数据到设备
- AudioFlinger 会定期把 PCM 数据交给 HAL,HAL 再写到驱动 buffer。
- 要保证写入节奏与硬件采样率匹配,否则会出现卡顿、爆音。
控制音量、路由
- 比如切换扬声器 / 听筒 / 耳机 / 蓝牙。
- 这些路由设置往往在 HAL 内部根据策略决定,然后写入驱动特定寄存器或 mixer 节点。
处理录音(输入流)
- 打开麦克风输入
- 从驱动读取 PCM 数据给上层(AudioRecord 或 MediaRecorder)
支持多种音频接口
- 原生的 Speaker / Mic
- 有线耳机、USB 音频、HDMI、蓝牙 A2DP/HFP
- HAL 根据设备状态和系统策略动态切换。
4.3 Audio HAL 接口结构(简化理解)
早期传统 HAL,基本会有一个audio_hw_device的结构体,大致类似(伪代码):
structaudio_hw_device{structhw_device_tcommon;// 通用HAL头int(*init_check)(conststructaudio_hw_device*dev);int(*set_voice_volume)(structaudio_hw_device*dev,floatvolume);int(*set_master_volume)(structaudio_hw_device*dev,floatvolume);int(*open_output_stream)(structaudio_hw_device*dev,audio_io_handle_thandle,audio_devices_tdevices,audio_output_flags_tflags,structaudio_config*config,structaudio_stream_out**stream_out);void(*close_output_stream)(structaudio_hw_device*dev,structaudio_stream_out*stream);// 还有一堆其他函数,比如输入流、路由、参数设置等等};厂商需要做的事情就是:
- 按 Google 定义的接口,提供一个实现了这些函数指针的结构体
- 在
open_output_stream()里,打开底层驱动设备/dev/snd/…,设置采样率等 - 在
write()函数里,把 AudioFlinger 交下来的数据写入驱动出口
上层 AudioFlinger 根本不关心你底下硬件长什么样,只调用这些函数。
4.4 实现流程总结(厂商视角)
按 Google 规定的 Audio HAL 接口版本,创建一个 C/C++ 模块:
- 通常编译成
audio.primary.<board>.so等文件放在/system/lib/hw/或/vendor/lib/hw/
- 通常编译成
实现一组标准函数:比如
open_output_stream(),close_output_stream(),set_parameters(),write()等。在这些函数里通过:
open()/ioctl()操作/dev/snd/xxx设备- 或者通过 ALSA 接口等
- 配置通道数、采样率、格式
AudioFlinger 启动时加载对应的 HAL 模块(通过 hw_get_module 接口),拿到
audio_hw_device实例。以后系统播放音频就走这个 HAL 模块。
五、典型 HAL 示例二:Camera HAL(摄像头硬件抽象层)
摄像头 HAL 比音频复杂很多,也是面试/系统开发中经常被问的内容。
5.1 摄像头调用大致链路
从 App 到镜头:
App 调用 Camera API(老的
android.hardware.Camera或新的camera2)Java 层 → Binder →
CameraService(system_server 或独立进程)CameraService调用 Camera HAL 的接口:get_number_of_cameras()get_camera_info()open()configure_streams()process_capture_request()等
Camera HAL 内部通过驱动控制传感器、ISP(图像信号处理)、Sensor、对焦马达、闪光灯等硬件
最终图像数据通过 BufferQueue / GraphicBuffer 等机制传给上层(可能走到 SurfaceFlinger、MediaRecorder 或直接给 App)
简单情景如下:
App:我要预览 + 拍照
CameraService:行,我帮你找对应摄像头,并让 HAL 开始输出 YUV buffer。
HAL:我去配置 sensor、ISP,控制硬件按要求出图。
HAL 把图像写入 buffer → 上交给上层 → App 在 SurfaceView / TextureView 上看到实时画面。
5.2 Camera HAL 的职责有哪些?
可以拆成几块:
设备枚举和信息报告
- 有多少个摄像头?前置/后置?
- 支持哪些分辨率?哪些帧率?
- 是否支持对焦、闪光灯、OIS、防抖、HDR、RAW 输出等。
打开 / 关闭摄像头
- 上电传感器
- 初始化 ISP
- 设置基础参数(时钟、总线、时序)
配置数据流(stream)
- 预览流、录像流、拍照流,分别以什么格式、多大尺寸、帧率多少
- 可能同时有多路输出(低分辨率预览 + 高清录像)
对焦、曝光、白平衡等控制
- 自动对焦(AF)
- 自动曝光(AE)
- 自动白平衡(AWB)
- 各种拍照模式(夜景、人像、连拍等)
图像数据输出 & 回调
- 将图像输出到上层配置的 buffer(可能是 gralloc buffer)
- 通知上层“这一帧已经完成,可以拿去用/编码/显示”。
5.3 Camera HAL 的几代演进(简单认个门)
Android Camera HAL 大致经历了几个版本:
- Camera HAL v1:传统模式,偏向老 API,预览/拍照接口简单粗暴。
- Camera HAL v2/v3:为了支持
camera2高级特性,引入了 request/result 模型,- App 提交一个 CaptureRequest(包含曝光、对焦、参数)
- HAL 输出一个 CaptureResult(包含实际拍摄结果)
重点不在记版本号,而是理解:
越往后,HAL 越像一个“图像管线调度中心”,
不再只是简单的“打开摄像头 + 出图”,
而是支持半专业级控制(手动模式、高速连拍、多摄像头协同等)。
5.4 Camera HAL 示例(概念伪代码)
简化一下 Camera HAL 里的结构(极简示意):
typedefstructcamera_module{hw_module_tcommon;int(*get_number_of_cameras)(void);int(*get_camera_info)(intcamera_id,structcamera_info*info);int(*open)(conststructhw_module_t*module,constchar*id,structhw_device_t**device);}camera_module_t;厂商提供:
get_number_of_cameras:比如返回 3(超广角 + 主摄 + 长焦)get_camera_info:告知每个 camera_id 的 facing(前/后)、orientation(角度)等open:打开某个摄像头,返回camera_device结构(里面有 start_preview, take_picture 等函数指针)
CameraService 会:
- 通过
hw_get_module("camera", &module)加载厂商的 HAL - 调
get_number_of_cameras()得知总数 - 调
open()打开具体摄像头 - 把逻辑封装成统一的接口提供给 App
对于新式 camera2 API,对应的 HAL v3 接口会复杂些,但本质仍是同一思路:
厂商实现一套标准函数,上层只管调用,不问细节。
六、典型 HAL 示例三:Sensor HAL(传感器)
传感器 HAL 相对简单些,但非常典型。它负责加速计、陀螺仪、光线、距离传感器等。
6.1 调用链路
- App 使用
SensorManager注册监听,如加速计、重力传感器。 - Java 层经由 JNI 到 C++ 层
SensorService。 SensorService调用 Sensor HAL。- HAL 从驱动读取传感器原始数据,进行必要处理,回传给上层。
- 上层框架将数据转成统一的事件(SensorEvent),通过回调给 App。
6.2 Sensor HAL 的职责
枚举可用传感器
- 包括类型(加速度、陀螺仪、磁场、光线、压力……)
- 精度、范围、功耗、最大输出频率等
打开/关闭传感器
- App 注册监听时,传感器 HAL 可能打开对应设备节点并开始采样。
- 没有任何 App 在用时,可以关闭,节省电量。
数据读取和上报
- 从驱动中读原始数据(可能是原始物理量)
- 转换为标准单位(如 m/s²、lux、度/秒)
- 按指定频率向上层推送事件
做简单的融合或滤波
- 有的 HAL 会做基本滤波去掉噪声
- 或者合成出某些虚拟传感器(如倾斜角、线性加速度)
6.3 一个简化版的 HAL 实现流程
对开发传感器 HAL 的厂商工程师来说:
根据谷歌提供的
sensors.h接口定义,实现:sensors_module_tsensors_poll_device_t
在 HAL 中实现函数:
get_sensors_list()返回支持的传感器列表activate(handle, enabled)打开/关闭某个传感器setDelay(handle, ns)设置数据输出间隔poll():阻塞或轮询方式从驱动读取事件并返回
编译生成
sensors.<board>.so放到指定目录。SensorService 启动时加载该模块,获取所有传感器信息。
后面 App 就可以通过 SensorManager 使用这些传感器了。
七、其他常见 HAL:Display / Fingerprint / Bluetooth / Radio++
这里简单点名,不展开细节,只让你知道:
显示 HAL(或 Gralloc HAL):
- 负责图形 buffer 分配、格式支持、显存管理
- 上层 SurfaceFlinger 通过它来分配/管理显存,最后显示到屏幕
指纹 HAL:
- 提供采集指纹数据、比对、Enroll(录入)、认证等接口
- 上层 FingerprintService/AUTHFramework 通过 HAL 做指纹识别
蓝牙 HAL:
- 帮 Bluetooth stack 与底层蓝牙控制器交互,扫描设备、配对、传输数据等
Radio HAL(RIL HAL):
- 和基带芯片打交道,负责电话、短信、移动数据连接
- 上层 TelephonyFramework 调用它完成拨号、接听、短信收发等
这些 HAL 的逻辑都类似:
Google 先定接口标准,厂商按标准实现,Native/Framework 层统一调度。
八、HAL 的实现套路:从接口定义到系统加载
前面例子都是“散讲”,我们这里归纳一下一个典型 HAL 的实现全过程(偏工程视角)。
8.1 Step 1:Google 定一个 HAL 接口“标准”
以传统 C 风格 HAL 为例:
- 在 AOSP 源码中定义一个头文件,如
audio.h、camera_common.h、sensors.h等。 - 标准中包含:
- 模块接口
hw_module_t/xxx_module_t - 设备接口
hw_device_t/xxx_device_t - 一堆函数指针和数据结构,描述 HAL 必须实现哪些功能。
- 模块接口
这一步相当于制定协议:
以后所有想接入 Android 系统的摄像头/音频设备,都得说这套“标准话”。
8.2 Step 2:设备厂商在 Vendor 源码里实现这个接口
厂商拿到芯片手册和驱动,实现对应 C/C++ 模块:
- 写一个
.so动态库,对外导出HAL_MODULE_INFO_SYM这样的符号 - 在库里填好函数指针,指向自己实现的函数
伪代码示意:
staticstructhw_module_methods_taudio_module_methods={.open=audio_device_open,};structaudio_moduleHAL_MODULE_INFO_SYM={.common={.tag=HARDWARE_MODULE_TAG,.module_api_version=AUDIO_MODULE_API_VERSION_CURRENT,.id=AUDIO_HARDWARE_MODULE_ID,.name="MyPhone Audio HW HAL",.author="My Company",.methods=&audio_module_methods,},// ...};系统通过这个HAL_MODULE_INFO_SYM知道“这是一个音频 HAL 模块”。
8.3 Step 3:HAL 模块被放入指定路径
例如:
/vendor/lib/hw/audio.primary.mysoc.so/vendor/lib/hw/camera.mysoc.so/vendor/lib/hw/sensors.mysoc.so
命名规则一般包含:
- 模块类型(camera、audio.primary、sensors等)
- 芯片/平台 ID(mysoc)
8.4 Step 4:系统服务启动时,通过 hw_get_module() 找到 HAL 模块并加载
系统里有个通用加载逻辑:
inthw_get_module(constchar*id,conststructhw_module_t**module);比如 CameraService 启动时:
hw_get_module(CAMERA_HARDWARE_MODULE_ID,(consthw_module_t**)&mModule);CAMERA_HARDWARE_MODULE_ID对应的是 “camera”hw_get_module会根据当前设备平台,在/vendor/lib/hw//system/lib/hw等路径里找名字匹配的 so- 找到后,dlopen 加载,拿到
HAL_MODULE_INFO_SYM,就能调用 HAL 的方法
8.5 Step 5:后续所有操作都通过这个 HAL 接口走
- 上层需要开摄像头 → 调用
mModule->open() - AudioFlinger 需要输出音频 → 调用
audio_hw_device->open_output_stream() - SensorService 要获取传感器事件 → 调用
sensors_poll_device->poll()…
从此之后,Framework 就不需要关心任何底层细节,只和 HAL 标准接口“对话”。
九、Project Treble 之后:新一代 HAL(HIDL / AIDL)简述
前面讲的大多是“传统 C 风格 HAL”。
从 Android 8.0(Oreo)开始,Google 推了一个大工程:Project Treble,其中一个核心是:
把 Vendor(厂商定制部分)和 System(Android 框架部分)彻底隔离开来,
让系统升级时尽量不动厂商的 HAL。
为此,引入了HIDL(HAL Interface Definition Language),后来又渐渐向 AIDL-based HAL 过渡。
9.1 HIDL:让 HAL 接口更加“面向对象 + 支持 Binder”
- 以前 HAL 接口都是 C 结构体 + 函数指针,系统通过
dlopen动态链接。 - HIDL 把 HAL 接口定义为一组 interface(IDL):
- 像 AIDL 一样,支持版本化、强类型、继承
- 支持通过 Binder 做 IPC,这样 HAL 实现甚至可以在单独进程里跑,不一定是 so 模块。
新的 HAL 典型调用链:
Framework Service(在 system 分区)
通过 HIDL/AIDL interface 调用 HAL 实现(在 vendor 分区)
彼此通过 Binder 通信,不直接耦合内存空间。
好处:
- 系统升级更容易:
- System 分区升级,但 HIDL 接口版本保持兼容,Vendor 分区不动。
- 安全性更高:
- HAL 可以跑在独立进程中,用 SELinux 限制权限,
- 即便 HAL 崩了,也不会把整块 system_server 或 Native 进程带崩。
9.2 AIDL HAL:进一步统一语言
Android 11 及之后,Google 趋向用 AIDL 统一 HAL 和上层接口定义:
- HAL 直接使用 AIDL 来描述接口
- 好处是工具链统一、语法一致
- 更方便验证和版本管理
对我们理解“HAL 做什么”这件事,其实没什么本质影响,只是:
原来 HAL 是 C 层动态库 + 函数指针,
现在 HAL 也可以是一个“系统进程”,通过 AIDL 与 Framework 交互。职责没变,只是“通信方式 + 形式”更现代、可维护。
十、从 App 开发者角度,再看一眼 HAL:它离你有多远?
最后,我们从普通开发者视角再收一收:
- 平时写 App,不会直接接触 HAL,你只能用 Framework 提供的 Camera、Audio、Sensor 等 API。
- 但当你遇到下面这些问题时,背后其实就是 HAL/驱动层在搞事情:
为什么不同手机上,Camera 行为略有差异?
- 比如对焦速度不一样、拍照声音强制开启、某些模式不支持。
- 很可能是不同厂商 Camera HAL 实现有区别。
为什么有些机型录音有底噪/延迟长?
- Audio HAL 和底层驱动配置不同。
- 有的厂商优化得好,有的敷衍。
传感器输出频率不稳定、重力感应“歪”?
- 传感器 HAL 中滤波、坐标系转换实现有差异。
指纹识别体验好坏差距巨大
- 一方面是硬件,另一方面也是 Fingerprint HAL 的算法、接入方式不同。
系统升级后某些硬件功能异常
- 很有可能是 Framework 升级了,但 HAL 没适配好,
- 或新的 HIDL/AIDL 接口版本不兼容。
大部分时候,这些坑不是你作为第三方 App 能解决的,
但你至少能:
- 通过了解 HAL 的存在,理解问题的“层级”
- 知道哪些问题是你的代码逻辑问题,哪些是底层实现问题
- 在做跨机型适配时,有更靠谱的预期和沟通方向(比如反馈给 ROM 厂时知道该说什么)
总结:一段话把 HAL 拎起来
我们用一段简洁的总结把文收住:
Android 的 HAL(硬件抽象层),就是连接“上层 Android 框架”和“底层硬件/驱动”的那层 C/C++ 模块或进程。
它的主要职责是:
- 把五花八门的硬件能力,抽象成一套统一、稳定的接口(Audio、Camera、Sensor 等);
- 屏蔽硬件差异,解耦上层系统和底层芯片实现,缓解 Android 的碎片化问题;
- 为系统服务提供可靠的硬件访问方式,同时保证一定的性能和安全性。
对普通 App 来说,HAL 是看不见摸不着的;
但你每天用的拍照、听歌、旋转屏幕、指纹解锁、打电话、连蓝牙……
背后都必然经过这一层翻译和适配。如果 Android 是一栋大楼,HAL 就是大楼里所有“电水气设备”的统一接口层——
住户(App)只要会转龙头、按开关就行;
具体是用什么水管、什么压力、什么电压,
都交给 HAL 去对接底层工程团队了。