1. Android系统架构概览:从Linux内核到应用层,HAL的定位与作用

做Android底层开发这么多年,我经常被问到同一个问题:"HAL到底是个什么东西?" 很多应用层开发者觉得它很神秘,驱动工程师又觉得它多此一举。其实说白了,HAL就是一座桥——一座连接Android框架和硬件驱动的桥。

今天这一章,我们先不急着写代码。先把整个Android系统的骨架搭起来,搞清楚每一层在干什么,HAL为什么非有不可。我个人习惯,做任何底层开发之前,先把架构图画在脑子里,否则后面调试起来会非常痛苦。

1.1 Android系统的分层结构

Android系统从上到下,大致可以分为五层。我画了一张图,你先感受一下这个层次关系:

Android 系统分层架构图 应用层 (Applications) 系统应用 | 第三方应用 | Launcher 应用框架层 (Java API Framework) Activity Manager | Window Manager | Content Providers 系统运行库层 (Native C/C++ Libraries & ART) WebKit | OpenGL | SQLite | Android Runtime 硬件抽象层 (HAL) Camera HAL | Audio HAL | Sensors HAL | GPS HAL Linux内核层 (Linux Kernel) Display Driver | Camera Driver | Audio Driver | Power Management Java Java C/C++ C/C++ C 用户空间 用户空间 用户空间 用户空间 内核空间

这张图我建议你保存下来。每次遇到问题,先定位问题出在哪一层,再动手去查。这个习惯帮我省了无数时间。

1.2 每一层都在干什么?

从上往下,我们快速过一遍:

  • 应用层:你手机上那些图标。电话、微信、相机。它们通过Android SDK调用系统服务。
  • 应用框架层:Google帮你封装好的Java API。比如你想打开相机,调的是 Camera.open(),而不是直接操作硬件寄存器。
  • 系统运行库层:C/C++写的核心库。WebView、OpenGL、MediaCodec都在这一层。ART虚拟机也在这里。
  • HAL层:我们这30章的主角。它定义了一套标准接口,让上层不用关心底层用的是哪家的芯片。
  • Linux内核层:真正的硬件驱动在这里。GPIO、I2C、SPI、中断处理,都是内核的事。

关键点:从应用层到HAL,都是用户空间。只有Linux内核跑在内核空间。这个区分很重要——用户空间崩溃了不会死机,内核空间崩了直接panic。

1.3 HAL的定位——为什么非要有它?

你可能会想:"为什么应用层不直接调内核驱动?绕这么大一圈干嘛?"

嗯,这个问题我当年也问过我的mentor。他给我讲了一个真实的故事:

早期Android版本(2.x时代),厂商要适配一个新硬件,得改框架层的Java代码。改完以后,Google一升级系统版本,厂商的修改全废了,又得重来。这就是所谓的"碎片化噩梦"。

HAL的出现,就是为了解决这个问题。它的定位非常清晰:

角色 职责 不做什么
接口标准化 定义固定的API,上层只管调用 不关心具体硬件实现
解耦 厂商实现HAL,Google升级框架 不强制厂商开源驱动
兼容性 同一套HAL接口适配不同芯片 不处理硬件差异

说白了,HAL就是一份契约。Google说:"你给我实现这几个函数,我就能用你的硬件。" 厂商说:"好,我按你的规矩来,你别管我里面怎么写的。"

我的经验:我在项目中遇到过一家芯片厂商,他们的HAL实现里直接操作了/dev/mem。这种做法虽然能跑,但Google CTS(兼容性测试)绝对过不了。HAL的正确做法是调用内核驱动的标准接口,而不是绕过它。

1.4 HAL的两种形态

Android HAL发展到现在,主要有两种形态。我列个表对比一下:

特性 传统HAL (Legacy HAL) HIDL HAL (Treble架构)
引入版本 Android 1.0 ~ 7.x Android 8.0+
通信方式 直接函数调用(dlopen + dlsym) Binder化IPC调用
接口定义 头文件(.h) .hal文件(自动生成代码)
升级影响 升级framework必须重新编译HAL framework和HAL可独立升级
典型模块 老版本Camera、Audio 新版本Camera、GPS、Sensors

Treble架构是Android 8.0的一次大手术。Google把HAL和framework彻底拆开,中间用HIDL(HAL Interface Definition Language)定义了一套稳定的IPC接口。这样一来,厂商的HAL实现可以独立于系统升级——Google发新版本,厂商不用重新写驱动。

注意:虽然Treble架构已经推广了好几年,但很多老设备或者IoT设备仍然在用传统HAL。作为工程师,两种形态你都得会。我们这门课会覆盖传统HAL和HIDL HAL两种实现方式。

1.5 一个典型的HAL调用链路

光说理论太枯燥。我们看一个实际例子:App打开相机预览,数据是怎么从摄像头传感器一路传到屏幕上的?

  1. App调用:Camera.open() → 进入Java Framework
  2. Framework:通过JNI调用Native层的CameraService
  3. CameraService:调用HAL接口 camera_device_t->ops->open()
  4. HAL实现:厂商的HAL库打开内核驱动的设备节点(如 /dev/video0)
  5. 内核驱动:通过I2C配置摄像头传感器,启动MIPI CSI数据流
  6. 数据回传:内核驱动→HAL→CameraService→Framework→App→SurfaceFlinger→屏幕

你看,从第1步到第3步,都是标准化的接口。从第4步开始,才是厂商的"黑盒"。HAL就是第3步和第4步之间的那个分界线。

// 传统HAL中,Camera模块的典型接口定义
// hardware/libhardware/include/hardware/camera.h

typedef struct camera_device {
    hw_device_t common;
    int (*set_preview_window)(struct camera_device *,
            struct preview_stream_ops *);
    void (*start_preview)(struct camera_device *);
    void (*stop_preview)(struct camera_device *);
    // ... 其他操作
} camera_device_t;

这段代码看起来简单,但背后涉及的东西很多。你想想看,start_preview这个函数指针背后,可能是几百行I2C配置代码,可能是复杂的ISP pipeline初始化。但上层完全不需要知道这些——这就是HAL的价值。

1.6 我的一点体会

做了这么多年底层开发,我越来越觉得HAL的设计哲学值得学习。它不追求"最优雅",而是追求"最实用"。Google知道厂商不可能把驱动代码全部开源,所以HAL给了厂商一个"黑盒"的合法位置。只要接口对,里面你随便折腾。

但同时,HAL也是一把双刃剑。如果接口设计得不好,或者厂商实现得敷衍,整个系统的稳定性和性能都会受影响。我在项目中就遇到过因为HAL实现里忘记释放锁,导致相机打开后卡死的情况。查了两天才定位到问题——嗯,这种坑后面我们会专门讲怎么避免。

本章小结:

  • Android系统分五层:应用层、框架层、运行库层、HAL层、内核层
  • HAL是用户空间和内核空间的分界线
  • HAL的核心作用是解耦和标准化
  • Treble架构让HAL和framework可以独立升级
  • HAL接口是函数指针或HIDL定义,具体实现由厂商完成