4、CarService 核心服务:CarService的启动流程、CarService API、Vehicle HAL 接口
好,我们直接进入正题。CarService 是整个 Android Automotive OS 的“心脏”。你想想看,车机上所有的硬件——空调、车窗、方向盘按键、车门锁——最终都要通过它来和上层 App 沟通。说白了,没有 CarService,你的车载系统就是个普通平板。
这一节,我会带你拆解三个核心问题:CarService 是怎么启动的?它暴露了哪些 API?以及它怎么跟底层的 Vehicle HAL 打交道? 这三个问题搞清楚了,你对整个车载系统的架构就有了骨架级的认知。
4.1 CarService 的启动流程
CarService 不是一个普通的 Android Service。它是在系统启动早期,由 SystemServer 主动拉起来的。我记得第一次看这个流程时,找了半天没找到 AndroidManifest.xml 里的声明——因为它根本就不是通过 PackageManager 启动的。
4.1.1 启动入口:SystemServer
在 SystemServer.java 的 run() 方法里,有一个专门的阶段叫 startCarService()。代码大致长这样:
// frameworks/base/services/java/com/android/server/SystemServer.java
private void startCarService() {
if (!isAutomotive()) {
return; // 非车机设备直接跳过
}
try {
// 加载 CarService 的 jar 包
Class<?> clazz = Class.forName("com.android.car.CarService");
Service carService = (Service) clazz.newInstance();
// 绑定到 SystemServer 的 ServiceManager
ServiceManager.addService("car_service", carService);
} catch (Exception e) {
Slog.e(TAG, "Failed to start CarService", e);
}
}
嗯,这里要注意:CarService 的 jar 包是预编译在 /system/framework/ 下的,不经过普通的 APK 安装流程。这样做的好处是——启动速度极快,不依赖 PackageManager 的扫描。
4.1.2 CarService 内部初始化
一旦 CarService 被实例化,它的 onCreate() 方法会做几件关键的事:
- 加载 Vehicle HAL 库:通过 JNI 调用
android.hardware.automotive.vehicle@2.0或更高版本的 HAL。 - 启动 VehicleHal 线程:这是一个独立的 native 线程,负责轮询车辆硬件事件。
- 注册各个子服务:比如
CarPropertyService、CarSensorService、CarAudioService等。 - 通知系统就绪:通过
SystemStateListener告知 SystemServer:“我准备好了,上层可以开始调用了。”
核心要点:CarService 的启动是同步阻塞的。SystemServer 会等它初始化完成,才继续启动其他系统服务。所以如果 Vehicle HAL 挂了,整个车机都会卡在启动动画上。我在项目中遇到过供应商提供的 HAL 实现有内存泄漏,导致每次冷启动都要等 30 秒以上——排查起来非常痛苦。
4.2 CarService API 体系
CarService 暴露的 API 分为两大类:系统级 API(标记为 @SystemApi)和 公开 API(标记为 @CarApi)。前者只有系统应用能调用,后者第三方应用也能用。
4.2.1 核心 API 一览
| API 名称 | 功能描述 | 访问级别 |
|---|---|---|
CarPropertyManager |
读写车辆属性(车速、里程、车门状态等) | 系统 + 第三方 |
CarSensorManager |
获取传感器数据(加速度、陀螺仪、GPS 等) | 系统 + 第三方 |
CarAudioManager |
控制音频焦点、音量、音区 | 系统 |
CarHvacManager |
空调控制(温度、风量、风向) | 系统 |
CarDiagnosticManager |
读取车辆诊断信息(OBD-II 相关) | 系统 |
我个人习惯把 CarPropertyManager 称为“万能 API”。为什么?因为几乎所有车辆状态都可以通过它来读写。你想想看,车速、车门锁、车窗位置、甚至方向盘角度——底层都是通过 Property ID 来区分的。
4.2.2 使用示例:读取车速
// 获取 Car 实例
Car car = Car.createCar(context);
// 等待连接
car.connect();
// 获取 PropertyManager
CarPropertyManager propertyManager =
(CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
// 读取车速(单位:km/h)
CarPropertyValue<Float> speedValue =
propertyManager.getProperty(Float.class, VehiclePropertyIds.PERF_VEHICLE_SPEED, 0);
float speed = speedValue.getValue();
Log.d(TAG, "当前车速: " + speed + " km/h");
避坑指南:我曾经在读取车速时忘了检查 speedValue 是否为 null。结果在车辆未启动(HAL 未就绪)时直接 crash 了。记住:所有 getProperty 调用都要判空,因为 HAL 可能返回 Status.NOT_AVAILABLE。
4.3 Vehicle HAL 接口
Vehicle HAL 是 CarService 和硬件之间的“翻译官”。它定义在 hardware/interfaces/automotive/vehicle/ 目录下,使用 HIDL 或 AIDL 描述。
4.3.1 HAL 的核心数据结构
所有车辆数据都通过 VehiclePropValue 结构体传递:
// VehiclePropValue 的核心字段
struct VehiclePropValue {
int32_t propId; // 属性 ID,例如 VEHICLE_SPEED = 0x2910
int32_t areaId; // 区域 ID,例如左前门 = 0x01
int32_t status; // 状态:AVAILABLE, UNAVAILABLE, ERROR
int64_t timestamp; // 时间戳(纳秒)
union {
int32_t int32Values[];
float floatValues[];
int64_t int64Values[];
bytes stringValue;
} value; // 实际数据
};
为什么要有 areaId?因为一辆车可能有四个车门、两个空调区域。你想想看,设置左前车窗和右后车窗,用的 propId 相同,但 areaId 不同。这个设计非常巧妙——用一套 API 覆盖了所有多区域设备。
4.3.2 HAL 的两种工作模式
Vehicle HAL 支持两种数据传递方式:
- 订阅模式(subscribe):CarService 注册一个回调,HAL 在数据变化时主动推送。适合车速、转速等高频变化的数据。
- 轮询模式(get/set):CarService 主动调用 HAL 的
get()或set()方法。适合车门状态、空调设置等低频操作。
我个人建议:能用订阅就别用轮询。我曾经接手过一个项目,前任工程师用轮询方式每秒读 10 次车速,结果 CPU 占用率直接飙到 30%。改成订阅后,降到 2% 不到。
4.3.3 HAL 接口定义示例(AIDL 版本)
// IVehicle.aidl
interface IVehicle {
// 获取属性值
VehiclePropValue get(int32_t propId, in GetValueRequest request);
// 设置属性值
Status set(in SetValueRequest request);
// 订阅属性变化
Status subscribe(in ISubscriptionCallback callback,
in SubscribeOptions options);
// 取消订阅
Status unsubscribe(in ISubscriptionCallback callback,
int32_t propId);
}
重要提醒:HAL 接口的调用是跨进程的。每次 get() 或 set() 都会触发一次 Binder 调用。如果你在 UI 线程频繁调用,ANR 是迟早的事。我建议所有 HAL 操作都放到后台线程,或者使用 CarPropertyManager 提供的异步回调版本。
4.4 整体架构图
下面这张图展示了 CarService、API 层和 Vehicle HAL 之间的关系。我特意把数据流向画了出来,方便你理解。
从上图你可以看到,数据流是单向的:App 通过 CarService API 调用 CarService,CarService 再通过 Vehicle HAL 与硬件交互。反过来,硬件事件也是通过 HAL 回调 -> CarService -> API -> App 这条路径传递的。
4.5 小结
这一节我们聊了三个核心话题:
- CarService 启动流程:由 SystemServer 在启动早期直接加载,不依赖 PackageManager。
- CarService API:分为系统级和公开级,
CarPropertyManager是最常用的万能 API。 - Vehicle HAL 接口:通过
VehiclePropValue结构体传递数据,支持订阅和轮询两种模式。
嗯,说实话,这三个知识点是车载开发的“三座大山”。翻过它们,后面的路就好走了。如果你在实际开发中遇到 CarService 启动失败或者 HAL 调用超时的问题,欢迎随时交流——我在这些坑里摔过不少次,经验还算丰富。
一句话总结:CarService 是桥梁,API 是桥面,HAL 是桥墩。桥面坏了可以修,桥墩塌了——嗯,那就要找供应商了。