EVCC AIDL / 米德宝联动 v1 协议¶
最后更新:
适用于受信任应用通过 EVCC 导出的 Binder Service 订阅车辆属性和 EVCC 变量。
当前协议版本:
1最后按源码核对:2026-08-27
1. 文档范围¶
本文档描述:
- EVCC 如何对外暴露米德宝联动 Binder Service;
- 两个 AIDL 接口及其 Binder ABI;
- 银河悬浮歌词、仪表 DIY、AutoDisplay 与自定义受信任应用的绑定、握手、初始同步和回调流程;
- AIDL 数据配置、导入、导出和多调用方开关;
- JSON 快照的字段、类型和兼容行为;
- 推送时机、断线恢复、调用方鉴权和协议升级约束;
- 当前实现已知的边界和风险。
本文档不把以下两条链路视为同一协议:
- 车机厂商的
ecarxcar_service/car_signalAIDL。它是车辆属性进入 EVCC 的数据源之一。 - 米德宝 BLE 直发模式。该模式使用 BLE 广播、GATT 或 MIBO 离线转发,不使用本文的 JSON 属性快照协议。
2. 角色与端点¶
| 角色 | 应用 | 包名 | 职责 |
|---|---|---|---|
| 服务端 | EVCC | com.kooo.evcc |
采集属性、生成快照、提供 Binder Service |
| 米德宝客户端 | 银河悬浮歌词 | com.galaxy.lyricsnext |
在“米德宝 AIDL 联动”开启时接收快照 |
| 内置自定义客户端 | 仪表 DIY | com.kooo.instrumentdiy |
在“旧版 AIDL 联动”开启时接收快照 |
| 内置自定义客户端 | AutoDisplay | com.simplerbit.autodisplay |
在“旧版 AIDL 联动”开启时接收快照 |
| 可配置客户端 | 用户指定应用 | 管理页配置 | 在“旧版 AIDL 联动”开启时按包名和签名接收快照 |
EVCC Service:
Class: com.kooo.evcc.midebao.MidebaoLinkBridgeService
Action: com.kooo.evcc.midebao.BIND
Exported: true
Manifest 源码:app/src/main/AndroidManifest.xml
客户端使用带包名限定的 Action 绑定:
Intent intent = new Intent("com.kooo.evcc.midebao.BIND");
intent.setPackage("com.kooo.evcc");
bindService(intent, connection, Context.BIND_AUTO_CREATE);
3. 整体数据链路¶
flowchart LR
A["VHAL / APVP / 车机 AIDL / CAPI / AdaptAPI / SOA"] --> B["EVCC 统一 Property"]
V["EVCC 自动化变量"] --> C["米德宝项目匹配"]
B --> C
C --> D["提取 rawValue / map 映射 / 去重"]
D --> E["v1 全量 JSON 快照"]
E --> F["IMidebaoLinkBridge"]
F --> G["受信任 AIDL 客户端"]
G --> H["数据面板 / 规则判断 / 客户端自有动作"]
如果某个属性恰好来自车机 AIDL,整体会经过两次 Binder 跨界:
车机 ecarxcar_service
-> EVCC AidlManager
-> EVCC 统一 Property
-> MidebaoLinkageManager
-> EVCC IMidebaoLinkBridge
-> 银河悬浮歌词
其他属性源只会经过最后一次跨应用 AIDL。
4. AIDL 接口定义¶
4.1 主接口¶
客户端通过主接口读取 EVCC 状态并注册回调。
package com.kooo.evcc.midebao;
import com.kooo.evcc.midebao.IMidebaoLinkCallback;
interface IMidebaoLinkBridge {
int getProtocolVersion();
String getStatus();
String getSnapshotJson();
void registerCallback(IMidebaoLinkCallback callback);
void unregisterCallback(IMidebaoLinkCallback callback);
}
源码:shared/src/main/aidl/com/kooo/evcc/midebao/IMidebaoLinkBridge.aidl
4.2 回调接口¶
EVCC 通过回调接口主动通知客户端。
package com.kooo.evcc.midebao;
oneway interface IMidebaoLinkCallback {
void onSnapshotChanged(String snapshotJson);
void onStatusChanged(String status);
}
源码:shared/src/main/aidl/com/kooo/evcc/midebao/IMidebaoLinkCallback.aidl
EVCC 与每个客户端保存的 .aidl 必须保持一致。
5. Binder ABI¶
5.1 主接口 ABI¶
接口描述符:
| 方法 | Transaction code | 调用方式 | Parcel 输入 | Parcel 返回 |
|---|---|---|---|---|
getProtocolVersion() |
FIRST_CALL_TRANSACTION + 0 |
同步 | 无 | int |
getStatus() |
+1 |
同步 | 无 | String |
getSnapshotJson() |
+2 |
同步 | 无 | String |
registerCallback(callback) |
+3 |
同步 | Callback Binder | 无 |
unregisterCallback(callback) |
+4 |
同步 | Callback Binder | 无 |
所有主接口方法均使用普通同步 transact(..., flags = 0):
- Proxy 创建请求 Parcel 和回复 Parcel;
- 请求 Parcel 首先写入接口描述符;
- Stub 使用
enforceInterface()校验描述符; - 成功时 Stub 写入
writeNoException()和返回值; SecurityException和RemoteException可通过 Binder 回传。
5.2 回调接口 ABI¶
回调接口描述符:
| 方法 | Transaction code | 调用方式 | Parcel 输入 |
|---|---|---|---|
onSnapshotChanged(snapshotJson) |
FIRST_CALL_TRANSACTION + 0 |
FLAG_ONEWAY |
String |
onStatusChanged(status) |
+1 |
FLAG_ONEWAY |
String |
oneway 含义:
- EVCC 只写请求 Parcel,不创建回复 Parcel;
- EVCC 不等待客户端完成 JSON 解析或规则执行;
- 协议层没有 ACK,单次回调失败不会重发该消息。
6. 绑定与握手时序¶
sequenceDiagram
participant G as "受信任 AIDL 客户端"
participant A as "Android Service"
participant E as "EVCC Bridge"
G->>A: bindService(action + package)
A->>E: onCreate / onBind
E-->>G: IBinder
G->>E: getProtocolVersion()
E-->>G: 1
G->>E: registerCallback(callback Binder)
E--)G: onStatusChanged(currentStatus)
E--)G: onSnapshotChanged(currentSnapshot)
E-->>G: registerCallback 返回
G->>E: getSnapshotJson()
E-->>G: 当前全量 JSON
loop 属性或变量发生变化
E--)G: onSnapshotChanged(fullSnapshot)
end
银河悬浮歌词参考客户端当前的具体顺序是:
- 将
IBinder转换为IMidebaoLinkBridge; - 调用
getProtocolVersion(); - 调用
registerCallback(); - 调用
getSnapshotJson()主动拉取当前快照; - 之后依赖
onSnapshotChanged()接收变化。
registerCallback() 内部会立即发送一次状态和快照,客户端随后又主动拉取一次。因此首次连接通常会处理两份内容相同或接近的快照。
其他客户端可以只依赖 registerCallback() 的首次推送,也可以在注册后再调用 getSnapshotJson()。无论采用哪种方式,都应把快照当作全量状态并允许重复处理。
7. 主接口方法语义¶
7.1 getProtocolVersion()¶
当前固定返回:
它表示跨应用快照协议版本,不是 EVCC 应用版本,也不是车机 AIDL 版本。
当前客户端只把该值写入日志,尚未强制验证 version == 1。
7.2 getStatus()¶
返回人类可读的运行状态,例如:
该字段是用户可读文本,不是稳定枚举。客户端不应通过匹配具体中文来决定业务逻辑。
7.3 getSnapshotJson()¶
返回 EVCC 内存中最新的一份全量快照。
该方法:
- 不会因为本次调用而强制刷新车辆属性;
- 不等待上游属性源完成连接;
- 适用于首次同步、断线恢复和调试查询。
7.4 registerCallback(callback)¶
服务端将 Callback Binder 注册到 RemoteCallbackList,然后立即调用:
onStatusChanged(statusText);onSnapshotChanged(snapshotJson)。
RemoteCallbackList 可在客户端 Binder 死亡后清理失效回调。
7.5 unregisterCallback(callback)¶
客户端正常停止时主动注销。如果客户端异常退出,Binder 死亡后也会由 RemoteCallbackList 清理。
服务端实现:app/src/main/java/com/kooo/evcc/midebao/MidebaoLinkageManager.kt
8. JSON 快照协议¶
8.1 正常运行快照¶
{
"version": 1,
"enabled": true,
"updatedAt": 1786075800000,
"items": [
{
"key": "gear",
"name": "档位",
"hasValue": true,
"value": "D",
"rawValue": "3",
"updatedAt": 1786075799123
},
{
"key": "speed",
"name": "车速",
"hasValue": true,
"value": "52",
"rawValue": "52",
"updatedAt": 1786075799456
}
]
}
8.2 停用快照¶
8.3 顶层字段¶
| 字段 | 类型 | 必需 | 语义 |
|---|---|---|---|
version |
int |
是 | 快照协议版本,当前为 1 |
enabled |
boolean |
是 | 属性联动的启用状态 |
status |
string |
否 | 停用或状态说明 |
updatedAt |
long |
是 | 本次快照生成时间,Unix 毫秒 |
items |
array |
是 | 当前所有已配置项目的完整列表 |
8.4 项目字段¶
| 字段 | 类型 | 必需 | 语义 |
|---|---|---|---|
key |
string |
是 | 稳定且唯一的业务标识,客户端规则依赖该值 |
name |
string |
是 | 用户可见名称 |
hasValue |
boolean |
是 | 是否已经成功读取有效值 |
value |
JSON 值 | 是 | 经过 map 映射后的值 |
rawValue |
string |
是 | 映射前的原始字符串 |
updatedAt |
long |
是 | 该项目最后变化时间,未取到值时为 0 |
未取到值的项目:
value 通常为字符串,但映射 JSON 也可使其成为数字或布尔值。当前客户端最终通过 String.valueOf() 将其统一转成字符串。新配置应将 value 限制为标量值,不应在其中嵌套对象或数组。
9. 客户端宽松解析行为¶
当前客户端使用 JSONObject.opt*() 宽松解析:
- 缺少
enabled:按false处理; - 缺少
items:按空数组处理; key缺失或为空:丢弃该项目;name缺失或为空:使用key;value和rawValue:统一转成字符串;- 缺少
hasValue:根据updatedAt和值是否为空推断; - 额外未知字段:忽略;
- 顶层
version:当前未强制验证。
因此,JSON 格式适合通过“只追加新字段”的方式扩展,不适合直接修改已有字段的类型或语义。
10. 值处理与推送时机¶
10.1 属性匹配¶
属性项目按下列条件精确匹配:
匹配后的处理顺序:
- 提取
rawValue; - 通过
map[rawValue]查找映射值; - 未命中映射时使用原值;
- 与内存中上一个原值和映射值比较;
- 值未改变时不生成新快照。
10.2 推送策略¶
realtime=true:变化后立即生成并推送快照;realtime=false:300 毫秒内的多次变化合并为一次推送;- 自动化变量:事件监听之外,每 1 秒补查一次;
- 初始值未读取成功:前 60 秒每 2 秒重试,之后每 10 秒重试;
- 每次
onSnapshotChanged()都传输完整items数组,不是单属性增量。
10.3 AIDL 数据配置¶
EVCC 中“米德宝 AIDL 联动”和“旧版 AIDL 联动”共用同一份项目配置。顶层格式:
{
"version": 1,
"items": [
{
"key": "speed",
"name": "车速",
"propertyId": 557849089,
"areaId": 0,
"realtime": true
},
{
"key": "music_bpm",
"name": "音乐 BPM",
"variable": "bpm",
"map": {
"0": "未识别"
},
"realtime": true
}
]
}
每个项目必须满足:
key非空且在整份配置中唯一;客户端规则应依赖key,不要依赖显示名;name非空,用于界面显示;propertyId和variable必须且只能填写一个;propertyId可为 JSON 数字、十进制字符串或0x十六进制字符串;运行时统一解析为 32 位正整数,使用表单编辑新项目时会写成十进制数字,直接导入的原始 JSON 则会按原字段形式保存并导出;areaId仅用于属性,省略时为0;variable使用不带$的 EVCC 变量名,可读取自定义、时间、GPS、bpm和music_state;map可选,键按原始字符串精确匹配,值应使用字符串、数字、布尔或null等标量;realtime省略时为false。
配置至少需要一个有效项目。导入文件最大为 8 MiB;EVCC 会在校验后去除格式空白,去除后的有效配置最大为 5 MiB。支持带 UTF-8 BOM 的 JSON;校验失败时不会覆盖当前配置。导出文件写入:
“恢复默认”会按当前车型读取云端车型配置中的 AIDL 数据默认值;车型没有配置时使用 EVCC 内置示例。
11. 连接生命周期与恢复¶
客户端遇到以下事件时会清除当前 Bridge 并进入重连:
onServiceDisconnected();onBindingDied();onNullBinding();- 握手方法抛出异常。
当前重连间隔:
协议没有持久化消息队列或离线重放。断线期间的中间变化不会逐条补发;恢复连接后,客户端直接获得当前最新的全量状态。
12. 调用方鉴权¶
Service 是 exported=true,但每个主接口方法都在 EVCC 内部执行调用方验证。鉴权同时受总开关和子开关约束:
- “米德宝 AIDL 联动”只允许银河悬浮歌词及其受信任证书;
- “旧版 AIDL 联动”允许仪表 DIY、AutoDisplay,以及最多一个用户配置的额外调用方;
- 内置调用方各自绑定自己的证书,不共用一个全局签名白名单;
- 额外调用方必须同时配置合法包名和至少一个 SHA-256 证书摘要。
每次方法调用的验证流程:
Binder.getCallingUid()
-> 枚举 UID 对应包名
-> 按当前子开关查找该包名的允许证书
-> 读取目标 APK 当前签名或签名历史
-> 计算并规范化 SHA-256
-> 任一实际摘要命中该包名允许列表才放行
未通过验证时抛出 SecurityException。
鉴权实现:app/src/main/java/com/kooo/evcc/midebao/MidebaoLinkBridgeService.kt
银河悬浮歌词的允许摘要可通过车辆适配配置更新;自定义额外调用方可在 EVCC 界面配置。不要把具体证书摘要写进公开业务 payload 或当作协议字段传给客户端。
需要注意:
bindService()本身不执行这组鉴权;- 鉴权发生在每个 Binder 方法的入口;
- 未授权应用可能触发 Service 创建,但无法成功调用 Bridge 方法;
- 回调在完成鉴权的
registerCallback()之后才能加入回调列表。
13. 协议不提供的能力¶
当前 AIDL 不包含:
- 写入车辆属性;
- 调用 EVCC 自动化动作;
- 反向发送机器人指令;
- 属性元数据,如
propertyId、areaId、数据类型和访问权限; - 属性来源,如 VHAL、APVP 或 AIDL;
- 消息序号、ACK、重放、分片和压缩。
客户端收到快照后,可在本地根据 key 和 value 展示或评估规则,再由客户端自己的能力执行后续动作。这不是反向 AIDL 调用 EVCC。
14. 协议升级约束¶
本接口是普通应用 AIDL,不是 Android Stable AIDL,没有 @VintfStability 或系统级 ABI 稳定保证。
修改时必须遵守:
- 不得调整现有 AIDL 方法顺序;
- 不得删除或修改现有方法的参数和返回类型;
- 新方法只能追加到接口末尾;
- EVCC 和客户端必须同步同一份
.aidl; - 不兼容变更必须提升
PROTOCOL_VERSION; - JSON 新能力应优先通过追加可选字段实现;
- 保留
key/name/hasValue/value/rawValue/updatedAt的现有语义。
由于当前客户端只记录版本号而不拒绝未知版本,单纯将版本提升到 2 不能防止旧客户端错误解析。进行不兼容升级时,客户端应增加明确的版本范围检查。
15. 当前已知边界¶
15.1 属性配置不能指定来源¶
配置只有 propertyId 和 areaId,没有 source。填写某个属性 ID 不代表强制从车机 AIDL 读取。
初始主动读取优先级:
实时阶段同时监听五个来源。如果不同来源存在相同的 propertyId + areaId,任何来源都可能更新同一个快照项目。
读取逻辑:app/src/main/java/com/kooo/evcc/miniprogram/MiniProgramPropertyHelper.kt
15.2 没有快照序号¶
协议只有时间戳,没有单调递增的 sequence。客户端当前也不会根据顶层 updatedAt 丢弃过期快照。
由于首次注册时同时存在回调推送和主动拉取,若握手期间恰好发生属性更新,理论上可能短暂交错处理新旧快照。
后续可在顶层增加单调 sequence,客户端忽略不大于当前序号的快照。
15.3 协议版本未强制检查¶
getProtocolVersion() 和 JSON 中的 version 均存在,但当前客户端没有拒绝未知版本。它们目前主要用于日志和人工识别。
15.4 配置错误时 enabled 语义不严谨¶
当配置解析失败时,当前实现会:
- 停止属性监听并将内部
running设为false; - 通过状态回调发送“配置错误”;
- 但生成的 JSON 快照仍可能是
enabled: true。
因此客户端不应仅依赖 enabled 判断链路健康度。后续宜将配置错误快照改为 enabled: false,或增加稳定的机器可读 state/errorCode。
15.5 多种联动方式独立运行¶
“米德宝 BLE 直发联动”“米德宝 AIDL 联动”和“旧版 AIDL 联动”是三个独立子开关:
- BLE 直发和 AIDL 可以同时开启;
- 米德宝 AIDL 与自定义 AIDL 可以同时向不同受信任应用推送同一份快照;
- 关闭某个子开关只停止对应出口,不应被理解为清空公共 AIDL 数据配置;
- 总开关关闭时所有联动出口一起停止。
运行调度:app/src/main/java/com/kooo/evcc/midebao/MidebaoLinkageRuntime.kt
15.6 无分片与流量控制¶
快照使用单个 Binder String 传输,没有分片、压缩和背压。协议只适合少量车辆信号,不应用来传输大量历史数据或二进制内容。
16. 建议的 v2 改进方向¶
如果后续升级协议,建议优先考虑:
- 顶层增加单调递增的
sequence; - 增加机器可读的
state和errorCode; - 客户端明确校验支持的协议版本范围;
- 将首次同步收敛为一条明确路径,避免回调与主动拉取重复;
- 如需强制属性数据源,在 EVCC 配置端增加
source,但是否对外传输需单独评估; - 修正配置错误时的
enabled/ 运行状态语义; - 如项目数量显著增长,再评估增量快照或更紧凑的 Parcelable 协议。
17. 源码索引¶
| 内容 | 文件 |
|---|---|
| 主 AIDL | shared/src/main/aidl/com/kooo/evcc/midebao/IMidebaoLinkBridge.aidl |
| 回调 AIDL | shared/src/main/aidl/com/kooo/evcc/midebao/IMidebaoLinkCallback.aidl |
| Binder Service | app/src/main/java/com/kooo/evcc/midebao/MidebaoLinkBridgeService.kt |
| 快照生成与回调 | app/src/main/java/com/kooo/evcc/midebao/MidebaoLinkageManager.kt |
| 协议版本与项目配置 | app/src/main/java/com/kooo/evcc/midebao/MidebaoLinkageConfig.kt |
| 多出口运行调度 | app/src/main/java/com/kooo/evcc/midebao/MidebaoLinkageRuntime.kt |
| 多数据源属性读取 | app/src/main/java/com/kooo/evcc/miniprogram/MiniProgramPropertyHelper.kt |
| AIDL 配置管理、导入与导出 | app/src/main/java/com/kooo/evcc/midebao/MidebaoLinkageCardController.kt |
银河悬浮歌词参考实现位于 Galaxy-Robot-Midebao-Car-Machine-Linkage 仓库的: