车辆信号调试 Q&A — CarService 与 VHAL
1、Android CarService 调试
1.1 辅助测试用 APP(代码仓库)
📎 附件:CarDemo4Test.apk(见下文网盘地址,亦可从 Daily Build 获取)
adb install -t -g CarDemo4Test.apk
# APP 中打开 Mock 时候点击,和命令行注入等效
# 注入雨刮速度为 间歇高速 (Body#WIPER_SPEED_MODE)
adb shell dumpsys car_service inject-vhal-event 0x63401301 1 2
# 注入雨刮维修模式为 ON (Body#WIPER_SERVICE_MODE)
adb shell dumpsys car_service inject-vhal-event 0x63401302 1 1开发人员可以直接拉取代码,来更新 App(代码仓库 更新 vehicle.support 版本即可,界面是依赖信号自动生成的)。
📷 [图片:CarDemo app 模块 build.gradle,dependencies 中 implementation ‘com.carveicle.support:0.1.1-SNAPSHOT’ 红色下划线标注]
- 其他 Mock 功能仅适用于信号通路完成前,debug 使用,切勿在车辆设备上使用。8295 上默认 Mock 关闭,pad 上默认开启。目前所有设备在开机后会保持默认状态(以防有人忘了清理配置)(提交)
# enable mock,仅为在通路不可用时,测试 APP 使用
adb shell settings put --user 0 GLOBAL vehicle_mock_data 1
# disable mock,切换至正常模式
adb shell settings put --user 0 GLOBAL vehicle_mock_data 0网盘地址(可用附件):
| 地址 | 密码 | |
|---|---|---|
| CarDemo | 名称:CarDemo4Test.apk 地址:https://kpan.mioffice.cn/webfolder/ext/IOQYWw3MAFA%40?n=0.10851838750182519 | t812 |
| Daily Build | https://cloud.mioffice.cn/#/product/file-store/objects/test-ci-package?regionId=cl1yo3fdj00000op5xhjta4xy |
1.2 通过 shell cmd 注入
# 下行 prop
# adb shell dumpsys car_service publish-vhal-event [propid] [areaid] [value]
# 例如,下发左一【areid=1】车门【prop=0x66403101】打开【value=1】
adb shell dumpsys car_service publish-vhal-event 0x66403101 1 1
# 如果信号为 global 类型,无 areaid,则 areaid 可不写,或填 0;
# 例如,打开【value=1】后视镜加热【prop= 0x61401106】
adb shell dumpsys car_service publish-vhal-event 0x61401106 1
adb shell dumpsys car_service publish-vhal-event 0x61401106 0 1
# geteventa
adb shell dumpsys car_service get-property-value 0x61401106 11.2.1 通过工具和 log 生成模拟注入命令
模拟信号脚本
📎 附件:LogPropMonitor.py(模拟信号脚本,见原文档附件)
# 解析 log,并执行模拟
python LogPropMonitor.py xxxx.log
# 将 log 中模拟命令转储为 cmd_list.sh 文件
python LogPropMonitor.py xxxx.log --save_only
# 将 log 中模拟命令转换为 cmd_list.sh 文件,并执行
python LogPropMonitor.py xxxx.log --output output.sh --exec1.3 Android CarService Log
1.3.1 推荐!!查看 Android 信号完整链路 Log(AndroidCarService + VHAL)
# 通过信号 PropID(PropID 列),如:
# 查看整车电源模式,信号 PropID 为 0x61407207
adb logcat | grep -i 0x61407207💡 如何正确找到 PropID? 可通过 DBC 定义的信号名在 SOA 表格里检索,比如: 整车电源模式的 DBC 信号名称为 “PwrModSts”,则在 SOA 表格搜索该名称,即可定位到 “Driving” sheet 中的一行,然后查看对应的 PropID 列即可。 如存在检索不到的,有可能是 SOA 表格更新不及时导致的,请联系 @宋自力 @姚鹏 @张佃伦 任何一位即可。
示例:
🍥 执行
adb logcat | grep 0x61407207显示结果:01-01 09:13:30.371 519 662 D vendor.micar.hardware.vehicle@1.0-service: [MiVehicleClient.cpp] onMiCarPropertyValue# On micar prop: {.timestamp=4402014747794, .prop=BASIC_INFO_POWER_MODE(0x61407207), .areaId=0x0, .status=AVAILABLE, .value={.int32Values = [1]{0}, .floatValues = [0]{}, .int64Values = [0]{}, .bytes = [0]{}, .stringValue = ""}} 01-01 09:13:30.379 1410 1502 I MiCarPropertyService: onPropertyChange: CarPropertyValue{name=Driving#POWER_MODE, mPropertyId=0x61407207, mAreaId=0x0, mStatus=0, mTimestamp=4402014747794, mValue=0}其中
MiVehicleClient.cpp为 VHAL 的 Log,MiCarPropertyService为 AndroidCarService 的 Log。 Log 的具体释义:1.3.2 及 2.4 章节
1.3.2 查看 AndroidCarService Log
# 使用命令
adb logcat | grep MiCarPropertyService🍥 下行 Log 例子
2022-08-29 17:53:34.050 1424-1773/com.android.car I/MiCarPropertyService: setProperty: CarPropertyValue{name=Body#COCKPIT_POWER_MODE_REPORT, mPropertyId=0x6141171a, mAreaId=0x0, mStatus=0, mTimestamp=0, mValue=[1, 0]}上行 Log 例子
2022-08-29 17:53:37.491 1424-3436/com.android.car I/MiCarPropertyService: onPropertyChange: CarPropertyValue{name=Driving#NAVIGATE_VISION_OVER_SPEED_TRIGGER, mPropertyId=0x61407709, mAreaId=0x0, mStatus=0, mTimestamp=17065965565, mValue=0}释义:
- name 表示其 Java API
- mPropertyId 和 mAreaId 是传输用的 propId 以及 areaId,均为 16 进制
- mStatus 的取值范围为 STATUS_AVAILABLE(0) / STATUS_UNAVAILABLE(1) / STATUS_ERROR(2)
- mValue 为具体值
- mTimestamp 为该信号产生的开机纳秒时间(SystemClock#elapsedRealtimeNanos)
2、Android VHAL 调试
2.1 如何替换 VHAL
⚠️ 平台差异(2026-07 更新):VHAL 已从 HIDL 迁移到 AIDL,二进制名随平台不同:
- 旧平台(E3 等,HIDL):
vendor.micar.hardware.vehicle@1.0-service- 新平台(newton / YU7 等,AIDL):
vendor.micar.hardware.vehicle@2.0-service替换前务必先确认本机实际文件名,不要照抄版本号(newton 上不存在
@1.0-service)。
adb root
adb remount
# 先确认本机 VHAL 文件名(newton=@2.0-service,旧 E3=@1.0-service)
adb shell ls -la /vendor/bin/hw/vendor.micar.hardware.vehicle@*-service
# 以下以 newton(@2.0-service)为例:
adb shell mv /vendor/bin/hw/vendor.micar.hardware.vehicle@2.0-service /vendor/bin/hw/vendor.micar.hardware.vehicle@2.0-service-bak
adb push vendor.micar.hardware.vehicle@2.0-service /vendor/bin/hw
adb shell sync
adb reboot
# 重新上电
# 或者找到 vhal 的 pid,kill 后进程会重启2.2 VHAL 调试命令(dumpsys)
⚠️ 重要变更(2026-07 更新,源码核实):新平台(newton / YU7 等)VHAL 已迁移到 AIDL,
lshal debug(HIDL 工具)已彻底失效。 实测lshal debug android.hardware.automotive.vehicle@2.0::IVehicle/...报Invalid fully-qualified name——lshal只认 HIDL 格式(pkg@ver::IFoo/inst),AIDL 接口从设计上就不走它。 调试入口改为dumpsys android.hardware.automotive.vehicle.IVehicle/micar。先做平台判定:
adb shell service list | grep automotive.vehicle # 输出 android.hardware.automotive.vehicle.IVehicle → AIDL(newton / 大多数新平台)→ 用 dumpsys # 输出 android.hardware.automotive.vehicle@2.0::IVehicle → HIDL(suiren / YU7 等老平台)→ 用 lshal
旧(HIDL,老平台) 新(AIDL,newton / YU7) lshal debug android.hardware.automotive.vehicle@2.0::IVehicle[/micar]dumpsys android.hardware.automotive.vehicle.IVehicle/micar实例名:
IVehicle/micar(micar 车控,3951 prop)/IVehicle/default(AOSP 标准,223 prop)。车控调试用/micar。 源码:子命令解析在vendor/micar/.../hal-impl/service/common/MiVehicleClient.cpp:501-516,只支持 3 个子命令。
① 读 prop(--get,自动遍历所有 area,无副作用):
# 看 service 总览(属性数 / client 数 / 接口版本)
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar
# 读单个 prop(自动列出该 prop 所有 area 的值)
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar --get 1715482881
# 读多个 prop
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar --get 1715482881 289408000② 下行设置 prop(--set,真实写入并下发到车端,谨慎使用):
# -i / -i64 / -f 接多值(向量);-s / -b / -a / -t 各 1 个
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar --set 1715482881 -i 1 -a 16
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar --set 0x11400400 -i 1 2 3 -a 1
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar --set 0x12345678 -b 0xDEADBEEF -a 1③ 上行注入伪造车端事件(--mock_from_car,不真写源,只扇出给订阅者):
# 每个 flag 只接 1 个值(多值需重复 flag);--stat 0=AVAILABLE / 1=UNAVAILABLE / 2=ERROR;timestamp 自动
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar --mock_from_car 1715482881 -i 1 -a 16 --stat 0
# bytes 数组
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar --mock_from_car 0x6170b40d -b 0x123456
# 多个 int32(重复 -i)
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar --mock_from_car 0x6141415c -i 1 -i 1⚠️
--set与--mock_from_car的-i/-f行为不同:前者接多值,后者每 flag 只接一值。-h/--help永远空输出(源码无 help 分支,未知子命令直接返回空)。 已废弃(AIDL 已删,勿用):--mock_set_enable、--log_optional_enable、--manager、--rebuild_connection。
④ dumpsys micar_property(推荐查配置,信息最全): 走框架属性层,dump 全部 ~3951 个 VehiclePropConfig(prop ID / access / area / 合法取值范围)。子命令:inject-vhal-event / get-property-value / set-property-value / list-vhal-props / get-carpropertyconfig(详见《MiCarPropertyService 接入文档》)。
⑤ 车端原始信号流:
adb shell milogcat -s CARSIGNAL # milogcat 即 logcat 封装,过滤 CARSIGNAL tag注:user 版系统下
cmd car_service的交互式 get/set 会被SecurityException拦截(需 userdebug/eng 版);VHAL 层dumpsys ... --get / --mock_from_car不受此限。
2.3 native get/set/sub 例子
2.4 Android VHAL Log
- 查看方法 1:
📷 [图片:查看 VHAL 服务进程及日志示例 — 上方
ps -e | grep micar显示 vendor.micar.hardware.vehicle@1.0-service 进程,下方logcat -pid 5407输出该服务日志含初始化信息及各节点 GRPC 服务名称]
- 查看方法 2:
# 执行命令;上行信号会过滤重复值,即只打印变化的信号
# VHAL 目前 LOG 默认等级为 D,可以更改为 V 来展示更多详情
adb shell setprop log.tag.MiVehicleClient V
adb logcat | grep -i MiVehicleClient🍥 下行例子
08-30 15:18:23.782 1053 1053 D vendor.micar.hardware.vehicle@1.0-service: [MiVehicleClient.cpp] setProperty# Set micar prop: {.timestamp=191923783154, .prop=DOOR_STATE(0x66403101), .areaId=0x10, .status=AVAILABLE, .value={.int32Values = [1]{1}, .floatValues = [0]{}, .int64Values = [0]{}, .bytes = [0]{}, .stringValue = ""}}上行例子
01-01 08:01:11.085 521 1018 V vendor.micar.hardware.vehicle@1.0-service: [MiVehicleClient.cpp] onPropertyValueReceived# Handle received prop: {.timestamp=62724916274, .prop=BASIC_INFO_POWER_MODE(0x61407207), .areaId=0x0, .status=AVAILABLE, .value={.int32Values = [1]{1}, .floatValues = [0]{}, .int64Values = [0]{}, .bytes = [0]{}, .stringValue = ""}}释义:
- .timestamp 表示其上行或下行触发时间
- .prop 表示信号名以及 propId
- .areaId 是传输用的 areaId,均为 16 进制
- .status 的取值范围为 AVAILABLE(0) / UNAVAILABLE(1) / ERROR(2)
- .value 为具体值
2.5 更多 LOG
# VHAL 收到 QNX 的消息可以通过以下命令打开,不会过滤重复信号
adb root
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/micar --log_optional_enable 1🍥
01-01 08:01:11.085 521 1018 V vendor.micar.hardware.vehicle@1.0-service: [MiVehicleClient.cpp] onPropertyValueReceived# Prop received: {.timestamp=62724916274, .prop=BASIC_INFO_POWER_MODE(0x61407207), .areaId=0x0, .status=AVAILABLE, .value={.int32Values = [1]{1}, .floatValues = [0]{}, .int64Values = [0]{}, .bytes = [0]{}, .stringValue = ""}}
3、QNX CarService
3.1 替换 carservice
adb root
adb remount
adb push carservice /mnt/nfs
adb shell sync
# 登录到 qnx
adb shell
busybox telnet 192.168.8.1
# 备份原有 carservice,然后覆盖
mv /emmc/svp/bin/carservice /emmc/svp/bin/carservice-bak
mv /var/share/carservice /emmc/svp/bin
cd /emmc/svp/bin
chmod 755 carservice
# 重新上电,或者执行以下命令进行手动启动 carservice
slay carservice # 杀掉原有进程
export LD_LIBRARY_PATH=/emmc/svp/lib:$LD_LIBRARY_PATH
./carservice3.2 carservice log
qnx 下通过下面命令抓取 carservice 日志。
💡 注意:添加日志只允许添加 debug/warn/error
# 查看 carservice 的所有 Log
tail -f /var/log/qnx/slog2/high_rate/carservice_1.log | grep carservice
# 查看具体某个信号的相关 Log
tail -f /var/log/qnx/slog2/high_rate/carservice_1.log | grep -i 0x61407207示例:
上行:
🍥
[carservice]: pps data received, messageid = 7353, function id = 254, command id = 6, data len = 3 [carservice]: [1, 0, 0] [carservice]: PipelineParser find index:VCUPrpsnPwrForDisp,raw data size:2 [carservice]: process signal:VCUPrpsnPwrForDisp [carservice]: func process--out value:65535,task size:1 [carservice]: proper id:7007742998922395648 [carservice]: PipelineParser int receive data,0,status:0 [carservice]: EnergyService publish prop=1631617313,area_id=0, value=0 [carservice]: PipelineParser find index:VCURgnPwrForDisp,raw data size:2 [carservice]: process signal:VCURgnPwrForDisp [carservice]: func process--out value:65535,task size:1 [carservice]: proper id:7007743003217362944 [carservice]: PipelineParser int receive data,0,status:0 [carservice]: EnergyService publish prop=1631617314,area_id=0, value=0 [carservice]: PipelineParser find index:VCUSpdLimDisp,raw data size:2 [carservice]: process signal:VCUSpdLimDisp [carservice]: func process--out value:255,task size:1 [carservice]: proper id:7007729800487895040 [carservice]: PipelineParser int receive data,255,status:0 [carservice]: DrivingService publish prop=0x61407520,area_id=0, value=255, status=0, value_size=1
下行:
🍥
[carservice]: ---------HandleVehicleRequest [prop = 1631614729 area_id= 0 value = 0]--------- [carservice]: transfer final signalName is:DCDTSASlifSpdDisp,value:0 [carservice]: PpsPubExecutor send to pps ok.
日志使用方式:
#include <log/logging.hpp>
INFO("start carservice process");
DEBUG("test %d",5);4、XCD 替换 MiVehicleService 方式
adb rootadb remountadb shellumount -l /system/priv-app/如果失败,重启 xcd 然后继续adb push MiVehicleService.apk /system/priv-app/MiVehicleService/- 重启