3、Android Automotive系统基础:Android Automotive架构概览、HAL层与AIDL接口、CarService与Vehicle HAL、Android系统服务与驱动交互

好,我们进入第三章。这一章,咱们聊聊Android Automotive的系统基础。

说实话,很多做Linux BSP的兄弟刚转过来时,最懵的就是这个。明明是个Android,怎么跟车打交道的?车载的HAL跟手机HAL有啥区别?CarService又是个什么鬼?

别急,我一个一个给你拆开讲。

3.1 Android Automotive架构概览

先看整体架构。Android Automotive本质上还是Android,但它多了一层专门跟车打交道的框架。

我习惯把整个系统分成四层来看:

  • 应用层:包括车载Launcher、设置、空调控制、地图导航等。这些App通过标准Android API和Car API来访问车辆数据。
  • 框架层:核心是CarService,它管理所有车辆相关的服务。还有CarPropertyManager、CarSensorManager等API。
  • HAL层:Vehicle HAL是专门为汽车定义的硬件抽象层。它定义了车辆属性和信号的标准接口。
  • 内核层:驱动、CAN总线、GPIO、I2C等。这部分跟QNX或Linux BSP基本一致。

嗯,这里要注意:Android Automotive的HAL层跟手机Android的HAL有本质区别。手机HAL是面向设备的(Camera、Audio、GPS),而Vehicle HAL是面向信号的(车速、车门状态、空调温度)。

核心观点:Vehicle HAL不是用来控制某个硬件外设的,它是用来收发车辆信号的。这是理解整个架构的关键。

我画了一张架构图,你看一眼就明白了。

应用层 车载Launcher | 空调控制 | 仪表盘 | 地图导航 框架层 (CarService) CarPropertyManager | CarSensorManager | CarAudioManager AIDL接口 | 权限管理 | 车辆属性订阅 HAL层 (Vehicle HAL) IVehicle HAL接口 | 属性定义 | 信号订阅/发布 内核层 / 硬件层 CAN驱动 | SPI/I2C | GPIO | 电源管理 QNX BSP / Linux Kernel

3.2 HAL层与AIDL接口

好,咱们深入一层。HAL层在Android Automotive里是怎么定义的?

Android从8.0开始推Treble架构,HAL接口用HIDL定义。到了Android 11之后,Google开始全面转向AIDL。现在的新项目,基本都是AIDL HAL了。

我个人建议:新项目直接上AIDL。别再用HIDL了,那玩意儿迟早被淘汰。

一个典型的Vehicle HAL AIDL接口长这样:

// IVehicle.aidl
package android.hardware.automotive.vehicle;

interface IVehicle {
    // 获取所有支持的车辆属性列表
    VehiclePropConfig[] getAllPropConfigs();
    
    // 获取指定属性的值
    StatusCode get(VehiclePropValue request, inout VehiclePropValue result);
    
    // 设置指定属性的值
    StatusCode set(VehiclePropValue value);
    
    // 订阅属性变化
    StatusCode subscribe(IVehicleCallback callback, 
                         in VehiclePropValue[] propValues, 
                         float sampleRate);
    
    // 取消订阅
    StatusCode unsubscribe(IVehicleCallback callback, 
                           in int[] propIds);
}

你看,这个接口定义得很干净。get/set是同步操作,subscribe/unsubscribe是异步通知。实际项目中,大部分车辆信号都是通过subscribe来获取的,比如车速、转速、车门状态。

实战经验:我在项目中遇到过一个问题——订阅车速信号时,采样率设得太高(100Hz),结果CPU占用率飙升。后来发现,车速信号10Hz就够用了。别盲目追求高采样率,够用就行。

再说说VehiclePropValue这个结构体。它定义了车辆属性的值:

// VehiclePropValue结构体
struct VehiclePropValue {
    int32_t propId;          // 属性ID,比如车速是0x11100200
    int32_t areaId;          // 区域ID,比如左前门、右前门
    int64_t timestamp;       // 时间戳,纳秒
    VehiclePropValueType value; // 值类型:int32, float, string, bytes等
    int32_t status;          // 状态:AVAILABLE, UNAVAILABLE, ERROR
};

