2、系统架构初探:Android Automotive 软件栈分层详解(应用层、框架层、硬件抽象层、内核层)

好,咱们正式开始聊架构。很多初学者一上来就被「分层」两个字吓住了,觉得这东西太抽象。其实说白了,Android Automotive 的软件栈就像一栋楼,每一层各司其职,你住你的客厅,我管我的水电。今天我就带你一层层爬上去,看看每层到底在干什么。

核心观点:Android Automotive 的软件栈分为四层——应用层、框架层、硬件抽象层(HAL)、内核层。每一层只跟相邻层打交道,这种「解耦」设计,是整车系统稳定性的基石。

2.1 整体架构鸟瞰

先给你一张全景图。我习惯把 Android Automotive 的架构想象成一个「倒金字塔」:最上面是用户能摸到的应用,最下面是跟硬件打交道的内核。中间两层负责「翻译」和「调度」。

应用层 (Application Layer) 车载桌面 · 地图导航 · 媒体播放 · 车辆设置 框架层 (Framework Layer) ActivityManager · CarService · 窗口管理 · 权限控制 硬件抽象层 (HAL) Vehicle HAL · Audio HAL · Camera HAL · Sensor HAL 内核层 (Kernel Layer) Linux Kernel · 设备驱动 · 电源管理 · 内存管理 用户空间 内核空间 Java/Kotlin Java/Native C++ C

这张图我建议你多看两眼。每一层都有自己明确的职责,而且层与层之间通过定义好的接口通信。你想想看,如果应用层能直接操作 GPIO 引脚,那系统得多乱?

2.2 应用层:用户看到的一切

应用层是离用户最近的一层。说白了,你在车机上点来点去的那些界面,全在这一层。包括车载桌面(Launcher)、导航、音乐、电话、车辆设置等等。

但这里有个关键点:Android Automotive 的应用层跟手机上的应用层,写法上几乎一样。你如果写过手机 App,上手车机 App 基本没门槛。唯一的区别是,车机应用需要依赖一些 Automotive 特有的 API,比如读取车速、控制空调。

我的经验:我在做第一个车机项目时,犯过一个低级错误——直接把手机上的地图 App 搬到了车机上。结果发现,车机屏幕是横屏的,而且分辨率跟手机完全不同。嗯,从那以后我学乖了,布局文件一定要用 auto 命名空间来适配。

应用层通常用 Java 或 Kotlin 编写,运行在 Android 的运行时环境(ART)中。每个应用都是一个独立的进程,拥有自己的沙箱。这种隔离机制,保证了即使某个应用崩溃,也不会影响整个系统。

2.3 框架层:系统的「大脑」

框架层是 Android Automotive 最复杂的一层。它提供了应用开发所需的所有 API,同时也负责管理应用的声明周期、窗口、权限等。

在车机场景下,框架层有一个非常重要的组件——CarService。这是 Google 专门为汽车定制的系统服务,负责管理车辆相关的硬件抽象。比如你要获取当前车速,应用层会调用 CarService 的 API,然后 CarService 再往下跟 HAL 层通信。

框架层还包含一些你耳熟能详的组件:

  • ActivityManagerService:管理应用的启动、切换、销毁。车机上尤其要注意「前台应用」的优先级,比如导航不能被音乐 App 挤掉。
  • WindowManagerService:管理窗口的层级和显示。车机有多屏场景(中控屏、仪表盘、HUD),窗口管理比手机复杂得多。
  • PackageManagerService:管理应用的安装和卸载。车机上的应用通常预装,但也要支持 OTA 更新。

注意:框架层运行在系统进程中,一旦崩溃会导致整个系统重启。我曾经在调试一个自定义的 CarService 扩展时,因为一个空指针异常导致车机无限重启……那滋味,别提了。所以,框架层的代码一定要经过严格的单元测试和压力测试。

2.4 硬件抽象层(HAL):承上启下的「翻译官」

HAL 层是 Android 架构的精髓之一。它的作用,是把内核驱动的具体实现「抽象」成统一的接口,供上层调用。这样一来,OEM 厂商可以自由更换硬件,只要实现对应的 HAL 接口就行,上层代码完全不用改。

在 Android Automotive 中,最重要的 HAL 是 Vehicle HAL。它定义了车辆属性的读写接口,比如车速、车门状态、空调温度、转向灯等。每个 OEM 需要根据自家硬件实现这些接口。

