如何通过 VehicleNetworkService 获取车速、油量等实时数据?
解读
在国内车载 Android 岗位面试中,这道题考察的是候选人对 Android Automotive OS(AAOS)整车信号链路的完整理解,而不仅仅是“调个 API”。
面试官想听到的是:
- 你是否知道 VehicleNetworkService 只是 HAL 层以上、Java 服务以下的一环,真正的车速、油量信号来自车机 CAN/LIN 总线;
- 你是否能把“权限 → HAL → VehicleService → Car API → 应用”这条链路串起来,并解释每一步的进程边界、SELinux 策略、采样频率与容错;
- 你是否清楚国内项目普遍裁剪 GMS、用 AIDL 扩展自定义属性、用 dbc 文件做信号对齐,以及整车厂对安全等级(ASIL-B)的强制要求;
- 你是否能在 16 ms 帧率内完成一次信号读取并避免 Binder 阻塞主线程。
答不到“HAL 实现是 C++、权限要 android.car.permission.CAR_INFO、采样要走 CarPropertyManager 的 registerCallback 异步线程”这三点,基本会被判定为“只写过手机 App”。
知识点
- Android Automotive 架构
- Car Service:system_server 进程内,通过 CarPropertyService、CarSensorService 等封装 Vehicle HAL。
- VehicleNetworkService:部分国产方案(如德赛、华阳、博泰)把原生 Vehicle HAL 再次包装成 Java 服务,向上提供 AIDL 接口,向下通过 Socket/CAN 与 MCU 通信;严格说它不属于 AOSP 标准,但国内项目普遍这么叫。
- Vehicle HAL 2.0:AIDL 描述,属性以 int32 向量传递,支持 onChange/onSet/onGet 三种回调。
- 关键车辆属性
- 车速:VehiclePropertyIds.PERF_VEHICLE_SPEED(ID 0x11600207),单位 0.01 m/s,float[]。
- 油量:VehiclePropertyIds.FUEL_LEVEL(ID 0x11400502),单位 0.1%,float[]。
- 区域 ID:支持全局(0)或分区(座椅、空调)属性。
- 权限与 SELinux
- android.car.permission.CAR_INFO、CAR_SPEED、CAR_FUEL 必须声明;
- system/priv-app 白名单 + selinux policy:allow appdomain hal_vehicle_hwservice:find;
- 国内项目常把权限映射到整车厂自己的“诊断安全等级”证书,APK 需预置签名到 /etc/permissions/*.xml。
- 实时性保证
- 采样周期最小 50 ms(车载网关限制),应用层用 CarPropertyManager.registerCallback 指定 UPDATE_RATE_NORMAL/CYCLE;
- 禁止主线程直接 getProperty(),需抛到 HandlerThread/Executor,避免 Binder 调用阻塞渲染;
- 对 60 fps 仪表屏,采用 RenderThread + SurfaceFlinger 同步信号,确保一次绘制 < 16 ms。
- 容错与诊断
- 信号无效时 HAL 返回 StatusCode.NOT_AVAILABLE,应用需降级显示“--”;
- 支持 CarDiagnosticManager 读取 DTC,结合车速、油量突变做合理性校验(plausibility check)。
答案
第一步:声明权限并把自己加入白名单
在 AndroidManifest.xml 中
<uses-permission android:name="android.car.permission.CAR_INFO"/>
<uses-permission android:name="android.car.permission.CAR_SPEED"/>
<uses-permission android:name="android.car.permission.CAR_FUEL"/>
同时确保 APK 预置到 /system/priv-app,并在 /etc/permissions/platform.xml 中授予对应签名级权限。
第二步:获取 Car 实例
Car car = Car.createCar(context, new ServiceConnection() { ... }, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT);
CarPropertyManager mgr = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
第三步:注册异步回调读取车速、油量
Executor executor = ContextCompat.getMainExecutor(context); // 实际用 HandlerThread 更稳妥
mgr.registerCallback(
CarPropertyManager.SENSOR_RATE_NORMAL,
VehiclePropertyIds.PERF_VEHICLE_SPEED,
executor,
(propId, zone, speeds) -> {
float kmh = speeds[0] * 0.036f; // 0.01 m/s -> km/h
speedView.setText(String.format(Locale.CHINA,"%.0f", kmh));
});
mgr.registerCallback(
CarPropertyManager.SENSOR_RATE_NORMAL,
VehiclePropertyIds.FUEL_LEVEL,
executor,
(propId, zone, levels) -> {
int percent = (int) (levels[0] * 0.1f);
fuelView.setText(percent + "%");
});
第四步:处理无效信号与生命周期
@Override
protected void onStop() {
super.onStop();
mgr.unregisterCallback(...);
car.disconnect();
}
若项目里存在“VehicleNetworkService”这一国产封装,可再包一层 AIDL:
IVehicleNetworkService netService = IVehicleNetworkService.Stub.asInterface(
ServiceManager.getService("vehicle_network"));
float speed = netService.getFloatProperty(0x11600207);
但面试时要强调:这仅是整车厂自定义扩展,最终仍落回 Vehicle HAL,因此 CarPropertyManager 才是官方推荐入口,可屏蔽厂商差异。
拓展思考
- 信号安全
车速直接影响 ADAS 功能,国内法规要求 ASIL-B,HAL 实现需做冗余校验(dual CAN 信号对比),应用层也要对 0→200 km/h 跳变做 500 ms 滤波。 - 功耗与采样频率
电量计本身耗电 < 1 mA,但频繁唤醒 AP 会导致 12 V 电瓶亏电;车载系统通常把油量采样周期降到 1 Hz,行车时才升到 5 Hz,可结合 CarPowerManager 的电源状态动态调整。 - 自定义属性
国内新能源车需要电池单体电压、增程器介入标志等 300+ 新信号,需在 vhal 层扩展 AIDL enum,并在 build 时把 vts 测试用例提交给车厂过检;面试可提“用 aidl-cpp 工具自动生成桩代码,减少手写 Binder 出错概率”。 - 替代路径
若车厂未开放 VehicleNetworkService,也可走 OBD-II 蓝牙 Dongle 读取 PID 0x0D(车速)、0x2F(油量),但 Android 10 以后需 BLUETOOTH_CONNECT 权限,且 500 ms 采样延迟无法满足仪表实时要求,只能做后装娱乐。 - 未来趋势
Android 14 引入“Vehicle Hardware Abstraction as a Service”(VHAL-AIDL-S),把 HAL 搬到独立 secure VM,应用通过 VirtIO 与 HAL 通信,车速、油量将变成受控 E2E 加密信号,传统 CarPropertyManager 可能只拿到脱敏后的区间值,面试中主动提及可体现对下一代架构的敏感度。