Android 14 Vehicle HAL (VHAL) 设计与实现深度解析

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
  • 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 不再支持 vehicle HIDL 包
  • 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_POWERTRAINandroid.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 时需要满足以下要求:

  1. 必须实现至少 41 个核心系统属性,包括车速、里程、VIN、车门状态等
  2. 必须通过 CTS Verifier 中的车载测试用例
  3. 属性 ID 分配:VENDOR 组属性 ID 从 0x0001 开始分配
  4. 性能要求get() 操作应在 50ms 内返回,set() 应在 100ms 内完成
  5. 并发安全:所有 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 开发调试建议

  1. 优先使用 FakeVehicleHardware 进行应用层开发调试
  2. 使用 vhal_emulator 模拟特定车辆场景
  3. 编写 VHAL 单元测试:使用 Mockito 模拟硬件层
  4. 属性变更日志 :在 set() 操作中打印详细的属性变化日志
  5. 监控 Binder 调用频率:避免高频属性订阅导致 Binder 缓冲区溢出

12. 总结与展望

12.1 VHAL 设计总结

VHAL 通过属性抽象异步事件分层设计三大核心思想,成功实现了车载硬件与 Android 框架的解耦:

  • 属性抽象:统一的 Property ID 编码体系,将千差万别的车辆信号标准化
  • 异步事件:通过订阅/通知机制,避免了轮询带来的性能浪费
  • 分层设计:从 ColorService → VehicleHalManager → DefaultVehicleHal → IVehicleHardware 的四层架构,每层职责清晰

12.2 未来演进趋势

随着汽车电子电气架构的演进,VHAL 也在持续进化:

  1. 更细粒度的属性划分:随着 SDV(软件定义汽车)的发展,未来会有更多的软件定义的车辆属性(如自动驾驶模式、场景模式等)

  2. 更高的实时性要求:对于 ADAS 辅助驾驶相关的属性,需要更低延迟的通信通道,可能引入更多零拷贝机制

  3. 云端 VHAL:车辆上云场景下,部分车辆属性可能通过云边协同的方式获取

  4. 跨域融合:随着中央计算平台 + 区域控制器架构的普及,VHAL 可能需要管理更复杂的跨 ECU 属性

12.3 参考资料


作者简介:Android 车载系统开发者,专注于 AAOS 平台架构与车载系统开发。欢迎在评论区交流讨论。

版权声明:本文为原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

相关推荐
AFinalStone8 小时前
Android 7系统网络(八)应用API层—ConnectivityManager使用与实战调试
android·网络
plainGeekDev8 小时前
kapt 替换为 KSP
android·java·kotlin
蜡台9 小时前
通过Gradle脚本声明更改Java变量
android·java·开发语言·python·kotlin·gradle·groovy
阿pin12 小时前
Android随笔-SharedPreferences
android·sp
阿pin12 小时前
Android随笔-DataStore
android·datastore
茶栀(*´I`*)13 小时前
Android 测试入门指南:ADB 基础配置与常用设备管理命令解析
android·adb
ihuyigui14 小时前
海外酒店预订短信接口
android·java·运维·服务器·前端·后端·架构
gxgldyh15 小时前
Android Framework源码解析(七):BootAnimation 启动流程解析——开机动画是如何显示出来的?
android
达达尼昂16 小时前
在 Claude Cowork 中用好 Claude Fable 5
android·人工智能·后端