Android 14 Vehicle HAL (VHAL) 设计与实现深度解析
1. VHAL 概述与架构定位
1.1 什么是 Vehicle HAL
Vehicle HAL(VHAL)是 Android Automotive OS(AAOS)中连接车载硬件与上层 Android 框架的核心抽象层。它定义了车辆硬件属性的标准化接口,使得 CarService 及其上层的车载应用可以以统一的方式读取车辆速度、车门状态、空调设置、档位信息等,而无需关心底层硬件的具体实现差异。
┌─────────────────────────────────────────────────┐
│ 车载应用层 │
│ (导航、音乐、车辆设置、仪表盘等) │
└──────────────────────┬──────────────────────────┘
│
┌──────────────────────▼──────────────────────────┐
│ CarService │
│ ┌──────────────────────────────┐ │
│ │ CarPropertyService │ │
│ │ (属性订阅/分发/权限管理) │ │
│ └──────────┬───────────────────┘ │
└───────────────┼──────────────────────────────────┘
│
┌───────────────▼──────────────────────────────────┐
│ VehicleHalManager │
│ (VHAL 服务端管理/多 HAL 支持) │
└───────────────┬──────────────────────────────────┘
│ Binder (AIDL)
┌───────────────▼──────────────────────────────────┐
│ Vehicle HAL (VHAL) │
│ ┌──────────────────────────────────┐ │
│ │ DefaultVehicleHal │ │
│ │ ┌────────────────────────┐ │ │
│ │ │ IVehicleHardware │ │ │
│ │ └────────────────────────┘ │ │
│ └──────────────────────────────────┘ │
└───────────────┬──────────────────────────────────┘
│
┌───────────────▼──────────────────────────────────┐
│ 车载硬件层 │
│ (CAN 总线、LIN 总线、以太网、私有协议等) │
└──────────────────────────────────────────────────┘
图 1:VHAL 在 AAOS 架构中的位置
从 Android 8.0 开始,Google 将 VHAL 定义为 Android 开源项目(AOSP)的一部分。经历了从 HIDL(Android 8-13)到 AIDL(Android 12+)的演进,到 Android 14 时,AIDL 版本的 VHAL(简称 VHAL V2)已成为官方推荐和默认实现。
1.2 VHAL 的核心职责
VHAL 需要完成以下几项核心工作:
- 属性定义与暴露:定义车辆支持的所有属性(车速、车门、空调等),并告知上层框架每个属性的访问模式、数据类型、变化模式
- 属性读取:接收上层发起的属性读取请求,从硬件获取最新值并返回
- 属性写入:接收上层发起的属性写入请求(如调节空调温度),转化为硬件指令
- 事件上报:当硬件状态发生变化时(如车门打开),主动通知上层框架
- 订阅管理:管理上层对特定属性的监听注册
2. Vehicle HAL 接口层详解(IVehicle AIDL)
2.1 IVehicle.aidl 核心接口
Android 14 中 VHAL 的核心接口定义位于 packages/services/Car/car-lib/src/android/car/vehicle/IVehicle.aidl。这是与 HIDL 时代最大的区别------接口使用 Android 标准的 AIDL 定义。
aidl
interface IVehicle {
/**
* 获取车辆支持的所有属性配置信息
*/
VehiclePropConfig[] getAllProperties();
/**
* 读取指定属性的当前值
* @param property 要读取的属性 ID
* @param areaId 区域 ID(如左前门、右后门等)
*/
VehiclePropValue get(in VehiclePropValue property);
/**
* 设置属性值(异步)
* @param propValue 要设置的值
*/
void set(in VehiclePropValue propValue);
/**
* 订阅属性变化事件
* @param listener 回调接口
* @param properties 要订阅的属性列表
* @param samplingRate 采样率(毫秒)
*/
void subscribe(in IVehicleCallback listener, in VehiclePropValue[] properties, in int samplingRate);
/**
* 取消订阅
* @param listener 回调接口
* @param properties 要取消订阅的属性列表
*/
void unsubscribe(in IVehicleCallback listener, in VehiclePropValue[] properties);
/**
* 写入诊断信息(用于 dumpsys 调试)
*/
String dump(String[] options);
}
2.2 IVehicleCallback 回调接口
对应地,IVehicleCallback.aidl 定义了属性变化通知的回调:
aidl
interface IVehicleCallback {
/**
* 属性值变化通知
* @param values 发生变化的属性值列表
*/
void onPropertyEvent(in VehiclePropValue[] propValues);
/**
* 属性设置操作完成通知
* @param action 操作类型
* @param property 属性 ID
*/
void onSetCompletion(in int action, in VehiclePropValue propValue);
/**
* 错误通知
* @param errorCode 错误码
* @param property 属性 ID
* @param areaId 区域 ID
*/
void onError(in int errorCode, in int property, in int areaId);
}
2.3 Android 14 VHAL 的 AIDL 演进
从 Android 12 开始,Google 引入了 VHAL 的 AIDL 版本,与原有的 HIDL 版本并存。到 Android 14,HIDL 版本的 VHAL 已被完全移除,AIDL 成为唯一的选择。
| 特性 | VHAL V1 (HIDL) | VHAL V2 (AIDL) |
|---|---|---|
| 接口定义语言 | HIDL (.hal) | AIDL (.aidl) |
| Binder 实现 | Passthrough/Binderized | 原生 Binder |
| 包名 | android.hardware.automotive.vehicle | android.car.vehicle |
| 分区 | vendor 分区 | system 分区 |
| 支持版本 | Android 8-13 | Android 12+ |
| Android 14 可用性 | ❌ 已移除 | ✅ 默认 |
3. Vehicle Property 属性系统
3.1 Property ID 编码规则
VHAL 最核心的设计就是属性系统。每个车辆属性由一个 32 位整数唯一标识,编码规则如下:
Bit: 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ Group │ Type / Area │ Property ID │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
字段说明:
-
Group(bit 29-31):属性所属分组
VEHICLE_PROPERTY_GROUP_SYSTEM(0x10000000):系统预定义属性VEHICLE_PROPERTY_GROUP_VENDOR(0x20000000):厂商自定义属性VEHICLE_PROPERTY_GROUP_MASK(0xE0000000)
-
Type / Area(bit 24-28):
- 低 24 位属性使用高 8 位表示
VehiclePropertyType - 支持区域的属性使用高 8 位表示
VehicleArea
- 低 24 位属性使用高 8 位表示
-
Property ID(bit 0-23):属性自身编号
3.2 PropertyConfig 配置结构
每个属性通过 VehiclePropConfig 描述其能力:
java
class VehiclePropConfig implements Parcelable {
public int prop; // 属性 ID
public int access; // 访问模式
public int changeMode; // 变化模式
public int[] configArray; // 配置数组(供应商可自定义)
public String[] configString; // 字符串配置
public float minSampleRate; // 最低采样率
public float maxSampleRate; // 最高采样率
public int[] areaConfigs; // 区域配置
}
访问模式 (access):
| 常量 | 值 | 说明 |
|---|---|---|
VEHICLE_PROPERTY_ACCESS_NONE |
0x00 | 无访问权限 |
VEHICLE_PROPERTY_ACCESS_READ |
0x01 | 仅可读取 |
VEHICLE_PROPERTY_ACCESS_WRITE |
0x02 | 仅可写入 |
VEHICLE_PROPERTY_ACCESS_READ_WRITE |
0x03 | 可读写 |
变化模式 (changeMode):
| 常量 | 值 | 说明 |
|---|---|---|
VEHICLE_PROPERTY_CHANGE_MODE_STATIC |
0x00 | 静态属性(不会变化) |
VEHICLE_PROPERTY_CHANGE_MODE_ON_CHANGE |
0x01 | 变化时通知 |
VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS |
0x02 | 连续变化(周期性上报) |
VEHICLE_PROPERTY_CHANGE_MODE_POLL |
0x03 | 轮询模式 |
3.3 AreaId 区域系统
车辆中许多属性是与特定区域相关的,VHAL 使用 AreaId 来区分:
java
// 区域类型定义
public static final int VEHICLE_AREA_TYPE_GLOBAL = 0x01000000;
public static final int VEHICLE_AREA_TYPE_WINDOW = 0x02000000;
public static final int VEHICLE_AREA_TYPE_SEAT = 0x04000000;
public static final int VEHICLE_AREA_TYPE_DOOR = 0x08000000;
public static final int VEHICLE_AREA_TYPE_MIRROR = 0x0C000000;
// 具体区域示例
public static final int SEAT_ROW_1_LEFT = 0x00200001;
public static final int SEAT_ROW_1_RIGHT = 0x00200002;
public static final int WINDOW_ROW_1_LEFT = 0x00400001;
3.4 常用系统预定义属性
Android 预定义了丰富的车辆属性,分类举例如下:
java
// 信息娱乐类
INFO_MAKE = 0x0100 // 车辆品牌
INFO_MODEL = 0x0101 // 车辆型号
INFO_MODEL_YEAR = 0x0102 // 车辆年份
INFO_FUEL_CAPACITY = 0x0103 // 油箱容量
// 动力系统类
ENGINE_OIL_LEVEL = 0x0300 // 机油液位
ENGINE_OIL_TEMPERATURE = 0x0301 // 机油温度
EV_BATTERY_LEVEL = 0x0302 // 电池电量
EV_CHARGE_PORT_OPEN = 0x0303 // 充电口状态
EV_CHARGE_STATE = 0x0304 // 充电状态
// 车身控制类
DOOR_POS = 0x0500 // 车门位置
DOOR_MOVE = 0x0501 // 车门动作
DOOR_LOCK = 0x0502 // 门锁状态
MIRROR_Z_POS = 0x0600 // 后视镜位置
MIRROR_Z_MOVE = 0x0601 // 后视镜动作
SEAT_OCCUPANCY = 0x0720 // 座位占用
// HVAC 类
HVAC_FAN_SPEED = 0x0A00 // 风扇速度
HVAC_TEMPERATURE_CURRENT = 0x0A01 // 当前温度
HVAC_TEMPERATURE_SET = 0x0A02 // 设定温度
HVAC_AC_ON = 0x0A03 // 空调开关
HVAC_AIR_RECIRCULATION = 0x0A04 // 内循环
// 驾驶信息类
VEHICLE_SPEED = 0x0D00 // 车速
ODOMETER = 0x0D02 // 里程数
FUEL_LEVEL = 0x0D04 // 油量
GEAR_SELECTION = 0x0D05 // 档位
// 安全系统类
NIGHT_MODE = 0x0E00 // 夜间模式
PARKING_BRAKE_ON = 0x0E01 // 手刹状态
4. Vehicle HAL 服务启动流程
4.1 系统启动链路
VHAL 服务在 Android 系统启动过程中按以下顺序初始化:
SystemServer
↓
CarServiceHelperService
↓ (BIND_CAR_SERVICE)
CarService 进程启动
↓
CarService.java#onCreate()
↓
VehicleHalManager.java#init()
↓
DefaultVehicleHal.java#init()
↓
IVehicleHardware#init() ← 硬件层初始化
4.2 CarService 中的初始化
CarService 是车辆服务的核心,它运行在一个独立的系统进程中。其初始化代码如下(简化):
java
// packages/services/Car/service/src/com/android/car/CarService.java
public class CarService extends Service {
@Override
public void onCreate() {
super.onCreate();
// 1. 初始化 VehicleHalManager
mVehicleHalManager = new VehicleHalManager(this);
mVehicleHalManager.init();
// 2. 初始化其他 CarService 子系统
mCarPropertyService = new CarPropertyService(mVehicleHalManager);
mCarPropertyService.init();
// 3. 注册服务到 ServiceManager
ServiceManager.addService("car_service", this);
}
}
4.3 VehicleHalManager 初始化
VehicleHalManager 是 CarService 中管理 VHAL 的核心组件:
java
// packages/services/Car/service/src/com/android/car/vehiclehal/VehicleHalManager.java
public class VehicleHalManager {
private IVehicle mHal;
public void init() {
// 1. 尝试加载 AIDL 版本的 VHAL
mHal = loadVehicleHal();
// 2. 获取所有属性配置,建立属性索引
VehiclePropConfig[] configs = mHal.getAllProperties();
buildPropertyIndex(configs);
// 3. 注册系统属性订阅
registerSystemSubscriptions();
}
private IVehicle loadVehicleHal() {
// 通过 ServiceManager 获取 VHAL 服务
// 或直接实例化 DefaultVehicleHal
return new DefaultVehicleHal(getContext());
}
}
4.4 DefaultVehicleHal 实现解析
DefaultVehicleHal 是 AOSP 提供的 VHAL 参考实现,位于 packages/services/Car/service/src/com/android/car/vehiclehal/:
java
public class DefaultVehicleHal extends IVehicle.Stub {
private static final String TAG = "DefaultVehicleHal";
private final IVehicleHardware mHardware;
private final HandlerThread mWorkerThread;
public DefaultVehicleHal(Context context) {
// 创建工作线程
mWorkerThread = new HandlerThread("VHAL-Worker");
mWorkerThread.start();
// 初始化硬件抽象层
mHardware = createHardware(context);
}
private IVehicleHardware createHardware(Context context) {
// 尝试加载硬件实现,如果不可用则使用 FakeVehicleHardware
IVehicleHardware hardware = loadNativeHardware();
if (hardware == null) {
Log.w(TAG, "Native hardware not available, using fake implementation");
hardware = new FakeVehicleHardware();
}
return hardware;
}
@Override
public VehiclePropConfig[] getAllProperties() {
return mHardware.getAllProperties();
}
@Override
public VehiclePropValue get(VehiclePropValue property) {
return mHardware.get(property);
}
@Override
public void set(VehiclePropValue propValue) {
// 异步写入
mWorkerThread.getHandler().post(() -> {
try {
mHardware.set(propValue);
// 通知回调写入完成
notifySetCompletion(propValue);
} catch (Exception e) {
Log.e(TAG, "Failed to set property", e);
notifyError(propValue.prop, -1);
}
});
}
@Override
public void subscribe(IVehicleCallback callback, VehiclePropValue[] properties,
int samplingRate) {
for (VehiclePropValue prop : properties) {
mHardware.subscribe(prop.prop, samplingRate,
value -> {
try {
callback.onPropertyEvent(new VehiclePropValue[]{value});
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
});
}
}
}
5. 异步事件与回调机制
5.1 订阅生命周期
VHAL 的事件订阅遵循标准的观察者模式:
上层模块(CarService/App) VehicleHalManager DefaultVehicleHal 硬件层
│ │ │ │
│ subscribe(prop) │ │ │
├──────────────────────────►│ subscribe(callback) │ │
│ ├───────────────────────►│ register(prop) │
│ │ ├────────────────────►│
│ │ │ │
│ │ │ 硬件状态变化 │
│ │ │◄────────────────────┤
│ │ onPropertyEvent() │ │
│ onPropertyEvent() │◄───────────────────────┤ │
│◄──────────────────────────┤ │ │
│ │ │ │
│ unsubscribe(prop) │ │ │
├──────────────────────────►│ unsubscribe(prop) │ │
│ ├───────────────────────►│ unregister(prop) │
│ │ ├────────────────────►│
图 2:VHAL 事件订阅与通知流程
5.2 回调线程模型
VHAL 的回调需要考虑线程安全。DefaultVehicleHal 内部使用 HandlerThread 保证属性操作的串行化:
java
public class DefaultVehicleHal extends IVehicle.Stub {
// 回调处理器负责将硬件事件分发到各个订阅者
private final Handler mWorkerHandler;
private final Map<Integer, List<HalSubscriber>> mSubscribers = new HashMap<>();
// AIDL 回调接口的调用,注意 Binder 线程池限制
private static class HalSubscriber {
final IVehicleCallback callback;
final int samplingRate;
void onPropertyEvent(VehiclePropValue value) {
try {
callback.onPropertyEvent(new VehiclePropValue[]{value});
} catch (DeadObjectException e) {
// 订阅者已死亡,需要移除
Log.w(TAG, "Subscriber died, removing subscription");
} catch (RemoteException e) {
Log.e(TAG, "Callback transport error", e);
}
}
}
}
5.3 HalProxy 与 multi-VHAL 支持
Android 14 引入了 HalProxy 机制,使得系统可以同时管理多个 VHAL 实例(例如主 VHAL + 模拟 VHAL):
java
// HalProxy 代理多个 IVehicle 实例
public class HalProxy extends IVehicle.Stub {
private final List<IVehicle> mHalList;
@Override
public VehiclePropConfig[] getAllProperties() {
// 合并所有 HAL 的属性配置
List<VehiclePropConfig> allConfigs = new ArrayList<>();
for (IVehicle hal : mHalList) {
allConfigs.addAll(Arrays.asList(hal.getAllProperties()));
}
return allConfigs.toArray(new VehiclePropConfig[0]);
}
@Override
public void set(VehiclePropValue propValue) {
// 找到支持该属性的 HAL 并转发
for (IVehicle hal : mHalList) {
if (hal.supportsProperty(propValue.prop)) {
hal.set(propValue);
return;
}
}
throw new IllegalArgumentException("Property not supported: " + propValue.prop);
}
}
6. DefaultVehicleHal 与参考实现
6.1 IVehicleHardware 接口
IVehicleHardware 是 VHAL 中连接真实硬件的抽象层:
java
// packages/services/Car/service/src/com/android/car/vehiclehal/IVehicleHardware.java
public interface IVehicleHardware {
/**
* 初始化硬件
*/
void init();
/**
* 获取属性配置
*/
VehiclePropConfig[] getAllProperties();
/**
* 读取属性
*/
VehiclePropValue get(VehiclePropValue request);
/**
* 写入属性
*/
void set(VehiclePropValue value);
/**
* 订阅属性变化
*/
void subscribe(int propertyId, int samplingRate, PropertyEventHandler handler);
/**
* 取消订阅
*/
void unsubscribe(int propertyId);
/**
* 释放资源
*/
void release();
/**
* 属性事件处理器
*/
@FunctionalInterface
interface PropertyEventHandler {
void onEvent(VehiclePropValue value);
}
/**
* 诊断转储
*/
String dump();
}
6.2 FakeVehicleHardware 模拟实现
对于开发和测试,AOSP 提供了 FakeVehicleHardware,模拟各种车辆属性和场景:
java
public class FakeVehicleHardware implements IVehicleHardware {
private final Map<Integer, VehiclePropValue> mStore = new HashMap<>();
private final Map<Integer, List<PropertyEventHandler>> mHandlers = new HashMap<>();
private ScheduledExecutorService mScheduler;
@Override
public void init() {
// 初始化模拟属性
mStore.put(VehicleProperty.HVAC_TEMPERATURE_SET,
createProp(VehicleProperty.HVAC_TEMPERATURE_SET, 220)); // 22.0°C
mStore.put(VehicleProperty.VEHICLE_SPEED,
createProp(VehicleProperty.VEHICLE_SPEED, 0));
mStore.put(VehicleProperty.PARKING_BRAKE_ON,
createProp(VehicleProperty.PARKING_BRAKE_ON, true));
mStore.put(VehicleProperty.GEAR_SELECTION,
createProp(VehicleProperty.GEAR_SELECTION, Gear.PARK));
// 启动模拟数据更新线程
mScheduler = Executors.newSingleThreadScheduledExecutor();
simulateSpeedChanges();
}
private void simulateSpeedChanges() {
mScheduler.scheduleAtFixedRate(() -> {
// 简单模拟速度变化
int currentSpeed = (int)(Math.random() * 120);
mStore.get(VehicleProperty.VEHICLE_SPEED).value.int32Values[0] = currentSpeed;
// 通知订阅者
VehiclePropValue speedValue = mStore.get(VehicleProperty.VEHICLE_SPEED);
List<PropertyEventHandler> handlers = mHandlers.get(VehicleProperty.VEHICLE_SPEED);
if (handlers != null) {
for (PropertyEventHandler handler : handlers) {
handler.onEvent(speedValue);
}
}
}, 1, 1, TimeUnit.SECONDS);
}
@Override
public VehiclePropConfig[] getAllProperties() {
// 返回模拟属性的配置
return new VehiclePropConfig[] {
new VehiclePropConfig.Builder(VehicleProperty.VEHICLE_SPEED)
.setAccess(VehiclePropertyAccess.READ)
.setChangeMode(VehiclePropertyChangeMode.CONTINUOUS)
.build(),
new VehiclePropConfig.Builder(VehicleProperty.HVAC_TEMPERATURE_SET)
.setAccess(VehiclePropertyAccess.READ_WRITE)
.setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
.setAreaConfigs(HVAC_TEMPERATURE_SET_AREA_CONFIGS)
.build(),
// ... 更多属性
};
}
}
7. Android 14 VHAL 新特性
7.1 完整 AIDL 化
Android 14 移除了所有 HIDL 兼容代码,VHAL 完全基于 AIDL。主要变化包括:
hal_support_zone不再支持vehicleHIDL 包- VHAL 服务统一通过
ServiceManager以 AIDL 接口注册 - 移除了
android.hardware.automotive.vehicle@*的所有 HIDL 包 IVehicle接口统一使用系统分区(system)而非供应商分区(vendor)
7.2 多显示屏支持
Android 14 增强了多显示屏场景下的 VHAL 支持:
java
// 新增多显示屏相关属性
HWC_DISPLAY_TYPE_PRIMARY = 0x1E01 // 主显示屏类型
HWC_DISPLAY_TYPE_SECONDARY = 0x1E02 // 副显示屏类型
// 空调多区域细化(支持更多座位配置)
HVAC_TEMPERATURE_SET_ROW2_LEFT = 0x0A20 // 第二排左空调温度
HVAC_TEMPERATURE_SET_ROW2_RIGHT = 0x0A21 // 第二排右空调温度
7.3 新能源车(EV/HEV)属性增强
随着电动汽车的普及,Android 14 新增了大量 EV 相关属性:
java
// 电池系统
EV_BATTERY_TEMPERATURE_AVERAGE = 0x0320 // 电池平均温度
EV_BATTERY_HEALTH = 0x0321 // 电池健康度
EV_BATTERY_SOH = 0x0322 // 电池健康状态(State of Health)
EV_BATTERY_SOC_DISPLAY = 0x0323 // 显示用 SOC
// 充电系统
EV_CHARGE_CURRENT_LIMIT = 0x0330 // 充电电流限制
EV_CHARGE_VOLTAGE_LIMIT = 0x0331 // 充电电压限制
EV_CHARGE_TIME_REMAINING = 0x0332 // 剩余充电时间
EV_CHARGE_POWER_OUTPUT = 0x0333 // 充电输出功率
// 续航估算
EV_RANGE_REMAINING = 0x0340 // 剩余续航里程
EV_RANGE_REMAINING_DISPLAY = 0x0341 // 显示用剩余续航
// 能量回收
EV_REGENERATIVE_BRAKING_LEVEL = 0x0350 // 能量回收等级
EV_REGENERATIVE_BRAKING_POWER = 0x0351 // 能量回收功率
7.4 安全增强
Android 14 在 VHAL 安全性方面做了以下改进:
- 权限粒度细化 :不同属性组对应不同的系统权限,如
android.car.permission.CAR_POWERTRAIN、android.car.permission.CAR_INFO等 - VHAL 操作审核 :
set()操作需要经过 PermissionEvaluator 的权限评估 - 校验增强:属性值范围校验在 VHAL 层强制执行
- 诊断日志 :所有 VHAL 的
set()操作都会记录到系统诊断日志
java
// VehicleHalManager 中的权限检查示例
public void set(VehiclePropValue propValue) {
// 1. 权限检查
int propertyId = propValue.prop;
String permission = getRequiredPermission(propertyId);
if (permission != null) {
mContext.enforceCallingPermission(permission, "set vehicle property");
}
// 2. 安全检查:验证属性值范围
VehiclePropConfig config = getPropertyConfig(propertyId);
if (!validateValueRange(propValue, config)) {
throw new SecurityException("Property value out of range");
}
// 3. 记录操作日志
logVehicleHalAction("set", propValue);
// 4. 执行写入
mHal.set(propValue);
}
8. CarService 与 VHAL 的通信
8.1 VehicleHalManager 内部架构
VehicleHalManager 是 VHAL 的上层管理者,负责协调多个客户端对 VHAL 的访问:
java
public class VehicleHalManager {
private final Context mContext;
private IVehicle mHal; // VHAL 实例
private Map<Integer, VehiclePropConfig> mPropConfigMap; // 属性配置索引
private Map<IBinder, HalClient> mClients; // 客户端列表
private PermissionEvaluator mPermissionEvaluator;
// 属性分发流程
public void onPropertyEvent(VehiclePropValue[] values, int[] zones) {
for (VehiclePropValue value : values) {
// 1. 查找订阅该属性的所有客户端
List<HalClient> subscribers = findSubscribers(value.prop);
// 2. 权限过滤
for (HalClient client : subscribers) {
if (client.hasPermissionForProperty(value.prop)) {
client.getCallback().onPropertyEvent(
new VehiclePropValue[]{value});
}
}
// 3. 更新 CarPropertyService 缓存
mCarPropertyService.notifyPropertyChanged(value);
}
}
}
8.2 CarPropertyService 的角色
CarPropertyService 是 CarService 中暴露给应用层的属性管理服务:
车载应用 (Car App)
│
├── CarPropertyManager.getProperty(propId, areaId)
│
▼
CarPropertyService (系统服务)
│
├── 缓存管理:维护所有属性的最新值
├── 权限控制:检查调用者权限
├── 属性转换:将 VHAL 属性值转换为应用层可理解的格式
│
▼
VehicleHalManager
│
▼
IVehicle (DefaultVehicleHal)
8.3 CarService 对 VHAL 的典型调用
以下是一个完整的属性调用链路,以读取当前车速为例:
java
// 应用层
CarPropertyManager manager = (CarPropertyManager)
car.getCarManager(Car.PROPERTY_SERVICE);
// 读取瞬时车速
Float speed = manager.getFloatProperty(
VehiclePropertyIds.VEHICLE_SPEED, 0);
// CarPropertyService 内部处理
public Float getFloatProperty(int propertyId, int areaId) {
// 1. 检查调用者权限
assertPermissionForProperty(propertyId);
// 2. 尝试从缓存获取
VehiclePropValue cached = mCachedProperties.get(propertyId, areaId);
if (cached != null && !isCacheStale(propertyId)) {
return cached.value.floatValues[0];
}
// 3. 缓存失效,通过 VehicleHalManager 从 VHAL 读取
VehiclePropValue request = new VehiclePropValue.Builder(propertyId)
.setAreaId(areaId)
.build();
VehiclePropValue result = mVehicleHalManager.get(request);
// 4. 更新缓存
mCachedProperties.put(propertyId, areaId, result);
return result.value.floatValues[0];
}
8.4 属性订阅的应用层示例
java
// 应用层订阅车速变化
CarPropertyManager manager = ...;
manager.registerCallback(propCallback,
VehiclePropertyIds.VEHICLE_SPEED,
CarPropertyManager.SENSOR_RATE_NORMAL);
// 回调实现
CarPropertyEventCallback propCallback = new CarPropertyEventCallback() {
@Override
public void onChangeEvent(CarPropertyValue value) {
float currentSpeed = (float) value.getValue();
updateSpeedometer(currentSpeed);
}
@Override
public void onErrorEvent(int propertyId, int zone) {
Log.e(TAG, "Property error: " + propertyId);
}
};
9. VHAL 调试与诊断
9.1 dumpsys 诊断
Android 提供了 dumpsys 命令查看 VHAL 内部状态:
bash
# 查看 CarService 信息(包含 VHAL 状态)
adb shell dumpsys car_service
# 查看特定属性值
adb shell dumpsys car_service --vhal-property 0x0D00
# 查看属性配置
adb shell dumpsys car_service --vhal-config
# 查看订阅信息
adb shell dumpsys car_service --vhal-subscriptions
dumpsys 输出示例:
*VehicleHalManager*
HalProxy active:
- DefaultVehicleHal (AIDL)
Registered properties: 236
Active subscriptions: 12
Property 0x0D00 (VEHICLE_SPEED):
Access: READ
Change Mode: CONTINUOUS
Current Value: 65 km/h
Subscribers: 3
- CarPropertyService@0x1234
- SpeedometerApp@0x5678
Property 0x0A02 (HVAC_TEMPERATURE_SET):
Access: READ_WRITE
Change Mode: ON_CHANGE
Area Configs: [ROW_1_LEFT, ROW_1_RIGHT]
Current Value: ROW_1_LEFT=22.5°C, ROW_1_RIGHT=24.0°C
9.2 vhal_emulator 工具
Android 提供了 vhal_emulator 用于开发和测试阶段模拟车辆信号:
bash
# 启动 vhal_emulator(需要 root)
adb root
adb shell vhal_emulator
# 设置车速为 60 km/h
vhal_emulator> setProp 0x0D00 60
# 设置空调温度为 25°C
vhal_emulator> setProp 0x0A02 250
# 模拟车门打开事件
vhal_emulator> setProp 0x0502 1 --area 0x00400001
# 查看当前属性值
vhal_emulator> getProp 0x0D00
# 列出所有支持属性
vhal_emulator> list
9.3 Logcat 过滤
针对 VHAL 的日志过滤:
bash
# VHAL 核心日志
adb logcat -s VehicleHalManager DefaultVehicleHal IVehicleHardware
# CarService 属性相关日志
adb logcat -s CarPropertyService CarPropertyManager
# 详细 VHAL 日志(查看所有属性操作)
adb logcat -s VHAL:* VehicleHal:* '*:S'
9.4 常见问题诊断
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 属性读取返回 null | 属性未注册或 hardware 未初始化 | getAllProperties() 检查属性列表 |
set() 回调无响应 |
权限不足或硬件层阻塞 | 检查 logcat 中的 SecurityException |
| 事件不触发 | 订阅失败或 sampling rate 设置错误 | dumpsys car_service --vhal-subscriptions |
| Binder 异常 | 回调对象被 GC 回收 | 确保保持对 IVehicleCallback 的强引用 |
| 属性值异常 | configArray 范围配置错误 | 检查 configArray 中的 min/max 值 |
10. OEM 自定义 VHAL 实现
10.1 选择实现策略
OEM 厂商可以根据自身需求选择不同的 VHAL 实现策略:
java
// 策略一:继承 DefaultVehicleHal(推荐)
public class MyVehicleHal extends DefaultVehicleHal {
public MyVehicleHal(Context context) {
super(context);
}
@Override
protected IVehicleHardware createHardware(Context context) {
return new MyVehicleHardware(); // 自定义硬件抽象
}
}
// 策略二:完全自定义实现(适用于特殊架构)
public class CustomVehicleHal extends IVehicle.Stub {
private final VehicleHalManager mManager;
// 直接实现所有接口方法
}
// 策略三:通过 HalProxy 叠加多个 HAL
// 适合同时使用模拟 HAL + 真实 HAL 的场景
10.2 自定义 Vendor 属性
OEM 可以定义自己的供应商属性(Vendor Property):
java
// 属性 ID 必须使用 VENDOR 组
public class MyVendorProperties {
// VENDOR 组 | INT32 | VENDOR_PROPERTY_1
public static final int MY_CUSTOM_PROP_1 =
0x20000000 | (0x04 << 24) | 0x0001;
// VENDOR 组 | STRING | VENDOR_PROPERTY_2
public static final int MY_VIN_OVERRIDE =
0x20000000 | (0x10 << 24) | 0x0002;
// 属性配置
public static VehiclePropConfig createMyPropConfig() {
return new VehiclePropConfig.Builder(MY_CUSTOM_PROP_1)
.setAccess(VehiclePropertyAccess.READ_WRITE)
.setChangeMode(VehiclePropertyChangeMode.ON_CHANGE)
.setConfigArray(new int[]{0, 100}) // min=0, max=100
.build();
}
}
10.3 硬件适配最佳实践
连接真实车载硬件时需要注意:
java
public class MyVehicleHardware implements IVehicleHardware {
private final CanBusService mCanBus;
private final PropertyMapper mPropertyMapper;
@Override
public void init() {
// 1. 建立与硬件的连接
mCanBus.connect();
// 2. 注册属性到 CAN ID 的映射
// VHAL 属性 → CAN 信号
mPropertyMapper.addMapping(
VehicleProperty.VEHICLE_SPEED,
new CanSignal(0x123, 8, 16, 32)); // CAN ID, start, length
// 3. 启动 CAN 消息监听
mCanBus.addMessageListener(0x123, this::onCanMessage);
}
@Override
public VehiclePropConfig[] getAllProperties() {
// 返回实际支持的属性
return new VehiclePropConfig[] {
MyVendorProperties.createMyPropConfig(),
createSystemPropConfig(VehicleProperty.VEHICLE_SPEED),
createSystemPropConfig(VehicleProperty.HVAC_TEMPERATURE_SET),
// ... 更多属性
};
}
private void onCanMessage(CanMessage message) {
// 解码 CAN 消息为 VHAL 属性
VehiclePropValue value = mPropertyMapper.decode(message);
if (value != null) {
// 通知订阅者
notifySubscribers(value);
}
}
}
10.4 认证与兼容性要求
OEM 实现 VHAL 时需要满足以下要求:
- 必须实现至少 41 个核心系统属性,包括车速、里程、VIN、车门状态等
- 必须通过 CTS Verifier 中的车载测试用例
- 属性 ID 分配:VENDOR 组属性 ID 从 0x0001 开始分配
- 性能要求 :
get()操作应在 50ms 内返回,set()应在 100ms 内完成 - 并发安全:所有 VHAL 接口实现必须是线程安全的
11. 常见问题与最佳实践
11.1 性能优化
java
// 批量读取优化
public void readMultipleProperties(int[] propIds) {
List<VehiclePropValue> requests = new ArrayList<>();
for (int id : propIds) {
requests.add(new VehiclePropValue.Builder(id).build());
}
// 最佳实践:批量调用而非多次 get()
// 但 IVehicle 目前不支持批量 get,需要在 HAL 层自行优化
for (VehiclePropValue request : requests) {
// 可以考虑在 HAL 层实现缓存
VehiclePropValue result = mHal.get(request);
processResult(result);
}
}
11.2 线程安全
java
public class ThreadSafeVehicleHardware implements IVehicleHardware {
private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
private final Map<Integer, VehiclePropValue> mValues = new HashMap<>();
@Override
public VehiclePropValue get(VehiclePropValue request) {
mLock.readLock().lock();
try {
VehiclePropValue value = mValues.get(request.prop);
if (value == null) {
// 从硬件读取
value = readFromHardware(request);
mValues.put(request.prop, value);
}
return deepCopy(value); // 返回副本防止并发修改
} finally {
mLock.readLock().unlock();
}
}
@Override
public void set(VehiclePropValue value) {
mLock.writeLock().lock();
try {
writeToHardware(value);
mValues.put(value.prop, value);
notifySubscribers(value);
} finally {
mLock.writeLock().unlock();
}
}
}
11.3 版本兼容
对于需要同时支持多个 Android 版本的 OEM:
java
public class CompatVehicleHal extends DefaultVehicleHal {
@Override
protected IVehicleHardware createHardware(Context context) {
String halType = SystemProperties.get("ro.vhal.type", "aidl");
if ("aidl".equals(halType)) {
// Android 12+ AIDL 模式
return new AidlVehicleHardware();
} else {
// 向后兼容模式
return new CompatVehicleHardware();
}
}
}
11.4 开发调试建议
- 优先使用 FakeVehicleHardware 进行应用层开发调试
- 使用 vhal_emulator 模拟特定车辆场景
- 编写 VHAL 单元测试:使用 Mockito 模拟硬件层
- 属性变更日志 :在
set()操作中打印详细的属性变化日志 - 监控 Binder 调用频率:避免高频属性订阅导致 Binder 缓冲区溢出
12. 总结与展望
12.1 VHAL 设计总结
VHAL 通过属性抽象 、异步事件 和分层设计三大核心思想,成功实现了车载硬件与 Android 框架的解耦:
- 属性抽象:统一的 Property ID 编码体系,将千差万别的车辆信号标准化
- 异步事件:通过订阅/通知机制,避免了轮询带来的性能浪费
- 分层设计:从 ColorService → VehicleHalManager → DefaultVehicleHal → IVehicleHardware 的四层架构,每层职责清晰
12.2 未来演进趋势
随着汽车电子电气架构的演进,VHAL 也在持续进化:
-
更细粒度的属性划分:随着 SDV(软件定义汽车)的发展,未来会有更多的软件定义的车辆属性(如自动驾驶模式、场景模式等)
-
更高的实时性要求:对于 ADAS 辅助驾驶相关的属性,需要更低延迟的通信通道,可能引入更多零拷贝机制
-
云端 VHAL:车辆上云场景下,部分车辆属性可能通过云边协同的方式获取
-
跨域融合:随着中央计算平台 + 区域控制器架构的普及,VHAL 可能需要管理更复杂的跨 ECU 属性
12.3 参考资料
- Android Vehicle HAL 官方文档
- AOSP IVehicle.aidl 源码
- AOSP DefaultVehicleHal 源码
- VehiclePropertyIds 定义
- Android Automotive 开发者指南
- Compatibility Test Suite (CTS) 车载测试
作者简介:Android 车载系统开发者,专注于 AAOS 平台架构与车载系统开发。欢迎在评论区交流讨论。
版权声明:本文为原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。