嗯,这里要注意propId的编码规则。Google定义了一套标准属性ID,比如:

属性名 属性ID 类型 说明
VEHICLE_SPEED 0x11100200 FLOAT 车速,单位km/h
ENGINE_RPM 0x11100300 FLOAT 发动机转速
DOOR_POSITION 0x11400200 INT32 车门位置,0=关闭,1=打开
HVAC_TEMPERATURE_SET 0x12400100 FLOAT 空调设定温度

这些ID是Google在hardware/interfaces/automotive/vehicle/里定义好的。你想想看,如果车厂要加自定义属性,怎么办?Google留了厂商自定义范围:0x20000000 ~ 0x2FFFFFFF。

避坑指南:我曾经在项目里直接用了0x20000001作为自定义属性ID,结果跟另一个模块冲突了。后来我们统一在文档里登记所有自定义ID,避免重复。建议你也这么做。

3.3 CarService与Vehicle HAL

CarService是Android Automotive框架层的核心。它运行在system_server进程里,负责管理所有车辆相关的服务。

说白了,CarService就是应用层和HAL层之间的桥梁。它做了三件事:

  1. 启动时加载Vehicle HAL:通过JNI调用HAL的open()方法,拿到IVehicle接口。
  2. 管理车辆属性:维护一个属性缓存池,处理get/set请求,管理订阅者列表。
  3. 权限检查:不是所有App都能读写车辆属性。CarService会检查调用方的权限。

CarService启动流程大致是这样的:

// CarService启动流程(简化版)
1. SystemServer启动CarServiceHelperService
2. CarServiceHelperService启动CarService(独立进程)
3. CarService初始化Vehicle HAL:
   - 加载libvehiclenative.so
   - 调用VehicleHalClient::init()
   - 获取IVehicle HAL接口
4. CarService注册所有标准车辆属性
5. 启动属性订阅循环(轮询或事件驱动)
6. 对外暴露CarPropertyManager等API

我记得第一次看这个流程时,有个地方让我困惑了很久——为什么CarService要单独跑一个进程?后来想明白了:如果CarService跟system_server同进程,一旦CarService崩溃,整个系统就挂了。独立进程可以做到故障隔离。

3.4 Android系统服务与驱动交互

最后,咱们聊聊驱动怎么跟系统服务交互。

在Android Automotive里,驱动跟系统服务的交互路径是这样的:

  • 驱动层:通过Linux内核的CAN子系统、GPIO子系统、I2C等,读取硬件信号。
  • HAL实现层:Vehicle HAL的实现(so库)通过ioctl、read/write、netlink等方式跟驱动通信。
  • HAL接口层:通过AIDL定义的IVehicle接口,把数据传给CarService。
  • 框架层:CarService把数据分发给订阅的App。

举个例子,车速信号的完整链路:

// 车速信号链路
CAN总线 → CAN驱动 → can_utils → Vehicle HAL实现 → 
IVehicle::get(VEHICLE_SPEED) → CarService → 
CarPropertyManager → 仪表盘App显示车速

你想想看,这条链路里哪个环节最容易出问题?

我个人经验是:HAL实现层。因为驱动层和框架层都有成熟的测试工具,但HAL实现层往往是车厂自己写的,bug最多。

调试技巧:当车速信号不准时,先别急着查驱动。用dumpsys car_service命令看看CarService里缓存的车速值是多少。如果缓存值是对的,那就是App的问题;如果缓存值不对,再往下查HAL实现。

还有一个常见问题:驱动上报的数据格式跟HAL定义的不一致。比如驱动上报的车速是km/h的整数,但HAL定义的是float类型。这时候HAL实现层要做单位转换。

我曾经遇到过一个坑:某个车厂的CAN总线车速单位是mph,但HAL定义的是km/h。HAL实现层忘了做转换,结果仪表盘显示的车速比实际快了60%。嗯,这种低级错误,代码审查时一定要盯紧。

好了,这一章的内容就这些。Android Automotive的架构其实不复杂,关键是要理解Vehicle HAL的设计思想——它是面向信号的,不是面向设备的。把这个想通了,后面写驱动、调HAL就顺了。