举个例子,读取车速的流程是这样的:

// 应用层调用
Car car = Car.createCar(context);
VehiclePropertyManager vpm = car.getVehiclePropertyManager();
int speed = vpm.getInt(VehicleProperty.PERF_VEHICLE_SPEED, 0);

// 框架层通过 CarService 转发到 Vehicle HAL
// Vehicle HAL 实现(C++)
status_t VehicleHal::get(VehiclePropValuePtr& value) {
    // 读取 CAN 总线数据,解析出车速
    int can_speed = readCanBus();
    value->value.int32Values[0] = can_speed;
    return OK;
}

你看,应用层根本不知道车速是从 CAN 总线读来的,还是从以太网读来的。这就是 HAL 的威力——接口不变,实现随意换

避坑指南:我曾经遇到过一个项目,OEM 的 Vehicle HAL 实现里,读取车速时没有做线程安全处理。结果多个应用同时读取车速时,数据出现了错乱。嗯,HAL 层的并发问题,一定要在实现时就考虑好。

除了 Vehicle HAL,车机上常见的 HAL 还有:

HAL 名称 作用 典型接口
Audio HAL 管理音频输入输出 openInputStream, setParameters
Camera HAL 管理摄像头(环视、DMS) openCamera, configureStreams
Sensor HAL 管理传感器(加速度计、陀螺仪) activate, batch, poll
GNSS HAL 管理定位(GPS/北斗) startNavigation, getLocation

2.5 内核层:真正的「幕后英雄」

内核层是 Android Automotive 的根基。它直接跟硬件打交道,管理进程调度、内存分配、设备驱动、电源管理等。没有内核,上面三层全是空中楼阁。

Android Automotive 的内核基于 Linux 主线内核,但 Google 会维护一个 Android Common Kernel,加入一些 Android 特有的功能,比如:

  • Binder 驱动:Android 进程间通信(IPC)的核心。车机上,CarService 跟 Vehicle HAL 的通信就是通过 Binder 完成的。
  • ashmem:匿名共享内存,用于高效传递大块数据,比如摄像头帧数据。
  • wakelocks:电源管理锁,防止系统在关键操作(如导航播报)时进入休眠。

车机对内核的要求比手机更高。因为车机要长时间运行,不能随便重启。而且车机要连接 CAN 总线、以太网、多种传感器,这些都需要内核驱动的支持。

关键点:内核层是 OEM 定制最多的部分。每个车厂的硬件方案不同,内核驱动就得跟着改。我建议你在做内核开发时,一定要遵循 Linux 内核的编码规范,否则合入主线时会很痛苦。

2.6 层与层之间的通信机制

搞清楚了每一层是干什么的,接下来得知道它们之间怎么「说话」。Android Automotive 的层间通信,主要靠三种机制:

  1. Binder IPC:应用层 ↔ 框架层,框架层 ↔ HAL 层(通过 HIDL/AIDL)。这是最常用的通信方式,基于 Binder 驱动。
  2. Socket/管道:HAL 层 ↔ 内核层。HAL 通过打开设备文件(如 /dev/vehicle)来跟内核驱动通信。
  3. 共享内存:用于大数据传输,比如摄像头数据从内核层传到 HAL 层,再传到框架层。

你想想看,这种分层设计的好处是什么?每一层都可以独立演进。比如,OEM 想换一个更高性能的摄像头,只需要改内核驱动和 Camera HAL,应用层和框架层完全不用动。这就是「解耦」的魅力。

我的习惯:在调试跨层问题时,我通常会先在 HAL 层打日志,确认数据是否正确。如果 HAL 层的数据是对的,那问题大概率出在上层;如果 HAL 层的数据就是错的,那直接去查内核驱动。这种「分层排查法」,能帮你节省大量时间。

2.7 小结

好了,Android Automotive 的四层架构,咱们算是走了一遍。从应用层到内核层,每一层都有自己的职责和边界。记住一句话:上层只管业务逻辑,下层只管硬件操作,中间层负责翻译和调度

下一章,我们会深入框架层,重点聊聊 CarService 的设计原理和实现细节。到时候我会拿一个实际的车机功能来拆解,保证让你看得过瘾。


公众号:蓝海资料掘金营,微信deep3321